diff --git a/README.md b/README.md index 14d04da..c1d8285 100644 --- a/README.md +++ b/README.md @@ -79,6 +79,11 @@ Configuration panel for system parameters and preferences: * **Reboot** the server directly from the UI. * Strictly concurrent protocol status polling — all supported protocols/services checked in parallel for immediate feedback. * **Asynchronous Processing**: Resilient, non-blocking background architecture prevents the UI panel from freezing, even if remote endpoints hang. +* **🔁 Cascade (double VPN)**: + * Route clients through an **entry** AmneziaWG/WireGuard server to an **exit** server (country-block bypass). + * Approach inspired by [ryderams/amneziawg-cascade](https://github.com/ryderams/amneziawg-cascade) (AWG2 entry→exit); applied via panel SSH — **no remote curl|bash** script download. + * Handshake wait + optional egress verify (curl --interface → ifconfig.me); exit return route persisted like ryderams vps2. + * Users keep using the **ENTRY** client config only — do not share the exit cascade peer. * **🧩 Marketplace & Templates**: * Market templates provide quick presets for installing and configuring supported protocols and services (including **Hysteria 2**). * Multi-protocol management lets you run and control multiple protocol instances on the same server. @@ -222,6 +227,12 @@ GitHub Actions workflows in `.github/workflows/`: ## 📋 Fix / changelog (this fork) +### v2.2.0 +* **Cascade (double VPN) restored** — stable entry→exit tunnel for AmneziaWG / WireGuard. +* Inspired by [ryderams/amneziawg-cascade](https://github.com/ryderams/amneziawg-cascade); logic runs over panel SSH (**no remote curl|bash**). +* Safety: require exit **handshake** when enabled; optional **egress verify** via cascade Address; subnet conflict check; exit `/32` return route with start.sh markers. +* UI on the server page with per-step settings (pin exit route, MSS clamp, verify egress, …). Defaults prefer **awg2**. + ### v2.1.0 * **NaiveProxy — stable**: production-ready install (Caddy + klzgrad/forwardproxy), share links always include `:443`. * **Client notes (important)**: @@ -253,7 +264,7 @@ GitHub Actions workflows in `.github/workflows/`: ### v1.6.0 * **3x-ui multi-panel**: register several 3x-ui servers in Settings; pick a panel and load VLESS inbounds over its API when creating users/invites (share link comes from 3x-ui). * **Docker / CI**: refreshed `Dockerfile` + compose, `.env.example`, CI checks, GHCR image workflow. -* **Cascade removed** for now (pending redesign — showed active while internet often did not work). +* **Cascade** was temporarily removed in this release (redesigned and restored in **v2.2.0**). * **WG/AWG backups**: ZIP export of client `.conf` + restore with loading feedback. * **API performance**: in-memory data cache, faster server check/stats. * **Share / guest**: one-tap “Copy key”. diff --git a/app.py b/app.py index 89577a1..bf3842b 100644 --- a/app.py +++ b/app.py @@ -46,6 +46,7 @@ from managers.awg_manager import AWGManager from managers.xray_manager import XrayManager from managers.wireguard_manager import WireGuardManager from managers.backup_manager import BackupManager +from managers.cascade_manager import CascadeManager, normalize_settings, DEFAULT_SETTINGS as CASCADE_DEFAULT_SETTINGS import telegram_bot as tg_bot # Configure logging @@ -101,7 +102,7 @@ else: application_path = os.path.dirname(__file__) DATA_FILE = os.path.join(application_path, 'data.json') # legacy JSON; used only for one-shot import / export -CURRENT_VERSION = "v2.1.0" +CURRENT_VERSION = "v2.2.0" RELEASES_REPO_URL = "https://git.evilfox.cc/test2/Amnezia-Web-Panel-main" RELEASES_API_LATEST = "https://git.evilfox.cc/api/v1/repos/test2/Amnezia-Web-Panel-main/releases/latest" BIN_DIR = os.environ.get('TUNNEL_BIN_DIR', os.path.join(application_path, 'bin')) @@ -1742,6 +1743,24 @@ class HysteriaSettingsRequest(BaseModel): email: Optional[str] = None renew_ssl: bool = False +class CascadeSetupRequest(BaseModel): + entry_protocol: str = 'awg2' + exit_server_id: Optional[int] = None + exit_protocol: str = 'awg2' + enabled: bool = True + # Per-step cascade tuning + pin_exit_route: Optional[bool] = True + remove_eth_masquerade: Optional[bool] = True + force_forward: Optional[bool] = True + mss_clamp: Optional[bool] = True + wait_handshake: Optional[bool] = True + handshake_timeout_sec: Optional[int] = 25 + allowed_ips: Optional[str] = '0.0.0.0/0' + keep_exit_dns: Optional[bool] = False + vpn_subnet_override: Optional[str] = '' + table_id: Optional[int] = 200 + rule_priority: Optional[int] = 100 + verify_egress: Optional[bool] = True class ProtocolRequest(BaseModel): protocol: str = 'awg' @@ -3231,6 +3250,239 @@ async def api_hysteria_update_settings(request: Request, server_id: int, req: Hy return JSONResponse({'error': str(e)}, status_code=500) +@app.post('/api/servers/{server_id}/cascade', tags=["Protocols"]) +async def api_cascade_get(request: Request, server_id: int): + """Return saved cascade settings and live tunnel status for this (entry) server.""" + if not _check_admin(request): + return JSONResponse({'error': 'Forbidden'}, status_code=403) + try: + data = await asyncio.to_thread(load_data) + if server_id >= len(data['servers']): + return JSONResponse({'error': 'Server not found'}, status_code=404) + server = data['servers'][server_id] + cascade = dict(server.get('cascade') or {}) + live = {'enabled': False, 'up': False} + entry_proto = cascade.get('entry_protocol') or '' + if cascade.get('enabled') and entry_proto and CascadeManager.is_wg_family(entry_proto): + def _status(): + ssh = get_ssh(server) + ssh.connect() + try: + return CascadeManager(ssh).status(entry_proto) + finally: + ssh.disconnect() + live = await asyncio.to_thread(_status) + return { + 'cascade': cascade, + 'live': live, + 'defaults': CASCADE_DEFAULT_SETTINGS, + 'servers': [ + { + 'id': i, + 'name': s.get('name') or s.get('host'), + 'host': s.get('host'), + 'protocols': list((s.get('protocols') or {}).keys()), + } + for i, s in enumerate(data['servers']) if i != server_id + ], + } + except Exception as e: + logger.exception("Error reading cascade status") + return JSONResponse({'error': str(e)}, status_code=500) + + +@app.post('/api/servers/{server_id}/cascade/setup', tags=["Protocols"]) +async def api_cascade_setup(request: Request, server_id: int, req: CascadeSetupRequest): + """Enable or disable double-VPN cascade: clients → this entry → exit server.""" + if not _check_admin(request): + return JSONResponse({'error': 'Forbidden'}, status_code=403) + if not CascadeManager.is_wg_family(req.entry_protocol): + return JSONResponse({'error': 'Cascade supports WireGuard / AmneziaWG only'}, status_code=400) + if req.enabled and not CascadeManager.is_wg_family(req.exit_protocol): + return JSONResponse({'error': 'Cascade supports WireGuard / AmneziaWG only'}, status_code=400) + + try: + data = await asyncio.to_thread(load_data) + if server_id >= len(data['servers']): + return JSONResponse({'error': 'Server not found'}, status_code=404) + entry = data['servers'][server_id] + settings = normalize_settings({ + 'pin_exit_route': req.pin_exit_route, + 'remove_eth_masquerade': req.remove_eth_masquerade, + 'force_forward': req.force_forward, + 'mss_clamp': req.mss_clamp, + 'wait_handshake': req.wait_handshake, + 'handshake_timeout_sec': req.handshake_timeout_sec, + 'allowed_ips': req.allowed_ips, + 'keep_exit_dns': req.keep_exit_dns, + 'vpn_subnet_override': req.vpn_subnet_override, + 'table_id': req.table_id, + 'rule_priority': req.rule_priority, + 'verify_egress': req.verify_egress, + }) + if not req.enabled: + def _disable(): + ssh = get_ssh(entry) + ssh.connect() + try: + return CascadeManager(ssh).disable( + req.entry_protocol, + settings=dict((entry.get('cascade') or {}).get('settings') or settings), + ) + finally: + ssh.disconnect() + + result = await asyncio.to_thread(_disable) + if result.get('status') == 'error': + return JSONResponse({'error': result.get('message', 'Failed to disable cascade')}, status_code=500) + prev = dict(entry.get('cascade') or {}) + entry['cascade'] = { + 'enabled': False, + 'entry_protocol': req.entry_protocol or prev.get('entry_protocol'), + 'exit_server_id': prev.get('exit_server_id'), + 'exit_protocol': prev.get('exit_protocol') or req.exit_protocol, + 'settings': settings, + 'updated_at': datetime.now().isoformat(timespec='seconds'), + } + async with DATA_LOCK: + await asyncio.to_thread(save_data, data) + return {'status': 'success', 'cascade': entry['cascade']} + + if req.exit_server_id is None: + return JSONResponse({'error': 'exit_server_id is required'}, status_code=400) + if req.exit_server_id < 0 or req.exit_server_id >= len(data['servers']): + return JSONResponse({'error': 'Exit server not found'}, status_code=404) + if req.exit_server_id == server_id: + return JSONResponse({'error': 'Entry and exit servers must be different'}, status_code=400) + + exit_srv = data['servers'][req.exit_server_id] + + def _enable(): + exit_ssh = get_ssh(exit_srv) + exit_ssh.connect() + try: + exit_mgr = get_protocol_manager(exit_ssh, req.exit_protocol) + clients = _manager_call(exit_mgr, 'get_clients', req.exit_protocol) or [] + cascade_name = f"cascade-from-{(entry.get('name') or entry.get('host') or 'entry')[:40]}" + cascade_name = re.sub(r'[^\w.\-]+', '_', cascade_name).strip('._') or 'cascade-entry' + existing = None + for c in clients: + ud = c.get('userData') or {} + if ud.get('clientName') == cascade_name or ud.get('cascade_entry_server_id') == server_id: + existing = c + break + + if existing: + client_id = existing.get('clientId') + conf = _manager_call( + exit_mgr, 'get_client_config', req.exit_protocol, + client_id, exit_srv['host'], + (exit_srv.get('protocols') or {}).get(req.exit_protocol, {}).get('port', '55424'), + ) + else: + add_res = _manager_call( + exit_mgr, 'add_client', req.exit_protocol, cascade_name, + exit_srv['host'], + (exit_srv.get('protocols') or {}).get(req.exit_protocol, {}).get('port', '55424'), + ) + if not add_res.get('client_id'): + raise RuntimeError(add_res.get('message') or 'Failed to create cascade peer on exit') + client_id = add_res['client_id'] + conf = add_res.get('config') + if not conf: + conf = _manager_call( + exit_mgr, 'get_client_config', req.exit_protocol, + client_id, exit_srv['host'], + (exit_srv.get('protocols') or {}).get(req.exit_protocol, {}).get('port', '55424'), + ) + try: + table = exit_mgr._get_clients_table(req.exit_protocol) if hasattr(exit_mgr, '_get_clients_table') else [] + for row in table: + if row.get('clientId') == client_id: + row.setdefault('userData', {})['cascade_entry_server_id'] = server_id + if hasattr(exit_mgr, '_save_clients_table'): + exit_mgr._save_clients_table(req.exit_protocol, table) + break + except Exception: + pass + finally: + exit_ssh.disconnect() + + entry_ssh = get_ssh(entry) + entry_ssh.connect() + try: + applied = CascadeManager(entry_ssh).apply( + req.entry_protocol, + conf, + exit_srv.get('host') or '', + settings=settings, + ) + finally: + entry_ssh.disconnect() + + if applied.get('status') != 'success': + raise RuntimeError(applied.get('message') or 'Failed to apply cascade on entry') + + if settings.get('wait_handshake') and not applied.get('handshake'): + try: + entry_ssh2 = get_ssh(entry) + entry_ssh2.connect() + try: + CascadeManager(entry_ssh2).disable(req.entry_protocol, settings=settings) + finally: + entry_ssh2.disconnect() + except Exception: + pass + raise RuntimeError( + applied.get('message') + or 'Cascade apply reported success but exit handshake is missing' + ) + + peer_ip = applied.get('cascade_peer_ip') or '' + if not peer_ip and conf: + peer_ip = CascadeManager._extract_address_ip(conf) + if peer_ip and hasattr(CascadeManager, 'ensure_exit_return_route'): + exit_ssh2 = get_ssh(exit_srv) + exit_ssh2.connect() + try: + route_res = CascadeManager(exit_ssh2).ensure_exit_return_route( + req.exit_protocol, peer_ip + ) + applied['exit_return_route'] = route_res + finally: + exit_ssh2.disconnect() + + return { + 'client_id': client_id, + 'client_name': cascade_name, + 'applied': applied, + } + + info = await asyncio.to_thread(_enable) + applied = info.get('applied') or {} + entry['cascade'] = { + 'enabled': True, + 'entry_protocol': req.entry_protocol, + 'exit_server_id': req.exit_server_id, + 'exit_protocol': req.exit_protocol, + 'exit_client_id': info['client_id'], + 'exit_client_name': info['client_name'], + 'settings': settings, + 'up': bool(applied.get('up')), + 'handshake': bool(applied.get('handshake')), + 'updated_at': datetime.now().isoformat(timespec='seconds'), + 'last_error': None, + 'diagnostics': applied.get('diagnostics') or '', + } + async with DATA_LOCK: + await asyncio.to_thread(save_data, data) + return {'status': 'success', 'cascade': entry['cascade'], 'applied': applied} + except Exception as e: + logger.exception("Error setting up cascade") + return JSONResponse({'error': str(e)}, status_code=500) + + + @app.post('/api/servers/{server_id}/uninstall', tags=["Protocols"]) async def api_uninstall_protocol(request: Request, server_id: int, req: ProtocolRequest): if not _check_admin(request): diff --git a/managers/cascade_manager.py b/managers/cascade_manager.py new file mode 100644 index 0000000..e5e3859 --- /dev/null +++ b/managers/cascade_manager.py @@ -0,0 +1,603 @@ +"""Cascade (double-VPN) between two panel-managed WireGuard/AWG servers. + +Traffic path: + Clients → Entry server (accessible) → Exit server (blocked / target) → Internet + +Implemented inside the entry Docker container as a second AWG/WG interface +(`cascade.conf`) with source-based routing for the VPN client subnet. + +Inspired by ryderams/amneziawg-cascade (AWG2 entry→exit), but executed via the +panel SSH session — no remote curl|bash script download. +""" + +from __future__ import annotations + +import ipaddress +import re +import shlex +from datetime import datetime +from typing import Optional + + +WG_FAMILY = {'awg', 'awg2', 'awg_legacy', 'wireguard'} +CASCADE_MARKER = '# amnezia-web-panel-cascade' +EXIT_ROUTE_BEGIN = '# BEGIN awg-cascade-vps2' +EXIT_ROUTE_END = '# END awg-cascade-vps2' + +DEFAULT_SETTINGS = { + 'pin_exit_route': True, + 'remove_eth_masquerade': True, + 'force_forward': True, + 'mss_clamp': True, + 'wait_handshake': True, + 'handshake_timeout_sec': 25, + 'allowed_ips': '0.0.0.0/0', + 'keep_exit_dns': False, + 'vpn_subnet_override': '', + 'table_id': 200, + 'rule_priority': 100, + 'verify_egress': True, +} + + +def normalize_settings(raw: Optional[dict]) -> dict: + settings = dict(DEFAULT_SETTINGS) + if isinstance(raw, dict): + for key, default in DEFAULT_SETTINGS.items(): + if key not in raw or raw[key] is None: + continue + if isinstance(default, bool): + settings[key] = bool(raw[key]) + elif isinstance(default, int): + try: + settings[key] = int(raw[key]) + except (TypeError, ValueError): + settings[key] = default + else: + settings[key] = str(raw[key]).strip() + settings['handshake_timeout_sec'] = max(5, min(120, int(settings['handshake_timeout_sec']))) + settings['table_id'] = max(1, min(252, int(settings['table_id']))) + settings['rule_priority'] = max(1, min(32765, int(settings['rule_priority']))) + if not settings.get('allowed_ips'): + settings['allowed_ips'] = DEFAULT_SETTINGS['allowed_ips'] + return settings + + +class CascadeManager: + def __init__(self, ssh_manager): + self.ssh = ssh_manager + + @staticmethod + def proto_base(protocol: str) -> str: + return str(protocol or '').split('__', 1)[0] + + @staticmethod + def is_wg_family(protocol: str) -> bool: + return CascadeManager.proto_base(protocol) in WG_FAMILY + + @staticmethod + def _container_name(protocol: str) -> str: + base = CascadeManager.proto_base(protocol) + match = re.search(r'__(\d+)$', str(protocol or '')) + idx = int(match.group(1)) if match else 1 + names = { + 'awg': 'amnezia-awg', + 'awg2': 'amnezia-awg2', + 'awg_legacy': 'amnezia-awg-legacy', + 'wireguard': 'amnezia-wireguard', + } + name = names.get(base) + if not name: + return '' + return name if idx <= 1 else f'{name}-{idx}' + + @staticmethod + def _wg_binary(protocol: str) -> str: + return 'wg' if CascadeManager.proto_base(protocol) in ('awg_legacy', 'wireguard') else 'awg' + + @staticmethod + def _quick_binary(protocol: str) -> str: + return 'wg-quick' if CascadeManager.proto_base(protocol) in ('awg_legacy', 'wireguard') else 'awg-quick' + + @staticmethod + def _conf_dir(protocol: str) -> str: + return '/opt/amnezia/wireguard' if CascadeManager.proto_base(protocol) == 'wireguard' else '/opt/amnezia/awg' + + @classmethod + def _cascade_conf(cls, protocol: str) -> str: + return f'{cls._conf_dir(protocol)}/cascade.conf' + + @classmethod + def _cascade_up(cls, protocol: str) -> str: + return f'{cls._conf_dir(protocol)}/cascade-up.sh' + + @staticmethod + def _server_conf(protocol: str) -> str: + base = CascadeManager.proto_base(protocol) + if base == 'wireguard': + return '/opt/amnezia/wireguard/wg0.conf' + if base == 'awg_legacy': + return '/opt/amnezia/awg/wg0.conf' + return '/opt/amnezia/awg/awg0.conf' + + @staticmethod + def _server_iface(protocol: str) -> str: + base = CascadeManager.proto_base(protocol) + if base in ('wireguard', 'awg_legacy'): + return 'wg0' + return 'awg0' + + @staticmethod + def _extract_endpoint_host(config_text: str) -> str: + for line in (config_text or '').splitlines(): + if line.strip().lower().startswith('endpoint'): + rhs = line.split('=', 1)[-1].strip() + return rhs.rsplit(':', 1)[0].strip().strip('[]') + return '' + + @staticmethod + def _extract_address_cidr(config_text: str) -> str: + for line in (config_text or '').splitlines(): + stripped = line.strip() + if stripped.lower().startswith('address'): + rhs = stripped.split('=', 1)[-1].strip() + return rhs.split(',')[0].strip() + return '' + + @staticmethod + def _extract_address_ip(config_text: str) -> str: + cidr = CascadeManager._extract_address_cidr(config_text) + if not cidr: + return '' + return cidr.split('/', 1)[0].strip() + + @staticmethod + def prepare_exit_client_config( + raw_config: str, + endpoint_host: str, + *, + keep_dns: bool = False, + allowed_ips: str = '0.0.0.0/0', + ) -> str: + """Adapt a normal client config for use as an entry→exit cascade tunnel.""" + effective_allowed = (allowed_ips or '').strip() or '0.0.0.0/0' + if effective_allowed in ('0.0.0.0/0, ::/0', '::/0, 0.0.0.0/0'): + effective_allowed = '0.0.0.0/0' + + lines = [] + in_interface = False + in_peer = False + has_table = False + has_mtu = False + for raw in (raw_config or '').splitlines(): + stripped = raw.strip() + lower = stripped.lower() + if stripped.startswith('[') and stripped.endswith(']'): + in_interface = stripped.lower() == '[interface]' + in_peer = stripped.lower() == '[peer]' + lines.append(stripped) + continue + if in_interface and lower.startswith('dns') and not keep_dns: + continue + if in_interface and lower.startswith('table'): + has_table = True + lines.append('Table = off') + continue + if in_interface and lower.startswith('mtu'): + has_mtu = True + lines.append(stripped) + continue + if lower.startswith('endpoint'): + port = '51820' + if '=' in stripped: + rhs = stripped.split('=', 1)[1].strip() + if ':' in rhs: + port = rhs.rsplit(':', 1)[-1] + host = (endpoint_host or '').strip() + if host: + lines.append(f'Endpoint = {host}:{port}') + else: + lines.append(stripped) + continue + if in_peer and lower.startswith('allowedips'): + lines.append(f'AllowedIPs = {effective_allowed}') + continue + lines.append(stripped) + + out = [] + inserted_iface = False + for line in lines: + out.append(line) + if not inserted_iface and line.strip().lower() == '[interface]': + if not has_table: + out.append('Table = off') + if not has_mtu: + out.append('MTU = 1280') + inserted_iface = True + return '\n'.join(out).strip() + '\n' + + @staticmethod + def _subnet_contains_ip(subnet: str, ip: str) -> bool: + try: + net = ipaddress.ip_network(subnet, strict=False) + return ipaddress.ip_address(ip) in net + except ValueError: + return False + + def status(self, entry_protocol: str) -> dict: + container = self._container_name(entry_protocol) + if not container: + return {'enabled': False, 'up': False, 'handshake': False, 'error': 'Unsupported protocol'} + conf = self._cascade_conf(entry_protocol) + wg_bin = self._wg_binary(entry_protocol) + out, _, _ = self.ssh.run_sudo_command( + f"docker exec {shlex.quote(container)} bash -lc " + f"'echo HAS=$(test -f {shlex.quote(conf)} && echo 1 || echo 0); " + f"echo IFACES=$({wg_bin} show interfaces 2>/dev/null); " + f"echo HANDSHAKE=$({wg_bin} show cascade latest-handshakes 2>/dev/null | awk \"{{print \\$2}}\" | head -1); " + f"echo TRANSFER=$({wg_bin} show cascade transfer 2>/dev/null | head -1); " + f"ip rule show 2>/dev/null | head -20; " + f"ip route show table 200 2>/dev/null | head -10'" + ) + text = out or '' + has_conf = 'HAS=1' in text + ifaces_line = '' + handshake_ts = 0 + for line in text.splitlines(): + if line.startswith('IFACES='): + ifaces_line = line.split('=', 1)[1].strip() + if line.startswith('HANDSHAKE='): + try: + handshake_ts = int(line.split('=', 1)[1].strip() or '0') + except ValueError: + handshake_ts = 0 + up = 'cascade' in ifaces_line.split() + handshake_ok = handshake_ts > 0 + return { + 'enabled': has_conf, + 'up': up, + 'handshake': handshake_ok, + 'handshake_ts': handshake_ts, + 'raw': text.strip()[:3000], + } + + def _vpn_subnet(self, entry_protocol: str, container: str, override: str = '') -> str: + if override and re.match(r'^\d+\.\d+\.\d+\.\d+/\d+$', override.strip()): + return override.strip() + conf = self._server_conf(entry_protocol) + out, _, _ = self.ssh.run_sudo_command( + f"docker exec {shlex.quote(container)} bash -lc " + f"\"grep -E '^Address' {shlex.quote(conf)} | head -1 | cut -d= -f2 | tr -d ' '\"" + ) + addr = (out or '').strip() + if not addr: + return '10.8.1.0/24' + if '/' in addr: + ip, cidr = addr.split('/', 1) + parts = ip.split('.') + if len(parts) == 4 and cidr.isdigit() and int(cidr) >= 24: + return f"{parts[0]}.{parts[1]}.{parts[2]}.0/{cidr}" + return addr + parts = addr.split('.') + if len(parts) == 4: + return f"{parts[0]}.{parts[1]}.{parts[2]}.0/24" + return '10.8.1.0/24' + + def ensure_exit_return_route(self, protocol: str, peer_ip: str) -> dict: + """Add /32 return route for the cascade peer on the exit container (ryderams vps2).""" + peer_ip = (peer_ip or '').strip().split('/', 1)[0] + if not re.match(r'^\d+\.\d+\.\d+\.\d+$', peer_ip): + return {'status': 'error', 'message': 'Invalid cascade peer IP'} + container = self._container_name(protocol) + if not container: + return {'status': 'error', 'message': 'Unsupported exit protocol'} + iface = self._server_iface(protocol) + route_cmd = f'ip route replace {peer_ip}/32 dev {iface}' + script = f"""#!/bin/bash +set -e +START=/opt/amnezia/start.sh +{route_cmd} 2>/dev/null || true +[ -f "$START" ] || {{ echo CASCADE_EXIT_ROUTE_OK; exit 0; }} +if grep -q '{EXIT_ROUTE_BEGIN}' "$START" 2>/dev/null; then + tmp=$(mktemp) + awk -v b='{EXIT_ROUTE_BEGIN}' -v e='{EXIT_ROUTE_END}' ' + $0==b {{skip=1; next}} + $0==e {{skip=0; next}} + !skip {{print}} + ' "$START" > "$tmp" && mv "$tmp" "$START" +fi +printf '\\n%s\\n%s\\n%s\\n' '{EXIT_ROUTE_BEGIN}' '{route_cmd} || true' '{EXIT_ROUTE_END}' >> "$START" +echo CASCADE_EXIT_ROUTE_OK +""" + self.ssh.upload_file(script, '/tmp/_amnz_cascade_exit_route.sh') + out, err, code = self.ssh.run_sudo_command( + f"docker cp /tmp/_amnz_cascade_exit_route.sh {shlex.quote(container)}:/tmp/_exit_route.sh && " + f"docker exec {shlex.quote(container)} bash /tmp/_exit_route.sh && " + f"docker exec {shlex.quote(container)} rm -f /tmp/_exit_route.sh", + timeout=60, + ) + self.ssh.run_command('rm -f /tmp/_amnz_cascade_exit_route.sh') + if code != 0 or 'CASCADE_EXIT_ROUTE_OK' not in (out or ''): + return { + 'status': 'error', + 'message': ((err or out) or 'Failed to set exit return route').strip()[:400], + } + return {'status': 'success', 'peer_ip': peer_ip, 'iface': iface} + + def apply( + self, + entry_protocol: str, + exit_client_config: str, + exit_host: str, + settings: Optional[dict] = None, + ) -> dict: + opts = normalize_settings(settings) + container = self._container_name(entry_protocol) + if not container: + return {'status': 'error', 'message': 'Unsupported entry protocol'} + + exists, _, code = self.ssh.run_sudo_command( + f"docker inspect -f '{{{{.State.Running}}}}' {shlex.quote(container)} 2>/dev/null" + ) + if code != 0 or exists.strip().lower() != 'true': + return {'status': 'error', 'message': f'Entry container {container} is not running'} + + cascade_conf_path = self._cascade_conf(entry_protocol) + cascade_up_path = self._cascade_up(entry_protocol) + cascade_conf = self.prepare_exit_client_config( + exit_client_config, + exit_host, + keep_dns=bool(opts['keep_exit_dns']), + allowed_ips=str(opts['allowed_ips']), + ) + endpoint_host = self._extract_endpoint_host(cascade_conf) or exit_host + cascade_addr_cidr = self._extract_address_cidr(cascade_conf) + cascade_peer_ip = self._extract_address_ip(cascade_conf) + subnet = self._vpn_subnet(entry_protocol, container, opts.get('vpn_subnet_override') or '') + + if cascade_peer_ip and self._subnet_contains_ip(subnet, cascade_peer_ip): + return { + 'status': 'error', + 'message': ( + f'Cascade tunnel Address {cascade_addr_cidr or cascade_peer_ip} is inside ' + f'entry client subnet {subnet}. Use a different VPN subnet on the exit server ' + f'(e.g. 10.99.0.0/24) as in ryderams/amneziawg-cascade.' + ), + } + + wg_bin = self._wg_binary(entry_protocol) + quick_bin = self._quick_binary(entry_protocol) + conf_dir = self._conf_dir(entry_protocol) + table_id = int(opts['table_id']) + prio = int(opts['rule_priority']) + hs_timeout = int(opts['handshake_timeout_sec']) + + pin_block = '' + if opts['pin_exit_route']: + pin_block = f""" +EXIT_IP="$EXIT_HOST" +if ! printf '%s' "$EXIT_IP" | grep -Eq '^[0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+$'; then + EXIT_IP=$(getent ahostsv4 "$EXIT_HOST" 2>/dev/null | awk '{{print $1; exit}}') + if [ -z "$EXIT_IP" ]; then + EXIT_IP=$(python3 - <<'PY' 2>/dev/null || true +import socket +print(socket.gethostbyname("{endpoint_host}")) +PY +) + fi +fi +GW=$(ip route | awk '/default/ {{print $3; exit}}') +DEV=$(ip route | awk '/default/ {{print $5; exit}}') +if [ -n "$EXIT_IP" ] && [ -n "$GW" ] && [ -n "$DEV" ]; then + ip route replace "$EXIT_IP/32" via "$GW" dev "$DEV" 2>/dev/null || true +fi +""" + + remove_masq = '' + if opts['remove_eth_masquerade']: + remove_masq = f""" +for ETH in eth0 eth1 eth2 ens3 ens5 enp0s3 enp1s0; do + while iptables -t nat -D POSTROUTING -s "$SUBNET" -o "$ETH" -j MASQUERADE 2>/dev/null; do :; done +done +""" + + forward_block = '' + if opts['force_forward']: + forward_block = f""" +sysctl -w net.ipv4.ip_forward=1 >/dev/null 2>&1 || true +for SRC_IF in awg0 wg0; do + iptables -C FORWARD -i "$SRC_IF" -o "$IFACE" -j ACCEPT 2>/dev/null \\ + || iptables -I FORWARD 1 -i "$SRC_IF" -o "$IFACE" -j ACCEPT 2>/dev/null || true + iptables -C FORWARD -i "$IFACE" -o "$SRC_IF" -j ACCEPT 2>/dev/null \\ + || iptables -I FORWARD 1 -i "$IFACE" -o "$SRC_IF" -j ACCEPT 2>/dev/null || true +done +iptables -C FORWARD -i "$IFACE" -m state --state RELATED,ESTABLISHED -j ACCEPT 2>/dev/null \\ + || iptables -I FORWARD 1 -i "$IFACE" -m state --state RELATED,ESTABLISHED -j ACCEPT +""" + + mss_block = '' + if opts['mss_clamp']: + mss_block = """ +iptables -t mangle -C FORWARD -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu 2>/dev/null \\ + || iptables -t mangle -A FORWARD -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu +""" + + handshake_block = '' + if opts['wait_handshake']: + handshake_block = f""" +OK=0 +for i in $(seq 1 {hs_timeout}); do + HS=$({shlex.quote(wg_bin)} show "$IFACE" latest-handshakes 2>/dev/null | awk '{{print $2}}' | head -1) + if [ -n "$HS" ] && [ "$HS" != "0" ]; then OK=1; break; fi + sleep 1 +done +if [ "$OK" != "1" ]; then + echo "CASCADE_NO_HANDSHAKE" >&2 + echo "Tunnel interface is up but exit peer did not handshake. Check exit server / UDP port / keys." >&2 + exit 42 +fi +""" + + up_script = f"""#!/bin/bash +{CASCADE_MARKER} +set -e +CONF={shlex.quote(cascade_conf_path)} +QUICK={shlex.quote(quick_bin)} +SUBNET={shlex.quote(subnet)} +EXIT_HOST={shlex.quote(endpoint_host)} +IFACE=cascade +TABLE={table_id} +PRIO={prio} + +"$QUICK" down "$CONF" 2>/dev/null || true +ip link delete "$IFACE" 2>/dev/null || true +ip rule del from "$SUBNET" table "$TABLE" 2>/dev/null || true +ip route flush table "$TABLE" 2>/dev/null || true + +{pin_block} + +"$QUICK" up "$CONF" + +ip route replace default dev "$IFACE" table "$TABLE" 2>/dev/null || ip route add default dev "$IFACE" table "$TABLE" +ip rule del from "$SUBNET" table "$TABLE" 2>/dev/null || true +ip rule add from "$SUBNET" table "$TABLE" priority "$PRIO" + +{remove_masq} +{forward_block} +iptables -t nat -C POSTROUTING -s "$SUBNET" -o "$IFACE" -j MASQUERADE 2>/dev/null \\ + || iptables -t nat -A POSTROUTING -s "$SUBNET" -o "$IFACE" -j MASQUERADE +{mss_block} +{handshake_block} + +echo CASCADE_UP_OK +""" + + self.ssh.run_sudo_command(f"docker exec {shlex.quote(container)} mkdir -p {shlex.quote(conf_dir)}") + self.ssh.upload_file(cascade_conf, '/tmp/_amnz_cascade.conf') + self.ssh.upload_file(up_script, '/tmp/_amnz_cascade_up.sh') + self.ssh.run_sudo_command( + f"docker cp /tmp/_amnz_cascade.conf {shlex.quote(container)}:{shlex.quote(cascade_conf_path)} && " + f"docker cp /tmp/_amnz_cascade_up.sh {shlex.quote(container)}:{shlex.quote(cascade_up_path)} && " + f"docker exec {shlex.quote(container)} chmod 644 {shlex.quote(cascade_conf_path)} && " + f"docker exec {shlex.quote(container)} chmod +x {shlex.quote(cascade_up_path)}" + ) + self.ssh.run_command('rm -f /tmp/_amnz_cascade.conf /tmp/_amnz_cascade_up.sh') + + self._ensure_start_hook(container, cascade_up_path) + + out, err, code = self.ssh.run_sudo_command( + f"docker exec {shlex.quote(container)} bash {shlex.quote(cascade_up_path)}", + timeout=max(90, hs_timeout + 40), + ) + combined = ((out or '') + '\n' + (err or '')).strip() + if code != 0 or 'CASCADE_UP_OK' not in (out or ''): + msg = combined + if 'CASCADE_NO_HANDSHAKE' in combined: + msg = ( + 'Туннель к серверу выхода поднят, но handshake не прошёл. ' + 'Проверьте, что на выходе открыт UDP-порт, протокол совпадает, ' + 'и входной сервер может достучаться до IP выхода.' + ) + return { + 'status': 'error', + 'message': (msg or 'Failed to bring cascade interface up').strip()[:900], + 'log': combined[:2000], + } + + st = self.status(entry_protocol) + handshake_ok = bool(st.get('handshake')) + + if opts.get('verify_egress', True) and handshake_ok and cascade_peer_ip: + curl_cmd = ( + f"curl --interface {shlex.quote(cascade_peer_ip)} -4 -s --max-time 12 " + f"https://ifconfig.me || true" + ) + egress_out, egress_err, _egress_code = self.ssh.run_sudo_command( + f"docker exec {shlex.quote(container)} bash -lc {shlex.quote(curl_cmd)}", + timeout=30, + ) + egress_ip = (egress_out or '').strip().splitlines()[-1].strip() if (egress_out or '').strip() else '' + if not egress_ip or not re.match(r'^\d+\.\d+\.\d+\.\d+$', egress_ip): + try: + self.disable(entry_protocol, settings=opts) + except Exception: + pass + detail = ((egress_err or egress_out) or 'empty response').strip()[:400] + return { + 'status': 'error', + 'message': ( + 'Cascade handshake OK but egress verify failed ' + f'(curl --interface {cascade_peer_ip} → ifconfig.me). ' + f'Detail: {detail}' + ), + 'handshake': True, + 'up': bool(st.get('up')), + 'log': combined[:1500], + } + + return { + 'status': 'success', + 'subnet': subnet, + 'endpoint': endpoint_host, + 'container': container, + 'cascade_peer_ip': cascade_peer_ip, + 'cascade_address': cascade_addr_cidr, + 'up': bool(st.get('up')), + 'handshake': handshake_ok, + 'settings': opts, + 'applied_at': datetime.now().isoformat(timespec='seconds'), + 'diagnostics': st.get('raw', '')[:1500], + } + + def _ensure_start_hook(self, container: str, cascade_up_path: str) -> None: + marker = CASCADE_MARKER + installer = f"""#!/bin/bash +set -e +START=/opt/amnezia/start.sh +[ -f "$START" ] || exit 0 +grep -q '{marker}' "$START" 2>/dev/null && exit 0 +printf '\\n%s\\n' '{marker}' >> "$START" +printf '%s\\n' 'if [ -x {cascade_up_path} ]; then bash {cascade_up_path} || true; fi' >> "$START" +""" + self.ssh.upload_file(installer, '/tmp/_amnz_cascade_hook.sh') + self.ssh.run_sudo_command( + f"docker cp /tmp/_amnz_cascade_hook.sh {shlex.quote(container)}:/tmp/_hook.sh && " + f"docker exec {shlex.quote(container)} bash /tmp/_hook.sh && " + f"docker exec {shlex.quote(container)} rm -f /tmp/_hook.sh" + ) + self.ssh.run_command('rm -f /tmp/_amnz_cascade_hook.sh') + + def disable(self, entry_protocol: str, settings: Optional[dict] = None) -> dict: + opts = normalize_settings(settings) + container = self._container_name(entry_protocol) + if not container: + return {'status': 'error', 'message': 'Unsupported entry protocol'} + conf = self._cascade_conf(entry_protocol) + up = self._cascade_up(entry_protocol) + quick_bin = self._quick_binary(entry_protocol) + table_id = int(opts['table_id']) + script = f"""#!/bin/bash +set +e +QUICK={shlex.quote(quick_bin)} +CONF={shlex.quote(conf)} +UP={shlex.quote(up)} +TABLE={table_id} +"$QUICK" down "$CONF" 2>/dev/null +ip link delete cascade 2>/dev/null +ip rule del table "$TABLE" 2>/dev/null +ip rule del table "$TABLE" 2>/dev/null +ip route flush table "$TABLE" 2>/dev/null +rm -f "$CONF" "$UP" +echo CASCADE_DOWN_OK +""" + self.ssh.upload_file(script, '/tmp/_amnz_cascade_down.sh') + out, err, code = self.ssh.run_sudo_command( + f"docker cp /tmp/_amnz_cascade_down.sh {shlex.quote(container)}:/tmp/_cascade_down.sh && " + f"docker exec {shlex.quote(container)} bash /tmp/_cascade_down.sh && " + f"docker exec {shlex.quote(container)} rm -f /tmp/_cascade_down.sh", + timeout=60, + ) + self.ssh.run_command('rm -f /tmp/_amnz_cascade_down.sh') + if code != 0 and 'CASCADE_DOWN_OK' not in (out or ''): + return {'status': 'success', 'message': (err or out or 'Cascade cleared (best effort)').strip()[:400]} + return {'status': 'success'} diff --git a/templates/server.html b/templates/server.html index ec2bca1..7da581b 100644 --- a/templates/server.html +++ b/templates/server.html @@ -1,4 +1,4 @@ -{% extends "base.html" %} +{% extends "base.html" %} {% from "macros/icons.html" import icon %} {% block title_extra %} — {{ server.name }}{% endblock %} @@ -157,6 +157,153 @@ + +
+
+
+

{{ _('cascade_title') }}

+

{{ _('cascade_desc') }}

+
+ {{ _('cascade_off') }} +
+
+
+
+ + +
+
+ + +
+
+ + +
+
+
{{ _('cascade_hint') }}
+
{{ _('cascade_client_note') }}
+
{{ _('cascade_security_note') }}
+ +
+ {{ _('cascade_settings_title') }} +

{{ _('cascade_settings_desc') }}

+
+ + + + + + + +
+
+
+ + +
+
+ + +
+
+ + +
+
+
+
+ + + {{ _('cascade_opt_allowed_ips_hint') }} +
+
+ + + {{ _('cascade_opt_vpn_subnet_hint') }} +
+
+
+ + + +
+ + + +
+ +
+
@@ -1429,6 +1576,7 @@ } updateStatusCounts(data.protocols || {}); showToast(_('server_check_complete'), 'success'); + loadCascade(); } catch (err) { showToast(_('error') + ': ' + err.message, 'error'); const statusDiv = document.getElementById('serverStatus'); @@ -2965,8 +3113,182 @@ } + + // ========== Cascade (double VPN) ========== + const CASCADE_PROTOS = ['awg2', 'awg', 'awg_legacy', 'wireguard']; + + function fillCascadeExitProtocols() { + const sel = document.getElementById('cascadeExitServer'); + const opt = sel.options[sel.selectedIndex]; + const protoSel = document.getElementById('cascadeExitProtocol'); + const raw = (opt && opt.dataset.protocols) ? opt.dataset.protocols.split(',').filter(Boolean) : []; + const available = raw.filter(p => CASCADE_PROTOS.includes(protoBase(p))); + const prev = protoSel.value; + protoSel.innerHTML = ''; + const list = available.length ? available : CASCADE_PROTOS; + list.forEach(p => { + const o = document.createElement('option'); + o.value = p; + o.textContent = getProtoTitle(p); + protoSel.appendChild(o); + }); + if (list.includes(prev)) protoSel.value = prev; + else if (list.includes('awg2')) protoSel.value = 'awg2'; + } + + function setCascadeBusy(on, text) { + const busy = document.getElementById('cascadeBusy'); + const busyText = document.getElementById('cascadeBusyText'); + const en = document.getElementById('cascadeEnableBtn'); + const dis = document.getElementById('cascadeDisableBtn'); + if (busyText && text) busyText.textContent = text; + if (busy) { + busy.classList.toggle('hidden', !on); + busy.style.display = on ? 'flex' : 'none'; + } + if (en) en.disabled = !!on; + if (dis) dis.disabled = !!on; + } + + function cascadeBool(id, fallback) { + const el = document.getElementById(id); + return el ? !!el.checked : !!fallback; + } + + function cascadeNum(id, fallback) { + const el = document.getElementById(id); + const n = el ? parseInt(el.value, 10) : NaN; + return Number.isFinite(n) ? n : fallback; + } + + function collectCascadeSettings() { + return { + pin_exit_route: cascadeBool('cascadePinExitRoute', true), + remove_eth_masquerade: cascadeBool('cascadeRemoveEthMasq', true), + force_forward: cascadeBool('cascadeForceForward', true), + mss_clamp: cascadeBool('cascadeMssClamp', true), + wait_handshake: cascadeBool('cascadeWaitHandshake', true), + verify_egress: cascadeBool('cascadeVerifyEgress', true), + keep_exit_dns: cascadeBool('cascadeKeepExitDns', false), + handshake_timeout_sec: cascadeNum('cascadeHandshakeTimeout', 25), + table_id: cascadeNum('cascadeTableId', 200), + rule_priority: cascadeNum('cascadeRulePriority', 100), + allowed_ips: (document.getElementById('cascadeAllowedIps') || {}).value || '0.0.0.0/0', + vpn_subnet_override: (document.getElementById('cascadeVpnSubnet') || {}).value || '', + }; + } + + function applyCascadeSettingsToForm(settings, defaults) { + const s = Object.assign({}, defaults || {}, settings || {}); + const setCheck = (id, val) => { const el = document.getElementById(id); if (el) el.checked = !!val; }; + const setVal = (id, val) => { const el = document.getElementById(id); if (el && val !== undefined && val !== null) el.value = val; }; + setCheck('cascadePinExitRoute', s.pin_exit_route !== false); + setCheck('cascadeRemoveEthMasq', s.remove_eth_masquerade !== false); + setCheck('cascadeForceForward', s.force_forward !== false); + setCheck('cascadeMssClamp', s.mss_clamp !== false); + setCheck('cascadeWaitHandshake', s.wait_handshake !== false); + setCheck('cascadeVerifyEgress', s.verify_egress !== false); + setCheck('cascadeKeepExitDns', !!s.keep_exit_dns); + setVal('cascadeHandshakeTimeout', s.handshake_timeout_sec ?? 25); + setVal('cascadeTableId', s.table_id ?? 200); + setVal('cascadeRulePriority', s.rule_priority ?? 100); + setVal('cascadeAllowedIps', s.allowed_ips || '0.0.0.0/0'); + setVal('cascadeVpnSubnet', s.vpn_subnet_override || ''); + } + + function updateCascadeDiag(cascade, live) { + const box = document.getElementById('cascadeDiagBox'); + if (!box) return; + const parts = []; + if (cascade && cascade.enabled) { + parts.push('handshake: ' + ((live && live.handshake) || cascade.handshake ? 'ok' : 'NONE')); + if (cascade.diagnostics) parts.push(String(cascade.diagnostics).trim()); + else if (live && live.raw) parts.push(String(live.raw).trim()); + if (cascade.last_error) parts.push('error: ' + cascade.last_error); + } + const text = parts.filter(Boolean).join('\n\n'); + box.textContent = text; + box.classList.toggle('hidden', !text); + } + + function updateCascadeBadge(cascade, live) { + const badge = document.getElementById('cascadeStatusBadge'); + if (!badge) return; + const enabled = !!(cascade && cascade.enabled); + const up = !!(live && live.up); + const hs = !!(live && live.handshake) || !!(cascade && cascade.handshake); + badge.className = 'badge ' + (enabled && up && hs ? 'badge-success' : (enabled ? 'badge-warn' : 'badge-warn')); + if (!enabled) badge.textContent = _('cascade_off'); + else if (up && hs) badge.textContent = _('cascade_on'); + else if (up && !hs) badge.textContent = _('cascade_no_handshake'); + else badge.textContent = _('cascade_configured_down'); + updateCascadeDiag(cascade, live); + } + + async function loadCascade() { + try { + const res = await apiCall(`/api/servers/${SERVER_ID}/cascade`, 'POST', {}); + const c = res.cascade || {}; + if (c.entry_protocol) document.getElementById('cascadeEntryProtocol').value = c.entry_protocol; + if (c.exit_server_id !== undefined && c.exit_server_id !== null && c.exit_server_id !== '') { + document.getElementById('cascadeExitServer').value = String(c.exit_server_id); + fillCascadeExitProtocols(); + } + if (c.exit_protocol) { + const exitProto = document.getElementById('cascadeExitProtocol'); + if ([...exitProto.options].some(o => o.value === c.exit_protocol)) { + exitProto.value = c.exit_protocol; + } + } + applyCascadeSettingsToForm(c.settings || {}, res.defaults || {}); + updateCascadeBadge(c, res.live || {}); + } catch (err) { + console.error(err); + } + } + + async function setupCascade(enable) { + const entryProtocol = document.getElementById('cascadeEntryProtocol').value; + const exitServerId = document.getElementById('cascadeExitServer').value; + const exitProtocol = document.getElementById('cascadeExitProtocol').value; + if (enable && exitServerId === '') { + showToast(_('cascade_exit_required'), 'error'); + return; + } + if (enable && !confirm(_('cascade_enable_confirm'))) return; + if (!enable && !confirm(_('cascade_disable_confirm'))) return; + + setCascadeBusy(true, enable ? _('cascade_working') : _('cascade_disabling')); + showToast(enable ? _('cascade_working') : _('cascade_disabling'), 'info'); + try { + const settings = collectCascadeSettings(); + const res = await apiCall(`/api/servers/${SERVER_ID}/cascade/setup`, 'POST', { + enabled: !!enable, + entry_protocol: entryProtocol, + exit_server_id: parseInt(exitServerId || '0', 10), + exit_protocol: exitProtocol, + ...settings, + }); + updateCascadeBadge(res.cascade || {}, { + up: !!(res.cascade && res.cascade.up), + handshake: !!(res.cascade && res.cascade.handshake), + }); + showToast(enable ? _('cascade_enabled') : _('cascade_disabled'), 'success'); + await loadCascade(); + } catch (err) { + showToast(_('error') + ': ' + err.message, 'error'); + } finally { + setCascadeBusy(false); + } + } + + function enableCascade() { return setupCascade(true); } + function disableCascade() { return setupCascade(false); } + // ========== Init ========== applyInstalledAppsVisibility(); + fillCascadeExitProtocols(); + loadCascade(); checkServer(); loadServerStats(); diff --git a/translations/en.json b/translations/en.json index c3ab889..d3aa81a 100644 --- a/translations/en.json +++ b/translations/en.json @@ -1,579 +1,624 @@ { - "nav_servers": "Servers", - "nav_users": "Users", - "nav_invites": "Invite links", - "nav_settings": "Settings", - "nav_connections": "Connections", - "nav_logout": "Logout", - "theme_dark": "Dark", - "theme_light": "Light", - "back_to_servers": "← Back to servers", - "server_check": "Check", - "server_checking": "Checking...", - "protocols": "Protocols", - "install": "Install", - "uninstall": "Uninstall", - "run": "Running", - "stop": "Stopped", - "not_installed": "Not installed", - "installed": "Installed", - "add_server": "Add Server", - "no_servers": "No servers added", - "name": "Name", - "host": "Host", - "port": "Port", - "status": "Status", - "actions": "Actions", - "edit": "Edit", - "delete": "Delete", - "save": "Save", - "cancel": "Cancel", - "loading": "Loading...", - "copy": "Copy", - "download": "Download", - "config": "Configuration", - "qr_code": "QR code", - "vpn_key": "VPN key", - "clients": "Clients", - "traffic": "Traffic", - "limit": "Limit", - "manage": "Manage", - "no_protocols": "No protocols", - "add_server_desc": "Add your first VPN server to get started. You'll need SSH credentials to connect.", - "ssh_host": "Host / IP", - "ssh_port": "SSH Port", - "ssh_user": "User", - "ssh_password": "Password", - "ssh_key": "SSH Key", - "ssh_key_placeholder": "Private SSH Key", - "ssh_key_hint": "Paste the content of your id_rsa or id_ed25519 file", - "connect": "Connect", - "connecting": "Connecting...", - "server_added": "Server added successfully", - "server_deleted": "Server deleted", - "delete_confirm": "Are you sure you want to delete this server?", - "success": "Success", - "error": "Error", - "info": "Info", - "login_title": "Login to Panel", - "username": "Username", - "password": "Password", - "captcha": "Captcha", - "captcha_placeholder": "Enter characters from image", - "captcha_hint": "Click to refresh image", - "login": "Login", - "logging_in": "Logging in...", - "login_error": "Login failed", - "check_done": "Check complete", - "check_error": "Connection error", - "ssh_ok": "SSH Connection", - "docker_ok": "Docker", - "docker_not_installed": "Docker not installed", - "awg_desc": "A newer version of the protocol based on awg-go. Supports advanced obfuscation with S3, S4 parameters.", - "awg_legacy_desc": "Original AWG version based on WireGuard kernel. Compatible with older client versions.", - "xray_desc": "Modern protocol that masks traffic as regular web traffic (XTLS-Reality). Resistant to deep packet analysis.", - "wireguard_desc": "Standard and fastest VPN protocol. Supported natively on all modern OS, but easily detected by DPI.", - "not_checked": "Not checked", - "connections": "Connections", - "add": "Add", - "loading_connections": "Loading connections...", - "no_connections": "No connections", - "no_connections_desc": "Add your first connection to generate a VPN configuration", - "install_protocol": "Install protocol", - "port_default_hint": "Default port: 55424. Make sure it's not busy", - "port_xray_hint": "Default port: 443 (recommended for Xray). Make sure it's not taken by another web server.", - "reinstall": "Reinstall", - "uninstall_confirm": "Uninstall {}? All connections and configurations will be lost.", - "stop_container_confirm": "Stop container {}?", - "start_container_confirm": "Start container {}?", - "stopping": "Stopping", - "starting": "Starting", - "stopped": "stopped", - "started": "started", - "server_config": "Server configuration", - "add_connection": "Add connection", - "connection_name": "Connection name", - "connection_name_placeholder": "Example: iPhone, Laptop", - "assign_to_user": "Assign to user (optional)", - "no_assign": "— No assignment —", - "create": "Create", - "creating": "Creating...", - "connection_created": "Connection \"{}\" created", - "delete_connection_confirm": "Delete this connection? The configuration will stop working.", - "connection_deleted": "Connection deleted", - "config_tab": "📄 Config", - "vpn_key_tab": "🔑 VPN-key", - "qr_code_tab": "📱 QR-code", - "copy_config": "📋 Copy", - "download_conf": "⬇ Download .conf", - "copy_key": "📋 Copy key", - "vpn_link_hint": "Paste this link into the AmneziaVPN app for automatic configuration", - "qr_hint": "Scan the QR code in the AmneziaVPN app", - "no_data": "No data", - "start_btn": "▶ Start", - "stop_btn": "⏹ Stop", - "config_btn": "⚙️ Config", - "done": "Done", - "installing": "Installing...", - "start_install": "Starting installation...", - "check_docker": "Checking Docker...", - "prepare_host": "Preparing host...", - "build_container": "Building container...", - "install_success": "Protocol installed successfully!", - "install_error": "Installation error: ", - "qr_error": "Failed to generate QR code", - "users_title": "Users", - "search_placeholder": "Search by name, email or Telegram...", - "add_user": "Add User", - "prev_page": "← Back", - "next_page": "Next →", - "page_info": "Page {} of {}", - "loading_users": "Loading users...", - "nothing_found": "Nothing found", - "search_empty_desc": "Try changing your query or create a new user", - "username_label": "Username", - "password_label": "Password", - "role_label": "Role", - "role_user_desc": "User — view only own connections", - "role_support_desc": "Support — server management", - "role_admin_desc": "Admin — full access", - "tg_id_label": "Telegram ID (opt.)", - "email_label": "Email (opt.)", - "traffic_limit_label": "Traffic limit (GB) (opt., 0 = ∞)", - "expiration_date_label": "Expiration date", - "no_expiration": "No expiry", - "expire_after_first_use": "Start after first use", - "expire_after_first_use_hint": "The day countdown starts when the user first gets a config (panel, share, or Telegram).", - "expire_days_required": "Enter a number of days greater than 0", - "expire_pending_label": "{} days after first use", - "expire_started_suffix": "countdown started", - "expire_already_started_hint": "Countdown already started. Valid until: {}. Uncheck to set a fixed date.", - "description_label": "Description (opt.)", - "auto_conn_title": "Automatic connection creation (optional)", - "server_label": "Server", - "no_create_conn": "— Do not create connection —", - "protocol_label": "Protocol", - "server_then_protocol_hint": "Choose a server first, then a protocol installed on it (WireGuard, AmneziaWG 2.0, etc.).", - "edit_user_title": "Edit user: {}", - "edit_user_tg": "Telegram ID (optional)", - "edit_user_email": "Email (optional)", - "edit_user_limit": "Traffic limit (GB) (0 = ∞)", - "new_password_hint": "New password (leave empty if no change needed)", - "op_type_label": "Operation type", - "create_new_conn": "Create new connection", - "link_existing_conn": "Link existing", - "select_existing_conn": "Select existing connection", - "existing_conn_hint": "Only connections not linked to users will be shown", - "conn_name_panel": "Connection name (in panel)", - "user_conns_title": "User connections", - "no_free_conns": "No free connections", - "select_connection": "Please select a connection", - "linking": "Linking...", - "conn_linked": "Connection linked", - "enabling": "Enabling", - "disabling": "Disabling", - "user_enabled": "User enabled", - "user_disabled": "User disabled", - "delete_user_confirm": "Delete user? All their connections will be deleted.", - "share_access": "Share access", - "enable_public_access": "Enable public access", - "share_password_label": "Password (optional)", - "share_password_hint": "If set, user must enter it", - "guest_settings_title": "Guest access", - "guest_enable": "Enable guest access (no registration)", - "guest_enable_hint": "Public link on the login page. Guests see connections of the selected user and can optionally create a new config.", - "guest_link_label": "Guest link", - "guest_link_placeholder": "Save settings to generate a link", - "guest_regen_token": "Regenerate link", - "guest_regen_confirm": "Regenerate guest link? The old link will stop working.", - "guest_token_updated": "Guest link updated", - "guest_user_label": "Guest connections user", - "guest_user_none": "— Select user —", - "guest_user_hint": "Create a regular user (e.g. guest), assign VPN connections — guests will see them without logging in.", - "guest_password_label": "Guest password (optional)", - "guest_password_keep": "Leave blank to keep current password", - "guest_clear_password": "Remove password protection", - "guest_allow_create": "Allow guests to create a new config", - "guest_access_btn": "Continue as guest", - "guest_access_login_hint": "No account required", - "guest_title": "Guest access", - "guest_subtitle": "Get a VPN config without registration", - "guest_protected_desc": "This guest page is password protected.", - "guest_get_config": "Get config", - "guest_get_config_hint": "A new VPN config will be created for you", - "guest_not_found": "404 Not Found", - "guest_not_found_desc": "Guest access is disabled or the link is invalid.", - "wrong_guest_password": "Wrong password", - "invites_title": "Invite links", - "invites_hint": "Create shareable links. Anyone with the link can create a VPN config — you set how many times.", - "invites_empty": "No invite links yet", - "invites_empty_desc": "Create a link and share it — no registration needed.", - "invite_create": "Create link", - "invite_edit": "Edit link", - "invite_active": "Active", - "invite_expired": "Expired", - "invite_exhausted": "No uses left", - "invite_uses": "Uses", - "invite_name_label": "Link name", - "invite_max_uses_label": "Max configs", - "invite_max_uses_hint": "How many configs can be created via this link. 0 = unlimited.", - "invite_user_label": "Store configs under user", - "invite_user_hint": "Created configs are attached to this panel user (for management).", - "invite_user_required": "Select a user to store configs under", - "invite_password_label": "Password (optional)", - "invite_expires_label": "Expires at (optional)", - "invite_duration_label": "Config lifetime (days)", - "invite_duration_hint": "Starts when the user clicks Get config. 0 = no expiry.", - "invite_duration_short": "Lifetime", - "invite_duration_starts_hint": "After you get a config it will work for {} day(s)", - "invite_config_expires_at": "Config valid until: {}", - "invite_sub_tab": "Subscription", - "days_short": "d", - "invite_clear_expires": "Remove expiration", - "invite_note_label": "Note (admin only)", - "invite_note_placeholder": "Internal comment", - "invite_reset_used": "Reset used counter to 0", - "invite_created": "Invite link created (URL copied)", - "invite_updated": "Invite link updated", - "invite_deleted": "Invite link deleted", - "invite_delete_confirm": "Delete this invite link?", - "invite_public_title": "Invite", - "invite_public_subtitle": "Get a VPN config via invite link", - "invite_protected_desc": "This invite link is password protected.", - "invite_get_config_hint": "Each click creates one new config and counts as a use", - "invite_uses_left": "Remaining uses: {}", - "invite_uses_left_unlimited": "Created so far: {} (unlimited)", - "invite_not_found": "404 Not Found", - "invite_not_found_desc": "Invite link is invalid or was deleted.", - "wrong_invite_password": "Wrong password", - "public_link_label": "Public link", - "disabled": "Disabled", - "out_of": "Of", - "settings_title": "Panel Settings", - "appearance": "🎨 Appearance", - "title_label": "Title", - "logo_label": "Logo (Emoji or symbol)", - "subtitle_label": "Subtitle", - "captcha_title": "🔐 Captcha", - "enable_captcha": "Enable captcha (multicolorcaptcha)", - "telegram_bot_title": "🤖 Telegram Bot", - "bot_token_label": "Bot Token (from @BotFather)", - "bot_status": "Status", - "bot_running": "✅ Running", - "bot_stopped": "⏹ Stopped", - "bot_stop_btn": "⏹ Stop", - "bot_start_btn": "▶️ Start", - "bot_hint": "After changing the token, save the settings — the bot will restart automatically. Users must have a Telegram ID in their profile.", - "api_docs_title": "🔌 API Documentation", - "api_docs_hint": "Use these interfaces to explore the panel's API capabilities", - "tunnels_title": "Tunnels", - "local_server": "Local Server", - "tunnel_install_enable": "Install & Enable", - "tunnel_enable": "Enable Tunnel", - "tunnel_running": "Tunnel running", - "tunnel_stop": "Stop", - "tunnel_stopping": "Stopping...", - "tunnel_stopped": "Tunnel stopped", - "tunnel_delete": "Delete", - "tunnel_deleted": "Tunnel deleted", - "tunnel_delete_confirm": "Stop and delete this panel-managed tunnel binary?", - "tunnel_starting": "Starting tunnel...", - "tunnel_started": "Tunnel started", - "tunnel_waiting_url": "Tunnel started. Waiting for public URL...", - "tunnel_no_public_url": "Public URL will appear here after the tunnel starts.", - "tunnels_hint": "Quick tunnels expose this local panel to the internet. Keep admin credentials strong and share public URLs only with trusted users.", - "ngrok_authtoken_placeholder": "ngrok authtoken (optional for some accounts)", - "import_users_title": "📤 Import Users", - "import_source_label": "Import / Sync Source", - "remnawave_url_label": "Remnawave URL", - "api_key_label": "API Key", - "enable_sync": "Enable synchronization", - "sync_hint": "Users will be created, disabled, and deleted automatically based on Remnawave data", - "sync_now_btn": "🔄 Sync now", - "delete_sync_btn": "🗑 Delete synchronization", - "auto_create_conns": "Create connections automatically", - "sync_server_label": "Server for new connections", - "save_changes": "💾 Save changes", - "sync_success": "Sync completed: {} users", - "sync_deleted": "Deleted {} synced users", - "delete_sync_confirm": "Are you sure you want to delete ALL synced users and their connections? This action is irreversible.", - "settings_saved": "Settings saved successfully", - "bot_started": "Bot started", - "bot_stopped_msg": "Bot stopped", - "sync_running": "Syncing...", - "deleting": "Deleting...", - "my_connections_title": "My Connections", - "show_config": "📄 Show config", - "no_connections_user_desc": "Contact administrator to create a VPN connection", - "share_title": "Connection Access", - "vpn_access": "VPN Access", - "user_label_share": "User", - "share_protected_desc": "This access is password protected. Please enter it to continue.", - "auth_success": "Auth success", - "loading_share_conns": "Loading connections...", - "no_active_conns": "You have no active connections yet.", - "show_settings_btn": "🔑 Show settings", - "share_copy_hint": "Tap the button — the key copies itself. Then paste it into your VPN app.", - "copy_key_big": "Copy key", - "copying_key": "Copying…", - "key_copied": "Key copied — paste it into the app", - "more_qr_download": "QR code & file", - "download_config_file": "Download config file", - "show_config_text": "Show config text", - "copied": "Copied!", - "active": "Active", - "site_description": "Amnezia Web Panel — management of VPN servers and protocols AmneziaWG", - "toggle_theme": "Change theme", - "copied_to_clipboard": "Copied to clipboard", - "share_not_found": "404 Not Found", - "share_not_found_desc": "Either the link is incorrect or access has been disabled.", - "invalid_captcha": "Invalid captcha", - "account_disabled": "Account disabled", - "invalid_login": "Invalid login or password", - "user_exists": "User with this name already exists", - "cannot_delete_self": "You cannot delete yourself", - "wrong_share_password": "Wrong password", - "saving": "Saving...", - "select_lang": "Select Language", - "lang_ru": "Русский", - "lang_en": "English", - "lang_fr": "Français", - "lang_zh": "中文 (Chinese)", - "lang_fa": "فارسی (Persian)", - "backup_title": "Simple Backup", - "download_backup": "Download data.json", - "restore_backup": "Restore from file", - "restore_confirm": "Are you sure? Current data will be overwritten and the application will restart.", - "restore_success": "Restore successful! Restarting...", - "invalid_backup_file": "Invalid backup file or structure", - "config_unavailable": "Configuration unavailable", - "config_unavailable_desc": "This client was created via the native Amnezia app.\\nThe private key is stored only on the user's device and cannot be recovered by the server.", - "client_public_key": "Client public key:", - "telemt_desc": "MTProxy-based Telegram proxy with advanced obfuscation and TLS emulation support.", - "hysteria_desc": "Hysteria 2 (QUIC/UDP) via official tobyxdd/hysteria (apernet/hysteria) with Let's Encrypt TLS. Admin sets the domain at install time.", - "hysteria_domain": "Domain", - "hysteria_email": "Let's Encrypt email", - "hysteria_port_hint": "UDP port for Hysteria (default 8998). Avoid 443 if nginx/xray already use UDP/TCP 443. Port 80/TCP must be free for certificate issuance.", - "hysteria_install_hint": "Point the domain A-record to this server before install. Port 80/TCP is used temporarily for Let's Encrypt HTTP-01 validation. BBR and salamander obfuscation are enabled automatically.", - "hysteria_dns_hint": "Create this DNS record before installation:", - "hysteria_settings_title": "Hysteria settings", - "hysteria_change_port": "Change port", - "hysteria_port_change_hint": "UDP listen port. If you see “address already in use”, pick a free port (e.g. 8443 or 8998), save, then re-import the client link.", - "hysteria_settings_saved": "Hysteria settings updated", - "hysteria_ports_warning": "Warning: free TCP ports 80 and 443 are required for stable operation (Let's Encrypt on 80, HTTPS/QUIC on 443).", - "hysteria_renew_ssl": "Renew SSL", - "hysteria_ssl_change_hint": "Changing the domain re-issues the Let's Encrypt certificate (port 80 must be free).", - "hysteria_ssl_required": "Domain and email are required for SSL", - "naiveproxy_desc": "NaiveProxy (stable) — HTTPS/HTTP2 via Caddy + forwardproxy, Let's Encrypt TLS. Client: use Karing. Do not use v2rayN; other clients untested.", - "naiveproxy_domain": "Domain", - "naiveproxy_email": "Let's Encrypt email", - "naiveproxy_port_hint": "TCP port 443 is fixed for NaiveProxy. Port 80/TCP must be free for certificate issuance.", - "naiveproxy_install_hint": "Point the domain A-record to this server before install. Free TCP ports 80 and 443 are required (Let's Encrypt and HTTPS proxy).", - "naiveproxy_dns_hint": "Create this DNS record before installation:", - "naiveproxy_ports_warning": "Warning: free TCP ports 80 and 443 are required for stable operation (Let's Encrypt on 80, HTTPS proxy on 443).", - "naiveproxy_client_hint": "Stable build. Do NOT use v2rayN. Confirmed working in Karing. Other clients are untested.", - "server_ssl_domain": "SSL domain (default)", - "server_ssl_email": "SSL email (default)", - "server_ssl_hint": "Used when installing Hysteria / NGINX on this server (Let's Encrypt).", - "container_logs": "Container logs", - "logs_btn": "📋 Logs", - "logs_live": "Live", - "logs_reconnect": "reconnecting…", - "no_logs": "No logs yet", - "container_status": "Status", - "exit_code": "Exit code", - "close": "Close", - "tls_emulation": "TLS Emulation", - "tls_domain": "Masking Domain", - "max_connections_limit": "Max Connections", - "max_connections_hint": "0 for unlimited. Limits total concurrent users.", - "traffic_reset_strategy_label": "Traffic reset strategy", - "traffic_reset_never": "Never reset", - "traffic_reset_daily": "Daily reset", - "traffic_reset_weekly": "Weekly reset", - "traffic_reset_monthly": "Monthly reset", - "traffic_reset_hint": "How often should user traffic be reset", - "traffic_used_total": "Total spent", - "traffic_reset_info": "Next reset: {}", - "services": "Services", - "dns_desc": "Personal DNS server to block ads and protect the privacy of your queries.", - "dns_internal_hint": "The service runs on the internal network (172.29.172.254) without binding to host ports.", - "telemt_port_hint": "Port for the Telegram proxy (typically 443).", - "socks5_desc": "Single-account SOCKS5 proxy (3proxy) with username/password authentication.", - "socks5_username": "Username", - "socks5_password": "Password", - "socks5_password_placeholder": "Leave empty to auto-generate", - "socks5_password_hint": "If left empty, a random 16-character password will be generated.", - "socks5_port_hint": "TCP port the proxy will listen on. The default in the official Amnezia client is 38080.", - "socks5_change_settings": "Change settings", - "socks5_settings_title": "SOCKS5 — Connection settings", - "socks5_port_change_hint": "Changing the port recreates the container; existing sessions will be dropped.", - "socks5_settings_saved": "SOCKS5 settings saved", - "adguard_desc": "AdGuard Home — DNS-based ad blocker with a web admin UI.", - "adguard_mode": "Installation mode", - "adguard_mode_sidebyside": "Side-by-side", - "adguard_mode_sidebyside_hint": "runs alongside AmneziaDNS on a separate internal IP (172.29.172.253)", - "adguard_mode_replace": "Replace AmneziaDNS", - "adguard_mode_replace_hint": "removes amnezia-dns and takes its IP — VPN clients use AdGuard immediately", - "adguard_dns_port": "DNS port (53)", - "adguard_dns_port_hint": "Internal DNS port served to VPN clients via the docker network.", - "adguard_web_port": "Web UI port", - "adguard_dot_port": "DoT port", - "adguard_doh_port": "DoH port", - "adguard_expose_web": "Expose to internet", - "adguard_expose": "Expose to host", - "adguard_install_hint": "Initial AdGuard setup runs through its built-in wizard via the web UI after install.", - "adguard_internal_ip": "Internal IP", - "adguard_web_ui": "Admin URL", - "adguard_open_ui": "Open Web UI", - "edit_server_title": "Edit server", - "edit_keep_credential": "Leave empty to keep the current value", - "server_saved": "Server settings saved", - "servers_reordered": "Servers reordered", - "ping_checking": "Checking connectivity...", - "offline": "offline", - "api_tokens_title": "API tokens", - "api_tokens_hint": "Bearer tokens for external integrations. Send the token in the `Authorization: Bearer ` header. Tokens have admin-equivalent rights and stop working if their owner is disabled or demoted.", - "api_tokens_empty": "No API tokens yet. Create one to integrate the panel with an external service.", - "api_tokens_create": "Create token", - "api_tokens_create_title": "Create API token", - "api_tokens_name_label": "Name", - "api_tokens_name_placeholder": "e.g. Linear integration, CI bot, monitoring", - "api_tokens_name_hint": "Used to identify this token in the list. The token value itself is generated for you.", - "api_tokens_name_required": "Token name is required", - "api_tokens_created_title": "Token created", - "api_tokens_value_label": "Token value", - "api_tokens_warning_title": "This is the only time you'll see this token.", - "api_tokens_warning_body": "Copy it now and store it in your integration's secret manager. The panel keeps only a hash — if you lose this value you'll need to revoke and recreate the token.", - "api_tokens_revoke": "Revoke", - "api_tokens_revoke_confirm": "Revoke token \"{}\"? Any integration using it will stop working.", - "api_tokens_revoked": "Token revoked", - "api_tokens_created": "Created", - "api_tokens_last_used": "Last used", - "api_tokens_owner": "Owner", - "api_tokens_never": "never", - "coming_soon": "Coming soon", - "promo_star_cta": "Star us on GitHub", - "aivpn_subtitle": "AI-driven protocol selection that picks the right tunnel for the moment. Land it sooner — drop a star.", - "revproxy_title": "Reverse Proxy", - "revproxy_subtitle": "High-performance traffic obfuscation: hide your VPN endpoint behind a polished public-facing site, drop the DPI footprint.", - "management": "Management", - "server_management": "Server Management", - "server_management_desc": "Perform system-level actions on your machine.", - "check_server_services": "Check the server services", - "reboot_server": "Reboot server", - "clear_server": "Clear server", - "remove_server": "Remove server from panel", - "ssl_title": "SSL / HTTPS Settings", - "enable_https": "Enable HTTPS", - "panel_port_label": "Panel Port", - "domain_label": "Domain name", - "cert_path_label": "SSL Certificate path (.pem)", - "key_path_label": "Private Key path (.pem)", - "or_paste_text": "OR paste certificate text", - "cert_text_label": "Certificate content (CRT)", - "key_text_label": "Private Key content (KEY)", - "ssl_hint": "After enabling, the panel will be accessible via https:// on the specified domain. Make sure the file paths or text data are correct.", - "about_title": "About & Updates", - "current_version": "Current version", - "check_updates": "Check for updates", - "checking_updates": "Checking...", - "update_available": "New version available", - "download_update": "Download update", - "up_to_date": "You have the latest version", - "warp_hint": "WARP routes this host outbound through Cloudflare. It does not create a public panel URL like Quick Tunnel or ngrok.", - "warp_install_hint": "Install the official Cloudflare WARP client first, then restart the panel.", - "warp_install_required": "Install WARP first", - "warp_connect": "Connect WARP", - "warp_disconnect": "Disconnect", - "warp_connected": "WARP connected", - "warp_disconnected": "WARP disconnected", - "warp_status_unknown": "WARP status is unknown.", - "warp_status_not_installed": "Cloudflare WARP CLI is not installed on this host.", - "warp_status_connected": "Cloudflare WARP is connected.", - "warp_status_connecting": "Cloudflare WARP is connecting.", - "warp_status_disconnected": "Cloudflare WARP is disconnected.", - "warp_status_not_registered": "Cloudflare WARP is installed but not registered yet. Connect will try to register automatically.", - "warp_status_service_unavailable": "Cloudflare WARP service is not available. Make sure warp-svc is running.", - "warp_status_error": "Cloudflare WARP returned an error.", - "install_starting": "Starting installation...", - "server_check_complete": "Check completed", - "server_connection_error": "Connection error", - "connections_load_error": "Error loading connections", - "connection_updated": "Connection updated", - "edit_connection": "Edit connection", - "enable_connection_progress": "Enabling connection...", - "disable_connection_progress": "Disabling connection...", - "connection_enabled": "Connection enabled", - "connection_disabled": "Connection disabled", - "gb": "GB", - "marketplace": "Templates", - "open_marketplace": "Open Templates", - "marketplace_desc": "Install protocol and service templates available for this server.", - "installed_apps": "Installed applications", - "installed_apps_hint": "Only installed protocols and services are shown here. Use Templates to add more.", - "no_installed_apps": "No installed applications yet", - "no_installed_apps_desc": "Open Templates to install protocols or services on this server.", - "install_another": "Install another", - "new_instance": "new instance", - "port_next_instance_hint": "Choose a free UDP port for the additional instance.", - "checking_server": "Checking server services...", - "web_servers": "Web servers", - "nginx_desc": "NGINX web server with Let's Encrypt HTTPS certificate and editable index.html.", - "nginx_domain": "Domain", - "nginx_email": "Let's Encrypt email", - "nginx_port_hint": "HTTPS port exposed on the server. Port 80/TCP must be free and reachable for Let's Encrypt validation.", - "nginx_install_hint": "The domain must already point to this server. Port 80/TCP is used temporarily/permanently for HTTP-01 certificate validation.", - "site": "Site", - "site_editor": "Site editor", - "site_saved": "Site saved", - "nginx_dns_hint": "Create this DNS record before installation:", - "backup": "Backup", - "backups": "Backups", - "backup_desc": "Backups are stored on the server and include protocol files (config, keys, clientsTable). For WireGuard / AWG you can also download a ZIP of all client .conf files.", - "create_backup": "Create backup", - "creating_backup": "Creating backup...", - "backup_created": "Backup created", - "no_backups": "No backups yet", - "no_backups_desc": "Create the first backup for this protocol.", - "export_client_configs": "Download all .conf", - "export_client_configs_hint": "ZIP with all WireGuard / AmneziaWG client configs (when the private key is stored).", - "exporting_client_configs": "Preparing ZIP…", - "export_client_configs_done": "Client configs ZIP downloaded", - "restore_protocol_backup": "Restore", - "restore_protocol_backup_confirm": "Restore this backup on the server? Current protocol files will be overwritten and the container will restart.", - "restoring_protocol_backup": "Restoring… please wait", - "restore_protocol_backup_done": "Backup restored", - "refresh": "Refresh", - "guest_allow_create_hint": "Creates a config for the guest user (3x-ui VLESS or selected server protocol).", - "invite_inbound_hint": "Inbounds are loaded from the selected 3x-ui panel. Only 3x-ui returns the config/share link.", - "invite_inbound_loading": "Loading inbounds from 3x-ui…", - "invite_inbound_empty": "No VLESS inbounds on this 3x-ui panel", - "invite_inbound_need_xui": "Configure 3x-ui in Settings first", - "invite_inbound_required": "Select a VLESS inbound from 3x-ui", - "invite_sub_url_missing_title": "Subscription URL is not set", - "invite_sub_url_missing_hint": "In Settings → 3x-ui set Subscription URL base so invite links issue /sub/… configs.", - "xui_url_label": "3x-ui URL", - "xui_url_hint": "Include scheme, port and webBasePath if configured (example: https://host:2053/secret)", - "xui_sub_url_label": "Subscription URL base", - "xui_sub_url_hint": "Public /sub base without trailing slash (example: https://host:2096/sub). Invite configs are issued as subscription links.", - "xui_api_token_label": "API Token (preferred)", - "xui_api_token_placeholder": "Settings → Security → API Token", - "xui_api_token_hint": "If set, username/password login is skipped", - "xui_username_label": "3x-ui Username", - "xui_password_label": "3x-ui Password", - "xui_sync_hint": "Clients from 3x-ui inbounds will be created, disabled, and deleted as panel users (matched by email)", - "xui_inbound_label": "VLESS inbound", - "xui_inbound_auto": "Auto (first VLESS inbound)", - "xui_inbound_hint": "Inbounds are loaded from 3x-ui. This site only creates the client and takes the ready share link — no local config generation.", - "xui_servers_title": "3x-ui servers", - "xui_servers_hint": "Add 3x-ui panels. When creating, pick a server and an inbound from the 3x-ui list — the panel returns the share link.", - "xui_servers_empty": "No 3x-ui servers yet. Click Add server.", - "xui_servers_manage_hint": "Panel URLs and subscription bases are managed in the “3x-ui servers” section below.", - "xui_server_add": "Add server", - "xui_server_name_label": "Name / description", - "xui_server_name_hint": "Shown in invites and user forms (e.g. US, NL, Home).", - "xui_server_select_label": "3x-ui server", - "xui_server_select_hint": "Pick a 3x-ui panel, then an inbound from the list that panel returns via API.", - "xui_server_delete_confirm": "Delete this 3x-ui server from the panel?" + "nav_servers": "Servers", + "nav_users": "Users", + "nav_invites": "Invite links", + "nav_settings": "Settings", + "nav_connections": "Connections", + "nav_logout": "Logout", + "theme_dark": "Dark", + "theme_light": "Light", + "back_to_servers": "← Back to servers", + "server_check": "Check", + "server_checking": "Checking...", + "protocols": "Protocols", + "install": "Install", + "uninstall": "Uninstall", + "run": "Running", + "stop": "Stopped", + "not_installed": "Not installed", + "installed": "Installed", + "add_server": "Add Server", + "no_servers": "No servers added", + "name": "Name", + "host": "Host", + "port": "Port", + "status": "Status", + "actions": "Actions", + "edit": "Edit", + "delete": "Delete", + "save": "Save", + "cancel": "Cancel", + "loading": "Loading...", + "copy": "Copy", + "download": "Download", + "config": "Configuration", + "qr_code": "QR code", + "vpn_key": "VPN key", + "clients": "Clients", + "traffic": "Traffic", + "limit": "Limit", + "manage": "Manage", + "no_protocols": "No protocols", + "add_server_desc": "Add your first VPN server to get started. You\u0027ll need SSH credentials to connect.", + "ssh_host": "Host / IP", + "ssh_port": "SSH Port", + "ssh_user": "User", + "ssh_password": "Password", + "ssh_key": "SSH Key", + "ssh_key_placeholder": "Private SSH Key", + "ssh_key_hint": "Paste the content of your id_rsa or id_ed25519 file", + "connect": "Connect", + "connecting": "Connecting...", + "server_added": "Server added successfully", + "server_deleted": "Server deleted", + "delete_confirm": "Are you sure you want to delete this server?", + "success": "Success", + "error": "Error", + "info": "Info", + "login_title": "Login to Panel", + "username": "Username", + "password": "Password", + "captcha": "Captcha", + "captcha_placeholder": "Enter characters from image", + "captcha_hint": "Click to refresh image", + "login": "Login", + "logging_in": "Logging in...", + "login_error": "Login failed", + "check_done": "Check complete", + "check_error": "Connection error", + "ssh_ok": "SSH Connection", + "docker_ok": "Docker", + "docker_not_installed": "Docker not installed", + "awg_desc": "A newer version of the protocol based on awg-go. Supports advanced obfuscation with S3, S4 parameters.", + "awg_legacy_desc": "Original AWG version based on WireGuard kernel. Compatible with older client versions.", + "xray_desc": "Modern protocol that masks traffic as regular web traffic (XTLS-Reality). Resistant to deep packet analysis.", + "wireguard_desc": "Standard and fastest VPN protocol. Supported natively on all modern OS, but easily detected by DPI.", + "not_checked": "Not checked", + "connections": "Connections", + "add": "Add", + "loading_connections": "Loading connections...", + "no_connections": "No connections", + "no_connections_desc": "Add your first connection to generate a VPN configuration", + "install_protocol": "Install protocol", + "port_default_hint": "Default port: 55424. Make sure it\u0027s not busy", + "port_xray_hint": "Default port: 443 (recommended for Xray). Make sure it\u0027s not taken by another web server.", + "reinstall": "Reinstall", + "uninstall_confirm": "Uninstall {}? All connections and configurations will be lost.", + "stop_container_confirm": "Stop container {}?", + "start_container_confirm": "Start container {}?", + "stopping": "Stopping", + "starting": "Starting", + "stopped": "stopped", + "started": "started", + "server_config": "Server configuration", + "add_connection": "Add connection", + "connection_name": "Connection name", + "connection_name_placeholder": "Example: iPhone, Laptop", + "assign_to_user": "Assign to user (optional)", + "no_assign": "— No assignment —", + "create": "Create", + "creating": "Creating...", + "connection_created": "Connection \"{}\" created", + "delete_connection_confirm": "Delete this connection? The configuration will stop working.", + "connection_deleted": "Connection deleted", + "config_tab": "📄 Config", + "vpn_key_tab": "🔑 VPN-key", + "qr_code_tab": "📱 QR-code", + "copy_config": "📋 Copy", + "download_conf": "⬇ Download .conf", + "copy_key": "📋 Copy key", + "vpn_link_hint": "Paste this link into the AmneziaVPN app for automatic configuration", + "qr_hint": "Scan the QR code in the AmneziaVPN app", + "no_data": "No data", + "start_btn": "▶ Start", + "stop_btn": "⏹ Stop", + "config_btn": "⚙️ Config", + "done": "Done", + "installing": "Installing...", + "start_install": "Starting installation...", + "check_docker": "Checking Docker...", + "prepare_host": "Preparing host...", + "build_container": "Building container...", + "install_success": "Protocol installed successfully!", + "install_error": "Installation error: ", + "qr_error": "Failed to generate QR code", + "users_title": "Users", + "search_placeholder": "Search by name, email or Telegram...", + "add_user": "Add User", + "prev_page": "← Back", + "next_page": "Next →", + "page_info": "Page {} of {}", + "loading_users": "Loading users...", + "nothing_found": "Nothing found", + "search_empty_desc": "Try changing your query or create a new user", + "username_label": "Username", + "password_label": "Password", + "role_label": "Role", + "role_user_desc": "User — view only own connections", + "role_support_desc": "Support — server management", + "role_admin_desc": "Admin — full access", + "tg_id_label": "Telegram ID (opt.)", + "email_label": "Email (opt.)", + "traffic_limit_label": "Traffic limit (GB) (opt., 0 = ∞)", + "expiration_date_label": "Expiration date", + "no_expiration": "No expiry", + "expire_after_first_use": "Start after first use", + "expire_after_first_use_hint": "The day countdown starts when the user first gets a config (panel, share, or Telegram).", + "expire_days_required": "Enter a number of days greater than 0", + "expire_pending_label": "{} days after first use", + "expire_started_suffix": "countdown started", + "expire_already_started_hint": "Countdown already started. Valid until: {}. Uncheck to set a fixed date.", + "description_label": "Description (opt.)", + "auto_conn_title": "Automatic connection creation (optional)", + "server_label": "Server", + "no_create_conn": "— Do not create connection —", + "protocol_label": "Protocol", + "server_then_protocol_hint": "Choose a server first, then a protocol installed on it (WireGuard, AmneziaWG 2.0, etc.).", + "edit_user_title": "Edit user: {}", + "edit_user_tg": "Telegram ID (optional)", + "edit_user_email": "Email (optional)", + "edit_user_limit": "Traffic limit (GB) (0 = ∞)", + "new_password_hint": "New password (leave empty if no change needed)", + "op_type_label": "Operation type", + "create_new_conn": "Create new connection", + "link_existing_conn": "Link existing", + "select_existing_conn": "Select existing connection", + "existing_conn_hint": "Only connections not linked to users will be shown", + "conn_name_panel": "Connection name (in panel)", + "user_conns_title": "User connections", + "no_free_conns": "No free connections", + "select_connection": "Please select a connection", + "linking": "Linking...", + "conn_linked": "Connection linked", + "enabling": "Enabling", + "disabling": "Disabling", + "user_enabled": "User enabled", + "user_disabled": "User disabled", + "delete_user_confirm": "Delete user? All their connections will be deleted.", + "share_access": "Share access", + "enable_public_access": "Enable public access", + "share_password_label": "Password (optional)", + "share_password_hint": "If set, user must enter it", + "guest_settings_title": "Guest access", + "guest_enable": "Enable guest access (no registration)", + "guest_enable_hint": "Public link on the login page. Guests see connections of the selected user and can optionally create a new config.", + "guest_link_label": "Guest link", + "guest_link_placeholder": "Save settings to generate a link", + "guest_regen_token": "Regenerate link", + "guest_regen_confirm": "Regenerate guest link? The old link will stop working.", + "guest_token_updated": "Guest link updated", + "guest_user_label": "Guest connections user", + "guest_user_none": "— Select user —", + "guest_user_hint": "Create a regular user (e.g. guest), assign VPN connections — guests will see them without logging in.", + "guest_password_label": "Guest password (optional)", + "guest_password_keep": "Leave blank to keep current password", + "guest_clear_password": "Remove password protection", + "guest_allow_create": "Allow guests to create a new config", + "guest_access_btn": "Continue as guest", + "guest_access_login_hint": "No account required", + "guest_title": "Guest access", + "guest_subtitle": "Get a VPN config without registration", + "guest_protected_desc": "This guest page is password protected.", + "guest_get_config": "Get config", + "guest_get_config_hint": "A new VPN config will be created for you", + "guest_not_found": "404 Not Found", + "guest_not_found_desc": "Guest access is disabled or the link is invalid.", + "wrong_guest_password": "Wrong password", + "invites_title": "Invite links", + "invites_hint": "Create shareable links. Anyone with the link can create a VPN config — you set how many times.", + "invites_empty": "No invite links yet", + "invites_empty_desc": "Create a link and share it — no registration needed.", + "invite_create": "Create link", + "invite_edit": "Edit link", + "invite_active": "Active", + "invite_expired": "Expired", + "invite_exhausted": "No uses left", + "invite_uses": "Uses", + "invite_name_label": "Link name", + "invite_max_uses_label": "Max configs", + "invite_max_uses_hint": "How many configs can be created via this link. 0 = unlimited.", + "invite_user_label": "Store configs under user", + "invite_user_hint": "Created configs are attached to this panel user (for management).", + "invite_user_required": "Select a user to store configs under", + "invite_password_label": "Password (optional)", + "invite_expires_label": "Expires at (optional)", + "invite_duration_label": "Config lifetime (days)", + "invite_duration_hint": "Starts when the user clicks Get config. 0 = no expiry.", + "invite_duration_short": "Lifetime", + "invite_duration_starts_hint": "After you get a config it will work for {} day(s)", + "invite_config_expires_at": "Config valid until: {}", + "invite_sub_tab": "Subscription", + "days_short": "d", + "invite_clear_expires": "Remove expiration", + "invite_note_label": "Note (admin only)", + "invite_note_placeholder": "Internal comment", + "invite_reset_used": "Reset used counter to 0", + "invite_created": "Invite link created (URL copied)", + "invite_updated": "Invite link updated", + "invite_deleted": "Invite link deleted", + "invite_delete_confirm": "Delete this invite link?", + "invite_public_title": "Invite", + "invite_public_subtitle": "Get a VPN config via invite link", + "invite_protected_desc": "This invite link is password protected.", + "invite_get_config_hint": "Each click creates one new config and counts as a use", + "invite_uses_left": "Remaining uses: {}", + "invite_uses_left_unlimited": "Created so far: {} (unlimited)", + "invite_not_found": "404 Not Found", + "invite_not_found_desc": "Invite link is invalid or was deleted.", + "wrong_invite_password": "Wrong password", + "public_link_label": "Public link", + "disabled": "Disabled", + "out_of": "Of", + "settings_title": "Panel Settings", + "appearance": "🎨 Appearance", + "title_label": "Title", + "logo_label": "Logo (Emoji or symbol)", + "subtitle_label": "Subtitle", + "captcha_title": "🔐 Captcha", + "enable_captcha": "Enable captcha (multicolorcaptcha)", + "telegram_bot_title": "🤖 Telegram Bot", + "bot_token_label": "Bot Token (from @BotFather)", + "bot_status": "Status", + "bot_running": "✅ Running", + "bot_stopped": "⏹ Stopped", + "bot_stop_btn": "⏹ Stop", + "bot_start_btn": "▶️ Start", + "bot_hint": "After changing the token, save the settings — the bot will restart automatically. Users must have a Telegram ID in their profile.", + "api_docs_title": "🔌 API Documentation", + "api_docs_hint": "Use these interfaces to explore the panel\u0027s API capabilities", + "tunnels_title": "Tunnels", + "local_server": "Local Server", + "tunnel_install_enable": "Install \u0026 Enable", + "tunnel_enable": "Enable Tunnel", + "tunnel_running": "Tunnel running", + "tunnel_stop": "Stop", + "tunnel_stopping": "Stopping...", + "tunnel_stopped": "Tunnel stopped", + "tunnel_delete": "Delete", + "tunnel_deleted": "Tunnel deleted", + "tunnel_delete_confirm": "Stop and delete this panel-managed tunnel binary?", + "tunnel_starting": "Starting tunnel...", + "tunnel_started": "Tunnel started", + "tunnel_waiting_url": "Tunnel started. Waiting for public URL...", + "tunnel_no_public_url": "Public URL will appear here after the tunnel starts.", + "tunnels_hint": "Quick tunnels expose this local panel to the internet. Keep admin credentials strong and share public URLs only with trusted users.", + "ngrok_authtoken_placeholder": "ngrok authtoken (optional for some accounts)", + "import_users_title": "📤 Import Users", + "import_source_label": "Import / Sync Source", + "remnawave_url_label": "Remnawave URL", + "api_key_label": "API Key", + "enable_sync": "Enable synchronization", + "sync_hint": "Users will be created, disabled, and deleted automatically based on Remnawave data", + "sync_now_btn": "🔄 Sync now", + "delete_sync_btn": "🗑 Delete synchronization", + "auto_create_conns": "Create connections automatically", + "sync_server_label": "Server for new connections", + "save_changes": "💾 Save changes", + "sync_success": "Sync completed: {} users", + "sync_deleted": "Deleted {} synced users", + "delete_sync_confirm": "Are you sure you want to delete ALL synced users and their connections? This action is irreversible.", + "settings_saved": "Settings saved successfully", + "bot_started": "Bot started", + "bot_stopped_msg": "Bot stopped", + "sync_running": "Syncing...", + "deleting": "Deleting...", + "my_connections_title": "My Connections", + "show_config": "📄 Show config", + "no_connections_user_desc": "Contact administrator to create a VPN connection", + "share_title": "Connection Access", + "vpn_access": "VPN Access", + "user_label_share": "User", + "share_protected_desc": "This access is password protected. Please enter it to continue.", + "auth_success": "Auth success", + "loading_share_conns": "Loading connections...", + "no_active_conns": "You have no active connections yet.", + "show_settings_btn": "🔑 Show settings", + "share_copy_hint": "Tap the button — the key copies itself. Then paste it into your VPN app.", + "copy_key_big": "Copy key", + "copying_key": "Copying…", + "key_copied": "Key copied — paste it into the app", + "more_qr_download": "QR code \u0026 file", + "download_config_file": "Download config file", + "show_config_text": "Show config text", + "copied": "Copied!", + "active": "Active", + "site_description": "Amnezia Web Panel — management of VPN servers and protocols AmneziaWG", + "toggle_theme": "Change theme", + "copied_to_clipboard": "Copied to clipboard", + "share_not_found": "404 Not Found", + "share_not_found_desc": "Either the link is incorrect or access has been disabled.", + "invalid_captcha": "Invalid captcha", + "account_disabled": "Account disabled", + "invalid_login": "Invalid login or password", + "user_exists": "User with this name already exists", + "cannot_delete_self": "You cannot delete yourself", + "wrong_share_password": "Wrong password", + "saving": "Saving...", + "select_lang": "Select Language", + "lang_ru": "Русский", + "lang_en": "English", + "lang_fr": "Français", + "lang_zh": "中文 (Chinese)", + "lang_fa": "فارسی (Persian)", + "backup_title": "Simple Backup", + "download_backup": "Download data.json", + "restore_backup": "Restore from file", + "restore_confirm": "Are you sure? Current data will be overwritten and the application will restart.", + "restore_success": "Restore successful! Restarting...", + "invalid_backup_file": "Invalid backup file or structure", + "config_unavailable": "Configuration unavailable", + "config_unavailable_desc": "This client was created via the native Amnezia app.\\nThe private key is stored only on the user\u0027s device and cannot be recovered by the server.", + "client_public_key": "Client public key:", + "telemt_desc": "MTProxy-based Telegram proxy with advanced obfuscation and TLS emulation support.", + "hysteria_desc": "Hysteria 2 (QUIC/UDP) via official tobyxdd/hysteria (apernet/hysteria) with Let\u0027s Encrypt TLS. Admin sets the domain at install time.", + "hysteria_domain": "Domain", + "hysteria_email": "Let\u0027s Encrypt email", + "hysteria_port_hint": "UDP port for Hysteria (default 8998). Avoid 443 if nginx/xray already use UDP/TCP 443. Port 80/TCP must be free for certificate issuance.", + "hysteria_install_hint": "Point the domain A-record to this server before install. Port 80/TCP is used temporarily for Let\u0027s Encrypt HTTP-01 validation. BBR and salamander obfuscation are enabled automatically.", + "hysteria_dns_hint": "Create this DNS record before installation:", + "hysteria_settings_title": "Hysteria settings", + "hysteria_change_port": "Change port", + "hysteria_port_change_hint": "UDP listen port. If you see “address already in use”, pick a free port (e.g. 8443 or 8998), save, then re-import the client link.", + "hysteria_settings_saved": "Hysteria settings updated", + "hysteria_ports_warning": "Warning: free TCP ports 80 and 443 are required for stable operation (Let\u0027s Encrypt on 80, HTTPS/QUIC on 443).", + "hysteria_renew_ssl": "Renew SSL", + "hysteria_ssl_change_hint": "Changing the domain re-issues the Let\u0027s Encrypt certificate (port 80 must be free).", + "hysteria_ssl_required": "Domain and email are required for SSL", + "naiveproxy_desc": "NaiveProxy (stable) — HTTPS/HTTP2 via Caddy + forwardproxy, Let\u0027s Encrypt TLS. Client: use Karing. Do not use v2rayN; other clients untested.", + "naiveproxy_domain": "Domain", + "naiveproxy_email": "Let\u0027s Encrypt email", + "naiveproxy_port_hint": "TCP port 443 is fixed for NaiveProxy. Port 80/TCP must be free for certificate issuance.", + "naiveproxy_install_hint": "Point the domain A-record to this server before install. Free TCP ports 80 and 443 are required (Let\u0027s Encrypt and HTTPS proxy).", + "naiveproxy_dns_hint": "Create this DNS record before installation:", + "naiveproxy_ports_warning": "Warning: free TCP ports 80 and 443 are required for stable operation (Let\u0027s Encrypt on 80, HTTPS proxy on 443).", + "naiveproxy_client_hint": "Stable build. Do NOT use v2rayN. Confirmed working in Karing. Other clients are untested.", + "server_ssl_domain": "SSL domain (default)", + "server_ssl_email": "SSL email (default)", + "server_ssl_hint": "Used when installing Hysteria / NGINX on this server (Let\u0027s Encrypt).", + "container_logs": "Container logs", + "logs_btn": "📋 Logs", + "logs_live": "Live", + "logs_reconnect": "reconnecting…", + "no_logs": "No logs yet", + "container_status": "Status", + "exit_code": "Exit code", + "close": "Close", + "tls_emulation": "TLS Emulation", + "tls_domain": "Masking Domain", + "max_connections_limit": "Max Connections", + "max_connections_hint": "0 for unlimited. Limits total concurrent users.", + "traffic_reset_strategy_label": "Traffic reset strategy", + "traffic_reset_never": "Never reset", + "traffic_reset_daily": "Daily reset", + "traffic_reset_weekly": "Weekly reset", + "traffic_reset_monthly": "Monthly reset", + "traffic_reset_hint": "How often should user traffic be reset", + "traffic_used_total": "Total spent", + "traffic_reset_info": "Next reset: {}", + "services": "Services", + "dns_desc": "Personal DNS server to block ads and protect the privacy of your queries.", + "dns_internal_hint": "The service runs on the internal network (172.29.172.254) without binding to host ports.", + "telemt_port_hint": "Port for the Telegram proxy (typically 443).", + "socks5_desc": "Single-account SOCKS5 proxy (3proxy) with username/password authentication.", + "socks5_username": "Username", + "socks5_password": "Password", + "socks5_password_placeholder": "Leave empty to auto-generate", + "socks5_password_hint": "If left empty, a random 16-character password will be generated.", + "socks5_port_hint": "TCP port the proxy will listen on. The default in the official Amnezia client is 38080.", + "socks5_change_settings": "Change settings", + "socks5_settings_title": "SOCKS5 — Connection settings", + "socks5_port_change_hint": "Changing the port recreates the container; existing sessions will be dropped.", + "socks5_settings_saved": "SOCKS5 settings saved", + "adguard_desc": "AdGuard Home — DNS-based ad blocker with a web admin UI.", + "adguard_mode": "Installation mode", + "adguard_mode_sidebyside": "Side-by-side", + "adguard_mode_sidebyside_hint": "runs alongside AmneziaDNS on a separate internal IP (172.29.172.253)", + "adguard_mode_replace": "Replace AmneziaDNS", + "adguard_mode_replace_hint": "removes amnezia-dns and takes its IP — VPN clients use AdGuard immediately", + "adguard_dns_port": "DNS port (53)", + "adguard_dns_port_hint": "Internal DNS port served to VPN clients via the docker network.", + "adguard_web_port": "Web UI port", + "adguard_dot_port": "DoT port", + "adguard_doh_port": "DoH port", + "adguard_expose_web": "Expose to internet", + "adguard_expose": "Expose to host", + "adguard_install_hint": "Initial AdGuard setup runs through its built-in wizard via the web UI after install.", + "adguard_internal_ip": "Internal IP", + "adguard_web_ui": "Admin URL", + "adguard_open_ui": "Open Web UI", + "edit_server_title": "Edit server", + "edit_keep_credential": "Leave empty to keep the current value", + "server_saved": "Server settings saved", + "servers_reordered": "Servers reordered", + "ping_checking": "Checking connectivity...", + "offline": "offline", + "api_tokens_title": "API tokens", + "api_tokens_hint": "Bearer tokens for external integrations. Send the token in the `Authorization: Bearer \u003ctoken\u003e` header. Tokens have admin-equivalent rights and stop working if their owner is disabled or demoted.", + "api_tokens_empty": "No API tokens yet. Create one to integrate the panel with an external service.", + "api_tokens_create": "Create token", + "api_tokens_create_title": "Create API token", + "api_tokens_name_label": "Name", + "api_tokens_name_placeholder": "e.g. Linear integration, CI bot, monitoring", + "api_tokens_name_hint": "Used to identify this token in the list. The token value itself is generated for you.", + "api_tokens_name_required": "Token name is required", + "api_tokens_created_title": "Token created", + "api_tokens_value_label": "Token value", + "api_tokens_warning_title": "This is the only time you\u0027ll see this token.", + "api_tokens_warning_body": "Copy it now and store it in your integration\u0027s secret manager. The panel keeps only a hash — if you lose this value you\u0027ll need to revoke and recreate the token.", + "api_tokens_revoke": "Revoke", + "api_tokens_revoke_confirm": "Revoke token \"{}\"? Any integration using it will stop working.", + "api_tokens_revoked": "Token revoked", + "api_tokens_created": "Created", + "api_tokens_last_used": "Last used", + "api_tokens_owner": "Owner", + "api_tokens_never": "never", + "coming_soon": "Coming soon", + "promo_star_cta": "Star us on GitHub", + "aivpn_subtitle": "AI-driven protocol selection that picks the right tunnel for the moment. Land it sooner — drop a star.", + "revproxy_title": "Reverse Proxy", + "revproxy_subtitle": "High-performance traffic obfuscation: hide your VPN endpoint behind a polished public-facing site, drop the DPI footprint.", + "management": "Management", + "server_management": "Server Management", + "server_management_desc": "Perform system-level actions on your machine.", + "check_server_services": "Check the server services", + "reboot_server": "Reboot server", + "clear_server": "Clear server", + "remove_server": "Remove server from panel", + "ssl_title": "SSL / HTTPS Settings", + "enable_https": "Enable HTTPS", + "panel_port_label": "Panel Port", + "domain_label": "Domain name", + "cert_path_label": "SSL Certificate path (.pem)", + "key_path_label": "Private Key path (.pem)", + "or_paste_text": "OR paste certificate text", + "cert_text_label": "Certificate content (CRT)", + "key_text_label": "Private Key content (KEY)", + "ssl_hint": "After enabling, the panel will be accessible via https:// on the specified domain. Make sure the file paths or text data are correct.", + "about_title": "About \u0026 Updates", + "current_version": "Current version", + "check_updates": "Check for updates", + "checking_updates": "Checking...", + "update_available": "New version available", + "download_update": "Download update", + "up_to_date": "You have the latest version", + "warp_hint": "WARP routes this host outbound through Cloudflare. It does not create a public panel URL like Quick Tunnel or ngrok.", + "warp_install_hint": "Install the official Cloudflare WARP client first, then restart the panel.", + "warp_install_required": "Install WARP first", + "warp_connect": "Connect WARP", + "warp_disconnect": "Disconnect", + "warp_connected": "WARP connected", + "warp_disconnected": "WARP disconnected", + "warp_status_unknown": "WARP status is unknown.", + "warp_status_not_installed": "Cloudflare WARP CLI is not installed on this host.", + "warp_status_connected": "Cloudflare WARP is connected.", + "warp_status_connecting": "Cloudflare WARP is connecting.", + "warp_status_disconnected": "Cloudflare WARP is disconnected.", + "warp_status_not_registered": "Cloudflare WARP is installed but not registered yet. Connect will try to register automatically.", + "warp_status_service_unavailable": "Cloudflare WARP service is not available. Make sure warp-svc is running.", + "warp_status_error": "Cloudflare WARP returned an error.", + "install_starting": "Starting installation...", + "server_check_complete": "Check completed", + "server_connection_error": "Connection error", + "connections_load_error": "Error loading connections", + "connection_updated": "Connection updated", + "edit_connection": "Edit connection", + "enable_connection_progress": "Enabling connection...", + "disable_connection_progress": "Disabling connection...", + "connection_enabled": "Connection enabled", + "connection_disabled": "Connection disabled", + "gb": "GB", + "marketplace": "Templates", + "open_marketplace": "Open Templates", + "marketplace_desc": "Install protocol and service templates available for this server.", + "installed_apps": "Installed applications", + "installed_apps_hint": "Only installed protocols and services are shown here. Use Templates to add more.", + "no_installed_apps": "No installed applications yet", + "no_installed_apps_desc": "Open Templates to install protocols or services on this server.", + "install_another": "Install another", + "new_instance": "new instance", + "port_next_instance_hint": "Choose a free UDP port for the additional instance.", + "checking_server": "Checking server services...", + "web_servers": "Web servers", + "nginx_desc": "NGINX web server with Let\u0027s Encrypt HTTPS certificate and editable index.html.", + "nginx_domain": "Domain", + "nginx_email": "Let\u0027s Encrypt email", + "nginx_port_hint": "HTTPS port exposed on the server. Port 80/TCP must be free and reachable for Let\u0027s Encrypt validation.", + "nginx_install_hint": "The domain must already point to this server. Port 80/TCP is used temporarily/permanently for HTTP-01 certificate validation.", + "site": "Site", + "site_editor": "Site editor", + "site_saved": "Site saved", + "nginx_dns_hint": "Create this DNS record before installation:", + "backup": "Backup", + "backups": "Backups", + "backup_desc": "Backups are stored on the server and include protocol files (config, keys, clientsTable). For WireGuard / AWG you can also download a ZIP of all client .conf files.", + "create_backup": "Create backup", + "creating_backup": "Creating backup...", + "backup_created": "Backup created", + "no_backups": "No backups yet", + "no_backups_desc": "Create the first backup for this protocol.", + "export_client_configs": "Download all .conf", + "export_client_configs_hint": "ZIP with all WireGuard / AmneziaWG client configs (when the private key is stored).", + "exporting_client_configs": "Preparing ZIP…", + "export_client_configs_done": "Client configs ZIP downloaded", + "restore_protocol_backup": "Restore", + "restore_protocol_backup_confirm": "Restore this backup on the server? Current protocol files will be overwritten and the container will restart.", + "restoring_protocol_backup": "Restoring… please wait", + "restore_protocol_backup_done": "Backup restored", + "refresh": "Refresh", + "guest_allow_create_hint": "Creates a config for the guest user (3x-ui VLESS or selected server protocol).", + "invite_inbound_hint": "Inbounds are loaded from the selected 3x-ui panel. Only 3x-ui returns the config/share link.", + "invite_inbound_loading": "Loading inbounds from 3x-ui…", + "invite_inbound_empty": "No VLESS inbounds on this 3x-ui panel", + "invite_inbound_need_xui": "Configure 3x-ui in Settings first", + "invite_inbound_required": "Select a VLESS inbound from 3x-ui", + "invite_sub_url_missing_title": "Subscription URL is not set", + "invite_sub_url_missing_hint": "In Settings → 3x-ui set Subscription URL base so invite links issue /sub/… configs.", + "xui_url_label": "3x-ui URL", + "xui_url_hint": "Include scheme, port and webBasePath if configured (example: https://host:2053/secret)", + "xui_sub_url_label": "Subscription URL base", + "xui_sub_url_hint": "Public /sub base without trailing slash (example: https://host:2096/sub). Invite configs are issued as subscription links.", + "xui_api_token_label": "API Token (preferred)", + "xui_api_token_placeholder": "Settings → Security → API Token", + "xui_api_token_hint": "If set, username/password login is skipped", + "xui_username_label": "3x-ui Username", + "xui_password_label": "3x-ui Password", + "xui_sync_hint": "Clients from 3x-ui inbounds will be created, disabled, and deleted as panel users (matched by email)", + "xui_inbound_label": "VLESS inbound", + "xui_inbound_auto": "Auto (first VLESS inbound)", + "xui_inbound_hint": "Inbounds are loaded from 3x-ui. This site only creates the client and takes the ready share link — no local config generation.", + "xui_servers_title": "3x-ui servers", + "xui_servers_hint": "Add 3x-ui panels. When creating, pick a server and an inbound from the 3x-ui list — the panel returns the share link.", + "xui_servers_empty": "No 3x-ui servers yet. Click Add server.", + "xui_servers_manage_hint": "Panel URLs and subscription bases are managed in the “3x-ui servers” section below.", + "xui_server_add": "Add server", + "xui_server_name_label": "Name / description", + "xui_server_name_hint": "Shown in invites and user forms (e.g. US, NL, Home).", + "xui_server_select_label": "3x-ui server", + "xui_server_select_hint": "Pick a 3x-ui panel, then an inbound from the list that panel returns via API.", + "xui_server_delete_confirm": "Delete this 3x-ui server from the panel?", + "cascade_title": "Cascade (double VPN)", + "cascade_desc": "Clients connect to this entry server; internet exits through another server — to bypass country blocks.", + "cascade_hint": "ryderams-style AWG2 cascade: entry and exit must both have AmneziaWG/WireGuard. Clients keep using the ENTRY config only.", + "cascade_client_note": "Users connect only to the ENTRY server. Do not give them the exit cascade peer.", + "cascade_security_note": "Logic runs via panel SSH (no remote curl|bash). Based on ryderams/amneziawg-cascade approach.", + "cascade_entry_protocol": "Protocol on this server (entry)", + "cascade_exit_server": "Exit server", + "cascade_exit_server_none": "— Select server —", + "cascade_exit_protocol": "Protocol on exit server", + "cascade_enable": "Enable cascade", + "cascade_disable": "Disable cascade", + "cascade_working": "Setting up cascade…", + "cascade_disabling": "Disabling cascade…", + "cascade_enabled": "Cascade enabled", + "cascade_disabled": "Cascade disabled", + "cascade_off": "Off", + "cascade_on": "Active", + "cascade_configured_down": "Configured, tunnel down", + "cascade_exit_required": "Select an exit server", + "cascade_enable_confirm": "Enable cascade? A service client will be created on the exit server and a tunnel will be set up on this entry server.", + "cascade_disable_confirm": "Disable cascade on this server?", + "cascade_no_handshake": "Up, no handshake", + "cascade_settings_title": "Cascade settings (per step)", + "cascade_settings_desc": "If pages spin forever with no internet — keep all options on and click Enable cascade again.", + "cascade_opt_pin_exit": "Pin route to exit server", + "cascade_opt_pin_exit_hint": "Keeps the exit tunnel from looping through the VPN itself.", + "cascade_opt_remove_eth_masq": "Remove client NAT on eth0", + "cascade_opt_remove_eth_masq_hint": "Clients must not egress directly from the entry server.", + "cascade_opt_force_forward": "Force FORWARD between interfaces", + "cascade_opt_force_forward_hint": "Client traffic → cascade → exit.", + "cascade_opt_mss_clamp": "TCP MSS clamp", + "cascade_opt_mss_clamp_hint": "Fixes endless page loads on double VPN.", + "cascade_opt_wait_handshake": "Wait for exit handshake", + "cascade_opt_wait_handshake_hint": "Do not mark cascade active if the exit peer never answers.", + "cascade_verify_egress": "Verify egress IP", + "cascade_verify_egress_hint": "After handshake, curl ifconfig.me via the cascade Address; fail and roll back if empty.", + "cascade_opt_keep_dns": "Keep DNS from exit config", + "cascade_opt_keep_dns_hint": "Usually off — the client sets DNS itself.", + "cascade_opt_handshake_timeout": "Handshake timeout (sec)", + "cascade_opt_table_id": "Routing table id", + "cascade_opt_rule_priority": "ip rule priority", + "cascade_opt_allowed_ips": "AllowedIPs for entry→exit tunnel", + "cascade_opt_allowed_ips_hint": "Prefer 0.0.0.0/0 (IPv4); ::/0 often breaks double VPN.", + "cascade_opt_vpn_subnet": "Client subnet override", + "cascade_opt_vpn_subnet_hint": "Empty = auto-detect from server Address" } diff --git a/translations/fa.json b/translations/fa.json index 6639a29..b768b02 100644 --- a/translations/fa.json +++ b/translations/fa.json @@ -1,522 +1,567 @@ { - "nav_servers": "سرورها", - "nav_users": "کاربران", - "nav_invites": "لینک دعوت", - "nav_settings": "تنظیمات", - "nav_connections": "اتصال‌ها", - "nav_logout": "خروج", - "theme_dark": "تاریک", - "theme_light": "روشن", - "back_to_servers": "← بازگشت به سرورها", - "server_check": "بررسی", - "server_checking": "در حال بررسی...", - "protocols": "پروتکل‌ها", - "install": "نصب", - "uninstall": "حذف نصب", - "run": "در حال اجرا", - "stop": "متوقف شده", - "not_installed": "نصب نشده", - "installed": "نصب شده", - "add_server": "افزودن سرور", - "no_servers": "هیچ سروری اضافه نشده است", - "name": "نام", - "host": "میزبان", - "port": "پورت", - "status": "وضعیت", - "actions": "عملکردها", - "edit": "ویرایش", - "delete": "حذف", - "save": "ذخیره", - "cancel": "لغو", - "loading": "در حال بارگذاری...", - "copy": "کپی", - "download": "دانلود", - "config": "پیکربندی", - "qr_code": "کد QR", - "vpn_key": "کلید VPN", - "clients": "کلاینت‌ها", - "traffic": "ترافیک", - "limit": "محدودیت", - "manage": "مدیریت", - "no_protocols": "بدون پروتکل", - "add_server_desc": "اولین سرور VPN خود را اضافه کنید. برای اتصال به اطلاعات SSH نیاز دارید.", - "ssh_host": "میزبان / IP", - "ssh_port": "پورت SSH", - "ssh_user": "نام کاربری", - "ssh_password": "رمز عبور", - "ssh_key": "کلید SSH", - "ssh_key_placeholder": "کلید خصوصی SSH", - "ssh_key_hint": "محتوای فایل id_rsa یا id_ed25519 خود را اینجا قرار دهید", - "connect": "اتصال", - "connecting": "در حال اتصال...", - "server_added": "سرور با موفقیت اضافه شد", - "server_deleted": "سرور حذف شد", - "delete_confirm": "آیا از حذف این سرور مطمئن هستید؟", - "success": "موفقیت", - "error": "خطا", - "info": "اطلاعات", - "login_title": "ورود به پنل", - "username": "نام کاربری", - "password": "رمز عبور", - "captcha": "کد امنیتی", - "captcha_placeholder": "کاراکترهای تصویر را وارد کنید", - "captcha_hint": "برای نوسازی تصویر کلیک کنید", - "login": "ورود", - "logging_in": "در حال ورود...", - "login_error": "ورود ناموفق بود", - "check_done": "بررسی کامل شد", - "check_error": "خطای اتصال", - "ssh_ok": "اتصال SSH برقرار است", - "docker_ok": "داکر نصب است", - "docker_not_installed": "داکر نصب نیست", - "awg_desc": "نسخه جدید پروتکل بر پایه awg-go. پشتیبانی از مبهم‌سازی پیشرفته (S3, S4).", - "awg_legacy_desc": "نسخه اصلی AWG. سازگار با نسخه‌های قدیمی کلاینت.", - "xray_desc": "تغییر ظاهر ترافیک به ترافیک معمولی وب (XTLS-Reality). مقاوم در برابر فیلترینگ شدید.", - "not_checked": "بررسی نشده", - "connections": "اتصال‌ها", - "add": "افزودن", - "loading_connections": "در حال بارگذاری اتصال‌ها...", - "no_connections": "بدون اتصال", - "no_connections_desc": "اولین اتصال خود را برای ایجاد پیکربندی VPN اضافه کنید", - "install_protocol": "نصب پروتکل", - "port_default_hint": "پورت پیش‌فرض: 55424. مطمئن شوید این پورت آزاد است.", - "port_xray_hint": "پورت پیشنهادی: 443. مطمئن شوید توسط وب‌سرور دیگری اشغال نشده باشد.", - "reinstall": "نصب مجدد", - "uninstall_confirm": "حذف نصب {}؟ تمام اتصال‌ها و پیکربندی‌ها از بین خواهند رفت.", - "stop_container_confirm": "توقف کانتینر {}؟", - "start_container_confirm": "شروع کانتینر {}؟", - "stopping": "در حال توقف", - "starting": "در حال شروع", - "stopped": "متوقف شد", - "started": "شروع شد", - "server_config": "پیکربندی سرور", - "add_connection": "افزودن اتصال", - "connection_name": "نام اتصال", - "connection_name_placeholder": "مثال: iPhone, Laptop", - "assign_to_user": "تخصیص به کاربر (اختیاری)", - "no_assign": "— بدون تخصیص —", - "create": "ایجاد", - "creating": "در حال ایجاد...", - "connection_created": "اتصال \"{}\" ایجاد شد", - "delete_connection_confirm": "این اتصال حذف شود؟ پیکربندی دیگر کار نخواهد کرد.", - "connection_deleted": "اتصال حذف شد", - "config_tab": "📄 فایل", - "vpn_key_tab": "🔑 کلید-VPN", - "qr_code_tab": "📱 کد-QR", - "copy_config": "📋 کپی", - "download_conf": "⬇ دانلود .conf", - "copy_key": "📋 کپی کلید", - "vpn_link_hint": "این لینک را برای تنظیم خودکار در برنامه AmneziaVPN وارد کنید", - "qr_hint": "کد QR را در برنامه AmneziaVPN اسکن کنید", - "no_data": "بدون داده", - "start_btn": "▶ شروع", - "stop_btn": "⏹ توقف", - "config_btn": "⚙️ تنظیم", - "done": "انجام شد", - "installing": "در حال نصب...", - "start_install": "شروع نصب...", - "check_docker": "بررسی داکر...", - "prepare_host": "آماده‌سازی میزبان...", - "build_container": "ساخت کانتینر...", - "install_success": "پروتکل با موفقیت نصب شد!", - "install_error": "خطا در نصب: ", - "qr_error": "ایجاد کد QR ناموفق بود", - "users_title": "کاربران", - "search_placeholder": "جستجو با نام، ایمیل یا تلگرام...", - "add_user": "افزودن کاربر", - "prev_page": "← قبلی", - "next_page": "بعدی →", - "page_info": "صفحه {} از {}", - "loading_users": "در حال بارگذاری کاربران...", - "nothing_found": "چیزی یافت نشد", - "search_empty_desc": "جستجوی دیگری را امتحان کنید یا کاربر جدید بسازید", - "username_label": "نام کاربری", - "password_label": "رمز عبور", - "role_label": "نقش", - "role_user_desc": "User — فقط مشاهده اتصال‌های خود", - "role_support_desc": "Support — مدیریت سرورها", - "role_admin_desc": "Admin — دسترسی کامل", - "tg_id_label": "آیدی تلگرام (اختیاری)", - "email_label": "ایمیل (اختیاری)", - "traffic_limit_label": "محدودیت ترافیک (گیگابایت) (0 = نامحدود)", - "description_label": "توضیحات (اختیاری)", - "auto_conn_title": "ایجاد خودکار اتصال (اختیاری)", - "server_label": "سرور", - "no_create_conn": "— عدم ایجاد اتصال —", - "protocol_label": "پروتکل", - "edit_user_title": "ویرایش کاربر: {}", - "edit_user_tg": "آیدی تلگرام (اختیاری)", - "edit_user_email": "ایمیل (اختیاری)", - "edit_user_limit": "محدودیت ترافیک (گیگابایت) (0 = نامحدود)", - "new_password_hint": "رمز عبور جدید (اگر تغییری نمی‌خواهید خالی بگذارید)", - "op_type_label": "نوع عملیات", - "create_new_conn": "ایجاد اتصال جدید", - "link_existing_conn": "اتصال به موجود", - "select_existing_conn": "انتخاب اتصال موجود", - "existing_conn_hint": "فقط اتصال‌های بدون کاربر نمایش داده می‌شوند", - "conn_name_panel": "نام اتصال (در پنل)", - "user_conns_title": "اتصال‌های کاربر", - "no_free_conns": "اتصال آزاد موجود نیست", - "select_connection": "لطفا یک اتصال انتخاب کنید", - "linking": "در حال اتصال...", - "conn_linked": "اتصال برقرار شد", - "enabling": "در حال فعال‌سازی", - "disabling": "در حال غیرفعال‌سازی", - "user_enabled": "کاربر فعال شد", - "user_disabled": "کاربر غیرفعال شد", - "delete_user_confirm": "کاربر حذف شود؟ تمام اتصال‌های او حذف خواهند شد.", - "share_access": "اشتراک‌گذاری دسترسی", - "enable_public_access": "فعال‌سازی دسترسی عمومی", - "share_password_label": "رمز عبور (اختیاری)", - "share_password_hint": "در صورت تنظیم، کاربر باید آن را وارد کند", - "guest_settings_title": "دسترسی مهمان", - "guest_enable": "فعال‌سازی دسترسی مهمان (بدون ثبت‌نام)", - "guest_enable_hint": "لینک عمومی در صفحه ورود. مهمانان اتصال‌های کاربر انتخاب‌شده را می‌بینند.", - "guest_link_label": "لینک مهمان", - "guest_link_placeholder": "برای ساخت لینک تنظیمات را ذخیره کنید", - "guest_regen_token": "بازسازی لینک", - "guest_regen_confirm": "لینک مهمان بازسازی شود؟ لینک قبلی از کار می‌افتد.", - "guest_token_updated": "لینک مهمان به‌روز شد", - "guest_user_label": "کاربر اتصال‌های مهمان", - "guest_user_none": "— انتخاب کاربر —", - "guest_user_hint": "یک کاربر عادی (مثل guest) بسازید و VPN اختصاص دهید.", - "guest_password_label": "رمز مهمان (اختیاری)", - "guest_password_keep": "برای حفظ رمز فعلی خالی بگذارید", - "guest_clear_password": "حذف محافظت با رمز", - "guest_allow_create": "اجازه ساخت کانفیگ جدید به مهمان", - "guest_access_btn": "ادامه به‌عنوان مهمان", - "guest_access_login_hint": "بدون حساب کاربری", - "guest_title": "دسترسی مهمان", - "guest_subtitle": "دریافت کانفیگ VPN بدون ثبت‌نام", - "guest_protected_desc": "این صفحه با رمز عبور محافظت شده است.", - "guest_get_config": "دریافت کانفیگ", - "guest_get_config_hint": "یک کانفیگ VPN جدید برای شما ساخته می‌شود", - "guest_not_found": "404 یافت نشد", - "guest_not_found_desc": "دسترسی مهمان غیرفعال است یا لینک نامعتبر است.", - "wrong_guest_password": "رمز عبور اشتباه است", - "invites_title": "لینک‌های دعوت", - "invites_hint": "لینک قابل اشتراک بسازید. با لینک می‌توان کانفیگ VPN ساخت — تعداد را شما تعیین می‌کنید.", - "invites_empty": "هنوز لینکی نیست", - "invites_empty_desc": "لینک بسازید و به اشتراک بگذارید — بدون ثبت‌نام.", - "invite_create": "ساخت لینک", - "invite_edit": "ویرایش لینک", - "invite_active": "فعال", - "invite_expired": "منقضی", - "invite_exhausted": "ظرفیت تمام شده", - "invite_uses": "استفاده‌ها", - "invite_name_label": "نام لینک", - "invite_max_uses_label": "حداکثر کانفیگ", - "invite_max_uses_hint": "چند بار می‌توان با این لینک کانفیگ ساخت. 0 = نامحدود.", - "invite_user_label": "کاربر ذخیره‌سازی", - "invite_user_hint": "کانفیگ‌های ساخته‌شده به این کاربر پنل وصل می‌شوند.", - "invite_user_required": "یک کاربر انتخاب کنید", - "invite_password_label": "رمز عبور (اختیاری)", - "invite_expires_label": "انقضا (اختیاری)", - "invite_duration_label": "مدت کانفیگ (روز)", - "invite_duration_hint": "از لحظه کلیک روی دریافت کانفیگ شروع می‌شود. 0 = بدون انقضا.", - "invite_duration_short": "مدت", - "invite_duration_starts_hint": "پس از دریافت، کانفیگ {} روز معتبر است", - "invite_config_expires_at": "اعتبار تا: {}", - "invite_sub_tab": "اشتراک", - "days_short": "روز", - "invite_clear_expires": "حذف تاریخ انقضا", - "invite_note_label": "یادداشت (فقط ادمین)", - "invite_note_placeholder": "توضیح داخلی", - "invite_reset_used": "ریست شمارنده استفاده", - "invite_created": "لینک ساخته شد (URL کپی شد)", - "invite_updated": "لینک به‌روز شد", - "invite_deleted": "لینک حذف شد", - "invite_delete_confirm": "این لینک حذف شود؟", - "invite_public_title": "دعوت", - "invite_public_subtitle": "دریافت کانفیگ VPN با لینک دعوت", - "invite_protected_desc": "این لینک با رمز محافظت شده است.", - "invite_get_config_hint": "هر کلیک یک کانفیگ جدید می‌سازد و یک استفاده کم می‌کند", - "invite_uses_left": "باقی‌مانده: {}", - "invite_uses_left_unlimited": "ساخته‌شده: {} (نامحدود)", - "invite_not_found": "404 یافت نشد", - "invite_not_found_desc": "لینک نامعتبر است یا حذف شده.", - "wrong_invite_password": "رمز عبور اشتباه است", - "public_link_label": "لینک عمومی", - "disabled": "غیرفعال", - "out_of": "از", - "settings_title": "تنظیمات پنل", - "appearance": "🎨 ظاهر", - "title_label": "عنوان (Title)", - "logo_label": "لوگو (ایموجی یا نماد)", - "subtitle_label": "زیرعنوان (Subtitle)", - "captcha_title": "🔐 کد امنیتی", - "enable_captcha": "فعال‌سازی کد امنیتی", - "telegram_bot_title": "🤖 ربات تلگرام", - "bot_token_label": "توکن ربات (از @BotFather)", - "bot_status": "وضعیت", - "bot_running": "✅ در حال اجرا", - "bot_stopped": "⏹ متوقف شده", - "bot_stop_btn": "⏹ توقف", - "bot_start_btn": "▶️ شروع", - "bot_hint": "پس از تغییر توکن تنظیمات را ذخیره کنید تا ربات بازراه‌اندازی شود.", - "api_docs_title": "🔌 مستندات API", - "api_docs_hint": "از این رابط‌ها برای بررسی قابلیت‌های API استفاده کنید", - "tunnels_title": "Tunnels", - "local_server": "سرور محلی", - "tunnel_install_enable": "نصب و فعال‌سازی", - "tunnel_enable": "فعال‌سازی تونل", - "tunnel_running": "تونل در حال اجرا است", - "tunnel_stop": "توقف", - "tunnel_stopping": "در حال توقف...", - "tunnel_stopped": "تونل متوقف شد", - "tunnel_delete": "حذف", - "tunnel_deleted": "تونل حذف شد", - "tunnel_delete_confirm": "این فایل اجرایی تونل مدیریت‌شده توسط پنل متوقف و حذف شود؟", - "tunnel_starting": "در حال شروع تونل...", - "tunnel_started": "تونل شروع شد", - "tunnel_waiting_url": "تونل شروع شد. در انتظار آدرس عمومی...", - "tunnel_no_public_url": "آدرس عمومی پس از شروع تونل اینجا نمایش داده می‌شود.", - "tunnels_hint": "تونل‌های سریع این پنل محلی را در اینترنت منتشر می‌کنند. رمز مدیر را قوی نگه دارید و آدرس‌های عمومی را فقط با کاربران مورد اعتماد به اشتراک بگذارید.", - "ngrok_authtoken_placeholder": "ngrok authtoken (برای برخی حساب‌ها اختیاری است)", - "import_users_title": "📤 وارد کردن کاربران", - "import_source_label": "منبع وارد کردن / همگام‌سازی", - "remnawave_url_label": "آدرس Remnawave", - "api_key_label": "کلید API", - "enable_sync": "فعال‌سازی همگام‌سازی", - "sync_hint": "کاربران طبق داده‌های Remnawave به‌طور خودکار مدیریت می‌شوند", - "sync_now_btn": "🔄 همگام‌سازی اکنون", - "delete_sync_btn": "🗑 حذف همگام‌سازی", - "auto_create_conns": "ایجاد خودکار اتصال‌ها", - "sync_server_label": "سرور برای اتصال‌های جدید", - "save_changes": "💾 ذخیره تغییرات", - "sync_success": "همگام‌سازی انجام شد: {} کاربر", - "sync_deleted": "{} کاربر همگام‌سازی شده حذف شدند", - "delete_sync_confirm": "آیا از حذف تمام کاربران همگام‌سازی شده مطمئن هستید؟", - "settings_saved": "تنظیمات با موفقیت ذخیره شد", - "bot_started": "ربات شروع به کار کرد", - "bot_stopped_msg": "ربات متوقف شد", - "sync_running": "در حال همگام‌سازی...", - "deleting": "در حال حذف...", - "my_connections_title": "اتصال‌های من", - "show_config": "📄 نمایش پیکربندی", - "no_connections_user_desc": "برای ایجاد اتصال VPN با مدیر تماس بگیرید", - "share_title": "دسترسی به اتصال", - "vpn_access": "دسترسی VPN", - "user_label_share": "کاربر", - "share_protected_desc": "این دسترسی با رمز عبور محافظت شده است.", - "auth_success": "ورود موفقیت‌آمیز", - "loading_share_conns": "در حال بارگذاری...", - "no_active_conns": "هنوز هیچ اتصال فعالی ندارید.", - "show_settings_btn": "🔑 نمایش تنظیمات", - "copied": "کپی شد!", - "active": "فعال", - "site_description": "پنل وب آمنزیا — مدیریت سرورها و پروتکل‌های AmneziaWG", - "toggle_theme": "تغییر تم", - "copied_to_clipboard": "در حافظه کپی شد", - "share_not_found": "404 یافت نشد", - "share_not_found_desc": "لینک اشتباه است یا دسترسی غیرفعال شده است.", - "invalid_captcha": "کد امنیتی اشتباه است", - "account_disabled": "حساب کاربری غیرفعال است", - "invalid_login": "نام کاربری یا رمز عبور اشتباه است", - "user_exists": "کاربری با این نام قبلاً ثبت شده است", - "cannot_delete_self": "شما نمی‌توانید خودتان را حذف کنید", - "wrong_share_password": "رمز عبور اشتباه است", - "saving": "در حال ذخیره...", - "select_lang": "انتخاب زبان", - "lang_ru": "Русский", - "lang_en": "English", - "lang_fr": "Français", - "lang_zh": "中文 (Chinese)", - "lang_fa": "فارسی (Persian)", - "backup_title": "پشتیبان‌گیری ساده", - "download_backup": "دانلود data.json", - "restore_backup": "بازیابی از فایل", - "restore_confirm": "آیا مطمئن هستید؟ داده‌های فعلی بازنویسی می‌شوند و برنامه دوباره راه‌اندازی خواهد شد.", - "restore_success": "بازیابی موفقیت‌آمیز بود! در حال راه‌اندازی مجدد...", - "invalid_backup_file": "فایل پشتیبان یا ساختار نامعتبر است", - "config_unavailable": "پیکربندی در دسترس نیست", - "config_unavailable_desc": "این کلاینت از طریق اپلیکیشن اصلی Amnezia ایجاد شده است.\\nکلید خصوصی فقط در دستگاه کاربر ذخیره می‌شود و توسط سرور قابل بازیابی نیست.", - "client_public_key": "کلید عمومی کلاینت:", - "management": "مدیریت", - "server_management": "مدیریت سرور", - "server_management_desc": "انجام اقدامات در سطح سیستم روی دستگاه شما.", - "check_server_services": "بررسی سرویس‌های سرور", - "reboot_server": "راه‌اندازی مجدد سرور", - "clear_server": "پاک‌سازی سرور", - "remove_server": "حذف سرور از پنل", - "telemt_desc": "پروکسی تلگرام بر پایه‌ی MTProxy با پوشش‌های پیشرفته.", - "hysteria_desc": "Hysteria 2 (QUIC/UDP) با تصویر رسمی tobyxdd/hysteria (apernet/hysteria) و TLS از Let's Encrypt. دامنه را ادمین هنگام نصب مشخص می‌کند.", - "hysteria_domain": "دامنه", - "hysteria_email": "ایمیل Let's Encrypt", - "hysteria_port_hint": "پورت UDP برای Hysteria (پیش‌فرض 8998). اگر 443 اشغال است پورت دیگری بگذارید.", - "hysteria_install_hint": "قبل از نصب، رکورد A دامنه باید به این سرور اشاره کند. BBR و obfuscation salamander به‌صورت خودکار فعال می‌شوند.", - "hysteria_dns_hint": "قبل از نصب این رکورد DNS را بسازید:", - "hysteria_settings_title": "تنظیمات Hysteria", - "hysteria_change_port": "تغییر پورت", - "hysteria_port_change_hint": "پورت UDP. اگر address already in use دیدید، پورت آزاد بگذارید (8443 یا 8998).", - "hysteria_settings_saved": "پورت Hysteria به‌روز شد", - "hysteria_ports_warning": "هشدار: برای کار پایدار پورت‌های TCP آزاد ۸۰ و ۴۴۳ لازم است (Let's Encrypt روی ۸۰، HTTPS/QUIC روی ۴۴۳).", - "hysteria_renew_ssl": "تمدید SSL", - "hysteria_ssl_change_hint": "تغییر دامنه گواهی Let's Encrypt را دوباره صادر می‌کند (پورت ۸۰ باید آزاد باشد).", - "hysteria_ssl_required": "برای SSL دامنه و ایمیل لازم است", - "naiveproxy_desc": "NaiveProxy (پایدار) — HTTPS/HTTP2 با Caddy + forwardproxy و TLS از Let's Encrypt. کلاینت: Karing. از v2rayN استفاده نکنید؛ بقیه آزمایش نشده‌اند.", - "naiveproxy_domain": "دامنه", - "naiveproxy_email": "ایمیل Let's Encrypt", - "naiveproxy_port_hint": "پورت TCP 443 برای NaiveProxy ثابت است. پورت 80/TCP باید برای صدور گواهی آزاد باشد.", - "naiveproxy_install_hint": "قبل از نصب، رکورد A دامنه باید به این سرور اشاره کند. پورت‌های TCP آزاد ۸۰ و ۴۴۳ لازم است (Let's Encrypt و پروکسی HTTPS).", - "naiveproxy_dns_hint": "قبل از نصب این رکورد DNS را بسازید:", - "naiveproxy_ports_warning": "هشدار: برای کار پایدار پورت‌های TCP آزاد ۸۰ و ۴۴۳ لازم است (Let's Encrypt روی ۸۰، پروکسی HTTPS روی ۴۴۳).", - "naiveproxy_client_hint": "نسخه پایدار. از v2rayN استفاده نکنید. در Karing تأیید شده. سایر کلاینت‌ها آزمایش نشده‌اند.", - "server_ssl_domain": "دامنه SSL (پیش‌فرض)", - "server_ssl_email": "ایمیل SSL (پیش‌فرض)", - "server_ssl_hint": "هنگام نصب Hysteria / NGINX روی این سرور استفاده می‌شود (Let's Encrypt).", - "container_logs": "لاگ‌های کانتینر", - "logs_btn": "📋 Logs", - "logs_live": "زنده", - "logs_reconnect": "در حال اتصال مجدد…", - "no_logs": "هنوز لاگی نیست", - "container_status": "وضعیت", - "exit_code": "کد خروج", - "dns_desc": "سرور DNS شخصی برای مسدود کردن تبلیغات و حفظ حریم خصوصی.", - "dns_internal_hint": "The service runs on the internal network (172.29.172.254) without binding to host ports.", - "telemt_port_hint": "Port for the Telegram proxy (typically 443).", - "socks5_desc": "Single-account SOCKS5 proxy (3proxy) with username/password authentication.", - "socks5_username": "Username", - "socks5_password": "Password", - "socks5_password_placeholder": "Leave empty to auto-generate", - "socks5_password_hint": "If left empty, a random 16-character password will be generated.", - "socks5_port_hint": "TCP port the proxy will listen on. The default in the official Amnezia client is 38080.", - "socks5_change_settings": "Change settings", - "socks5_settings_title": "SOCKS5 — Connection settings", - "socks5_port_change_hint": "Changing the port recreates the container; existing sessions will be dropped.", - "socks5_settings_saved": "SOCKS5 settings saved", - "adguard_desc": "AdGuard Home — DNS-based ad blocker with a web admin UI.", - "adguard_mode": "Installation mode", - "adguard_mode_sidebyside": "Side-by-side", - "adguard_mode_sidebyside_hint": "runs alongside AmneziaDNS on a separate internal IP (172.29.172.253)", - "adguard_mode_replace": "Replace AmneziaDNS", - "adguard_mode_replace_hint": "removes amnezia-dns and takes its IP — VPN clients use AdGuard immediately", - "adguard_dns_port": "DNS port (53)", - "adguard_dns_port_hint": "Internal DNS port served to VPN clients via the docker network.", - "adguard_web_port": "Web UI port", - "adguard_dot_port": "DoT port", - "adguard_doh_port": "DoH port", - "adguard_expose_web": "Expose to internet", - "adguard_expose": "Expose to host", - "adguard_install_hint": "Initial AdGuard setup runs through its built-in wizard via the web UI after install.", - "adguard_internal_ip": "Internal IP", - "adguard_web_ui": "Admin URL", - "adguard_open_ui": "Open Web UI", - "edit_server_title": "Edit server", - "edit_keep_credential": "Leave empty to keep the current value", - "server_saved": "Server settings saved", - "servers_reordered": "Servers reordered", - "ping_checking": "Checking connectivity...", - "offline": "offline", - "api_tokens_title": "API tokens", - "api_tokens_hint": "Bearer tokens for external integrations. Send the token in the `Authorization: Bearer ` header. Tokens have admin-equivalent rights and stop working if their owner is disabled or demoted.", - "api_tokens_empty": "No API tokens yet. Create one to integrate the panel with an external service.", - "api_tokens_create": "Create token", - "api_tokens_create_title": "Create API token", - "api_tokens_name_label": "Name", - "api_tokens_name_placeholder": "e.g. Linear integration, CI bot, monitoring", - "api_tokens_name_hint": "Used to identify this token in the list. The token value itself is generated for you.", - "api_tokens_name_required": "Token name is required", - "api_tokens_created_title": "Token created", - "api_tokens_value_label": "Token value", - "api_tokens_warning_title": "This is the only time you'll see this token.", - "api_tokens_warning_body": "Copy it now and store it in your integration's secret manager. The panel keeps only a hash — if you lose this value you'll need to revoke and recreate the token.", - "api_tokens_revoke": "Revoke", - "api_tokens_revoke_confirm": "Revoke token \"{}\"? Any integration using it will stop working.", - "api_tokens_revoked": "Token revoked", - "api_tokens_created": "Created", - "api_tokens_last_used": "Last used", - "api_tokens_owner": "Owner", - "api_tokens_never": "never", - "coming_soon": "Coming soon", - "promo_star_cta": "Star us on GitHub", - "aivpn_subtitle": "AI-driven protocol selection that picks the right tunnel for the moment. Land it sooner — drop a star.", - "revproxy_title": "Reverse Proxy", - "revproxy_subtitle": "High-performance traffic obfuscation: hide your VPN endpoint behind a polished public-facing site, drop the DPI footprint.", - "warp_hint": "WARP routes this host outbound through Cloudflare. It does not create a public panel URL like Quick Tunnel or ngrok.", - "warp_install_hint": "Install the official Cloudflare WARP client first, then restart the panel.", - "warp_install_required": "Install WARP first", - "warp_connect": "Connect WARP", - "warp_disconnect": "Disconnect", - "warp_connected": "WARP connected", - "warp_disconnected": "WARP disconnected", - "warp_status_unknown": "WARP status is unknown.", - "warp_status_not_installed": "Cloudflare WARP CLI is not installed on this host.", - "warp_status_connected": "Cloudflare WARP is connected.", - "warp_status_connecting": "Cloudflare WARP is connecting.", - "warp_status_disconnected": "Cloudflare WARP is disconnected.", - "warp_status_not_registered": "Cloudflare WARP is installed but not registered yet. Connect will try to register automatically.", - "warp_status_service_unavailable": "Cloudflare WARP service is not available. Make sure warp-svc is running.", - "warp_status_error": "Cloudflare WARP returned an error.", - "install_starting": "Starting installation...", - "server_check_complete": "Check completed", - "server_connection_error": "Connection error", - "connections_load_error": "Error loading connections", - "connection_updated": "Connection updated", - "edit_connection": "Edit connection", - "enable_connection_progress": "Enabling connection...", - "disable_connection_progress": "Disabling connection...", - "connection_enabled": "Connection enabled", - "connection_disabled": "Connection disabled", - "gb": "GB", - "marketplace": "Templates", - "open_marketplace": "Open Templates", - "marketplace_desc": "Install protocol and service templates available for this server.", - "installed_apps": "Installed applications", - "installed_apps_hint": "Only installed protocols and services are shown here. Use Templates to add more.", - "no_installed_apps": "No installed applications yet", - "no_installed_apps_desc": "Open Templates to install protocols or services on this server.", - "install_another": "Install another", - "new_instance": "new instance", - "port_next_instance_hint": "Choose a free UDP port for the additional instance.", - "web_servers": "Web servers", - "nginx_desc": "NGINX web server with Let's Encrypt HTTPS certificate and editable index.html.", - "nginx_domain": "Domain", - "nginx_email": "Let's Encrypt email", - "nginx_port_hint": "HTTPS port exposed on the server. Port 80/TCP must be free and reachable for Let's Encrypt validation.", - "nginx_install_hint": "The domain must already point to this server. Port 80/TCP is used for HTTP-01 certificate validation.", - "site": "Site", - "site_editor": "Site editor", - "site_saved": "Site saved", - "nginx_dns_hint": "Create this DNS record before installation:", - "backup": "Backup", - "backups": "Backups", - "backup_desc": "Backups are created on the server and contain valuable protocol files needed to restore or recreate it later.", - "create_backup": "Create backup", - "creating_backup": "Creating backup...", - "backup_created": "Backup created", - "no_backups": "No backups yet", - "no_backups_desc": "Create the first backup for this protocol.", - "refresh": "Refresh", - "guest_allow_create_hint": "برای کاربر مهمان کانفیگ می‌سازد (3x-ui VLESS یا پروتکل سرور).", - "invite_inbound_hint": "یک‌بار inbound مورد نیاز VLESS را انتخاب کنید", - "invite_inbound_loading": "در حال بارگذاری…", - "invite_inbound_empty": "inbound VLESS یافت نشد", - "invite_inbound_need_xui": "ابتدا 3x-ui را در تنظیمات پیکربندی کنید", - "invite_inbound_required": "یک inbound VLESS انتخاب کنید", - "invite_sub_url_missing_title": "آدرس اشتراک تنظیم نشده", - "invite_sub_url_missing_hint": "در تنظیمات → 3x-ui پایه /sub را وارد کنید.", - "xui_url_label": "آدرس 3x-ui", - "xui_url_hint": "شامل پروتکل، پورت و webBasePath در صورت وجود", - "xui_sub_url_label": "آدرس پایه اشتراک", - "xui_sub_url_hint": "پایه عمومی /sub بدون اسلش پایانی (مثال: https://host:2096/sub).", - "xui_api_token_label": "توکن API (ترجیحی)", - "xui_api_token_placeholder": "Settings → Security → API Token", - "xui_api_token_hint": "در صورت تنظیم، ورود با نام کاربری/رمز رد می‌شود", - "xui_username_label": "نام کاربری 3x-ui", - "xui_password_label": "رمز عبور 3x-ui", - "xui_sync_hint": "کلاینت‌های 3x-ui بر اساس email به کاربران پنل همگام می‌شوند", - "xui_inbound_label": "ورودی VLESS", - "xui_inbound_auto": "خودکار (اولین ورودی VLESS)", - "xui_inbound_hint": "برای ساخت کانفیگ 3x-ui VLESS استفاده می‌شود. ابتدا یک ورودی VLESS در 3x-ui بسازید.", - "xui_servers_title": "سرورهای 3x-ui", - "xui_servers_hint": "چند پنل 3x-ui اضافه کنید. هنگام ساخت، سرور را با نام انتخاب کنید تا URL اشتراک همان سرور داده شود.", - "xui_servers_empty": "هنوز سرور 3x-ui نیست. افزودن را بزنید.", - "xui_servers_manage_hint": "آدرس پنل و پایه اشتراک در بخش «سرورهای 3x-ui» مدیریت می‌شود.", - "xui_server_add": "افزودن سرور", - "xui_server_name_label": "نام / توضیح", - "xui_server_name_hint": "در دعوت‌ها نمایش داده می‌شود (مثل US، NL).", - "xui_server_select_label": "سرور 3x-ui", - "xui_server_select_hint": "پنل 3x-ui که URL اشتراک می‌دهد را انتخاب کنید.", - "xui_server_delete_confirm": "این سرور 3x-ui حذف شود؟" + "nav_servers": "سرورها", + "nav_users": "کاربران", + "nav_invites": "لینک دعوت", + "nav_settings": "تنظیمات", + "nav_connections": "اتصال‌ها", + "nav_logout": "خروج", + "theme_dark": "تاریک", + "theme_light": "روشن", + "back_to_servers": "← بازگشت به سرورها", + "server_check": "بررسی", + "server_checking": "در حال بررسی...", + "protocols": "پروتکل‌ها", + "install": "نصب", + "uninstall": "حذف نصب", + "run": "در حال اجرا", + "stop": "متوقف شده", + "not_installed": "نصب نشده", + "installed": "نصب شده", + "add_server": "افزودن سرور", + "no_servers": "هیچ سروری اضافه نشده است", + "name": "نام", + "host": "میزبان", + "port": "پورت", + "status": "وضعیت", + "actions": "عملکردها", + "edit": "ویرایش", + "delete": "حذف", + "save": "ذخیره", + "cancel": "لغو", + "loading": "در حال بارگذاری...", + "copy": "کپی", + "download": "دانلود", + "config": "پیکربندی", + "qr_code": "کد QR", + "vpn_key": "کلید VPN", + "clients": "کلاینت‌ها", + "traffic": "ترافیک", + "limit": "محدودیت", + "manage": "مدیریت", + "no_protocols": "بدون پروتکل", + "add_server_desc": "اولین سرور VPN خود را اضافه کنید. برای اتصال به اطلاعات SSH نیاز دارید.", + "ssh_host": "میزبان / IP", + "ssh_port": "پورت SSH", + "ssh_user": "نام کاربری", + "ssh_password": "رمز عبور", + "ssh_key": "کلید SSH", + "ssh_key_placeholder": "کلید خصوصی SSH", + "ssh_key_hint": "محتوای فایل id_rsa یا id_ed25519 خود را اینجا قرار دهید", + "connect": "اتصال", + "connecting": "در حال اتصال...", + "server_added": "سرور با موفقیت اضافه شد", + "server_deleted": "سرور حذف شد", + "delete_confirm": "آیا از حذف این سرور مطمئن هستید؟", + "success": "موفقیت", + "error": "خطا", + "info": "اطلاعات", + "login_title": "ورود به پنل", + "username": "نام کاربری", + "password": "رمز عبور", + "captcha": "کد امنیتی", + "captcha_placeholder": "کاراکترهای تصویر را وارد کنید", + "captcha_hint": "برای نوسازی تصویر کلیک کنید", + "login": "ورود", + "logging_in": "در حال ورود...", + "login_error": "ورود ناموفق بود", + "check_done": "بررسی کامل شد", + "check_error": "خطای اتصال", + "ssh_ok": "اتصال SSH برقرار است", + "docker_ok": "داکر نصب است", + "docker_not_installed": "داکر نصب نیست", + "awg_desc": "نسخه جدید پروتکل بر پایه awg-go. پشتیبانی از مبهم‌سازی پیشرفته (S3, S4).", + "awg_legacy_desc": "نسخه اصلی AWG. سازگار با نسخه‌های قدیمی کلاینت.", + "xray_desc": "تغییر ظاهر ترافیک به ترافیک معمولی وب (XTLS-Reality). مقاوم در برابر فیلترینگ شدید.", + "not_checked": "بررسی نشده", + "connections": "اتصال‌ها", + "add": "افزودن", + "loading_connections": "در حال بارگذاری اتصال‌ها...", + "no_connections": "بدون اتصال", + "no_connections_desc": "اولین اتصال خود را برای ایجاد پیکربندی VPN اضافه کنید", + "install_protocol": "نصب پروتکل", + "port_default_hint": "پورت پیش‌فرض: 55424. مطمئن شوید این پورت آزاد است.", + "port_xray_hint": "پورت پیشنهادی: 443. مطمئن شوید توسط وب‌سرور دیگری اشغال نشده باشد.", + "reinstall": "نصب مجدد", + "uninstall_confirm": "حذف نصب {}؟ تمام اتصال‌ها و پیکربندی‌ها از بین خواهند رفت.", + "stop_container_confirm": "توقف کانتینر {}؟", + "start_container_confirm": "شروع کانتینر {}؟", + "stopping": "در حال توقف", + "starting": "در حال شروع", + "stopped": "متوقف شد", + "started": "شروع شد", + "server_config": "پیکربندی سرور", + "add_connection": "افزودن اتصال", + "connection_name": "نام اتصال", + "connection_name_placeholder": "مثال: iPhone, Laptop", + "assign_to_user": "تخصیص به کاربر (اختیاری)", + "no_assign": "— بدون تخصیص —", + "create": "ایجاد", + "creating": "در حال ایجاد...", + "connection_created": "اتصال \"{}\" ایجاد شد", + "delete_connection_confirm": "این اتصال حذف شود؟ پیکربندی دیگر کار نخواهد کرد.", + "connection_deleted": "اتصال حذف شد", + "config_tab": "📄 فایل", + "vpn_key_tab": "🔑 کلید-VPN", + "qr_code_tab": "📱 کد-QR", + "copy_config": "📋 کپی", + "download_conf": "⬇ دانلود .conf", + "copy_key": "📋 کپی کلید", + "vpn_link_hint": "این لینک را برای تنظیم خودکار در برنامه AmneziaVPN وارد کنید", + "qr_hint": "کد QR را در برنامه AmneziaVPN اسکن کنید", + "no_data": "بدون داده", + "start_btn": "▶ شروع", + "stop_btn": "⏹ توقف", + "config_btn": "⚙️ تنظیم", + "done": "انجام شد", + "installing": "در حال نصب...", + "start_install": "شروع نصب...", + "check_docker": "بررسی داکر...", + "prepare_host": "آماده‌سازی میزبان...", + "build_container": "ساخت کانتینر...", + "install_success": "پروتکل با موفقیت نصب شد!", + "install_error": "خطا در نصب: ", + "qr_error": "ایجاد کد QR ناموفق بود", + "users_title": "کاربران", + "search_placeholder": "جستجو با نام، ایمیل یا تلگرام...", + "add_user": "افزودن کاربر", + "prev_page": "← قبلی", + "next_page": "بعدی →", + "page_info": "صفحه {} از {}", + "loading_users": "در حال بارگذاری کاربران...", + "nothing_found": "چیزی یافت نشد", + "search_empty_desc": "جستجوی دیگری را امتحان کنید یا کاربر جدید بسازید", + "username_label": "نام کاربری", + "password_label": "رمز عبور", + "role_label": "نقش", + "role_user_desc": "User — فقط مشاهده اتصال‌های خود", + "role_support_desc": "Support — مدیریت سرورها", + "role_admin_desc": "Admin — دسترسی کامل", + "tg_id_label": "آیدی تلگرام (اختیاری)", + "email_label": "ایمیل (اختیاری)", + "traffic_limit_label": "محدودیت ترافیک (گیگابایت) (0 = نامحدود)", + "description_label": "توضیحات (اختیاری)", + "auto_conn_title": "ایجاد خودکار اتصال (اختیاری)", + "server_label": "سرور", + "no_create_conn": "— عدم ایجاد اتصال —", + "protocol_label": "پروتکل", + "edit_user_title": "ویرایش کاربر: {}", + "edit_user_tg": "آیدی تلگرام (اختیاری)", + "edit_user_email": "ایمیل (اختیاری)", + "edit_user_limit": "محدودیت ترافیک (گیگابایت) (0 = نامحدود)", + "new_password_hint": "رمز عبور جدید (اگر تغییری نمی‌خواهید خالی بگذارید)", + "op_type_label": "نوع عملیات", + "create_new_conn": "ایجاد اتصال جدید", + "link_existing_conn": "اتصال به موجود", + "select_existing_conn": "انتخاب اتصال موجود", + "existing_conn_hint": "فقط اتصال‌های بدون کاربر نمایش داده می‌شوند", + "conn_name_panel": "نام اتصال (در پنل)", + "user_conns_title": "اتصال‌های کاربر", + "no_free_conns": "اتصال آزاد موجود نیست", + "select_connection": "لطفا یک اتصال انتخاب کنید", + "linking": "در حال اتصال...", + "conn_linked": "اتصال برقرار شد", + "enabling": "در حال فعال‌سازی", + "disabling": "در حال غیرفعال‌سازی", + "user_enabled": "کاربر فعال شد", + "user_disabled": "کاربر غیرفعال شد", + "delete_user_confirm": "کاربر حذف شود؟ تمام اتصال‌های او حذف خواهند شد.", + "share_access": "اشتراک‌گذاری دسترسی", + "enable_public_access": "فعال‌سازی دسترسی عمومی", + "share_password_label": "رمز عبور (اختیاری)", + "share_password_hint": "در صورت تنظیم، کاربر باید آن را وارد کند", + "guest_settings_title": "دسترسی مهمان", + "guest_enable": "فعال‌سازی دسترسی مهمان (بدون ثبت‌نام)", + "guest_enable_hint": "لینک عمومی در صفحه ورود. مهمانان اتصال‌های کاربر انتخاب‌شده را می‌بینند.", + "guest_link_label": "لینک مهمان", + "guest_link_placeholder": "برای ساخت لینک تنظیمات را ذخیره کنید", + "guest_regen_token": "بازسازی لینک", + "guest_regen_confirm": "لینک مهمان بازسازی شود؟ لینک قبلی از کار می‌افتد.", + "guest_token_updated": "لینک مهمان به‌روز شد", + "guest_user_label": "کاربر اتصال‌های مهمان", + "guest_user_none": "— انتخاب کاربر —", + "guest_user_hint": "یک کاربر عادی (مثل guest) بسازید و VPN اختصاص دهید.", + "guest_password_label": "رمز مهمان (اختیاری)", + "guest_password_keep": "برای حفظ رمز فعلی خالی بگذارید", + "guest_clear_password": "حذف محافظت با رمز", + "guest_allow_create": "اجازه ساخت کانفیگ جدید به مهمان", + "guest_access_btn": "ادامه به‌عنوان مهمان", + "guest_access_login_hint": "بدون حساب کاربری", + "guest_title": "دسترسی مهمان", + "guest_subtitle": "دریافت کانفیگ VPN بدون ثبت‌نام", + "guest_protected_desc": "این صفحه با رمز عبور محافظت شده است.", + "guest_get_config": "دریافت کانفیگ", + "guest_get_config_hint": "یک کانفیگ VPN جدید برای شما ساخته می‌شود", + "guest_not_found": "404 یافت نشد", + "guest_not_found_desc": "دسترسی مهمان غیرفعال است یا لینک نامعتبر است.", + "wrong_guest_password": "رمز عبور اشتباه است", + "invites_title": "لینک‌های دعوت", + "invites_hint": "لینک قابل اشتراک بسازید. با لینک می‌توان کانفیگ VPN ساخت — تعداد را شما تعیین می‌کنید.", + "invites_empty": "هنوز لینکی نیست", + "invites_empty_desc": "لینک بسازید و به اشتراک بگذارید — بدون ثبت‌نام.", + "invite_create": "ساخت لینک", + "invite_edit": "ویرایش لینک", + "invite_active": "فعال", + "invite_expired": "منقضی", + "invite_exhausted": "ظرفیت تمام شده", + "invite_uses": "استفاده‌ها", + "invite_name_label": "نام لینک", + "invite_max_uses_label": "حداکثر کانفیگ", + "invite_max_uses_hint": "چند بار می‌توان با این لینک کانفیگ ساخت. 0 = نامحدود.", + "invite_user_label": "کاربر ذخیره‌سازی", + "invite_user_hint": "کانفیگ‌های ساخته‌شده به این کاربر پنل وصل می‌شوند.", + "invite_user_required": "یک کاربر انتخاب کنید", + "invite_password_label": "رمز عبور (اختیاری)", + "invite_expires_label": "انقضا (اختیاری)", + "invite_duration_label": "مدت کانفیگ (روز)", + "invite_duration_hint": "از لحظه کلیک روی دریافت کانفیگ شروع می‌شود. 0 = بدون انقضا.", + "invite_duration_short": "مدت", + "invite_duration_starts_hint": "پس از دریافت، کانفیگ {} روز معتبر است", + "invite_config_expires_at": "اعتبار تا: {}", + "invite_sub_tab": "اشتراک", + "days_short": "روز", + "invite_clear_expires": "حذف تاریخ انقضا", + "invite_note_label": "یادداشت (فقط ادمین)", + "invite_note_placeholder": "توضیح داخلی", + "invite_reset_used": "ریست شمارنده استفاده", + "invite_created": "لینک ساخته شد (URL کپی شد)", + "invite_updated": "لینک به‌روز شد", + "invite_deleted": "لینک حذف شد", + "invite_delete_confirm": "این لینک حذف شود؟", + "invite_public_title": "دعوت", + "invite_public_subtitle": "دریافت کانفیگ VPN با لینک دعوت", + "invite_protected_desc": "این لینک با رمز محافظت شده است.", + "invite_get_config_hint": "هر کلیک یک کانفیگ جدید می‌سازد و یک استفاده کم می‌کند", + "invite_uses_left": "باقی‌مانده: {}", + "invite_uses_left_unlimited": "ساخته‌شده: {} (نامحدود)", + "invite_not_found": "404 یافت نشد", + "invite_not_found_desc": "لینک نامعتبر است یا حذف شده.", + "wrong_invite_password": "رمز عبور اشتباه است", + "public_link_label": "لینک عمومی", + "disabled": "غیرفعال", + "out_of": "از", + "settings_title": "تنظیمات پنل", + "appearance": "🎨 ظاهر", + "title_label": "عنوان (Title)", + "logo_label": "لوگو (ایموجی یا نماد)", + "subtitle_label": "زیرعنوان (Subtitle)", + "captcha_title": "🔐 کد امنیتی", + "enable_captcha": "فعال‌سازی کد امنیتی", + "telegram_bot_title": "🤖 ربات تلگرام", + "bot_token_label": "توکن ربات (از @BotFather)", + "bot_status": "وضعیت", + "bot_running": "✅ در حال اجرا", + "bot_stopped": "⏹ متوقف شده", + "bot_stop_btn": "⏹ توقف", + "bot_start_btn": "▶️ شروع", + "bot_hint": "پس از تغییر توکن تنظیمات را ذخیره کنید تا ربات بازراه‌اندازی شود.", + "api_docs_title": "🔌 مستندات API", + "api_docs_hint": "از این رابط‌ها برای بررسی قابلیت‌های API استفاده کنید", + "tunnels_title": "Tunnels", + "local_server": "سرور محلی", + "tunnel_install_enable": "نصب و فعال‌سازی", + "tunnel_enable": "فعال‌سازی تونل", + "tunnel_running": "تونل در حال اجرا است", + "tunnel_stop": "توقف", + "tunnel_stopping": "در حال توقف...", + "tunnel_stopped": "تونل متوقف شد", + "tunnel_delete": "حذف", + "tunnel_deleted": "تونل حذف شد", + "tunnel_delete_confirm": "این فایل اجرایی تونل مدیریت‌شده توسط پنل متوقف و حذف شود؟", + "tunnel_starting": "در حال شروع تونل...", + "tunnel_started": "تونل شروع شد", + "tunnel_waiting_url": "تونل شروع شد. در انتظار آدرس عمومی...", + "tunnel_no_public_url": "آدرس عمومی پس از شروع تونل اینجا نمایش داده می‌شود.", + "tunnels_hint": "تونل‌های سریع این پنل محلی را در اینترنت منتشر می‌کنند. رمز مدیر را قوی نگه دارید و آدرس‌های عمومی را فقط با کاربران مورد اعتماد به اشتراک بگذارید.", + "ngrok_authtoken_placeholder": "ngrok authtoken (برای برخی حساب‌ها اختیاری است)", + "import_users_title": "📤 وارد کردن کاربران", + "import_source_label": "منبع وارد کردن / همگام‌سازی", + "remnawave_url_label": "آدرس Remnawave", + "api_key_label": "کلید API", + "enable_sync": "فعال‌سازی همگام‌سازی", + "sync_hint": "کاربران طبق داده‌های Remnawave به‌طور خودکار مدیریت می‌شوند", + "sync_now_btn": "🔄 همگام‌سازی اکنون", + "delete_sync_btn": "🗑 حذف همگام‌سازی", + "auto_create_conns": "ایجاد خودکار اتصال‌ها", + "sync_server_label": "سرور برای اتصال‌های جدید", + "save_changes": "💾 ذخیره تغییرات", + "sync_success": "همگام‌سازی انجام شد: {} کاربر", + "sync_deleted": "{} کاربر همگام‌سازی شده حذف شدند", + "delete_sync_confirm": "آیا از حذف تمام کاربران همگام‌سازی شده مطمئن هستید؟", + "settings_saved": "تنظیمات با موفقیت ذخیره شد", + "bot_started": "ربات شروع به کار کرد", + "bot_stopped_msg": "ربات متوقف شد", + "sync_running": "در حال همگام‌سازی...", + "deleting": "در حال حذف...", + "my_connections_title": "اتصال‌های من", + "show_config": "📄 نمایش پیکربندی", + "no_connections_user_desc": "برای ایجاد اتصال VPN با مدیر تماس بگیرید", + "share_title": "دسترسی به اتصال", + "vpn_access": "دسترسی VPN", + "user_label_share": "کاربر", + "share_protected_desc": "این دسترسی با رمز عبور محافظت شده است.", + "auth_success": "ورود موفقیت‌آمیز", + "loading_share_conns": "در حال بارگذاری...", + "no_active_conns": "هنوز هیچ اتصال فعالی ندارید.", + "show_settings_btn": "🔑 نمایش تنظیمات", + "copied": "کپی شد!", + "active": "فعال", + "site_description": "پنل وب آمنزیا — مدیریت سرورها و پروتکل‌های AmneziaWG", + "toggle_theme": "تغییر تم", + "copied_to_clipboard": "در حافظه کپی شد", + "share_not_found": "404 یافت نشد", + "share_not_found_desc": "لینک اشتباه است یا دسترسی غیرفعال شده است.", + "invalid_captcha": "کد امنیتی اشتباه است", + "account_disabled": "حساب کاربری غیرفعال است", + "invalid_login": "نام کاربری یا رمز عبور اشتباه است", + "user_exists": "کاربری با این نام قبلاً ثبت شده است", + "cannot_delete_self": "شما نمی‌توانید خودتان را حذف کنید", + "wrong_share_password": "رمز عبور اشتباه است", + "saving": "در حال ذخیره...", + "select_lang": "انتخاب زبان", + "lang_ru": "Русский", + "lang_en": "English", + "lang_fr": "Français", + "lang_zh": "中文 (Chinese)", + "lang_fa": "فارسی (Persian)", + "backup_title": "پشتیبان‌گیری ساده", + "download_backup": "دانلود data.json", + "restore_backup": "بازیابی از فایل", + "restore_confirm": "آیا مطمئن هستید؟ داده‌های فعلی بازنویسی می‌شوند و برنامه دوباره راه‌اندازی خواهد شد.", + "restore_success": "بازیابی موفقیت‌آمیز بود! در حال راه‌اندازی مجدد...", + "invalid_backup_file": "فایل پشتیبان یا ساختار نامعتبر است", + "config_unavailable": "پیکربندی در دسترس نیست", + "config_unavailable_desc": "این کلاینت از طریق اپلیکیشن اصلی Amnezia ایجاد شده است.\\nکلید خصوصی فقط در دستگاه کاربر ذخیره می‌شود و توسط سرور قابل بازیابی نیست.", + "client_public_key": "کلید عمومی کلاینت:", + "management": "مدیریت", + "server_management": "مدیریت سرور", + "server_management_desc": "انجام اقدامات در سطح سیستم روی دستگاه شما.", + "check_server_services": "بررسی سرویس‌های سرور", + "reboot_server": "راه‌اندازی مجدد سرور", + "clear_server": "پاک‌سازی سرور", + "remove_server": "حذف سرور از پنل", + "telemt_desc": "پروکسی تلگرام بر پایه‌ی MTProxy با پوشش‌های پیشرفته.", + "hysteria_desc": "Hysteria 2 (QUIC/UDP) با تصویر رسمی tobyxdd/hysteria (apernet/hysteria) و TLS از Let\u0027s Encrypt. دامنه را ادمین هنگام نصب مشخص می‌کند.", + "hysteria_domain": "دامنه", + "hysteria_email": "ایمیل Let\u0027s Encrypt", + "hysteria_port_hint": "پورت UDP برای Hysteria (پیش‌فرض 8998). اگر 443 اشغال است پورت دیگری بگذارید.", + "hysteria_install_hint": "قبل از نصب، رکورد A دامنه باید به این سرور اشاره کند. BBR و obfuscation salamander به‌صورت خودکار فعال می‌شوند.", + "hysteria_dns_hint": "قبل از نصب این رکورد DNS را بسازید:", + "hysteria_settings_title": "تنظیمات Hysteria", + "hysteria_change_port": "تغییر پورت", + "hysteria_port_change_hint": "پورت UDP. اگر address already in use دیدید، پورت آزاد بگذارید (8443 یا 8998).", + "hysteria_settings_saved": "پورت Hysteria به‌روز شد", + "hysteria_ports_warning": "هشدار: برای کار پایدار پورت‌های TCP آزاد ۸۰ و ۴۴۳ لازم است (Let\u0027s Encrypt روی ۸۰، HTTPS/QUIC روی ۴۴۳).", + "hysteria_renew_ssl": "تمدید SSL", + "hysteria_ssl_change_hint": "تغییر دامنه گواهی Let\u0027s Encrypt را دوباره صادر می‌کند (پورت ۸۰ باید آزاد باشد).", + "hysteria_ssl_required": "برای SSL دامنه و ایمیل لازم است", + "naiveproxy_desc": "NaiveProxy (پایدار) — HTTPS/HTTP2 با Caddy + forwardproxy و TLS از Let\u0027s Encrypt. کلاینت: Karing. از v2rayN استفاده نکنید؛ بقیه آزمایش نشده‌اند.", + "naiveproxy_domain": "دامنه", + "naiveproxy_email": "ایمیل Let\u0027s Encrypt", + "naiveproxy_port_hint": "پورت TCP 443 برای NaiveProxy ثابت است. پورت 80/TCP باید برای صدور گواهی آزاد باشد.", + "naiveproxy_install_hint": "قبل از نصب، رکورد A دامنه باید به این سرور اشاره کند. پورت‌های TCP آزاد ۸۰ و ۴۴۳ لازم است (Let\u0027s Encrypt و پروکسی HTTPS).", + "naiveproxy_dns_hint": "قبل از نصب این رکورد DNS را بسازید:", + "naiveproxy_ports_warning": "هشدار: برای کار پایدار پورت‌های TCP آزاد ۸۰ و ۴۴۳ لازم است (Let\u0027s Encrypt روی ۸۰، پروکسی HTTPS روی ۴۴۳).", + "naiveproxy_client_hint": "نسخه پایدار. از v2rayN استفاده نکنید. در Karing تأیید شده. سایر کلاینت‌ها آزمایش نشده‌اند.", + "server_ssl_domain": "دامنه SSL (پیش‌فرض)", + "server_ssl_email": "ایمیل SSL (پیش‌فرض)", + "server_ssl_hint": "هنگام نصب Hysteria / NGINX روی این سرور استفاده می‌شود (Let\u0027s Encrypt).", + "container_logs": "لاگ‌های کانتینر", + "logs_btn": "📋 Logs", + "logs_live": "زنده", + "logs_reconnect": "در حال اتصال مجدد…", + "no_logs": "هنوز لاگی نیست", + "container_status": "وضعیت", + "exit_code": "کد خروج", + "dns_desc": "سرور DNS شخصی برای مسدود کردن تبلیغات و حفظ حریم خصوصی.", + "dns_internal_hint": "The service runs on the internal network (172.29.172.254) without binding to host ports.", + "telemt_port_hint": "Port for the Telegram proxy (typically 443).", + "socks5_desc": "Single-account SOCKS5 proxy (3proxy) with username/password authentication.", + "socks5_username": "Username", + "socks5_password": "Password", + "socks5_password_placeholder": "Leave empty to auto-generate", + "socks5_password_hint": "If left empty, a random 16-character password will be generated.", + "socks5_port_hint": "TCP port the proxy will listen on. The default in the official Amnezia client is 38080.", + "socks5_change_settings": "Change settings", + "socks5_settings_title": "SOCKS5 — Connection settings", + "socks5_port_change_hint": "Changing the port recreates the container; existing sessions will be dropped.", + "socks5_settings_saved": "SOCKS5 settings saved", + "adguard_desc": "AdGuard Home — DNS-based ad blocker with a web admin UI.", + "adguard_mode": "Installation mode", + "adguard_mode_sidebyside": "Side-by-side", + "adguard_mode_sidebyside_hint": "runs alongside AmneziaDNS on a separate internal IP (172.29.172.253)", + "adguard_mode_replace": "Replace AmneziaDNS", + "adguard_mode_replace_hint": "removes amnezia-dns and takes its IP — VPN clients use AdGuard immediately", + "adguard_dns_port": "DNS port (53)", + "adguard_dns_port_hint": "Internal DNS port served to VPN clients via the docker network.", + "adguard_web_port": "Web UI port", + "adguard_dot_port": "DoT port", + "adguard_doh_port": "DoH port", + "adguard_expose_web": "Expose to internet", + "adguard_expose": "Expose to host", + "adguard_install_hint": "Initial AdGuard setup runs through its built-in wizard via the web UI after install.", + "adguard_internal_ip": "Internal IP", + "adguard_web_ui": "Admin URL", + "adguard_open_ui": "Open Web UI", + "edit_server_title": "Edit server", + "edit_keep_credential": "Leave empty to keep the current value", + "server_saved": "Server settings saved", + "servers_reordered": "Servers reordered", + "ping_checking": "Checking connectivity...", + "offline": "offline", + "api_tokens_title": "API tokens", + "api_tokens_hint": "Bearer tokens for external integrations. Send the token in the `Authorization: Bearer \u003ctoken\u003e` header. Tokens have admin-equivalent rights and stop working if their owner is disabled or demoted.", + "api_tokens_empty": "No API tokens yet. Create one to integrate the panel with an external service.", + "api_tokens_create": "Create token", + "api_tokens_create_title": "Create API token", + "api_tokens_name_label": "Name", + "api_tokens_name_placeholder": "e.g. Linear integration, CI bot, monitoring", + "api_tokens_name_hint": "Used to identify this token in the list. The token value itself is generated for you.", + "api_tokens_name_required": "Token name is required", + "api_tokens_created_title": "Token created", + "api_tokens_value_label": "Token value", + "api_tokens_warning_title": "This is the only time you\u0027ll see this token.", + "api_tokens_warning_body": "Copy it now and store it in your integration\u0027s secret manager. The panel keeps only a hash — if you lose this value you\u0027ll need to revoke and recreate the token.", + "api_tokens_revoke": "Revoke", + "api_tokens_revoke_confirm": "Revoke token \"{}\"? Any integration using it will stop working.", + "api_tokens_revoked": "Token revoked", + "api_tokens_created": "Created", + "api_tokens_last_used": "Last used", + "api_tokens_owner": "Owner", + "api_tokens_never": "never", + "coming_soon": "Coming soon", + "promo_star_cta": "Star us on GitHub", + "aivpn_subtitle": "AI-driven protocol selection that picks the right tunnel for the moment. Land it sooner — drop a star.", + "revproxy_title": "Reverse Proxy", + "revproxy_subtitle": "High-performance traffic obfuscation: hide your VPN endpoint behind a polished public-facing site, drop the DPI footprint.", + "warp_hint": "WARP routes this host outbound through Cloudflare. It does not create a public panel URL like Quick Tunnel or ngrok.", + "warp_install_hint": "Install the official Cloudflare WARP client first, then restart the panel.", + "warp_install_required": "Install WARP first", + "warp_connect": "Connect WARP", + "warp_disconnect": "Disconnect", + "warp_connected": "WARP connected", + "warp_disconnected": "WARP disconnected", + "warp_status_unknown": "WARP status is unknown.", + "warp_status_not_installed": "Cloudflare WARP CLI is not installed on this host.", + "warp_status_connected": "Cloudflare WARP is connected.", + "warp_status_connecting": "Cloudflare WARP is connecting.", + "warp_status_disconnected": "Cloudflare WARP is disconnected.", + "warp_status_not_registered": "Cloudflare WARP is installed but not registered yet. Connect will try to register automatically.", + "warp_status_service_unavailable": "Cloudflare WARP service is not available. Make sure warp-svc is running.", + "warp_status_error": "Cloudflare WARP returned an error.", + "install_starting": "Starting installation...", + "server_check_complete": "Check completed", + "server_connection_error": "Connection error", + "connections_load_error": "Error loading connections", + "connection_updated": "Connection updated", + "edit_connection": "Edit connection", + "enable_connection_progress": "Enabling connection...", + "disable_connection_progress": "Disabling connection...", + "connection_enabled": "Connection enabled", + "connection_disabled": "Connection disabled", + "gb": "GB", + "marketplace": "Templates", + "open_marketplace": "Open Templates", + "marketplace_desc": "Install protocol and service templates available for this server.", + "installed_apps": "Installed applications", + "installed_apps_hint": "Only installed protocols and services are shown here. Use Templates to add more.", + "no_installed_apps": "No installed applications yet", + "no_installed_apps_desc": "Open Templates to install protocols or services on this server.", + "install_another": "Install another", + "new_instance": "new instance", + "port_next_instance_hint": "Choose a free UDP port for the additional instance.", + "web_servers": "Web servers", + "nginx_desc": "NGINX web server with Let\u0027s Encrypt HTTPS certificate and editable index.html.", + "nginx_domain": "Domain", + "nginx_email": "Let\u0027s Encrypt email", + "nginx_port_hint": "HTTPS port exposed on the server. Port 80/TCP must be free and reachable for Let\u0027s Encrypt validation.", + "nginx_install_hint": "The domain must already point to this server. Port 80/TCP is used for HTTP-01 certificate validation.", + "site": "Site", + "site_editor": "Site editor", + "site_saved": "Site saved", + "nginx_dns_hint": "Create this DNS record before installation:", + "backup": "Backup", + "backups": "Backups", + "backup_desc": "Backups are created on the server and contain valuable protocol files needed to restore or recreate it later.", + "create_backup": "Create backup", + "creating_backup": "Creating backup...", + "backup_created": "Backup created", + "no_backups": "No backups yet", + "no_backups_desc": "Create the first backup for this protocol.", + "refresh": "Refresh", + "guest_allow_create_hint": "برای کاربر مهمان کانفیگ می‌سازد (3x-ui VLESS یا پروتکل سرور).", + "invite_inbound_hint": "یک‌بار inbound مورد نیاز VLESS را انتخاب کنید", + "invite_inbound_loading": "در حال بارگذاری…", + "invite_inbound_empty": "inbound VLESS یافت نشد", + "invite_inbound_need_xui": "ابتدا 3x-ui را در تنظیمات پیکربندی کنید", + "invite_inbound_required": "یک inbound VLESS انتخاب کنید", + "invite_sub_url_missing_title": "آدرس اشتراک تنظیم نشده", + "invite_sub_url_missing_hint": "در تنظیمات → 3x-ui پایه /sub را وارد کنید.", + "xui_url_label": "آدرس 3x-ui", + "xui_url_hint": "شامل پروتکل، پورت و webBasePath در صورت وجود", + "xui_sub_url_label": "آدرس پایه اشتراک", + "xui_sub_url_hint": "پایه عمومی /sub بدون اسلش پایانی (مثال: https://host:2096/sub).", + "xui_api_token_label": "توکن API (ترجیحی)", + "xui_api_token_placeholder": "Settings → Security → API Token", + "xui_api_token_hint": "در صورت تنظیم، ورود با نام کاربری/رمز رد می‌شود", + "xui_username_label": "نام کاربری 3x-ui", + "xui_password_label": "رمز عبور 3x-ui", + "xui_sync_hint": "کلاینت‌های 3x-ui بر اساس email به کاربران پنل همگام می‌شوند", + "xui_inbound_label": "ورودی VLESS", + "xui_inbound_auto": "خودکار (اولین ورودی VLESS)", + "xui_inbound_hint": "برای ساخت کانفیگ 3x-ui VLESS استفاده می‌شود. ابتدا یک ورودی VLESS در 3x-ui بسازید.", + "xui_servers_title": "سرورهای 3x-ui", + "xui_servers_hint": "چند پنل 3x-ui اضافه کنید. هنگام ساخت، سرور را با نام انتخاب کنید تا URL اشتراک همان سرور داده شود.", + "xui_servers_empty": "هنوز سرور 3x-ui نیست. افزودن را بزنید.", + "xui_servers_manage_hint": "آدرس پنل و پایه اشتراک در بخش «سرورهای 3x-ui» مدیریت می‌شود.", + "xui_server_add": "افزودن سرور", + "xui_server_name_label": "نام / توضیح", + "xui_server_name_hint": "در دعوت‌ها نمایش داده می‌شود (مثل US، NL).", + "xui_server_select_label": "سرور 3x-ui", + "xui_server_select_hint": "پنل 3x-ui که URL اشتراک می‌دهد را انتخاب کنید.", + "xui_server_delete_confirm": "این سرور 3x-ui حذف شود؟", + "cascade_title": "Cascade (double VPN)", + "cascade_desc": "Clients connect to this entry server; internet exits through another server — to bypass country blocks.", + "cascade_hint": "ryderams-style AWG2 cascade: entry and exit must both have AmneziaWG/WireGuard. Clients keep using the ENTRY config only.", + "cascade_client_note": "Users connect only to the ENTRY server. Do not give them the exit cascade peer.", + "cascade_security_note": "Logic runs via panel SSH (no remote curl|bash). Based on ryderams/amneziawg-cascade approach.", + "cascade_entry_protocol": "Protocol on this server (entry)", + "cascade_exit_server": "Exit server", + "cascade_exit_server_none": "— Select server —", + "cascade_exit_protocol": "Protocol on exit server", + "cascade_enable": "Enable cascade", + "cascade_disable": "Disable cascade", + "cascade_working": "Setting up cascade…", + "cascade_disabling": "Disabling cascade…", + "cascade_enabled": "Cascade enabled", + "cascade_disabled": "Cascade disabled", + "cascade_off": "Off", + "cascade_on": "Active", + "cascade_configured_down": "Configured, tunnel down", + "cascade_exit_required": "Select an exit server", + "cascade_enable_confirm": "Enable cascade? A service client will be created on the exit server and a tunnel will be set up on this entry server.", + "cascade_disable_confirm": "Disable cascade on this server?", + "cascade_no_handshake": "Up, no handshake", + "cascade_settings_title": "Cascade settings (per step)", + "cascade_settings_desc": "If pages spin forever with no internet — keep all options on and click Enable cascade again.", + "cascade_opt_pin_exit": "Pin route to exit server", + "cascade_opt_pin_exit_hint": "Keeps the exit tunnel from looping through the VPN itself.", + "cascade_opt_remove_eth_masq": "Remove client NAT on eth0", + "cascade_opt_remove_eth_masq_hint": "Clients must not egress directly from the entry server.", + "cascade_opt_force_forward": "Force FORWARD between interfaces", + "cascade_opt_force_forward_hint": "Client traffic → cascade → exit.", + "cascade_opt_mss_clamp": "TCP MSS clamp", + "cascade_opt_mss_clamp_hint": "Fixes endless page loads on double VPN.", + "cascade_opt_wait_handshake": "Wait for exit handshake", + "cascade_opt_wait_handshake_hint": "Do not mark cascade active if the exit peer never answers.", + "cascade_verify_egress": "Verify egress IP", + "cascade_verify_egress_hint": "After handshake, curl ifconfig.me via the cascade Address; fail and roll back if empty.", + "cascade_opt_keep_dns": "Keep DNS from exit config", + "cascade_opt_keep_dns_hint": "Usually off — the client sets DNS itself.", + "cascade_opt_handshake_timeout": "Handshake timeout (sec)", + "cascade_opt_table_id": "Routing table id", + "cascade_opt_rule_priority": "ip rule priority", + "cascade_opt_allowed_ips": "AllowedIPs for entry→exit tunnel", + "cascade_opt_allowed_ips_hint": "Prefer 0.0.0.0/0 (IPv4); ::/0 often breaks double VPN.", + "cascade_opt_vpn_subnet": "Client subnet override", + "cascade_opt_vpn_subnet_hint": "Empty = auto-detect from server Address" } diff --git a/translations/fr.json b/translations/fr.json index 77ef622..acb0b97 100644 --- a/translations/fr.json +++ b/translations/fr.json @@ -1,522 +1,567 @@ { - "nav_servers": "Serveurs", - "nav_users": "Utilisateurs", - "nav_invites": "Liens d'invitation", - "nav_settings": "Paramètres", - "nav_connections": "Connexions", - "nav_logout": "Déconnexion", - "theme_dark": "Sombre", - "theme_light": "Clair", - "back_to_servers": "← Retour aux serveurs", - "server_check": "Vérifier", - "server_checking": "Vérification...", - "protocols": "Protocoles", - "install": "Installer", - "uninstall": "Désinstaller", - "run": "En cours", - "stop": "Arrêté", - "not_installed": "Pas installé", - "installed": "Installé", - "add_server": "Ajouter un serveur", - "no_servers": "Aucun serveur ajouté", - "name": "Nom", - "host": "Hôte", - "port": "Port", - "status": "Statut", - "actions": "Actions", - "edit": "Modifier", - "delete": "Supprimer", - "save": "Enregistrer", - "cancel": "Annuler", - "loading": "Chargement...", - "copy": "Copier", - "download": "Télécharger", - "config": "Configuration", - "qr_code": "Code QR", - "vpn_key": "Clé VPN", - "clients": "Clients", - "traffic": "Trafic", - "limit": "Limite", - "manage": "Gérer", - "no_protocols": "Aucun protocole", - "add_server_desc": "Ajoutez votre premier serveur VPN pour commencer. Identifiants SSH requis.", - "ssh_host": "Hôte / IP", - "ssh_port": "Port SSH", - "ssh_user": "Utilisateur", - "ssh_password": "Mot de passe", - "ssh_key": "Clé SSH", - "ssh_key_placeholder": "Clé SSH privée", - "ssh_key_hint": "Collez le contenu de votre fichier id_rsa ou id_ed25519", - "connect": "Connecter", - "connecting": "Connexion...", - "server_added": "Serveur ajouté avec succès", - "server_deleted": "Serveur supprimé", - "delete_confirm": "Voulez-vous vraiment supprimer ce serveur ?", - "success": "Succès", - "error": "Erreur", - "info": "Info", - "login_title": "Connexion au Panneau", - "username": "Nom d'utilisateur", - "password": "Mot de passe", - "captcha": "Captcha", - "captcha_placeholder": "Entrez les caractères", - "captcha_hint": "Cliquez pour rafraîchir", - "login": "Connexion", - "logging_in": "Connexion...", - "login_error": "Échec de la connexion", - "check_done": "Vérification terminée", - "check_error": "Erreur de connexion", - "ssh_ok": "Connexion SSH", - "docker_ok": "Docker", - "docker_not_installed": "Docker non installé", - "awg_desc": "Version moderne basée sur awg-go. Obfuscation avancée (S3, S4).", - "awg_legacy_desc": "Version AWG originale. Compatible avec les anciens clients.", - "xray_desc": "Masque le trafic en trafic web normal (XTLS-Reality). Résiste au DPI.", - "not_checked": "Non vérifié", - "connections": "Connexions", - "add": "Ajouter", - "loading_connections": "Chargement des connexions...", - "no_connections": "Aucune connexion", - "no_connections_desc": "Ajoutez votre première connexion pour générer un fichier VPN", - "install_protocol": "Installer le protocole", - "port_default_hint": "Port par défaut : 55424. Assurez-vous qu'il est libre.", - "port_xray_hint": "Port recommandé : 443. Assurez-vous qu'il n'est pas utilisé par un serveur web.", - "reinstall": "Réinstaller", - "uninstall_confirm": "Désinstaller {} ? Toutes les données seront perdues.", - "stop_container_confirm": "Arrêter le conteneur {} ?", - "start_container_confirm": "Démarrer le conteneur {} ?", - "stopping": "Arrêt", - "starting": "Démarrage", - "stopped": "arrêté", - "started": "démarré", - "server_config": "Configuration du serveur", - "add_connection": "Ajouter une connexion", - "connection_name": "Nom de la connexion", - "connection_name_placeholder": "Ex: iPhone, Laptop", - "assign_to_user": "Assigner à l'utilisateur (optionnel)", - "no_assign": "— Aucune assignation —", - "create": "Créer", - "creating": "Création...", - "connection_created": "Connexion \"{}\" créée", - "delete_connection_confirm": "Supprimer cette connexion ? Elle cessera de fonctionner.", - "connection_deleted": "Connexion supprimée", - "config_tab": "📄 Config", - "vpn_key_tab": "🔑 Clé-VPN", - "qr_code_tab": "📱 Code-QR", - "copy_config": "📋 Copier", - "download_conf": "⬇ Télécharger .conf", - "copy_key": "📋 Copier la clé", - "vpn_link_hint": "Collez ce lien dans AmneziaVPN pour une config automatique", - "qr_hint": "Scannez le code QR dans l'application AmneziaVPN", - "no_data": "Aucune donnée", - "start_btn": "▶ Start", - "stop_btn": "⏹ Stop", - "config_btn": "⚙️ Config", - "done": "Terminé", - "installing": "Installation...", - "start_install": "Début de l'installation...", - "check_docker": "Vérification de Docker...", - "prepare_host": "Préparation de l'hôte...", - "build_container": "Construction du conteneur...", - "install_success": "Protocole installé avec succès !", - "install_error": "Erreur d'installation : ", - "qr_error": "Échec de génération du QR code", - "users_title": "Utilisateurs", - "search_placeholder": "Recherche par nom, email ou Telegram...", - "add_user": "Ajouter Utilisateur", - "prev_page": "← Retour", - "next_page": "Suivant →", - "page_info": "Page {} sur {}", - "loading_users": "Chargement des utilisateurs...", - "nothing_found": "Rien trouvé", - "search_empty_desc": "Essayez une autre requête ou créez un utilisateur", - "username_label": "Nom d'utilisateur", - "password_label": "Mot de passe", - "role_label": "Rôle", - "role_user_desc": "User — voit ses propres connexions", - "role_support_desc": "Support — gestion des serveurs", - "role_admin_desc": "Admin — accès complet", - "tg_id_label": "ID Telegram (opt.)", - "email_label": "Email (opt.)", - "traffic_limit_label": "Limite trafic (Go) (0 = illimité)", - "description_label": "Description (opt.)", - "auto_conn_title": "Création auto de connexion (optionnel)", - "server_label": "Serveur", - "no_create_conn": "— Ne pas créer —", - "protocol_label": "Protocole", - "edit_user_title": "Modifier utilisateur : {}", - "edit_user_tg": "ID Telegram (optionnel)", - "edit_user_email": "Email (optionnel)", - "edit_user_limit": "Limite trafic (Go) (0 = illimité)", - "new_password_hint": "Nouveau mot de passe (laisser vide si inchangé)", - "op_type_label": "Type d'opération", - "create_new_conn": "Créer nouvelle connexion", - "link_existing_conn": "Lier existante", - "select_existing_conn": "Sélectionner connexion", - "existing_conn_hint": "Seules les connexions libres sont affichées", - "conn_name_panel": "Nom (dans le panneau)", - "user_conns_title": "Connexions utilisateur", - "no_free_conns": "Aucune connexion libre", - "select_connection": "Veuillez sélectionner une connexion", - "linking": "Liaison...", - "conn_linked": "Connexion liée", - "enabling": "Activation", - "disabling": "Désactivation", - "user_enabled": "Utilisateur activé", - "user_disabled": "Utilisateur désactivé", - "delete_user_confirm": "Supprimer l'utilisateur ? Ses connexions seront supprimées.", - "share_access": "Partager l'accès", - "enable_public_access": "Activer l'accès public", - "share_password_label": "Mot de passe (optionnel)", - "share_password_hint": "Si défini, requis pour l'accès", - "guest_settings_title": "Accès invité", - "guest_enable": "Activer l'accès invité (sans inscription)", - "guest_enable_hint": "Lien public sur la page de connexion. Les invités voient les connexions de l'utilisateur choisi.", - "guest_link_label": "Lien invité", - "guest_link_placeholder": "Enregistrez pour générer le lien", - "guest_regen_token": "Régénérer le lien", - "guest_regen_confirm": "Régénérer le lien invité ? L'ancien ne fonctionnera plus.", - "guest_token_updated": "Lien invité mis à jour", - "guest_user_label": "Utilisateur des connexions invitées", - "guest_user_none": "— Choisir —", - "guest_user_hint": "Créez un utilisateur (ex. guest) et assignez des VPN.", - "guest_password_label": "Mot de passe invité (optionnel)", - "guest_password_keep": "Laisser vide pour conserver", - "guest_clear_password": "Retirer le mot de passe", - "guest_allow_create": "Autoriser la création d'une config", - "guest_access_btn": "Continuer en invité", - "guest_access_login_hint": "Sans compte", - "guest_title": "Accès invité", - "guest_subtitle": "Obtenir une config VPN sans inscription", - "guest_protected_desc": "Page protégée par mot de passe.", - "guest_get_config": "Obtenir la config", - "guest_get_config_hint": "Une nouvelle config VPN sera créée", - "guest_not_found": "404 Introuvable", - "guest_not_found_desc": "Accès invité désactivé ou lien invalide.", - "wrong_guest_password": "Mauvais mot de passe", - "invites_title": "Liens d'invitation", - "invites_hint": "Créez des liens partageables. Le destinataire peut créer une config VPN — vous fixez le nombre d'utilisations.", - "invites_empty": "Aucun lien", - "invites_empty_desc": "Créez un lien à partager — sans inscription.", - "invite_create": "Créer un lien", - "invite_edit": "Modifier le lien", - "invite_active": "Actif", - "invite_expired": "Expiré", - "invite_exhausted": "Plus d'utilisations", - "invite_uses": "Utilisations", - "invite_name_label": "Nom du lien", - "invite_max_uses_label": "Configs max", - "invite_max_uses_hint": "Nombre de configs créables via ce lien. 0 = illimité.", - "invite_user_label": "Utilisateur de stockage", - "invite_user_hint": "Les configs créées sont attachées à cet utilisateur.", - "invite_user_required": "Choisissez un utilisateur", - "invite_password_label": "Mot de passe (optionnel)", - "invite_expires_label": "Expiration (optionnel)", - "invite_duration_label": "Durée du config (jours)", - "invite_duration_hint": "Commence quand l'utilisateur clique Obtenir. 0 = illimité.", - "invite_duration_short": "Durée", - "invite_duration_starts_hint": "Après obtention, valable {} jour(s)", - "invite_config_expires_at": "Valable jusqu'au : {}", - "invite_sub_tab": "Abonnement", - "days_short": "j", - "invite_clear_expires": "Retirer l'expiration", - "invite_note_label": "Note (admin)", - "invite_note_placeholder": "Commentaire interne", - "invite_reset_used": "Réinitialiser le compteur", - "invite_created": "Lien créé (URL copiée)", - "invite_updated": "Lien mis à jour", - "invite_deleted": "Lien supprimé", - "invite_delete_confirm": "Supprimer ce lien ?", - "invite_public_title": "Invitation", - "invite_public_subtitle": "Obtenir une config VPN via invitation", - "invite_protected_desc": "Lien protégé par mot de passe.", - "invite_get_config_hint": "Chaque clic crée une config et compte comme une utilisation", - "invite_uses_left": "Restantes : {}", - "invite_uses_left_unlimited": "Créées : {} (illimité)", - "invite_not_found": "404 Introuvable", - "invite_not_found_desc": "Lien invalide ou supprimé.", - "wrong_invite_password": "Mauvais mot de passe", - "public_link_label": "Lien public", - "disabled": "Désactivé", - "out_of": "sur", - "settings_title": "Paramètres du Panneau", - "appearance": "🎨 Apparence", - "title_label": "Titre", - "logo_label": "Logo (Emoji ou symbole)", - "subtitle_label": "Sous-titre", - "captcha_title": "🔐 Captcha", - "enable_captcha": "Activer captcha", - "telegram_bot_title": "🤖 Bot Telegram", - "bot_token_label": "Bot Token (@BotFather)", - "bot_status": "Statut", - "bot_running": "✅ En cours", - "bot_stopped": "⏹ Arrêté", - "bot_stop_btn": "⏹ Arrêter", - "bot_start_btn": "▶️ Démarrer", - "bot_hint": "Sauvegardez après modification du token. Le bot redémarrera.", - "api_docs_title": "🔌 Documentation API", - "api_docs_hint": "Utilisez ces interfaces pour explorer l'API", - "tunnels_title": "Tunnels", - "local_server": "Serveur local", - "tunnel_install_enable": "Installer et activer", - "tunnel_enable": "Activer le tunnel", - "tunnel_running": "Tunnel en cours", - "tunnel_stop": "Arrêter", - "tunnel_stopping": "Arrêt...", - "tunnel_stopped": "Tunnel arrêté", - "tunnel_delete": "Supprimer", - "tunnel_deleted": "Tunnel supprimé", - "tunnel_delete_confirm": "Arrêter et supprimer ce binaire de tunnel géré par le panneau ?", - "tunnel_starting": "Démarrage du tunnel...", - "tunnel_started": "Tunnel démarré", - "tunnel_waiting_url": "Tunnel démarré. En attente de l'URL publique...", - "tunnel_no_public_url": "L'URL publique apparaîtra ici après le démarrage du tunnel.", - "tunnels_hint": "Les tunnels rapides exposent ce panneau local sur Internet. Gardez des identifiants administrateur robustes et partagez les URL publiques uniquement avec des utilisateurs de confiance.", - "ngrok_authtoken_placeholder": "ngrok authtoken (optionnel pour certains comptes)", - "import_users_title": "📤 Import Utilisateurs", - "import_source_label": "Source Import / Sync", - "remnawave_url_label": "URL Remnawave", - "api_key_label": "Clé API", - "enable_sync": "Activer synchronisation", - "sync_hint": "Synchro automatique avec Remnawave", - "sync_now_btn": "🔄 Sync maintenant", - "delete_sync_btn": "🗑 Supprimer synchro", - "auto_create_conns": "Créer connexions auto", - "sync_server_label": "Serveur pour nouvelles connexions", - "save_changes": "💾 Enregistrer changements", - "sync_success": "Sync terminée : {} utilisateurs", - "sync_deleted": "Supprimé {} utilisateurs sync", - "delete_sync_confirm": "Supprimer TOUS les utilisateurs synchronisés ? Irréversible.", - "settings_saved": "Paramètres enregistrés", - "bot_started": "Bot démarré", - "bot_stopped_msg": "Bot arrêté", - "sync_running": "Synchronisation...", - "deleting": "Suppression...", - "my_connections_title": "Mes Connexions", - "show_config": "📄 Voir config", - "no_connections_user_desc": "Contactez l'admin pour une connexion", - "share_title": "Accès Connexion", - "vpn_access": "Accès VPN", - "user_label_share": "Utilisateur", - "share_protected_desc": "Accès protégé par mot de passe.", - "auth_success": "Succès Auth", - "loading_share_conns": "Chargement...", - "no_active_conns": "Aucune connexion active.", - "show_settings_btn": "🔑 Voir paramètres", - "copied": "Copié !", - "active": "Actif", - "site_description": "Panneau Web Amnezia — gestion serveurs VPN et AmneziaWG", - "toggle_theme": "Changer thème", - "copied_to_clipboard": "Copié dans le presse-papier", - "share_not_found": "404 Introuvable", - "share_not_found_desc": "Lien invalide ou accès désactivé.", - "invalid_captcha": "Captcha invalide", - "account_disabled": "Compte désactivé", - "invalid_login": "Login ou password incorrect", - "user_exists": "L'utilisateur existe déjà", - "cannot_delete_self": "Vous ne pouvez pas vous supprimer", - "wrong_share_password": "Mauvais mot de passe", - "saving": "Enregistrement...", - "select_lang": "Choisir la langue", - "lang_ru": "Русский", - "lang_en": "English", - "lang_fr": "Français", - "lang_zh": "中文 (Chinese)", - "lang_fa": "فارسی (Persian)", - "backup_title": "Sauvegarde Simple", - "download_backup": "Télécharger data.json", - "restore_backup": "Restaurer depuis un fichier", - "restore_confirm": "Êtes-vous sûr ? Les données actuelles seront écrasées et l'application redémarrera.", - "restore_success": "Restauration réussie ! Redémarrage...", - "invalid_backup_file": "Fichier de sauvegarde ou structure invalide", - "config_unavailable": "Configuration indisponible", - "config_unavailable_desc": "Ce client a été créé via l'application native Amnezia.\\nLa clé privée est stockée uniquement sur l'appareil de l'utilisateur et ne peut pas être récupérée par le serveur.", - "client_public_key": "Clé publique du client :", - "management": "Gestion", - "server_management": "Gestion du serveur", - "server_management_desc": "Effectuer des actions au niveau du système sur votre machine.", - "check_server_services": "Vérifier les services du serveur", - "reboot_server": "Redémarrer le serveur", - "clear_server": "Réinitialiser le serveur", - "remove_server": "Retirer le serveur du panneau", - "telemt_desc": "Proxy Telegram basé sur MTProxy avec obfuscation avancée.", - "hysteria_desc": "Hysteria 2 (QUIC/UDP) via l'image officielle tobyxdd/hysteria (apernet/hysteria) avec TLS Let's Encrypt. Le domaine est défini par l'admin à l'installation.", - "hysteria_domain": "Domaine", - "hysteria_email": "Email Let's Encrypt", - "hysteria_port_hint": "Port UDP pour Hysteria (8998 par défaut). Évitez 443 s'il est déjà pris.", - "hysteria_install_hint": "L'enregistrement A du domaine doit pointer vers ce serveur. BBR et obfuscation salamander sont activés automatiquement.", - "hysteria_dns_hint": "Créez cet enregistrement DNS avant l'installation :", - "hysteria_settings_title": "Paramètres Hysteria", - "hysteria_change_port": "Changer le port", - "hysteria_port_change_hint": "Port UDP. Si « address already in use », choisissez un port libre (8443 ou 8998).", - "hysteria_settings_saved": "Port Hysteria mis à jour", - "hysteria_ports_warning": "Attention : les ports TCP 80 et 443 doivent être libres pour un fonctionnement stable (Let's Encrypt sur 80, HTTPS/QUIC sur 443).", - "hysteria_renew_ssl": "Renouveler SSL", - "hysteria_ssl_change_hint": "Changer le domaine réémet le certificat Let's Encrypt (le port 80 doit être libre).", - "hysteria_ssl_required": "Domaine et email requis pour SSL", - "naiveproxy_desc": "NaiveProxy (stable) — HTTPS/HTTP2 via Caddy + forwardproxy, TLS Let's Encrypt. Client : utilisez Karing. N'utilisez pas v2rayN ; autres clients non testés.", - "naiveproxy_domain": "Domaine", - "naiveproxy_email": "Email Let's Encrypt", - "naiveproxy_port_hint": "Le port TCP 443 est fixe pour NaiveProxy. Le port 80/TCP doit être libre pour l'émission du certificat.", - "naiveproxy_install_hint": "L'enregistrement A du domaine doit pointer vers ce serveur. Les ports TCP 80 et 443 doivent être libres (Let's Encrypt et proxy HTTPS).", - "naiveproxy_dns_hint": "Créez cet enregistrement DNS avant l'installation :", - "naiveproxy_ports_warning": "Attention : les ports TCP 80 et 443 doivent être libres pour un fonctionnement stable (Let's Encrypt sur 80, proxy HTTPS sur 443).", - "naiveproxy_client_hint": "Version stable. N'utilisez PAS v2rayN. Fonctionne avec Karing. Autres clients non testés.", - "server_ssl_domain": "Domaine SSL (par défaut)", - "server_ssl_email": "Email SSL (par défaut)", - "server_ssl_hint": "Utilisé lors de l'installation de Hysteria / NGINX sur ce serveur (Let's Encrypt).", - "container_logs": "Logs du conteneur", - "logs_btn": "📋 Logs", - "logs_live": "Temps réel", - "logs_reconnect": "reconnexion…", - "no_logs": "Pas encore de logs", - "container_status": "Statut", - "exit_code": "Code de sortie", - "dns_desc": "Serveur DNS personnel pour bloquer les publicités et protéger la vie privée.", - "dns_internal_hint": "The service runs on the internal network (172.29.172.254) without binding to host ports.", - "telemt_port_hint": "Port for the Telegram proxy (typically 443).", - "socks5_desc": "Single-account SOCKS5 proxy (3proxy) with username/password authentication.", - "socks5_username": "Username", - "socks5_password": "Password", - "socks5_password_placeholder": "Leave empty to auto-generate", - "socks5_password_hint": "If left empty, a random 16-character password will be generated.", - "socks5_port_hint": "TCP port the proxy will listen on. The default in the official Amnezia client is 38080.", - "socks5_change_settings": "Change settings", - "socks5_settings_title": "SOCKS5 — Connection settings", - "socks5_port_change_hint": "Changing the port recreates the container; existing sessions will be dropped.", - "socks5_settings_saved": "SOCKS5 settings saved", - "adguard_desc": "AdGuard Home — DNS-based ad blocker with a web admin UI.", - "adguard_mode": "Installation mode", - "adguard_mode_sidebyside": "Side-by-side", - "adguard_mode_sidebyside_hint": "runs alongside AmneziaDNS on a separate internal IP (172.29.172.253)", - "adguard_mode_replace": "Replace AmneziaDNS", - "adguard_mode_replace_hint": "removes amnezia-dns and takes its IP — VPN clients use AdGuard immediately", - "adguard_dns_port": "DNS port (53)", - "adguard_dns_port_hint": "Internal DNS port served to VPN clients via the docker network.", - "adguard_web_port": "Web UI port", - "adguard_dot_port": "DoT port", - "adguard_doh_port": "DoH port", - "adguard_expose_web": "Expose to internet", - "adguard_expose": "Expose to host", - "adguard_install_hint": "Initial AdGuard setup runs through its built-in wizard via the web UI after install.", - "adguard_internal_ip": "Internal IP", - "adguard_web_ui": "Admin URL", - "adguard_open_ui": "Open Web UI", - "edit_server_title": "Edit server", - "edit_keep_credential": "Leave empty to keep the current value", - "server_saved": "Server settings saved", - "servers_reordered": "Servers reordered", - "ping_checking": "Checking connectivity...", - "offline": "offline", - "api_tokens_title": "API tokens", - "api_tokens_hint": "Bearer tokens for external integrations. Send the token in the `Authorization: Bearer ` header. Tokens have admin-equivalent rights and stop working if their owner is disabled or demoted.", - "api_tokens_empty": "No API tokens yet. Create one to integrate the panel with an external service.", - "api_tokens_create": "Create token", - "api_tokens_create_title": "Create API token", - "api_tokens_name_label": "Name", - "api_tokens_name_placeholder": "e.g. Linear integration, CI bot, monitoring", - "api_tokens_name_hint": "Used to identify this token in the list. The token value itself is generated for you.", - "api_tokens_name_required": "Token name is required", - "api_tokens_created_title": "Token created", - "api_tokens_value_label": "Token value", - "api_tokens_warning_title": "This is the only time you'll see this token.", - "api_tokens_warning_body": "Copy it now and store it in your integration's secret manager. The panel keeps only a hash — if you lose this value you'll need to revoke and recreate the token.", - "api_tokens_revoke": "Revoke", - "api_tokens_revoke_confirm": "Revoke token \"{}\"? Any integration using it will stop working.", - "api_tokens_revoked": "Token revoked", - "api_tokens_created": "Created", - "api_tokens_last_used": "Last used", - "api_tokens_owner": "Owner", - "api_tokens_never": "never", - "coming_soon": "Coming soon", - "promo_star_cta": "Star us on GitHub", - "aivpn_subtitle": "AI-driven protocol selection that picks the right tunnel for the moment. Land it sooner — drop a star.", - "revproxy_title": "Reverse Proxy", - "revproxy_subtitle": "High-performance traffic obfuscation: hide your VPN endpoint behind a polished public-facing site, drop the DPI footprint.", - "warp_hint": "WARP routes this host outbound through Cloudflare. It does not create a public panel URL like Quick Tunnel or ngrok.", - "warp_install_hint": "Install the official Cloudflare WARP client first, then restart the panel.", - "warp_install_required": "Install WARP first", - "warp_connect": "Connect WARP", - "warp_disconnect": "Disconnect", - "warp_connected": "WARP connected", - "warp_disconnected": "WARP disconnected", - "warp_status_unknown": "WARP status is unknown.", - "warp_status_not_installed": "Cloudflare WARP CLI is not installed on this host.", - "warp_status_connected": "Cloudflare WARP is connected.", - "warp_status_connecting": "Cloudflare WARP is connecting.", - "warp_status_disconnected": "Cloudflare WARP is disconnected.", - "warp_status_not_registered": "Cloudflare WARP is installed but not registered yet. Connect will try to register automatically.", - "warp_status_service_unavailable": "Cloudflare WARP service is not available. Make sure warp-svc is running.", - "warp_status_error": "Cloudflare WARP returned an error.", - "install_starting": "Starting installation...", - "server_check_complete": "Check completed", - "server_connection_error": "Connection error", - "connections_load_error": "Error loading connections", - "connection_updated": "Connection updated", - "edit_connection": "Edit connection", - "enable_connection_progress": "Enabling connection...", - "disable_connection_progress": "Disabling connection...", - "connection_enabled": "Connection enabled", - "connection_disabled": "Connection disabled", - "gb": "GB", - "marketplace": "Templates", - "open_marketplace": "Open Templates", - "marketplace_desc": "Install protocol and service templates available for this server.", - "installed_apps": "Installed applications", - "installed_apps_hint": "Only installed protocols and services are shown here. Use Templates to add more.", - "no_installed_apps": "No installed applications yet", - "no_installed_apps_desc": "Open Templates to install protocols or services on this server.", - "install_another": "Install another", - "new_instance": "new instance", - "port_next_instance_hint": "Choose a free UDP port for the additional instance.", - "web_servers": "Serveurs web", - "nginx_desc": "Serveur web NGINX avec certificat HTTPS Let's Encrypt et index.html modifiable.", - "nginx_domain": "Domaine", - "nginx_email": "Email Let's Encrypt", - "nginx_port_hint": "Port HTTPS exposé sur le serveur. Le port 80/TCP doit être libre et accessible pour la validation Let's Encrypt.", - "nginx_install_hint": "Le domaine doit déjà pointer vers ce serveur. Le port 80/TCP est utilisé pour la validation HTTP-01 du certificat.", - "site": "Site", - "site_editor": "Éditeur du site", - "site_saved": "Site enregistré", - "nginx_dns_hint": "Creez cet enregistrement DNS avant l installation :", - "backup": "Backup", - "backups": "Backups", - "backup_desc": "Backups are created on the server and contain valuable protocol files needed to restore or recreate it later.", - "create_backup": "Create backup", - "creating_backup": "Creating backup...", - "backup_created": "Backup created", - "no_backups": "No backups yet", - "no_backups_desc": "Create the first backup for this protocol.", - "refresh": "Refresh", - "guest_allow_create_hint": "Crée une config pour l'utilisateur invité (3x-ui VLESS ou protocole serveur).", - "invite_inbound_hint": "Choisissez l'inbound VLESS une fois", - "invite_inbound_loading": "Chargement…", - "invite_inbound_empty": "Aucun inbound VLESS", - "invite_inbound_need_xui": "Configurez 3x-ui d'abord", - "invite_inbound_required": "Sélectionnez un inbound VLESS", - "invite_sub_url_missing_title": "URL d'abonnement manquante", - "invite_sub_url_missing_hint": "Dans Paramètres → 3x-ui, définissez l'URL de base /sub.", - "xui_url_label": "URL 3x-ui", - "xui_url_hint": "Inclure le schéma, le port et webBasePath si configuré", - "xui_sub_url_label": "URL de base d'abonnement", - "xui_sub_url_hint": "Base /sub publique sans slash final (ex: https://host:2096/sub).", - "xui_api_token_label": "Jeton API (préféré)", - "xui_api_token_placeholder": "Settings → Security → API Token", - "xui_api_token_hint": "Si défini, le login/mot de passe est ignoré", - "xui_username_label": "Identifiant 3x-ui", - "xui_password_label": "Mot de passe 3x-ui", - "xui_sync_hint": "Les clients 3x-ui deviennent des utilisateurs du panneau (liés par email)", - "xui_inbound_label": "Inbound VLESS", - "xui_inbound_auto": "Auto (premier inbound VLESS)", - "xui_inbound_hint": "Utilisé pour créer des configs 3x-ui VLESS. Créez d'abord un inbound VLESS dans 3x-ui.", - "xui_servers_title": "Serveurs 3x-ui", - "xui_servers_hint": "Ajoutez plusieurs panneaux 3x-ui. À la création, choisissez le serveur par nom — son URL d'abonnement est utilisée.", - "xui_servers_empty": "Aucun serveur 3x-ui. Cliquez sur Ajouter.", - "xui_servers_manage_hint": "Les URL et bases d'abonnement se gèrent dans la section « Serveurs 3x-ui » ci-dessous.", - "xui_server_add": "Ajouter un serveur", - "xui_server_name_label": "Nom / description", - "xui_server_name_hint": "Affiché dans les invitations (ex: US, NL).", - "xui_server_select_label": "Serveur 3x-ui", - "xui_server_select_hint": "Choisissez le panneau 3x-ui qui émettra l'URL d'abonnement.", - "xui_server_delete_confirm": "Supprimer ce serveur 3x-ui ?" + "nav_servers": "Serveurs", + "nav_users": "Utilisateurs", + "nav_invites": "Liens d\u0027invitation", + "nav_settings": "Paramètres", + "nav_connections": "Connexions", + "nav_logout": "Déconnexion", + "theme_dark": "Sombre", + "theme_light": "Clair", + "back_to_servers": "← Retour aux serveurs", + "server_check": "Vérifier", + "server_checking": "Vérification...", + "protocols": "Protocoles", + "install": "Installer", + "uninstall": "Désinstaller", + "run": "En cours", + "stop": "Arrêté", + "not_installed": "Pas installé", + "installed": "Installé", + "add_server": "Ajouter un serveur", + "no_servers": "Aucun serveur ajouté", + "name": "Nom", + "host": "Hôte", + "port": "Port", + "status": "Statut", + "actions": "Actions", + "edit": "Modifier", + "delete": "Supprimer", + "save": "Enregistrer", + "cancel": "Annuler", + "loading": "Chargement...", + "copy": "Copier", + "download": "Télécharger", + "config": "Configuration", + "qr_code": "Code QR", + "vpn_key": "Clé VPN", + "clients": "Clients", + "traffic": "Trafic", + "limit": "Limite", + "manage": "Gérer", + "no_protocols": "Aucun protocole", + "add_server_desc": "Ajoutez votre premier serveur VPN pour commencer. Identifiants SSH requis.", + "ssh_host": "Hôte / IP", + "ssh_port": "Port SSH", + "ssh_user": "Utilisateur", + "ssh_password": "Mot de passe", + "ssh_key": "Clé SSH", + "ssh_key_placeholder": "Clé SSH privée", + "ssh_key_hint": "Collez le contenu de votre fichier id_rsa ou id_ed25519", + "connect": "Connecter", + "connecting": "Connexion...", + "server_added": "Serveur ajouté avec succès", + "server_deleted": "Serveur supprimé", + "delete_confirm": "Voulez-vous vraiment supprimer ce serveur ?", + "success": "Succès", + "error": "Erreur", + "info": "Info", + "login_title": "Connexion au Panneau", + "username": "Nom d\u0027utilisateur", + "password": "Mot de passe", + "captcha": "Captcha", + "captcha_placeholder": "Entrez les caractères", + "captcha_hint": "Cliquez pour rafraîchir", + "login": "Connexion", + "logging_in": "Connexion...", + "login_error": "Échec de la connexion", + "check_done": "Vérification terminée", + "check_error": "Erreur de connexion", + "ssh_ok": "Connexion SSH", + "docker_ok": "Docker", + "docker_not_installed": "Docker non installé", + "awg_desc": "Version moderne basée sur awg-go. Obfuscation avancée (S3, S4).", + "awg_legacy_desc": "Version AWG originale. Compatible avec les anciens clients.", + "xray_desc": "Masque le trafic en trafic web normal (XTLS-Reality). Résiste au DPI.", + "not_checked": "Non vérifié", + "connections": "Connexions", + "add": "Ajouter", + "loading_connections": "Chargement des connexions...", + "no_connections": "Aucune connexion", + "no_connections_desc": "Ajoutez votre première connexion pour générer un fichier VPN", + "install_protocol": "Installer le protocole", + "port_default_hint": "Port par défaut : 55424. Assurez-vous qu\u0027il est libre.", + "port_xray_hint": "Port recommandé : 443. Assurez-vous qu\u0027il n\u0027est pas utilisé par un serveur web.", + "reinstall": "Réinstaller", + "uninstall_confirm": "Désinstaller {} ? Toutes les données seront perdues.", + "stop_container_confirm": "Arrêter le conteneur {} ?", + "start_container_confirm": "Démarrer le conteneur {} ?", + "stopping": "Arrêt", + "starting": "Démarrage", + "stopped": "arrêté", + "started": "démarré", + "server_config": "Configuration du serveur", + "add_connection": "Ajouter une connexion", + "connection_name": "Nom de la connexion", + "connection_name_placeholder": "Ex: iPhone, Laptop", + "assign_to_user": "Assigner à l\u0027utilisateur (optionnel)", + "no_assign": "— Aucune assignation —", + "create": "Créer", + "creating": "Création...", + "connection_created": "Connexion \"{}\" créée", + "delete_connection_confirm": "Supprimer cette connexion ? Elle cessera de fonctionner.", + "connection_deleted": "Connexion supprimée", + "config_tab": "📄 Config", + "vpn_key_tab": "🔑 Clé-VPN", + "qr_code_tab": "📱 Code-QR", + "copy_config": "📋 Copier", + "download_conf": "⬇ Télécharger .conf", + "copy_key": "📋 Copier la clé", + "vpn_link_hint": "Collez ce lien dans AmneziaVPN pour une config automatique", + "qr_hint": "Scannez le code QR dans l\u0027application AmneziaVPN", + "no_data": "Aucune donnée", + "start_btn": "▶ Start", + "stop_btn": "⏹ Stop", + "config_btn": "⚙️ Config", + "done": "Terminé", + "installing": "Installation...", + "start_install": "Début de l\u0027installation...", + "check_docker": "Vérification de Docker...", + "prepare_host": "Préparation de l\u0027hôte...", + "build_container": "Construction du conteneur...", + "install_success": "Protocole installé avec succès !", + "install_error": "Erreur d\u0027installation : ", + "qr_error": "Échec de génération du QR code", + "users_title": "Utilisateurs", + "search_placeholder": "Recherche par nom, email ou Telegram...", + "add_user": "Ajouter Utilisateur", + "prev_page": "← Retour", + "next_page": "Suivant →", + "page_info": "Page {} sur {}", + "loading_users": "Chargement des utilisateurs...", + "nothing_found": "Rien trouvé", + "search_empty_desc": "Essayez une autre requête ou créez un utilisateur", + "username_label": "Nom d\u0027utilisateur", + "password_label": "Mot de passe", + "role_label": "Rôle", + "role_user_desc": "User — voit ses propres connexions", + "role_support_desc": "Support — gestion des serveurs", + "role_admin_desc": "Admin — accès complet", + "tg_id_label": "ID Telegram (opt.)", + "email_label": "Email (opt.)", + "traffic_limit_label": "Limite trafic (Go) (0 = illimité)", + "description_label": "Description (opt.)", + "auto_conn_title": "Création auto de connexion (optionnel)", + "server_label": "Serveur", + "no_create_conn": "— Ne pas créer —", + "protocol_label": "Protocole", + "edit_user_title": "Modifier utilisateur : {}", + "edit_user_tg": "ID Telegram (optionnel)", + "edit_user_email": "Email (optionnel)", + "edit_user_limit": "Limite trafic (Go) (0 = illimité)", + "new_password_hint": "Nouveau mot de passe (laisser vide si inchangé)", + "op_type_label": "Type d\u0027opération", + "create_new_conn": "Créer nouvelle connexion", + "link_existing_conn": "Lier existante", + "select_existing_conn": "Sélectionner connexion", + "existing_conn_hint": "Seules les connexions libres sont affichées", + "conn_name_panel": "Nom (dans le panneau)", + "user_conns_title": "Connexions utilisateur", + "no_free_conns": "Aucune connexion libre", + "select_connection": "Veuillez sélectionner une connexion", + "linking": "Liaison...", + "conn_linked": "Connexion liée", + "enabling": "Activation", + "disabling": "Désactivation", + "user_enabled": "Utilisateur activé", + "user_disabled": "Utilisateur désactivé", + "delete_user_confirm": "Supprimer l\u0027utilisateur ? Ses connexions seront supprimées.", + "share_access": "Partager l\u0027accès", + "enable_public_access": "Activer l\u0027accès public", + "share_password_label": "Mot de passe (optionnel)", + "share_password_hint": "Si défini, requis pour l\u0027accès", + "guest_settings_title": "Accès invité", + "guest_enable": "Activer l\u0027accès invité (sans inscription)", + "guest_enable_hint": "Lien public sur la page de connexion. Les invités voient les connexions de l\u0027utilisateur choisi.", + "guest_link_label": "Lien invité", + "guest_link_placeholder": "Enregistrez pour générer le lien", + "guest_regen_token": "Régénérer le lien", + "guest_regen_confirm": "Régénérer le lien invité ? L\u0027ancien ne fonctionnera plus.", + "guest_token_updated": "Lien invité mis à jour", + "guest_user_label": "Utilisateur des connexions invitées", + "guest_user_none": "— Choisir —", + "guest_user_hint": "Créez un utilisateur (ex. guest) et assignez des VPN.", + "guest_password_label": "Mot de passe invité (optionnel)", + "guest_password_keep": "Laisser vide pour conserver", + "guest_clear_password": "Retirer le mot de passe", + "guest_allow_create": "Autoriser la création d\u0027une config", + "guest_access_btn": "Continuer en invité", + "guest_access_login_hint": "Sans compte", + "guest_title": "Accès invité", + "guest_subtitle": "Obtenir une config VPN sans inscription", + "guest_protected_desc": "Page protégée par mot de passe.", + "guest_get_config": "Obtenir la config", + "guest_get_config_hint": "Une nouvelle config VPN sera créée", + "guest_not_found": "404 Introuvable", + "guest_not_found_desc": "Accès invité désactivé ou lien invalide.", + "wrong_guest_password": "Mauvais mot de passe", + "invites_title": "Liens d\u0027invitation", + "invites_hint": "Créez des liens partageables. Le destinataire peut créer une config VPN — vous fixez le nombre d\u0027utilisations.", + "invites_empty": "Aucun lien", + "invites_empty_desc": "Créez un lien à partager — sans inscription.", + "invite_create": "Créer un lien", + "invite_edit": "Modifier le lien", + "invite_active": "Actif", + "invite_expired": "Expiré", + "invite_exhausted": "Plus d\u0027utilisations", + "invite_uses": "Utilisations", + "invite_name_label": "Nom du lien", + "invite_max_uses_label": "Configs max", + "invite_max_uses_hint": "Nombre de configs créables via ce lien. 0 = illimité.", + "invite_user_label": "Utilisateur de stockage", + "invite_user_hint": "Les configs créées sont attachées à cet utilisateur.", + "invite_user_required": "Choisissez un utilisateur", + "invite_password_label": "Mot de passe (optionnel)", + "invite_expires_label": "Expiration (optionnel)", + "invite_duration_label": "Durée du config (jours)", + "invite_duration_hint": "Commence quand l\u0027utilisateur clique Obtenir. 0 = illimité.", + "invite_duration_short": "Durée", + "invite_duration_starts_hint": "Après obtention, valable {} jour(s)", + "invite_config_expires_at": "Valable jusqu\u0027au : {}", + "invite_sub_tab": "Abonnement", + "days_short": "j", + "invite_clear_expires": "Retirer l\u0027expiration", + "invite_note_label": "Note (admin)", + "invite_note_placeholder": "Commentaire interne", + "invite_reset_used": "Réinitialiser le compteur", + "invite_created": "Lien créé (URL copiée)", + "invite_updated": "Lien mis à jour", + "invite_deleted": "Lien supprimé", + "invite_delete_confirm": "Supprimer ce lien ?", + "invite_public_title": "Invitation", + "invite_public_subtitle": "Obtenir une config VPN via invitation", + "invite_protected_desc": "Lien protégé par mot de passe.", + "invite_get_config_hint": "Chaque clic crée une config et compte comme une utilisation", + "invite_uses_left": "Restantes : {}", + "invite_uses_left_unlimited": "Créées : {} (illimité)", + "invite_not_found": "404 Introuvable", + "invite_not_found_desc": "Lien invalide ou supprimé.", + "wrong_invite_password": "Mauvais mot de passe", + "public_link_label": "Lien public", + "disabled": "Désactivé", + "out_of": "sur", + "settings_title": "Paramètres du Panneau", + "appearance": "🎨 Apparence", + "title_label": "Titre", + "logo_label": "Logo (Emoji ou symbole)", + "subtitle_label": "Sous-titre", + "captcha_title": "🔐 Captcha", + "enable_captcha": "Activer captcha", + "telegram_bot_title": "🤖 Bot Telegram", + "bot_token_label": "Bot Token (@BotFather)", + "bot_status": "Statut", + "bot_running": "✅ En cours", + "bot_stopped": "⏹ Arrêté", + "bot_stop_btn": "⏹ Arrêter", + "bot_start_btn": "▶️ Démarrer", + "bot_hint": "Sauvegardez après modification du token. Le bot redémarrera.", + "api_docs_title": "🔌 Documentation API", + "api_docs_hint": "Utilisez ces interfaces pour explorer l\u0027API", + "tunnels_title": "Tunnels", + "local_server": "Serveur local", + "tunnel_install_enable": "Installer et activer", + "tunnel_enable": "Activer le tunnel", + "tunnel_running": "Tunnel en cours", + "tunnel_stop": "Arrêter", + "tunnel_stopping": "Arrêt...", + "tunnel_stopped": "Tunnel arrêté", + "tunnel_delete": "Supprimer", + "tunnel_deleted": "Tunnel supprimé", + "tunnel_delete_confirm": "Arrêter et supprimer ce binaire de tunnel géré par le panneau ?", + "tunnel_starting": "Démarrage du tunnel...", + "tunnel_started": "Tunnel démarré", + "tunnel_waiting_url": "Tunnel démarré. En attente de l\u0027URL publique...", + "tunnel_no_public_url": "L\u0027URL publique apparaîtra ici après le démarrage du tunnel.", + "tunnels_hint": "Les tunnels rapides exposent ce panneau local sur Internet. Gardez des identifiants administrateur robustes et partagez les URL publiques uniquement avec des utilisateurs de confiance.", + "ngrok_authtoken_placeholder": "ngrok authtoken (optionnel pour certains comptes)", + "import_users_title": "📤 Import Utilisateurs", + "import_source_label": "Source Import / Sync", + "remnawave_url_label": "URL Remnawave", + "api_key_label": "Clé API", + "enable_sync": "Activer synchronisation", + "sync_hint": "Synchro automatique avec Remnawave", + "sync_now_btn": "🔄 Sync maintenant", + "delete_sync_btn": "🗑 Supprimer synchro", + "auto_create_conns": "Créer connexions auto", + "sync_server_label": "Serveur pour nouvelles connexions", + "save_changes": "💾 Enregistrer changements", + "sync_success": "Sync terminée : {} utilisateurs", + "sync_deleted": "Supprimé {} utilisateurs sync", + "delete_sync_confirm": "Supprimer TOUS les utilisateurs synchronisés ? Irréversible.", + "settings_saved": "Paramètres enregistrés", + "bot_started": "Bot démarré", + "bot_stopped_msg": "Bot arrêté", + "sync_running": "Synchronisation...", + "deleting": "Suppression...", + "my_connections_title": "Mes Connexions", + "show_config": "📄 Voir config", + "no_connections_user_desc": "Contactez l\u0027admin pour une connexion", + "share_title": "Accès Connexion", + "vpn_access": "Accès VPN", + "user_label_share": "Utilisateur", + "share_protected_desc": "Accès protégé par mot de passe.", + "auth_success": "Succès Auth", + "loading_share_conns": "Chargement...", + "no_active_conns": "Aucune connexion active.", + "show_settings_btn": "🔑 Voir paramètres", + "copied": "Copié !", + "active": "Actif", + "site_description": "Panneau Web Amnezia — gestion serveurs VPN et AmneziaWG", + "toggle_theme": "Changer thème", + "copied_to_clipboard": "Copié dans le presse-papier", + "share_not_found": "404 Introuvable", + "share_not_found_desc": "Lien invalide ou accès désactivé.", + "invalid_captcha": "Captcha invalide", + "account_disabled": "Compte désactivé", + "invalid_login": "Login ou password incorrect", + "user_exists": "L\u0027utilisateur existe déjà", + "cannot_delete_self": "Vous ne pouvez pas vous supprimer", + "wrong_share_password": "Mauvais mot de passe", + "saving": "Enregistrement...", + "select_lang": "Choisir la langue", + "lang_ru": "Русский", + "lang_en": "English", + "lang_fr": "Français", + "lang_zh": "中文 (Chinese)", + "lang_fa": "فارسی (Persian)", + "backup_title": "Sauvegarde Simple", + "download_backup": "Télécharger data.json", + "restore_backup": "Restaurer depuis un fichier", + "restore_confirm": "Êtes-vous sûr ? Les données actuelles seront écrasées et l\u0027application redémarrera.", + "restore_success": "Restauration réussie ! Redémarrage...", + "invalid_backup_file": "Fichier de sauvegarde ou structure invalide", + "config_unavailable": "Configuration indisponible", + "config_unavailable_desc": "Ce client a été créé via l\u0027application native Amnezia.\\nLa clé privée est stockée uniquement sur l\u0027appareil de l\u0027utilisateur et ne peut pas être récupérée par le serveur.", + "client_public_key": "Clé publique du client :", + "management": "Gestion", + "server_management": "Gestion du serveur", + "server_management_desc": "Effectuer des actions au niveau du système sur votre machine.", + "check_server_services": "Vérifier les services du serveur", + "reboot_server": "Redémarrer le serveur", + "clear_server": "Réinitialiser le serveur", + "remove_server": "Retirer le serveur du panneau", + "telemt_desc": "Proxy Telegram basé sur MTProxy avec obfuscation avancée.", + "hysteria_desc": "Hysteria 2 (QUIC/UDP) via l\u0027image officielle tobyxdd/hysteria (apernet/hysteria) avec TLS Let\u0027s Encrypt. Le domaine est défini par l\u0027admin à l\u0027installation.", + "hysteria_domain": "Domaine", + "hysteria_email": "Email Let\u0027s Encrypt", + "hysteria_port_hint": "Port UDP pour Hysteria (8998 par défaut). Évitez 443 s\u0027il est déjà pris.", + "hysteria_install_hint": "L\u0027enregistrement A du domaine doit pointer vers ce serveur. BBR et obfuscation salamander sont activés automatiquement.", + "hysteria_dns_hint": "Créez cet enregistrement DNS avant l\u0027installation :", + "hysteria_settings_title": "Paramètres Hysteria", + "hysteria_change_port": "Changer le port", + "hysteria_port_change_hint": "Port UDP. Si « address already in use », choisissez un port libre (8443 ou 8998).", + "hysteria_settings_saved": "Port Hysteria mis à jour", + "hysteria_ports_warning": "Attention : les ports TCP 80 et 443 doivent être libres pour un fonctionnement stable (Let\u0027s Encrypt sur 80, HTTPS/QUIC sur 443).", + "hysteria_renew_ssl": "Renouveler SSL", + "hysteria_ssl_change_hint": "Changer le domaine réémet le certificat Let\u0027s Encrypt (le port 80 doit être libre).", + "hysteria_ssl_required": "Domaine et email requis pour SSL", + "naiveproxy_desc": "NaiveProxy (stable) — HTTPS/HTTP2 via Caddy + forwardproxy, TLS Let\u0027s Encrypt. Client : utilisez Karing. N\u0027utilisez pas v2rayN ; autres clients non testés.", + "naiveproxy_domain": "Domaine", + "naiveproxy_email": "Email Let\u0027s Encrypt", + "naiveproxy_port_hint": "Le port TCP 443 est fixe pour NaiveProxy. Le port 80/TCP doit être libre pour l\u0027émission du certificat.", + "naiveproxy_install_hint": "L\u0027enregistrement A du domaine doit pointer vers ce serveur. Les ports TCP 80 et 443 doivent être libres (Let\u0027s Encrypt et proxy HTTPS).", + "naiveproxy_dns_hint": "Créez cet enregistrement DNS avant l\u0027installation :", + "naiveproxy_ports_warning": "Attention : les ports TCP 80 et 443 doivent être libres pour un fonctionnement stable (Let\u0027s Encrypt sur 80, proxy HTTPS sur 443).", + "naiveproxy_client_hint": "Version stable. N\u0027utilisez PAS v2rayN. Fonctionne avec Karing. Autres clients non testés.", + "server_ssl_domain": "Domaine SSL (par défaut)", + "server_ssl_email": "Email SSL (par défaut)", + "server_ssl_hint": "Utilisé lors de l\u0027installation de Hysteria / NGINX sur ce serveur (Let\u0027s Encrypt).", + "container_logs": "Logs du conteneur", + "logs_btn": "📋 Logs", + "logs_live": "Temps réel", + "logs_reconnect": "reconnexion…", + "no_logs": "Pas encore de logs", + "container_status": "Statut", + "exit_code": "Code de sortie", + "dns_desc": "Serveur DNS personnel pour bloquer les publicités et protéger la vie privée.", + "dns_internal_hint": "The service runs on the internal network (172.29.172.254) without binding to host ports.", + "telemt_port_hint": "Port for the Telegram proxy (typically 443).", + "socks5_desc": "Single-account SOCKS5 proxy (3proxy) with username/password authentication.", + "socks5_username": "Username", + "socks5_password": "Password", + "socks5_password_placeholder": "Leave empty to auto-generate", + "socks5_password_hint": "If left empty, a random 16-character password will be generated.", + "socks5_port_hint": "TCP port the proxy will listen on. The default in the official Amnezia client is 38080.", + "socks5_change_settings": "Change settings", + "socks5_settings_title": "SOCKS5 — Connection settings", + "socks5_port_change_hint": "Changing the port recreates the container; existing sessions will be dropped.", + "socks5_settings_saved": "SOCKS5 settings saved", + "adguard_desc": "AdGuard Home — DNS-based ad blocker with a web admin UI.", + "adguard_mode": "Installation mode", + "adguard_mode_sidebyside": "Side-by-side", + "adguard_mode_sidebyside_hint": "runs alongside AmneziaDNS on a separate internal IP (172.29.172.253)", + "adguard_mode_replace": "Replace AmneziaDNS", + "adguard_mode_replace_hint": "removes amnezia-dns and takes its IP — VPN clients use AdGuard immediately", + "adguard_dns_port": "DNS port (53)", + "adguard_dns_port_hint": "Internal DNS port served to VPN clients via the docker network.", + "adguard_web_port": "Web UI port", + "adguard_dot_port": "DoT port", + "adguard_doh_port": "DoH port", + "adguard_expose_web": "Expose to internet", + "adguard_expose": "Expose to host", + "adguard_install_hint": "Initial AdGuard setup runs through its built-in wizard via the web UI after install.", + "adguard_internal_ip": "Internal IP", + "adguard_web_ui": "Admin URL", + "adguard_open_ui": "Open Web UI", + "edit_server_title": "Edit server", + "edit_keep_credential": "Leave empty to keep the current value", + "server_saved": "Server settings saved", + "servers_reordered": "Servers reordered", + "ping_checking": "Checking connectivity...", + "offline": "offline", + "api_tokens_title": "API tokens", + "api_tokens_hint": "Bearer tokens for external integrations. Send the token in the `Authorization: Bearer \u003ctoken\u003e` header. Tokens have admin-equivalent rights and stop working if their owner is disabled or demoted.", + "api_tokens_empty": "No API tokens yet. Create one to integrate the panel with an external service.", + "api_tokens_create": "Create token", + "api_tokens_create_title": "Create API token", + "api_tokens_name_label": "Name", + "api_tokens_name_placeholder": "e.g. Linear integration, CI bot, monitoring", + "api_tokens_name_hint": "Used to identify this token in the list. The token value itself is generated for you.", + "api_tokens_name_required": "Token name is required", + "api_tokens_created_title": "Token created", + "api_tokens_value_label": "Token value", + "api_tokens_warning_title": "This is the only time you\u0027ll see this token.", + "api_tokens_warning_body": "Copy it now and store it in your integration\u0027s secret manager. The panel keeps only a hash — if you lose this value you\u0027ll need to revoke and recreate the token.", + "api_tokens_revoke": "Revoke", + "api_tokens_revoke_confirm": "Revoke token \"{}\"? Any integration using it will stop working.", + "api_tokens_revoked": "Token revoked", + "api_tokens_created": "Created", + "api_tokens_last_used": "Last used", + "api_tokens_owner": "Owner", + "api_tokens_never": "never", + "coming_soon": "Coming soon", + "promo_star_cta": "Star us on GitHub", + "aivpn_subtitle": "AI-driven protocol selection that picks the right tunnel for the moment. Land it sooner — drop a star.", + "revproxy_title": "Reverse Proxy", + "revproxy_subtitle": "High-performance traffic obfuscation: hide your VPN endpoint behind a polished public-facing site, drop the DPI footprint.", + "warp_hint": "WARP routes this host outbound through Cloudflare. It does not create a public panel URL like Quick Tunnel or ngrok.", + "warp_install_hint": "Install the official Cloudflare WARP client first, then restart the panel.", + "warp_install_required": "Install WARP first", + "warp_connect": "Connect WARP", + "warp_disconnect": "Disconnect", + "warp_connected": "WARP connected", + "warp_disconnected": "WARP disconnected", + "warp_status_unknown": "WARP status is unknown.", + "warp_status_not_installed": "Cloudflare WARP CLI is not installed on this host.", + "warp_status_connected": "Cloudflare WARP is connected.", + "warp_status_connecting": "Cloudflare WARP is connecting.", + "warp_status_disconnected": "Cloudflare WARP is disconnected.", + "warp_status_not_registered": "Cloudflare WARP is installed but not registered yet. Connect will try to register automatically.", + "warp_status_service_unavailable": "Cloudflare WARP service is not available. Make sure warp-svc is running.", + "warp_status_error": "Cloudflare WARP returned an error.", + "install_starting": "Starting installation...", + "server_check_complete": "Check completed", + "server_connection_error": "Connection error", + "connections_load_error": "Error loading connections", + "connection_updated": "Connection updated", + "edit_connection": "Edit connection", + "enable_connection_progress": "Enabling connection...", + "disable_connection_progress": "Disabling connection...", + "connection_enabled": "Connection enabled", + "connection_disabled": "Connection disabled", + "gb": "GB", + "marketplace": "Templates", + "open_marketplace": "Open Templates", + "marketplace_desc": "Install protocol and service templates available for this server.", + "installed_apps": "Installed applications", + "installed_apps_hint": "Only installed protocols and services are shown here. Use Templates to add more.", + "no_installed_apps": "No installed applications yet", + "no_installed_apps_desc": "Open Templates to install protocols or services on this server.", + "install_another": "Install another", + "new_instance": "new instance", + "port_next_instance_hint": "Choose a free UDP port for the additional instance.", + "web_servers": "Serveurs web", + "nginx_desc": "Serveur web NGINX avec certificat HTTPS Let\u0027s Encrypt et index.html modifiable.", + "nginx_domain": "Domaine", + "nginx_email": "Email Let\u0027s Encrypt", + "nginx_port_hint": "Port HTTPS exposé sur le serveur. Le port 80/TCP doit être libre et accessible pour la validation Let\u0027s Encrypt.", + "nginx_install_hint": "Le domaine doit déjà pointer vers ce serveur. Le port 80/TCP est utilisé pour la validation HTTP-01 du certificat.", + "site": "Site", + "site_editor": "Éditeur du site", + "site_saved": "Site enregistré", + "nginx_dns_hint": "Creez cet enregistrement DNS avant l installation :", + "backup": "Backup", + "backups": "Backups", + "backup_desc": "Backups are created on the server and contain valuable protocol files needed to restore or recreate it later.", + "create_backup": "Create backup", + "creating_backup": "Creating backup...", + "backup_created": "Backup created", + "no_backups": "No backups yet", + "no_backups_desc": "Create the first backup for this protocol.", + "refresh": "Refresh", + "guest_allow_create_hint": "Crée une config pour l\u0027utilisateur invité (3x-ui VLESS ou protocole serveur).", + "invite_inbound_hint": "Choisissez l\u0027inbound VLESS une fois", + "invite_inbound_loading": "Chargement…", + "invite_inbound_empty": "Aucun inbound VLESS", + "invite_inbound_need_xui": "Configurez 3x-ui d\u0027abord", + "invite_inbound_required": "Sélectionnez un inbound VLESS", + "invite_sub_url_missing_title": "URL d\u0027abonnement manquante", + "invite_sub_url_missing_hint": "Dans Paramètres → 3x-ui, définissez l\u0027URL de base /sub.", + "xui_url_label": "URL 3x-ui", + "xui_url_hint": "Inclure le schéma, le port et webBasePath si configuré", + "xui_sub_url_label": "URL de base d\u0027abonnement", + "xui_sub_url_hint": "Base /sub publique sans slash final (ex: https://host:2096/sub).", + "xui_api_token_label": "Jeton API (préféré)", + "xui_api_token_placeholder": "Settings → Security → API Token", + "xui_api_token_hint": "Si défini, le login/mot de passe est ignoré", + "xui_username_label": "Identifiant 3x-ui", + "xui_password_label": "Mot de passe 3x-ui", + "xui_sync_hint": "Les clients 3x-ui deviennent des utilisateurs du panneau (liés par email)", + "xui_inbound_label": "Inbound VLESS", + "xui_inbound_auto": "Auto (premier inbound VLESS)", + "xui_inbound_hint": "Utilisé pour créer des configs 3x-ui VLESS. Créez d\u0027abord un inbound VLESS dans 3x-ui.", + "xui_servers_title": "Serveurs 3x-ui", + "xui_servers_hint": "Ajoutez plusieurs panneaux 3x-ui. À la création, choisissez le serveur par nom — son URL d\u0027abonnement est utilisée.", + "xui_servers_empty": "Aucun serveur 3x-ui. Cliquez sur Ajouter.", + "xui_servers_manage_hint": "Les URL et bases d\u0027abonnement se gèrent dans la section « Serveurs 3x-ui » ci-dessous.", + "xui_server_add": "Ajouter un serveur", + "xui_server_name_label": "Nom / description", + "xui_server_name_hint": "Affiché dans les invitations (ex: US, NL).", + "xui_server_select_label": "Serveur 3x-ui", + "xui_server_select_hint": "Choisissez le panneau 3x-ui qui émettra l\u0027URL d\u0027abonnement.", + "xui_server_delete_confirm": "Supprimer ce serveur 3x-ui ?", + "cascade_title": "Cascade (double VPN)", + "cascade_desc": "Clients connect to this entry server; internet exits through another server — to bypass country blocks.", + "cascade_hint": "ryderams-style AWG2 cascade: entry and exit must both have AmneziaWG/WireGuard. Clients keep using the ENTRY config only.", + "cascade_client_note": "Users connect only to the ENTRY server. Do not give them the exit cascade peer.", + "cascade_security_note": "Logic runs via panel SSH (no remote curl|bash). Based on ryderams/amneziawg-cascade approach.", + "cascade_entry_protocol": "Protocol on this server (entry)", + "cascade_exit_server": "Exit server", + "cascade_exit_server_none": "— Select server —", + "cascade_exit_protocol": "Protocol on exit server", + "cascade_enable": "Enable cascade", + "cascade_disable": "Disable cascade", + "cascade_working": "Setting up cascade…", + "cascade_disabling": "Disabling cascade…", + "cascade_enabled": "Cascade enabled", + "cascade_disabled": "Cascade disabled", + "cascade_off": "Off", + "cascade_on": "Active", + "cascade_configured_down": "Configured, tunnel down", + "cascade_exit_required": "Select an exit server", + "cascade_enable_confirm": "Enable cascade? A service client will be created on the exit server and a tunnel will be set up on this entry server.", + "cascade_disable_confirm": "Disable cascade on this server?", + "cascade_no_handshake": "Up, no handshake", + "cascade_settings_title": "Cascade settings (per step)", + "cascade_settings_desc": "If pages spin forever with no internet — keep all options on and click Enable cascade again.", + "cascade_opt_pin_exit": "Pin route to exit server", + "cascade_opt_pin_exit_hint": "Keeps the exit tunnel from looping through the VPN itself.", + "cascade_opt_remove_eth_masq": "Remove client NAT on eth0", + "cascade_opt_remove_eth_masq_hint": "Clients must not egress directly from the entry server.", + "cascade_opt_force_forward": "Force FORWARD between interfaces", + "cascade_opt_force_forward_hint": "Client traffic → cascade → exit.", + "cascade_opt_mss_clamp": "TCP MSS clamp", + "cascade_opt_mss_clamp_hint": "Fixes endless page loads on double VPN.", + "cascade_opt_wait_handshake": "Wait for exit handshake", + "cascade_opt_wait_handshake_hint": "Do not mark cascade active if the exit peer never answers.", + "cascade_verify_egress": "Verify egress IP", + "cascade_verify_egress_hint": "After handshake, curl ifconfig.me via the cascade Address; fail and roll back if empty.", + "cascade_opt_keep_dns": "Keep DNS from exit config", + "cascade_opt_keep_dns_hint": "Usually off — the client sets DNS itself.", + "cascade_opt_handshake_timeout": "Handshake timeout (sec)", + "cascade_opt_table_id": "Routing table id", + "cascade_opt_rule_priority": "ip rule priority", + "cascade_opt_allowed_ips": "AllowedIPs for entry→exit tunnel", + "cascade_opt_allowed_ips_hint": "Prefer 0.0.0.0/0 (IPv4); ::/0 often breaks double VPN.", + "cascade_opt_vpn_subnet": "Client subnet override", + "cascade_opt_vpn_subnet_hint": "Empty = auto-detect from server Address" } diff --git a/translations/ru.json b/translations/ru.json index bcb3442..3c3db44 100644 --- a/translations/ru.json +++ b/translations/ru.json @@ -1,579 +1,624 @@ { - "nav_servers": "Серверы", - "nav_users": "Пользователи", - "nav_invites": "Ссылки", - "nav_settings": "Настройки", - "nav_connections": "Подключения", - "nav_logout": "Выход", - "theme_dark": "Темная", - "theme_light": "Светлая", - "back_to_servers": "← Назад к серверам", - "server_check": "Проверить", - "server_checking": "Проверка...", - "protocols": "Протоколы", - "install": "Установить", - "uninstall": "Удалить", - "run": "Работает", - "stop": "Остановлен", - "not_installed": "Не установлен", - "installed": "Установлен", - "add_server": "Добавить сервер", - "no_servers": "Серверы не добавлены", - "name": "Название", - "host": "Адрес", - "port": "Порт", - "status": "Статус", - "actions": "Действия", - "edit": "Редактировать", - "delete": "Удалить", - "save": "Сохранить", - "cancel": "Отмена", - "loading": "Загрузка...", - "copy": "Копировать", - "download": "Скачать", - "config": "Конфигурация", - "qr_code": "QR-код", - "vpn_key": "VPN-ключ", - "clients": "Клиенты", - "traffic": "Трафик", - "limit": "Limit", - "manage": "Управление", - "no_protocols": "Нет протоколов", - "add_server_desc": "Добавьте свой первый VPN сервер для начала работы. Вам понадобятся SSH-данные для подключения.", - "ssh_host": "Хост / IP", - "ssh_port": "SSH Порт", - "ssh_user": "Пользователь", - "ssh_password": "Пароль", - "ssh_key": "SSH Ключ", - "ssh_key_placeholder": "Приватный SSH ключ", - "ssh_key_hint": "Вставьте содержимое вашего файла id_rsa или id_ed25519", - "connect": "Подключить", - "connecting": "Подключение...", - "server_added": "Сервер успешно добавлен", - "server_deleted": "Сервер удалён", - "delete_confirm": "Вы уверены, что хотите удалить этот сервер?", - "success": "Успешно", - "error": "Ошибка", - "info": "Информация", - "login_title": "Вход в панель", - "username": "Логин", - "password": "Пароль", - "captcha": "Капча", - "captcha_placeholder": "Введите символы с картинки", - "captcha_hint": "Нажмите для обновления картинки", - "login": "Войти", - "logging_in": "Вход...", - "login_error": "Ошибка входа", - "check_done": "Проверка завершена", - "check_error": "Ошибка подключения", - "ssh_ok": "SSH подключение", - "docker_ok": "Docker", - "docker_not_installed": "Docker не установлен", - "awg_desc": "Новая версия протокола на основе awg-go. Поддерживает расширенную обфускацию с параметрами S3, S4.", - "awg_legacy_desc": "Оригинальная версия AWG на базе ядра WireGuard. Совместима с клиентами старых версий.", - "xray_desc": "Современный протокол с маскировкой под обычный веб-трафик (XTLS-Reality). Устойчив к глубокому анализу пакетов.", - "wireguard_desc": "Стандартный и самый быстрый VPN-протокол. Встроен во все современные ОС, но легко блокируется DPI.", - "not_checked": "Не проверено", - "connections": "Подключения", - "add": "Добавить", - "loading_connections": "Загрузка подключений...", - "no_connections": "Нет подключений", - "no_connections_desc": "Добавьте первое подключение для генерации VPN конфигурации", - "install_protocol": "Установить протокол", - "port_default_hint": "Порт по умолчанию: 55424. Убедитесь, что он не занят", - "port_xray_hint": "Порт по умолчанию: 443 (рекомендуется для Xray). Убедитесь, что он не занят другим веб-сервером.", - "reinstall": "Переустановить", - "uninstall_confirm": "Удалить {}? Все подключения и конфигурации будут потеряны.", - "stop_container_confirm": "Остановить контейнер {}?", - "start_container_confirm": "Запустить контейнер {}?", - "stopping": "Остановка", - "starting": "Запуск", - "stopped": "остановлен", - "started": "запущен", - "server_config": "Конфигурация сервера", - "add_connection": "Добавить подключение", - "connection_name": "Имя подключения", - "connection_name_placeholder": "Например: iPhone, Laptop", - "assign_to_user": "Назначить пользователю (опционально)", - "no_assign": "— Без привязки —", - "create": "Создать", - "creating": "Создание...", - "connection_created": "Подключение \"{}\" создано", - "delete_connection_confirm": "Удалить это подключение? Конфигурация перестанет работать.", - "connection_deleted": "Подключение удалено", - "config_tab": "📄 Конфиг", - "vpn_key_tab": "🔑 VPN-ключ", - "qr_code_tab": "📱 QR-код", - "copy_config": "📋 Копировать", - "download_conf": "⬇ Скачать .conf", - "copy_key": "📋 Копировать ключ", - "vpn_link_hint": "Вставьте эту ссылку в приложение AmneziaVPN для автоматической настройки", - "qr_hint": "Отсканируйте QR-код в приложении AmneziaVPN", - "no_data": "Нет данных", - "start_btn": "▶ Старт", - "stop_btn": "⏹ Стоп", - "config_btn": "⚙️ Конфиг", - "done": "Готово", - "installing": "Установка...", - "start_install": "Начинаем установку...", - "check_docker": "Проверка Docker...", - "prepare_host": "Подготовка хоста...", - "build_container": "Сборка контейнера...", - "install_success": "Протокол установлен успешно!", - "install_error": "Ошибка установки: ", - "qr_error": "Не удалось сгенерировать QR-код", - "users_title": "Пользователи", - "search_placeholder": "Поиск по имени, email или Telegram...", - "add_user": "Добавить пользователя", - "prev_page": "← Назад", - "next_page": "Вперёд →", - "page_info": "Страница {} из {}", - "loading_users": "Загрузка пользователей...", - "nothing_found": "Ничего не найдено", - "search_empty_desc": "Попробуйте изменить запрос или создайте нового пользователя", - "username_label": "Имя пользователя", - "password_label": "Пароль", - "role_label": "Роль", - "role_user_desc": "User — только просмотр своих подключений", - "role_support_desc": "Support — управление серверами", - "role_admin_desc": "Admin — полный доступ", - "tg_id_label": "Telegram ID (опц.)", - "email_label": "Email (опц.)", - "traffic_limit_label": "Лимит трафика (ГБ) (опц., 0 = ∞)", - "expiration_date_label": "Срок действия (до)", - "no_expiration": "Без срока", - "expire_after_first_use": "Старт после первого использования", - "expire_after_first_use_hint": "Отсчёт дней начнётся, когда пользователь впервые получит конфиг (панель, share или Telegram).", - "expire_days_required": "Укажите число дней больше 0", - "expire_pending_label": "{} дн. после первого использования", - "expire_started_suffix": "отсчёт уже начат", - "expire_already_started_hint": "Отсчёт уже начат. Срок до: {}. Снимите галочку, чтобы задать фиксированную дату.", - "description_label": "Описание (опц.)", - "auto_conn_title": "Автоматическое создание подключения (опционально)", - "server_label": "Сервер", - "no_create_conn": "— Не создавать подключение —", - "protocol_label": "Протокол", - "server_then_protocol_hint": "Сначала выберите сервер, затем протокол, установленный на нём (WireGuard, AmneziaWG 2.0 и т.д.).", - "edit_user_title": "Редактировать пользователя: {}", - "edit_user_tg": "Telegram ID (опционально)", - "edit_user_email": "Email (опционально)", - "edit_user_limit": "Лимит трафика (ГБ) (0 = ∞)", - "new_password_hint": "Новый пароль (оставьте пустым, если не нужно менять)", - "op_type_label": "Тип операции", - "create_new_conn": "Создать новое подключение", - "link_existing_conn": "Привязать существующее", - "select_existing_conn": "Выберите существующее подключение", - "existing_conn_hint": "Будут показаны только не привязанные к пользователям подключения", - "conn_name_panel": "Имя подключения (в панели)", - "user_conns_title": "Подключения пользователя", - "no_free_conns": "Нет свободных подключений", - "select_connection": "Выберите подключение", - "linking": "Привязка...", - "conn_linked": "Подключение привязано", - "enabling": "Включение", - "disabling": "Отключение", - "user_enabled": "Пользователь включён", - "user_disabled": "Пользователь отключён", - "delete_user_confirm": "Удалить пользователя? Все его подключения будут удалены.", - "share_access": "Поделиться доступом", - "enable_public_access": "Включить публичный доступ", - "share_password_label": "Пароль (опционально)", - "share_password_hint": "Если установлен, пользователь должен будет его ввести", - "guest_settings_title": "Гостевой доступ", - "guest_enable": "Включить гостевой доступ (без регистрации)", - "guest_enable_hint": "Публичная ссылка на странице входа. Гости видят подключения выбранного пользователя и могут создать новый конфиг.", - "guest_link_label": "Гостевая ссылка", - "guest_link_placeholder": "Сохраните настройки, чтобы создать ссылку", - "guest_regen_token": "Обновить ссылку", - "guest_regen_confirm": "Обновить гостевую ссылку? Старая перестанет работать.", - "guest_token_updated": "Гостевая ссылка обновлена", - "guest_user_label": "Пользователь для гостевых подключений", - "guest_user_none": "— Выберите пользователя —", - "guest_user_hint": "Создайте обычного пользователя (например guest), назначьте VPN — гости увидят их без входа.", - "guest_password_label": "Пароль гостя (опционально)", - "guest_password_keep": "Оставьте пустым, чтобы сохранить текущий", - "guest_clear_password": "Убрать защиту паролем", - "guest_allow_create": "Разрешить гостям создавать новый конфиг", - "guest_access_btn": "Войти как гость", - "guest_access_login_hint": "Регистрация не нужна", - "guest_title": "Гостевой доступ", - "guest_subtitle": "Получите VPN-конфиг без регистрации", - "guest_protected_desc": "Эта страница защищена паролем.", - "guest_get_config": "Получить конфиг", - "guest_get_config_hint": "Для вас будет создан новый VPN-конфиг", - "guest_not_found": "404 Не найдено", - "guest_not_found_desc": "Гостевой доступ отключён или ссылка неверна.", - "wrong_guest_password": "Неверный пароль", - "invites_title": "Ссылки для конфигов", - "invites_hint": "Создавайте ссылки для раздачи. По ссылке можно создать VPN-конфиг — сколько раз, указываете вы.", - "invites_empty": "Ссылок пока нет", - "invites_empty_desc": "Создайте ссылку и отправьте её — регистрация не нужна.", - "invite_create": "Создать ссылку", - "invite_edit": "Редактировать ссылку", - "invite_active": "Активна", - "invite_expired": "Истекла", - "invite_exhausted": "Лимит исчерпан", - "invite_uses": "Использований", - "invite_name_label": "Название ссылки", - "invite_max_uses_label": "Сколько конфигов", - "invite_max_uses_hint": "Сколько раз можно создать конфиг по этой ссылке. 0 = без лимита.", - "invite_user_label": "Пользователь для хранения", - "invite_user_hint": "Созданные конфиги привязываются к этому пользователю панели.", - "invite_user_required": "Выберите пользователя для хранения конфигов", - "invite_password_label": "Пароль (опционально)", - "invite_expires_label": "Срок действия (опционально)", - "invite_duration_label": "Срок конфига (дни)", - "invite_duration_hint": "Отсчёт начинается, когда пользователь нажал «Получить конфиг». 0 = без срока.", - "invite_duration_short": "Срок", - "invite_duration_starts_hint": "После получения конфиг будет действовать {} дн.", - "invite_config_expires_at": "Конфиг действует до: {}", - "invite_sub_tab": "Подписка", - "days_short": "дн", - "invite_clear_expires": "Убрать срок действия", - "invite_note_label": "Заметка (только админ)", - "invite_note_placeholder": "Внутренний комментарий", - "invite_reset_used": "Сбросить счётчик использований", - "invite_created": "Ссылка создана (URL скопирован)", - "invite_updated": "Ссылка обновлена", - "invite_deleted": "Ссылка удалена", - "invite_delete_confirm": "Удалить эту ссылку?", - "invite_public_title": "Приглашение", - "invite_public_subtitle": "Получите VPN-конфиг по ссылке", - "invite_protected_desc": "Эта ссылка защищена паролем.", - "invite_get_config_hint": "Каждое нажатие создаёт новый конфиг и тратит одно использование", - "invite_uses_left": "Осталось использований: {}", - "invite_uses_left_unlimited": "Уже создано: {} (без лимита)", - "invite_not_found": "404 Не найдено", - "invite_not_found_desc": "Ссылка неверна или была удалена.", - "wrong_invite_password": "Неверный пароль", - "public_link_label": "Публичная ссылка", - "disabled": "Отключён", - "out_of": "Из", - "settings_title": "Настройки панели", - "appearance": "🎨 Внешний вид", - "title_label": "Заголовок (Title)", - "logo_label": "Логотип (Emoji или символ)", - "subtitle_label": "Подзаголовок (Subtitle)", - "captcha_title": "🔐 Капча", - "enable_captcha": "Включить капчу (multicolorcaptcha)", - "telegram_bot_title": "🤖 Telegram Bot", - "bot_token_label": "Bot Token (от @BotFather)", - "bot_status": "Статус", - "bot_running": "✅ Запущен", - "bot_stopped": "⏹ Остановлен", - "bot_stop_btn": "⏹ Остановить", - "bot_start_btn": "▶️ Запустить", - "bot_hint": "После изменения токена сохраните настройки — бот перезапустится автоматически. Пользователи должны иметь Telegram ID в профиле.", - "api_docs_title": "🔌 API Документация", - "api_docs_hint": "Используйте эти интерфейсы для изучения возможностей API панели", - "tunnels_title": "Туннели", - "local_server": "Локальный сервер", - "tunnel_install_enable": "Установить и включить", - "tunnel_enable": "Включить туннель", - "tunnel_running": "Туннель запущен", - "tunnel_stop": "Остановить", - "tunnel_stopping": "Остановка...", - "tunnel_stopped": "Туннель остановлен", - "tunnel_delete": "Удалить", - "tunnel_deleted": "Туннель удалён", - "tunnel_delete_confirm": "Остановить и удалить бинарный файл туннеля, установленный панелью?", - "tunnel_starting": "Запуск туннеля...", - "tunnel_started": "Туннель запущен", - "tunnel_waiting_url": "Туннель запущен. Ждем публичный адрес...", - "tunnel_no_public_url": "Публичный адрес появится здесь после запуска туннеля.", - "tunnels_hint": "Быстрые туннели публикуют локальную панель в интернете. Используйте надежный пароль администратора и передавайте публичные адреса только доверенным пользователям.", - "ngrok_authtoken_placeholder": "ngrok authtoken (для некоторых аккаунтов необязательно)", - "import_users_title": "📤 Импорт пользователей", - "import_source_label": "Источник импорта / Синхронизации", - "remnawave_url_label": "Remnawave URL", - "api_key_label": "API Key", - "enable_sync": "Включить синхронизацию", - "sync_hint": "Пользователи будут создаваться, отключаться и удаляться автоматически на основе данных из Remnawave", - "sync_now_btn": "🔄 Синхронизировать сейчас", - "delete_sync_btn": "🗑 Удалить синхронизацию", - "auto_create_conns": "Создавать подключения автоматически", - "sync_server_label": "Сервер для новых подключений", - "save_changes": "💾 Сохранить изменения", - "sync_success": "Синхронизация завершена: {} пользователей", - "sync_deleted": "Удалено {} синхронизированных пользователей", - "delete_sync_confirm": "Вы уверены, что хотите удалить ВСЕХ синхронизированных пользователей и их подключения? Это действие необратимо.", - "settings_saved": "Настройки успешно сохранены", - "bot_started": "Бот запущен", - "bot_stopped_msg": "Бот остановлен", - "sync_running": "Синхронизация...", - "deleting": "Удаление...", - "my_connections_title": "Мои подключения", - "show_config": "📄 Показать конфиг", - "no_connections_user_desc": "Обратитесь к администратору для создания VPN подключения", - "share_title": "Доступ к подключениям", - "vpn_access": "VPN Доступ", - "user_label_share": "Пользователь", - "share_protected_desc": "Этот доступ защищен паролем. Пожалуйста, введите его для продолжения.", - "auth_success": "Вход выполнен", - "loading_share_conns": "Загрузка подключений...", - "no_active_conns": "У вас пока нет активных подключений.", - "show_settings_btn": "🔑 Показать настройки", - "share_copy_hint": "Нажмите кнопку — ключ скопируется сам. Потом вставьте его в приложение VPN.", - "copy_key_big": "Скопировать ключ", - "copying_key": "Копирую…", - "key_copied": "Ключ скопирован — вставьте в приложение", - "more_qr_download": "QR-код и файл", - "download_config_file": "Скачать файл конфига", - "show_config_text": "Показать текст конфига", - "copied": "Скопировано!", - "active": "Активен", - "site_description": "Amnezia Web Panel — управление VPN серверами и протоколами AmneziaWG", - "toggle_theme": "Сменить тему", - "copied_to_clipboard": "Скопировано в буфер обмена", - "share_not_found": "404 Не Найдено", - "share_not_found_desc": "Либо ссылка неверна, либо доступ был отключен.", - "invalid_captcha": "Неверная капча", - "account_disabled": "Аккаунт отключён", - "invalid_login": "Неверный логин или пароль", - "user_exists": "Пользователь с таким именем уже существует", - "cannot_delete_self": "Нельзя удалить самого себя", - "wrong_share_password": "Неверный пароль", - "saving": "Сохранение...", - "select_lang": "Выберите язык", - "lang_ru": "Русский", - "lang_en": "English", - "lang_fr": "Français", - "lang_zh": "中文 (Chinese)", - "lang_fa": "فارسی (Persian)", - "backup_title": "Резервное копирование", - "download_backup": "Скачать data.json", - "restore_backup": "Восстановить из файла", - "restore_confirm": "Вы уверены? Текущие данные будут перезаписаны, и приложение перезагрузится.", - "restore_success": "Восстановление успешно! Перезагрузка...", - "invalid_backup_file": "Неверный файл резервной копии или структура", - "config_unavailable": "Конфигурация недоступна", - "config_unavailable_desc": "Этот клиент был создан через нативное приложение Amnezia.\\nПриватный ключ хранится только на устройстве пользователя и не может быть восстановлен сервером.", - "client_public_key": "Публичный ключ клиента:", - "telemt_desc": "Прокси для Telegram на базе MTProxy с продвинутой обфускацией и эмуляцией TLS.", - "hysteria_desc": "Hysteria 2 (QUIC/UDP) на официальном образе tobyxdd/hysteria (apernet/hysteria) с TLS от Let's Encrypt. Домен указывает админ при установке.", - "hysteria_domain": "Домен", - "hysteria_email": "Email для Let's Encrypt", - "hysteria_port_hint": "UDP-порт Hysteria (по умолчанию 8998). Не ставьте 443, если он уже занят nginx/xray. Порт 80/TCP должен быть свободен для выпуска сертификата.", - "hysteria_install_hint": "Перед установкой A-запись домена должна указывать на этот сервер. Порт 80/TCP временно используется для HTTP-01 проверки Let's Encrypt. BBR и obfuscation salamander включаются автоматически.", - "hysteria_dns_hint": "Перед установкой создайте DNS-запись:", - "hysteria_settings_title": "Настройки Hysteria", - "hysteria_change_port": "Сменить порт", - "hysteria_port_change_hint": "UDP-порт прослушивания. Если ошибка «address already in use» — выберите свободный порт (8443 или 8998), сохраните и заново импортируйте ссылку в клиент.", - "hysteria_settings_saved": "Настройки Hysteria обновлены", - "hysteria_ports_warning": "Внимание: для стабильной работы нужны свободные TCP-порты 80 и 443 (Let's Encrypt на 80, HTTPS/QUIC на 443).", - "hysteria_renew_ssl": "Обновить SSL", - "hysteria_ssl_change_hint": "Смена домена заново выпускает сертификат Let's Encrypt (порт 80 должен быть свободен).", - "hysteria_ssl_required": "Для SSL нужны домен и email", - "naiveproxy_desc": "NaiveProxy (стабильный) — HTTPS/HTTP2 через Caddy + forwardproxy, TLS Let's Encrypt. Клиент: используйте Karing. Не используйте v2rayN; остальные клиенты под вопросом.", - "naiveproxy_domain": "Домен", - "naiveproxy_email": "Email для Let's Encrypt", - "naiveproxy_port_hint": "TCP-порт 443 фиксирован для NaiveProxy. Порт 80/TCP должен быть свободен для выпуска сертификата.", - "naiveproxy_install_hint": "Перед установкой A-запись домена должна указывать на этот сервер. Нужны свободные TCP-порты 80 и 443 (Let's Encrypt и HTTPS-прокси).", - "naiveproxy_dns_hint": "Перед установкой создайте DNS-запись:", - "naiveproxy_ports_warning": "Внимание: для стабильной работы нужны свободные TCP-порты 80 и 443 (Let's Encrypt на 80, HTTPS-прокси на 443).", - "naiveproxy_client_hint": "Стабильная версия. НЕ используйте v2rayN. Точно работает в Karing. Остальные клиенты под вопросом.", - "server_ssl_domain": "SSL-домен (по умолчанию)", - "server_ssl_email": "SSL-email (по умолчанию)", - "server_ssl_hint": "Подставляется при установке Hysteria / NGINX на этом сервере (Let's Encrypt).", - "container_logs": "Логи контейнера", - "logs_btn": "📋 Логи", - "logs_live": "В реальном времени", - "logs_reconnect": "переподключение…", - "no_logs": "Логов пока нет", - "container_status": "Статус", - "exit_code": "Код выхода", - "close": "Закрыть", - "tls_emulation": "TLS-эмуляция", - "tls_domain": "Домен маскировки", - "max_connections_limit": "Макс. соединений", - "max_connections_hint": "0 — без ограничений. Ограничивает одновременных пользователей.", - "traffic_reset_strategy_label": "Стратегия сброса трафика", - "traffic_reset_never": "Никогда не сбрасывать", - "traffic_reset_daily": "Сброс ежедневно", - "traffic_reset_weekly": "Сброс еженедельно", - "traffic_reset_monthly": "Сброс ежемесячно", - "traffic_reset_hint": "Как часто следует сбрасывать трафик пользователя", - "traffic_used_total": "Всего потрачено", - "traffic_reset_info": "Следующий сброс: {}", - "services": "Сервисы", - "dns_desc": "Персональный DNS-сервер для блокировки рекламы и защиты приватности ваших запросов.", - "dns_internal_hint": "Сервис будет запущен во внутренней сети (172.29.172.254) без привязки к портам хоста.", - "telemt_port_hint": "Порт для Telegram-прокси (обычно 443).", - "socks5_desc": "SOCKS5-прокси (3proxy) с одной учётной записью и аутентификацией по логину/паролю.", - "socks5_username": "Логин", - "socks5_password": "Пароль", - "socks5_password_placeholder": "Оставьте пустым для автогенерации", - "socks5_password_hint": "Если поле пустое, будет сгенерирован случайный пароль на 16 символов.", - "socks5_port_hint": "TCP-порт, на котором будет слушать прокси. В официальном клиенте Amnezia по умолчанию 38080.", - "socks5_change_settings": "Изменить настройки", - "socks5_settings_title": "SOCKS5 — Настройки подключения", - "socks5_port_change_hint": "Смена порта пересоздаёт контейнер — текущие соединения оборвутся.", - "socks5_settings_saved": "Настройки SOCKS5 сохранены", - "adguard_desc": "AdGuard Home — DNS-блокировщик рекламы с веб-интерфейсом администратора.", - "adguard_mode": "Режим установки", - "adguard_mode_sidebyside": "Рядом с AmneziaDNS", - "adguard_mode_sidebyside_hint": "поднимается параллельно AmneziaDNS на отдельном внутреннем IP (172.29.172.253)", - "adguard_mode_replace": "Заменить AmneziaDNS", - "adguard_mode_replace_hint": "удаляет контейнер amnezia-dns и забирает его IP — VPN-клиенты сразу пойдут в AdGuard", - "adguard_dns_port": "DNS-порт (53)", - "adguard_dns_port_hint": "Внутренний DNS-порт, на который ходят VPN-клиенты через docker-сеть.", - "adguard_web_port": "Порт Web UI", - "adguard_dot_port": "Порт DoT", - "adguard_doh_port": "Порт DoH", - "adguard_expose_web": "Опубликовать наружу", - "adguard_expose": "Опубликовать наружу", - "adguard_install_hint": "Первоначальная настройка AdGuard проходит через мастер на веб-интерфейсе после установки.", - "adguard_internal_ip": "Внутренний IP", - "adguard_web_ui": "Адрес панели", - "adguard_open_ui": "Открыть Web UI", - "edit_server_title": "Изменить сервер", - "edit_keep_credential": "Оставьте пустым, чтобы сохранить текущее значение", - "server_saved": "Настройки сервера сохранены", - "servers_reordered": "Порядок серверов изменён", - "ping_checking": "Проверяем соединение...", - "offline": "недоступен", - "api_tokens_title": "API-токены", - "api_tokens_hint": "Bearer-токены для внешних интеграций. Передавайте токен в заголовке `Authorization: Bearer `. Токены имеют права администратора и перестают работать, если их владелец отключён или понижен в роли.", - "api_tokens_empty": "Пока нет ни одного API-токена. Создайте, чтобы подключить панель к внешнему сервису.", - "api_tokens_create": "Создать токен", - "api_tokens_create_title": "Создание API-токена", - "api_tokens_name_label": "Название", - "api_tokens_name_placeholder": "напр. Linear, CI-бот, мониторинг", - "api_tokens_name_hint": "Нужно, чтобы отличать токен в списке. Сам токен сгенерируется автоматически.", - "api_tokens_name_required": "Укажите название токена", - "api_tokens_created_title": "Токен создан", - "api_tokens_value_label": "Значение токена", - "api_tokens_warning_title": "Это единственный раз, когда вы увидите этот токен.", - "api_tokens_warning_body": "Скопируйте его сейчас и сохраните в менеджере секретов вашей интеграции. Панель хранит только хэш — если потеряете значение, токен придётся отозвать и создать заново.", - "api_tokens_revoke": "Отозвать", - "api_tokens_revoke_confirm": "Отозвать токен «{}»? Любая интеграция, использующая его, перестанет работать.", - "api_tokens_revoked": "Токен отозван", - "api_tokens_created": "Создан", - "api_tokens_last_used": "Последнее использование", - "api_tokens_owner": "Владелец", - "api_tokens_never": "никогда", - "coming_soon": "Скоро", - "promo_star_cta": "Поставить звезду на GitHub", - "aivpn_subtitle": "ИИ-подбор протокола, который сам выбирает правильный туннель под текущие условия. Поможете звездой — выйдет быстрее.", - "revproxy_title": "Reverse Proxy", - "revproxy_subtitle": "Высокопроизводительная маскировка трафика: прячем VPN за обычным сайтом, снижаем заметность для DPI.", - "management": "Управление", - "server_management": "Управление сервером", - "server_management_desc": "Выполнение системных действий на вашей машине.", - "check_server_services": "Проверить сервисы сервера", - "reboot_server": "Перезагрузить сервер", - "clear_server": "Очистить сервер", - "remove_server": "Удалить сервер из панели", - "ssl_title": "SSL / HTTPS Настройки", - "enable_https": "Включить HTTPS", - "panel_port_label": "Порт панели", - "domain_label": "Доменное имя", - "cert_path_label": "Путь к SSL сертификату (.pem)", - "key_path_label": "Путь к приватному ключу (.pem)", - "or_paste_text": "ИЛИ вставьте текст сертификатов", - "cert_text_label": "Содержимое сертификата (CRT)", - "key_text_label": "Содержимое ключа (KEY)", - "ssl_hint": "После включения панель будет доступна по https:// на указанном домене. Убедитесь, что пути к файлам или текстовые данные верны.", - "about_title": "О программе и обновления", - "current_version": "Текущая версия", - "check_updates": "Проверить обновления", - "checking_updates": "Проверка...", - "update_available": "Доступна новая версия", - "download_update": "Скачать обновление", - "up_to_date": "У вас установлена последняя версия", - "warp_hint": "WARP направляет исходящий трафик этого хоста через Cloudflare. Он не создаёт публичный URL панели как Quick Tunnel или ngrok.", - "warp_install_hint": "Сначала установите официальный клиент Cloudflare WARP, затем перезапустите панель.", - "warp_install_required": "Сначала установите WARP", - "warp_connect": "Подключить WARP", - "warp_disconnect": "Отключить", - "warp_connected": "WARP подключён", - "warp_disconnected": "WARP отключён", - "warp_status_unknown": "Статус WARP неизвестен.", - "warp_status_not_installed": "Cloudflare WARP CLI не установлен на этом хосте.", - "warp_status_connected": "Cloudflare WARP подключён.", - "warp_status_connecting": "Cloudflare WARP подключается.", - "warp_status_disconnected": "Cloudflare WARP отключён.", - "warp_status_not_registered": "Cloudflare WARP установлен, но ещё не зарегистрирован. Подключение попробует зарегистрировать его автоматически.", - "warp_status_service_unavailable": "Сервис Cloudflare WARP недоступен. Убедитесь, что warp-svc запущен.", - "warp_status_error": "Cloudflare WARP вернул ошибку.", - "install_starting": "Начинаем установку...", - "server_check_complete": "Проверка завершена", - "server_connection_error": "Ошибка подключения", - "connections_load_error": "Ошибка загрузки подключений", - "connection_updated": "Подключение обновлено", - "edit_connection": "Редактировать подключение", - "enable_connection_progress": "Включение подключения...", - "disable_connection_progress": "Отключение подключения...", - "connection_enabled": "Подключение включено", - "connection_disabled": "Подключение отключено", - "gb": "ГБ", - "marketplace": "Шаблоны", - "open_marketplace": "Открыть шаблоны", - "marketplace_desc": "Установите доступные шаблоны протоколов и сервисов для этого сервера.", - "installed_apps": "Установленные приложения", - "installed_apps_hint": "Здесь отображаются только установленные протоколы и сервисы. Чтобы добавить новые, откройте шаблоны.", - "no_installed_apps": "Пока нет установленных приложений", - "no_installed_apps_desc": "Откройте шаблоны, чтобы установить протоколы или сервисы на этот сервер.", - "install_another": "Установить ещё один", - "new_instance": "новый экземпляр", - "port_next_instance_hint": "Выберите свободный UDP-порт для дополнительного экземпляра.", - "checking_server": "Проверяем сервисы сервера...", - "web_servers": "Веб-серверы", - "nginx_desc": "NGINX веб-сервер с HTTPS-сертификатом Let's Encrypt и редактируемым index.html.", - "nginx_domain": "Домен", - "nginx_email": "Email для Let's Encrypt", - "nginx_port_hint": "HTTPS-порт на сервере. Порт 80/TCP должен быть свободен и доступен для проверки Let's Encrypt.", - "nginx_install_hint": "Домен уже должен указывать на этот сервер. Порт 80/TCP используется для HTTP-01 проверки сертификата.", - "site": "Сайт", - "site_editor": "Редактор сайта", - "site_saved": "Сайт сохранён", - "nginx_dns_hint": "Перед установкой создайте DNS-запись:", - "backup": "Бекап", - "backups": "Бекапы", - "backup_desc": "Бекапы создаются на сервере и содержат файлы протокола (конфиг, ключи, clientsTable). Для WireGuard / AWG можно также скачать ZIP со всеми клиентскими .conf.", - "create_backup": "Создать бекап", - "creating_backup": "Создание бекапа...", - "backup_created": "Бекап создан", - "no_backups": "Бекапов пока нет", - "no_backups_desc": "Создайте первый бекап для этого протокола.", - "export_client_configs": "Скачать все .conf", - "export_client_configs_hint": "ZIP со всеми клиентскими конфигами WireGuard / AmneziaWG (если сохранён приватный ключ).", - "exporting_client_configs": "Готовлю ZIP…", - "export_client_configs_done": "ZIP с конфигами скачан", - "restore_protocol_backup": "Восстановить", - "restore_protocol_backup_confirm": "Восстановить этот бекап на сервер? Текущие файлы протокола будут перезаписаны, контейнер перезапустится.", - "restoring_protocol_backup": "Восстановление… подождите", - "restore_protocol_backup_done": "Бекап восстановлен", - "refresh": "Обновить", - "guest_allow_create_hint": "Создаёт конфиг у гостевого пользователя (3x-ui VLESS или протокол выбранного сервера).", - "invite_inbound_hint": "Список inbound загружается из выбранной панели 3x-ui. Конфиг и ссылку отдаёт только 3x-ui.", - "invite_inbound_loading": "Загрузка inbound из 3x-ui…", - "invite_inbound_empty": "VLESS inbound не найдены на этой панели 3x-ui", - "invite_inbound_need_xui": "Сначала настройте 3x-ui в Настройках", - "invite_inbound_required": "Выберите VLESS inbound из 3x-ui", - "invite_sub_url_missing_title": "Не задан URL подписки", - "invite_sub_url_missing_hint": "В Настройки → 3x-ui укажите «Базовый URL подписки», чтобы ссылки выдавали /sub/… конфиги.", - "xui_url_label": "URL 3x-ui", - "xui_url_hint": "Укажите схему, порт и webBasePath при наличии (пример: https://host:2053/secret)", - "xui_sub_url_label": "Базовый URL подписки", - "xui_sub_url_hint": "Публичный /sub без слэша в конце (пример: https://host:2096/sub). Конфиги по ссылкам выдаются как subscription URL.", - "xui_api_token_label": "API Token (предпочтительно)", - "xui_api_token_placeholder": "Settings → Security → API Token", - "xui_api_token_hint": "Если задан, вход по логину/паролю не используется", - "xui_username_label": "Логин 3x-ui", - "xui_password_label": "Пароль 3x-ui", - "xui_sync_hint": "Клиенты из inbounds 3x-ui будут создаваться, отключаться и удаляться как пользователи панели (связь по email)", - "xui_inbound_label": "VLESS inbound", - "xui_inbound_auto": "Авто (первый VLESS inbound)", - "xui_inbound_hint": "Inbound подгружается из 3x-ui. Сайт только создаёт клиента и забирает готовую ссылку — без своей генерации конфига.", - "xui_servers_title": "Серверы 3x-ui", - "xui_servers_hint": "Добавьте панели 3x-ui. При создании выберите сервер и inbound из списка 3x-ui — ссылку вернёт панель.", - "xui_servers_empty": "Серверов 3x-ui пока нет. Нажмите «Добавить сервер».", - "xui_servers_manage_hint": "URL панелей и базовые URL подписок настраиваются в разделе «Серверы 3x-ui» ниже.", - "xui_server_add": "Добавить сервер", - "xui_server_name_label": "Название / описание", - "xui_server_name_hint": "Так сервер будет отображаться в инвайтах и у пользователей (например: US, NL, Home).", - "xui_server_select_label": "Сервер 3x-ui", - "xui_server_select_hint": "Выберите панель 3x-ui, затем inbound из списка, который она отдаёт по API.", - "xui_server_delete_confirm": "Удалить этот сервер 3x-ui из панели?" + "nav_servers": "Серверы", + "nav_users": "Пользователи", + "nav_invites": "Ссылки", + "nav_settings": "Настройки", + "nav_connections": "Подключения", + "nav_logout": "Выход", + "theme_dark": "Темная", + "theme_light": "Светлая", + "back_to_servers": "← Назад к серверам", + "server_check": "Проверить", + "server_checking": "Проверка...", + "protocols": "Протоколы", + "install": "Установить", + "uninstall": "Удалить", + "run": "Работает", + "stop": "Остановлен", + "not_installed": "Не установлен", + "installed": "Установлен", + "add_server": "Добавить сервер", + "no_servers": "Серверы не добавлены", + "name": "Название", + "host": "Адрес", + "port": "Порт", + "status": "Статус", + "actions": "Действия", + "edit": "Редактировать", + "delete": "Удалить", + "save": "Сохранить", + "cancel": "Отмена", + "loading": "Загрузка...", + "copy": "Копировать", + "download": "Скачать", + "config": "Конфигурация", + "qr_code": "QR-код", + "vpn_key": "VPN-ключ", + "clients": "Клиенты", + "traffic": "Трафик", + "limit": "Limit", + "manage": "Управление", + "no_protocols": "Нет протоколов", + "add_server_desc": "Добавьте свой первый VPN сервер для начала работы. Вам понадобятся SSH-данные для подключения.", + "ssh_host": "Хост / IP", + "ssh_port": "SSH Порт", + "ssh_user": "Пользователь", + "ssh_password": "Пароль", + "ssh_key": "SSH Ключ", + "ssh_key_placeholder": "Приватный SSH ключ", + "ssh_key_hint": "Вставьте содержимое вашего файла id_rsa или id_ed25519", + "connect": "Подключить", + "connecting": "Подключение...", + "server_added": "Сервер успешно добавлен", + "server_deleted": "Сервер удалён", + "delete_confirm": "Вы уверены, что хотите удалить этот сервер?", + "success": "Успешно", + "error": "Ошибка", + "info": "Информация", + "login_title": "Вход в панель", + "username": "Логин", + "password": "Пароль", + "captcha": "Капча", + "captcha_placeholder": "Введите символы с картинки", + "captcha_hint": "Нажмите для обновления картинки", + "login": "Войти", + "logging_in": "Вход...", + "login_error": "Ошибка входа", + "check_done": "Проверка завершена", + "check_error": "Ошибка подключения", + "ssh_ok": "SSH подключение", + "docker_ok": "Docker", + "docker_not_installed": "Docker не установлен", + "awg_desc": "Новая версия протокола на основе awg-go. Поддерживает расширенную обфускацию с параметрами S3, S4.", + "awg_legacy_desc": "Оригинальная версия AWG на базе ядра WireGuard. Совместима с клиентами старых версий.", + "xray_desc": "Современный протокол с маскировкой под обычный веб-трафик (XTLS-Reality). Устойчив к глубокому анализу пакетов.", + "wireguard_desc": "Стандартный и самый быстрый VPN-протокол. Встроен во все современные ОС, но легко блокируется DPI.", + "not_checked": "Не проверено", + "connections": "Подключения", + "add": "Добавить", + "loading_connections": "Загрузка подключений...", + "no_connections": "Нет подключений", + "no_connections_desc": "Добавьте первое подключение для генерации VPN конфигурации", + "install_protocol": "Установить протокол", + "port_default_hint": "Порт по умолчанию: 55424. Убедитесь, что он не занят", + "port_xray_hint": "Порт по умолчанию: 443 (рекомендуется для Xray). Убедитесь, что он не занят другим веб-сервером.", + "reinstall": "Переустановить", + "uninstall_confirm": "Удалить {}? Все подключения и конфигурации будут потеряны.", + "stop_container_confirm": "Остановить контейнер {}?", + "start_container_confirm": "Запустить контейнер {}?", + "stopping": "Остановка", + "starting": "Запуск", + "stopped": "остановлен", + "started": "запущен", + "server_config": "Конфигурация сервера", + "add_connection": "Добавить подключение", + "connection_name": "Имя подключения", + "connection_name_placeholder": "Например: iPhone, Laptop", + "assign_to_user": "Назначить пользователю (опционально)", + "no_assign": "— Без привязки —", + "create": "Создать", + "creating": "Создание...", + "connection_created": "Подключение \"{}\" создано", + "delete_connection_confirm": "Удалить это подключение? Конфигурация перестанет работать.", + "connection_deleted": "Подключение удалено", + "config_tab": "📄 Конфиг", + "vpn_key_tab": "🔑 VPN-ключ", + "qr_code_tab": "📱 QR-код", + "copy_config": "📋 Копировать", + "download_conf": "⬇ Скачать .conf", + "copy_key": "📋 Копировать ключ", + "vpn_link_hint": "Вставьте эту ссылку в приложение AmneziaVPN для автоматической настройки", + "qr_hint": "Отсканируйте QR-код в приложении AmneziaVPN", + "no_data": "Нет данных", + "start_btn": "▶ Старт", + "stop_btn": "⏹ Стоп", + "config_btn": "⚙️ Конфиг", + "done": "Готово", + "installing": "Установка...", + "start_install": "Начинаем установку...", + "check_docker": "Проверка Docker...", + "prepare_host": "Подготовка хоста...", + "build_container": "Сборка контейнера...", + "install_success": "Протокол установлен успешно!", + "install_error": "Ошибка установки: ", + "qr_error": "Не удалось сгенерировать QR-код", + "users_title": "Пользователи", + "search_placeholder": "Поиск по имени, email или Telegram...", + "add_user": "Добавить пользователя", + "prev_page": "← Назад", + "next_page": "Вперёд →", + "page_info": "Страница {} из {}", + "loading_users": "Загрузка пользователей...", + "nothing_found": "Ничего не найдено", + "search_empty_desc": "Попробуйте изменить запрос или создайте нового пользователя", + "username_label": "Имя пользователя", + "password_label": "Пароль", + "role_label": "Роль", + "role_user_desc": "User — только просмотр своих подключений", + "role_support_desc": "Support — управление серверами", + "role_admin_desc": "Admin — полный доступ", + "tg_id_label": "Telegram ID (опц.)", + "email_label": "Email (опц.)", + "traffic_limit_label": "Лимит трафика (ГБ) (опц., 0 = ∞)", + "expiration_date_label": "Срок действия (до)", + "no_expiration": "Без срока", + "expire_after_first_use": "Старт после первого использования", + "expire_after_first_use_hint": "Отсчёт дней начнётся, когда пользователь впервые получит конфиг (панель, share или Telegram).", + "expire_days_required": "Укажите число дней больше 0", + "expire_pending_label": "{} дн. после первого использования", + "expire_started_suffix": "отсчёт уже начат", + "expire_already_started_hint": "Отсчёт уже начат. Срок до: {}. Снимите галочку, чтобы задать фиксированную дату.", + "description_label": "Описание (опц.)", + "auto_conn_title": "Автоматическое создание подключения (опционально)", + "server_label": "Сервер", + "no_create_conn": "— Не создавать подключение —", + "protocol_label": "Протокол", + "server_then_protocol_hint": "Сначала выберите сервер, затем протокол, установленный на нём (WireGuard, AmneziaWG 2.0 и т.д.).", + "edit_user_title": "Редактировать пользователя: {}", + "edit_user_tg": "Telegram ID (опционально)", + "edit_user_email": "Email (опционально)", + "edit_user_limit": "Лимит трафика (ГБ) (0 = ∞)", + "new_password_hint": "Новый пароль (оставьте пустым, если не нужно менять)", + "op_type_label": "Тип операции", + "create_new_conn": "Создать новое подключение", + "link_existing_conn": "Привязать существующее", + "select_existing_conn": "Выберите существующее подключение", + "existing_conn_hint": "Будут показаны только не привязанные к пользователям подключения", + "conn_name_panel": "Имя подключения (в панели)", + "user_conns_title": "Подключения пользователя", + "no_free_conns": "Нет свободных подключений", + "select_connection": "Выберите подключение", + "linking": "Привязка...", + "conn_linked": "Подключение привязано", + "enabling": "Включение", + "disabling": "Отключение", + "user_enabled": "Пользователь включён", + "user_disabled": "Пользователь отключён", + "delete_user_confirm": "Удалить пользователя? Все его подключения будут удалены.", + "share_access": "Поделиться доступом", + "enable_public_access": "Включить публичный доступ", + "share_password_label": "Пароль (опционально)", + "share_password_hint": "Если установлен, пользователь должен будет его ввести", + "guest_settings_title": "Гостевой доступ", + "guest_enable": "Включить гостевой доступ (без регистрации)", + "guest_enable_hint": "Публичная ссылка на странице входа. Гости видят подключения выбранного пользователя и могут создать новый конфиг.", + "guest_link_label": "Гостевая ссылка", + "guest_link_placeholder": "Сохраните настройки, чтобы создать ссылку", + "guest_regen_token": "Обновить ссылку", + "guest_regen_confirm": "Обновить гостевую ссылку? Старая перестанет работать.", + "guest_token_updated": "Гостевая ссылка обновлена", + "guest_user_label": "Пользователь для гостевых подключений", + "guest_user_none": "— Выберите пользователя —", + "guest_user_hint": "Создайте обычного пользователя (например guest), назначьте VPN — гости увидят их без входа.", + "guest_password_label": "Пароль гостя (опционально)", + "guest_password_keep": "Оставьте пустым, чтобы сохранить текущий", + "guest_clear_password": "Убрать защиту паролем", + "guest_allow_create": "Разрешить гостям создавать новый конфиг", + "guest_access_btn": "Войти как гость", + "guest_access_login_hint": "Регистрация не нужна", + "guest_title": "Гостевой доступ", + "guest_subtitle": "Получите VPN-конфиг без регистрации", + "guest_protected_desc": "Эта страница защищена паролем.", + "guest_get_config": "Получить конфиг", + "guest_get_config_hint": "Для вас будет создан новый VPN-конфиг", + "guest_not_found": "404 Не найдено", + "guest_not_found_desc": "Гостевой доступ отключён или ссылка неверна.", + "wrong_guest_password": "Неверный пароль", + "invites_title": "Ссылки для конфигов", + "invites_hint": "Создавайте ссылки для раздачи. По ссылке можно создать VPN-конфиг — сколько раз, указываете вы.", + "invites_empty": "Ссылок пока нет", + "invites_empty_desc": "Создайте ссылку и отправьте её — регистрация не нужна.", + "invite_create": "Создать ссылку", + "invite_edit": "Редактировать ссылку", + "invite_active": "Активна", + "invite_expired": "Истекла", + "invite_exhausted": "Лимит исчерпан", + "invite_uses": "Использований", + "invite_name_label": "Название ссылки", + "invite_max_uses_label": "Сколько конфигов", + "invite_max_uses_hint": "Сколько раз можно создать конфиг по этой ссылке. 0 = без лимита.", + "invite_user_label": "Пользователь для хранения", + "invite_user_hint": "Созданные конфиги привязываются к этому пользователю панели.", + "invite_user_required": "Выберите пользователя для хранения конфигов", + "invite_password_label": "Пароль (опционально)", + "invite_expires_label": "Срок действия (опционально)", + "invite_duration_label": "Срок конфига (дни)", + "invite_duration_hint": "Отсчёт начинается, когда пользователь нажал «Получить конфиг». 0 = без срока.", + "invite_duration_short": "Срок", + "invite_duration_starts_hint": "После получения конфиг будет действовать {} дн.", + "invite_config_expires_at": "Конфиг действует до: {}", + "invite_sub_tab": "Подписка", + "days_short": "дн", + "invite_clear_expires": "Убрать срок действия", + "invite_note_label": "Заметка (только админ)", + "invite_note_placeholder": "Внутренний комментарий", + "invite_reset_used": "Сбросить счётчик использований", + "invite_created": "Ссылка создана (URL скопирован)", + "invite_updated": "Ссылка обновлена", + "invite_deleted": "Ссылка удалена", + "invite_delete_confirm": "Удалить эту ссылку?", + "invite_public_title": "Приглашение", + "invite_public_subtitle": "Получите VPN-конфиг по ссылке", + "invite_protected_desc": "Эта ссылка защищена паролем.", + "invite_get_config_hint": "Каждое нажатие создаёт новый конфиг и тратит одно использование", + "invite_uses_left": "Осталось использований: {}", + "invite_uses_left_unlimited": "Уже создано: {} (без лимита)", + "invite_not_found": "404 Не найдено", + "invite_not_found_desc": "Ссылка неверна или была удалена.", + "wrong_invite_password": "Неверный пароль", + "public_link_label": "Публичная ссылка", + "disabled": "Отключён", + "out_of": "Из", + "settings_title": "Настройки панели", + "appearance": "🎨 Внешний вид", + "title_label": "Заголовок (Title)", + "logo_label": "Логотип (Emoji или символ)", + "subtitle_label": "Подзаголовок (Subtitle)", + "captcha_title": "🔐 Капча", + "enable_captcha": "Включить капчу (multicolorcaptcha)", + "telegram_bot_title": "🤖 Telegram Bot", + "bot_token_label": "Bot Token (от @BotFather)", + "bot_status": "Статус", + "bot_running": "✅ Запущен", + "bot_stopped": "⏹ Остановлен", + "bot_stop_btn": "⏹ Остановить", + "bot_start_btn": "▶️ Запустить", + "bot_hint": "После изменения токена сохраните настройки — бот перезапустится автоматически. Пользователи должны иметь Telegram ID в профиле.", + "api_docs_title": "🔌 API Документация", + "api_docs_hint": "Используйте эти интерфейсы для изучения возможностей API панели", + "tunnels_title": "Туннели", + "local_server": "Локальный сервер", + "tunnel_install_enable": "Установить и включить", + "tunnel_enable": "Включить туннель", + "tunnel_running": "Туннель запущен", + "tunnel_stop": "Остановить", + "tunnel_stopping": "Остановка...", + "tunnel_stopped": "Туннель остановлен", + "tunnel_delete": "Удалить", + "tunnel_deleted": "Туннель удалён", + "tunnel_delete_confirm": "Остановить и удалить бинарный файл туннеля, установленный панелью?", + "tunnel_starting": "Запуск туннеля...", + "tunnel_started": "Туннель запущен", + "tunnel_waiting_url": "Туннель запущен. Ждем публичный адрес...", + "tunnel_no_public_url": "Публичный адрес появится здесь после запуска туннеля.", + "tunnels_hint": "Быстрые туннели публикуют локальную панель в интернете. Используйте надежный пароль администратора и передавайте публичные адреса только доверенным пользователям.", + "ngrok_authtoken_placeholder": "ngrok authtoken (для некоторых аккаунтов необязательно)", + "import_users_title": "📤 Импорт пользователей", + "import_source_label": "Источник импорта / Синхронизации", + "remnawave_url_label": "Remnawave URL", + "api_key_label": "API Key", + "enable_sync": "Включить синхронизацию", + "sync_hint": "Пользователи будут создаваться, отключаться и удаляться автоматически на основе данных из Remnawave", + "sync_now_btn": "🔄 Синхронизировать сейчас", + "delete_sync_btn": "🗑 Удалить синхронизацию", + "auto_create_conns": "Создавать подключения автоматически", + "sync_server_label": "Сервер для новых подключений", + "save_changes": "💾 Сохранить изменения", + "sync_success": "Синхронизация завершена: {} пользователей", + "sync_deleted": "Удалено {} синхронизированных пользователей", + "delete_sync_confirm": "Вы уверены, что хотите удалить ВСЕХ синхронизированных пользователей и их подключения? Это действие необратимо.", + "settings_saved": "Настройки успешно сохранены", + "bot_started": "Бот запущен", + "bot_stopped_msg": "Бот остановлен", + "sync_running": "Синхронизация...", + "deleting": "Удаление...", + "my_connections_title": "Мои подключения", + "show_config": "📄 Показать конфиг", + "no_connections_user_desc": "Обратитесь к администратору для создания VPN подключения", + "share_title": "Доступ к подключениям", + "vpn_access": "VPN Доступ", + "user_label_share": "Пользователь", + "share_protected_desc": "Этот доступ защищен паролем. Пожалуйста, введите его для продолжения.", + "auth_success": "Вход выполнен", + "loading_share_conns": "Загрузка подключений...", + "no_active_conns": "У вас пока нет активных подключений.", + "show_settings_btn": "🔑 Показать настройки", + "share_copy_hint": "Нажмите кнопку — ключ скопируется сам. Потом вставьте его в приложение VPN.", + "copy_key_big": "Скопировать ключ", + "copying_key": "Копирую…", + "key_copied": "Ключ скопирован — вставьте в приложение", + "more_qr_download": "QR-код и файл", + "download_config_file": "Скачать файл конфига", + "show_config_text": "Показать текст конфига", + "copied": "Скопировано!", + "active": "Активен", + "site_description": "Amnezia Web Panel — управление VPN серверами и протоколами AmneziaWG", + "toggle_theme": "Сменить тему", + "copied_to_clipboard": "Скопировано в буфер обмена", + "share_not_found": "404 Не Найдено", + "share_not_found_desc": "Либо ссылка неверна, либо доступ был отключен.", + "invalid_captcha": "Неверная капча", + "account_disabled": "Аккаунт отключён", + "invalid_login": "Неверный логин или пароль", + "user_exists": "Пользователь с таким именем уже существует", + "cannot_delete_self": "Нельзя удалить самого себя", + "wrong_share_password": "Неверный пароль", + "saving": "Сохранение...", + "select_lang": "Выберите язык", + "lang_ru": "Русский", + "lang_en": "English", + "lang_fr": "Français", + "lang_zh": "中文 (Chinese)", + "lang_fa": "فارسی (Persian)", + "backup_title": "Резервное копирование", + "download_backup": "Скачать data.json", + "restore_backup": "Восстановить из файла", + "restore_confirm": "Вы уверены? Текущие данные будут перезаписаны, и приложение перезагрузится.", + "restore_success": "Восстановление успешно! Перезагрузка...", + "invalid_backup_file": "Неверный файл резервной копии или структура", + "config_unavailable": "Конфигурация недоступна", + "config_unavailable_desc": "Этот клиент был создан через нативное приложение Amnezia.\\nПриватный ключ хранится только на устройстве пользователя и не может быть восстановлен сервером.", + "client_public_key": "Публичный ключ клиента:", + "telemt_desc": "Прокси для Telegram на базе MTProxy с продвинутой обфускацией и эмуляцией TLS.", + "hysteria_desc": "Hysteria 2 (QUIC/UDP) на официальном образе tobyxdd/hysteria (apernet/hysteria) с TLS от Let\u0027s Encrypt. Домен указывает админ при установке.", + "hysteria_domain": "Домен", + "hysteria_email": "Email для Let\u0027s Encrypt", + "hysteria_port_hint": "UDP-порт Hysteria (по умолчанию 8998). Не ставьте 443, если он уже занят nginx/xray. Порт 80/TCP должен быть свободен для выпуска сертификата.", + "hysteria_install_hint": "Перед установкой A-запись домена должна указывать на этот сервер. Порт 80/TCP временно используется для HTTP-01 проверки Let\u0027s Encrypt. BBR и obfuscation salamander включаются автоматически.", + "hysteria_dns_hint": "Перед установкой создайте DNS-запись:", + "hysteria_settings_title": "Настройки Hysteria", + "hysteria_change_port": "Сменить порт", + "hysteria_port_change_hint": "UDP-порт прослушивания. Если ошибка «address already in use» — выберите свободный порт (8443 или 8998), сохраните и заново импортируйте ссылку в клиент.", + "hysteria_settings_saved": "Настройки Hysteria обновлены", + "hysteria_ports_warning": "Внимание: для стабильной работы нужны свободные TCP-порты 80 и 443 (Let\u0027s Encrypt на 80, HTTPS/QUIC на 443).", + "hysteria_renew_ssl": "Обновить SSL", + "hysteria_ssl_change_hint": "Смена домена заново выпускает сертификат Let\u0027s Encrypt (порт 80 должен быть свободен).", + "hysteria_ssl_required": "Для SSL нужны домен и email", + "naiveproxy_desc": "NaiveProxy (стабильный) — HTTPS/HTTP2 через Caddy + forwardproxy, TLS Let\u0027s Encrypt. Клиент: используйте Karing. Не используйте v2rayN; остальные клиенты под вопросом.", + "naiveproxy_domain": "Домен", + "naiveproxy_email": "Email для Let\u0027s Encrypt", + "naiveproxy_port_hint": "TCP-порт 443 фиксирован для NaiveProxy. Порт 80/TCP должен быть свободен для выпуска сертификата.", + "naiveproxy_install_hint": "Перед установкой A-запись домена должна указывать на этот сервер. Нужны свободные TCP-порты 80 и 443 (Let\u0027s Encrypt и HTTPS-прокси).", + "naiveproxy_dns_hint": "Перед установкой создайте DNS-запись:", + "naiveproxy_ports_warning": "Внимание: для стабильной работы нужны свободные TCP-порты 80 и 443 (Let\u0027s Encrypt на 80, HTTPS-прокси на 443).", + "naiveproxy_client_hint": "Стабильная версия. НЕ используйте v2rayN. Точно работает в Karing. Остальные клиенты под вопросом.", + "server_ssl_domain": "SSL-домен (по умолчанию)", + "server_ssl_email": "SSL-email (по умолчанию)", + "server_ssl_hint": "Подставляется при установке Hysteria / NGINX на этом сервере (Let\u0027s Encrypt).", + "container_logs": "Логи контейнера", + "logs_btn": "📋 Логи", + "logs_live": "В реальном времени", + "logs_reconnect": "переподключение…", + "no_logs": "Логов пока нет", + "container_status": "Статус", + "exit_code": "Код выхода", + "close": "Закрыть", + "tls_emulation": "TLS-эмуляция", + "tls_domain": "Домен маскировки", + "max_connections_limit": "Макс. соединений", + "max_connections_hint": "0 — без ограничений. Ограничивает одновременных пользователей.", + "traffic_reset_strategy_label": "Стратегия сброса трафика", + "traffic_reset_never": "Никогда не сбрасывать", + "traffic_reset_daily": "Сброс ежедневно", + "traffic_reset_weekly": "Сброс еженедельно", + "traffic_reset_monthly": "Сброс ежемесячно", + "traffic_reset_hint": "Как часто следует сбрасывать трафик пользователя", + "traffic_used_total": "Всего потрачено", + "traffic_reset_info": "Следующий сброс: {}", + "services": "Сервисы", + "dns_desc": "Персональный DNS-сервер для блокировки рекламы и защиты приватности ваших запросов.", + "dns_internal_hint": "Сервис будет запущен во внутренней сети (172.29.172.254) без привязки к портам хоста.", + "telemt_port_hint": "Порт для Telegram-прокси (обычно 443).", + "socks5_desc": "SOCKS5-прокси (3proxy) с одной учётной записью и аутентификацией по логину/паролю.", + "socks5_username": "Логин", + "socks5_password": "Пароль", + "socks5_password_placeholder": "Оставьте пустым для автогенерации", + "socks5_password_hint": "Если поле пустое, будет сгенерирован случайный пароль на 16 символов.", + "socks5_port_hint": "TCP-порт, на котором будет слушать прокси. В официальном клиенте Amnezia по умолчанию 38080.", + "socks5_change_settings": "Изменить настройки", + "socks5_settings_title": "SOCKS5 — Настройки подключения", + "socks5_port_change_hint": "Смена порта пересоздаёт контейнер — текущие соединения оборвутся.", + "socks5_settings_saved": "Настройки SOCKS5 сохранены", + "adguard_desc": "AdGuard Home — DNS-блокировщик рекламы с веб-интерфейсом администратора.", + "adguard_mode": "Режим установки", + "adguard_mode_sidebyside": "Рядом с AmneziaDNS", + "adguard_mode_sidebyside_hint": "поднимается параллельно AmneziaDNS на отдельном внутреннем IP (172.29.172.253)", + "adguard_mode_replace": "Заменить AmneziaDNS", + "adguard_mode_replace_hint": "удаляет контейнер amnezia-dns и забирает его IP — VPN-клиенты сразу пойдут в AdGuard", + "adguard_dns_port": "DNS-порт (53)", + "adguard_dns_port_hint": "Внутренний DNS-порт, на который ходят VPN-клиенты через docker-сеть.", + "adguard_web_port": "Порт Web UI", + "adguard_dot_port": "Порт DoT", + "adguard_doh_port": "Порт DoH", + "adguard_expose_web": "Опубликовать наружу", + "adguard_expose": "Опубликовать наружу", + "adguard_install_hint": "Первоначальная настройка AdGuard проходит через мастер на веб-интерфейсе после установки.", + "adguard_internal_ip": "Внутренний IP", + "adguard_web_ui": "Адрес панели", + "adguard_open_ui": "Открыть Web UI", + "edit_server_title": "Изменить сервер", + "edit_keep_credential": "Оставьте пустым, чтобы сохранить текущее значение", + "server_saved": "Настройки сервера сохранены", + "servers_reordered": "Порядок серверов изменён", + "ping_checking": "Проверяем соединение...", + "offline": "недоступен", + "api_tokens_title": "API-токены", + "api_tokens_hint": "Bearer-токены для внешних интеграций. Передавайте токен в заголовке `Authorization: Bearer \u003ctoken\u003e`. Токены имеют права администратора и перестают работать, если их владелец отключён или понижен в роли.", + "api_tokens_empty": "Пока нет ни одного API-токена. Создайте, чтобы подключить панель к внешнему сервису.", + "api_tokens_create": "Создать токен", + "api_tokens_create_title": "Создание API-токена", + "api_tokens_name_label": "Название", + "api_tokens_name_placeholder": "напр. Linear, CI-бот, мониторинг", + "api_tokens_name_hint": "Нужно, чтобы отличать токен в списке. Сам токен сгенерируется автоматически.", + "api_tokens_name_required": "Укажите название токена", + "api_tokens_created_title": "Токен создан", + "api_tokens_value_label": "Значение токена", + "api_tokens_warning_title": "Это единственный раз, когда вы увидите этот токен.", + "api_tokens_warning_body": "Скопируйте его сейчас и сохраните в менеджере секретов вашей интеграции. Панель хранит только хэш — если потеряете значение, токен придётся отозвать и создать заново.", + "api_tokens_revoke": "Отозвать", + "api_tokens_revoke_confirm": "Отозвать токен «{}»? Любая интеграция, использующая его, перестанет работать.", + "api_tokens_revoked": "Токен отозван", + "api_tokens_created": "Создан", + "api_tokens_last_used": "Последнее использование", + "api_tokens_owner": "Владелец", + "api_tokens_never": "никогда", + "coming_soon": "Скоро", + "promo_star_cta": "Поставить звезду на GitHub", + "aivpn_subtitle": "ИИ-подбор протокола, который сам выбирает правильный туннель под текущие условия. Поможете звездой — выйдет быстрее.", + "revproxy_title": "Reverse Proxy", + "revproxy_subtitle": "Высокопроизводительная маскировка трафика: прячем VPN за обычным сайтом, снижаем заметность для DPI.", + "management": "Управление", + "server_management": "Управление сервером", + "server_management_desc": "Выполнение системных действий на вашей машине.", + "check_server_services": "Проверить сервисы сервера", + "reboot_server": "Перезагрузить сервер", + "clear_server": "Очистить сервер", + "remove_server": "Удалить сервер из панели", + "ssl_title": "SSL / HTTPS Настройки", + "enable_https": "Включить HTTPS", + "panel_port_label": "Порт панели", + "domain_label": "Доменное имя", + "cert_path_label": "Путь к SSL сертификату (.pem)", + "key_path_label": "Путь к приватному ключу (.pem)", + "or_paste_text": "ИЛИ вставьте текст сертификатов", + "cert_text_label": "Содержимое сертификата (CRT)", + "key_text_label": "Содержимое ключа (KEY)", + "ssl_hint": "После включения панель будет доступна по https:// на указанном домене. Убедитесь, что пути к файлам или текстовые данные верны.", + "about_title": "О программе и обновления", + "current_version": "Текущая версия", + "check_updates": "Проверить обновления", + "checking_updates": "Проверка...", + "update_available": "Доступна новая версия", + "download_update": "Скачать обновление", + "up_to_date": "У вас установлена последняя версия", + "warp_hint": "WARP направляет исходящий трафик этого хоста через Cloudflare. Он не создаёт публичный URL панели как Quick Tunnel или ngrok.", + "warp_install_hint": "Сначала установите официальный клиент Cloudflare WARP, затем перезапустите панель.", + "warp_install_required": "Сначала установите WARP", + "warp_connect": "Подключить WARP", + "warp_disconnect": "Отключить", + "warp_connected": "WARP подключён", + "warp_disconnected": "WARP отключён", + "warp_status_unknown": "Статус WARP неизвестен.", + "warp_status_not_installed": "Cloudflare WARP CLI не установлен на этом хосте.", + "warp_status_connected": "Cloudflare WARP подключён.", + "warp_status_connecting": "Cloudflare WARP подключается.", + "warp_status_disconnected": "Cloudflare WARP отключён.", + "warp_status_not_registered": "Cloudflare WARP установлен, но ещё не зарегистрирован. Подключение попробует зарегистрировать его автоматически.", + "warp_status_service_unavailable": "Сервис Cloudflare WARP недоступен. Убедитесь, что warp-svc запущен.", + "warp_status_error": "Cloudflare WARP вернул ошибку.", + "install_starting": "Начинаем установку...", + "server_check_complete": "Проверка завершена", + "server_connection_error": "Ошибка подключения", + "connections_load_error": "Ошибка загрузки подключений", + "connection_updated": "Подключение обновлено", + "edit_connection": "Редактировать подключение", + "enable_connection_progress": "Включение подключения...", + "disable_connection_progress": "Отключение подключения...", + "connection_enabled": "Подключение включено", + "connection_disabled": "Подключение отключено", + "gb": "ГБ", + "marketplace": "Шаблоны", + "open_marketplace": "Открыть шаблоны", + "marketplace_desc": "Установите доступные шаблоны протоколов и сервисов для этого сервера.", + "installed_apps": "Установленные приложения", + "installed_apps_hint": "Здесь отображаются только установленные протоколы и сервисы. Чтобы добавить новые, откройте шаблоны.", + "no_installed_apps": "Пока нет установленных приложений", + "no_installed_apps_desc": "Откройте шаблоны, чтобы установить протоколы или сервисы на этот сервер.", + "install_another": "Установить ещё один", + "new_instance": "новый экземпляр", + "port_next_instance_hint": "Выберите свободный UDP-порт для дополнительного экземпляра.", + "checking_server": "Проверяем сервисы сервера...", + "web_servers": "Веб-серверы", + "nginx_desc": "NGINX веб-сервер с HTTPS-сертификатом Let\u0027s Encrypt и редактируемым index.html.", + "nginx_domain": "Домен", + "nginx_email": "Email для Let\u0027s Encrypt", + "nginx_port_hint": "HTTPS-порт на сервере. Порт 80/TCP должен быть свободен и доступен для проверки Let\u0027s Encrypt.", + "nginx_install_hint": "Домен уже должен указывать на этот сервер. Порт 80/TCP используется для HTTP-01 проверки сертификата.", + "site": "Сайт", + "site_editor": "Редактор сайта", + "site_saved": "Сайт сохранён", + "nginx_dns_hint": "Перед установкой создайте DNS-запись:", + "backup": "Бекап", + "backups": "Бекапы", + "backup_desc": "Бекапы создаются на сервере и содержат файлы протокола (конфиг, ключи, clientsTable). Для WireGuard / AWG можно также скачать ZIP со всеми клиентскими .conf.", + "create_backup": "Создать бекап", + "creating_backup": "Создание бекапа...", + "backup_created": "Бекап создан", + "no_backups": "Бекапов пока нет", + "no_backups_desc": "Создайте первый бекап для этого протокола.", + "export_client_configs": "Скачать все .conf", + "export_client_configs_hint": "ZIP со всеми клиентскими конфигами WireGuard / AmneziaWG (если сохранён приватный ключ).", + "exporting_client_configs": "Готовлю ZIP…", + "export_client_configs_done": "ZIP с конфигами скачан", + "restore_protocol_backup": "Восстановить", + "restore_protocol_backup_confirm": "Восстановить этот бекап на сервер? Текущие файлы протокола будут перезаписаны, контейнер перезапустится.", + "restoring_protocol_backup": "Восстановление… подождите", + "restore_protocol_backup_done": "Бекап восстановлен", + "refresh": "Обновить", + "guest_allow_create_hint": "Создаёт конфиг у гостевого пользователя (3x-ui VLESS или протокол выбранного сервера).", + "invite_inbound_hint": "Список inbound загружается из выбранной панели 3x-ui. Конфиг и ссылку отдаёт только 3x-ui.", + "invite_inbound_loading": "Загрузка inbound из 3x-ui…", + "invite_inbound_empty": "VLESS inbound не найдены на этой панели 3x-ui", + "invite_inbound_need_xui": "Сначала настройте 3x-ui в Настройках", + "invite_inbound_required": "Выберите VLESS inbound из 3x-ui", + "invite_sub_url_missing_title": "Не задан URL подписки", + "invite_sub_url_missing_hint": "В Настройки → 3x-ui укажите «Базовый URL подписки», чтобы ссылки выдавали /sub/… конфиги.", + "xui_url_label": "URL 3x-ui", + "xui_url_hint": "Укажите схему, порт и webBasePath при наличии (пример: https://host:2053/secret)", + "xui_sub_url_label": "Базовый URL подписки", + "xui_sub_url_hint": "Публичный /sub без слэша в конце (пример: https://host:2096/sub). Конфиги по ссылкам выдаются как subscription URL.", + "xui_api_token_label": "API Token (предпочтительно)", + "xui_api_token_placeholder": "Settings → Security → API Token", + "xui_api_token_hint": "Если задан, вход по логину/паролю не используется", + "xui_username_label": "Логин 3x-ui", + "xui_password_label": "Пароль 3x-ui", + "xui_sync_hint": "Клиенты из inbounds 3x-ui будут создаваться, отключаться и удаляться как пользователи панели (связь по email)", + "xui_inbound_label": "VLESS inbound", + "xui_inbound_auto": "Авто (первый VLESS inbound)", + "xui_inbound_hint": "Inbound подгружается из 3x-ui. Сайт только создаёт клиента и забирает готовую ссылку — без своей генерации конфига.", + "xui_servers_title": "Серверы 3x-ui", + "xui_servers_hint": "Добавьте панели 3x-ui. При создании выберите сервер и inbound из списка 3x-ui — ссылку вернёт панель.", + "xui_servers_empty": "Серверов 3x-ui пока нет. Нажмите «Добавить сервер».", + "xui_servers_manage_hint": "URL панелей и базовые URL подписок настраиваются в разделе «Серверы 3x-ui» ниже.", + "xui_server_add": "Добавить сервер", + "xui_server_name_label": "Название / описание", + "xui_server_name_hint": "Так сервер будет отображаться в инвайтах и у пользователей (например: US, NL, Home).", + "xui_server_select_label": "Сервер 3x-ui", + "xui_server_select_hint": "Выберите панель 3x-ui, затем inbound из списка, который она отдаёт по API.", + "xui_server_delete_confirm": "Удалить этот сервер 3x-ui из панели?", + "cascade_title": "Каскад (двойной VPN)", + "cascade_desc": "Клиенты подключаются к этому серверу (вход), а интернет выходит через другой сервер (выход) — чтобы обойти блокировку страны.", + "cascade_hint": "Каскад в стиле ryderams (AWG2): на входе и выходе должен быть AmneziaWG/WireGuard. Клиенты продолжают использовать только конфиг ВХОДНОГО сервера.", + "cascade_client_note": "Пользователи подключаются только к ВХОДНОМУ серверу. Не выдавайте им служебный peer выхода.", + "cascade_security_note": "Логика выполняется через SSH панели (без удалённого curl|bash). Подход на основе ryderams/amneziawg-cascade.", + "cascade_entry_protocol": "Протокол на этом сервере (вход)", + "cascade_exit_server": "Сервер выхода", + "cascade_exit_server_none": "— Выберите сервер —", + "cascade_exit_protocol": "Протокол на сервере выхода", + "cascade_enable": "Включить каскад", + "cascade_disable": "Отключить каскад", + "cascade_working": "Настраиваю каскад…", + "cascade_disabling": "Отключаю каскад…", + "cascade_enabled": "Каскад включён", + "cascade_disabled": "Каскад отключён", + "cascade_off": "Выключен", + "cascade_on": "Активен", + "cascade_configured_down": "Настроен, туннель вниз", + "cascade_exit_required": "Выберите сервер выхода", + "cascade_enable_confirm": "Включить каскад? На выходном сервере будет создан служебный клиент, а на этом — туннель к нему.", + "cascade_disable_confirm": "Отключить каскад на этом сервере?", + "cascade_no_handshake": "Туннель вверх, нет handshake", + "cascade_settings_title": "Настройки каскада (по шагам)", + "cascade_settings_desc": "Если страницы «крутятся», а интернета нет — оставьте все галочки включёнными и снова нажмите «Включить каскад».", + "cascade_opt_pin_exit": "Закрепить маршрут к серверу выхода", + "cascade_opt_pin_exit_hint": "Чтобы туннель к выходу не ушёл в петлю через сам VPN.", + "cascade_opt_remove_eth_masq": "Убрать NAT клиентов в eth0", + "cascade_opt_remove_eth_masq_hint": "Клиенты не должны выходить в интернет напрямую с входного сервера.", + "cascade_opt_force_forward": "Разрешить FORWARD между интерфейсами", + "cascade_opt_force_forward_hint": "Трафик клиентов → cascade → выход.", + "cascade_opt_mss_clamp": "TCP MSS clamp", + "cascade_opt_mss_clamp_hint": "Чинит «вечную загрузку» сайтов при двойном VPN.", + "cascade_opt_wait_handshake": "Ждать handshake с выходом", + "cascade_opt_wait_handshake_hint": "Не помечать каскад «активным», если выход не отвечает.", + "cascade_verify_egress": "Проверять внешний IP", + "cascade_verify_egress_hint": "После handshake — curl ifconfig.me через Address каскада; при пустом ответе откат.", + "cascade_opt_keep_dns": "Оставить DNS из конфига выхода", + "cascade_opt_keep_dns_hint": "Обычно выключено — DNS берёт клиент сам.", + "cascade_opt_handshake_timeout": "Таймаут handshake (сек)", + "cascade_opt_table_id": "Таблица маршрутов", + "cascade_opt_rule_priority": "Приоритет ip rule", + "cascade_opt_allowed_ips": "AllowedIPs туннеля вход→выход", + "cascade_opt_allowed_ips_hint": "Лучше 0.0.0.0/0 (IPv4); ::/0 часто ломает двойной VPN.", + "cascade_opt_vpn_subnet": "Подсеть клиентов (override)", + "cascade_opt_vpn_subnet_hint": "Пусто = определить автоматически из Address сервера" } diff --git a/translations/zh.json b/translations/zh.json index 83a2671..3318bd5 100644 --- a/translations/zh.json +++ b/translations/zh.json @@ -1,522 +1,567 @@ { - "nav_servers": "服务器", - "nav_users": "用户列表", - "nav_invites": "邀请链接", - "nav_settings": "设置", - "nav_connections": "连接管理", - "nav_logout": "登出", - "theme_dark": "深色模式", - "theme_light": "浅色模式", - "back_to_servers": "← 返回服务器列表", - "server_check": "检查状态", - "server_checking": "正在检查...", - "protocols": "协议", - "install": "安装", - "uninstall": "卸载", - "run": "运行中", - "stop": "已停止", - "not_installed": "未安装", - "installed": "已安装", - "add_server": "添加服务器", - "no_servers": "尚未添加服务器", - "name": "名称", - "host": "地址", - "port": "端口", - "status": "状态", - "actions": "操作", - "edit": "编辑", - "delete": "删除", - "save": "保存", - "cancel": "取消", - "loading": "加载中...", - "copy": "复制", - "download": "下载", - "config": "配置", - "qr_code": "二维码", - "vpn_key": "VPN 密钥", - "clients": "客户端", - "traffic": "流量", - "limit": "限制", - "manage": "管理", - "no_protocols": "无协议", - "add_server_desc": "添加您的第一台 VPN 服务器。需要 SSH 凭据进行连接。", - "ssh_host": "主机 / IP", - "ssh_port": "SSH 端口", - "ssh_user": "用户名", - "ssh_password": "密码", - "ssh_key": "SSH 密钥", - "ssh_key_placeholder": "私钥 (Private Key)", - "ssh_key_hint": "粘贴 id_rsa 或 id_ed25519 文件的内容", - "connect": "连接", - "connecting": "正在连接...", - "server_added": "服务器添加成功", - "server_deleted": "服务器已删除", - "delete_confirm": "确定要删除此服务器吗?", - "success": "成功", - "error": "错误", - "info": "信息", - "login_title": "登录管理面板", - "username": "用户名", - "password": "密码", - "captcha": "验证码", - "captcha_placeholder": "输入图中字符", - "captcha_hint": "点击刷新验证码", - "login": "登录", - "logging_in": "登录中...", - "login_error": "登录失败", - "check_done": "检查完成", - "check_error": "连接错误", - "ssh_ok": "SSH 连接正常", - "docker_ok": "Docker 正常", - "docker_not_installed": "Docker 未安装", - "awg_desc": "基于 awg-go 的新版协议。支持 S3, S4 高级混淆。", - "awg_legacy_desc": "原始 AWG 版本。兼容旧版客户端。", - "xray_desc": "将流量伪装成普通网页流量 (XTLS-Reality),抗封锁能力强。", - "not_checked": "未检查", - "connections": "连接", - "add": "添加", - "loading_connections": "正在加载连接...", - "no_connections": "无连接", - "no_connections_desc": "添加首个连接以生成 VPN 配置文件", - "install_protocol": "安装协议", - "port_default_hint": "默认端口: 55424。请确保端口未被占用。", - "port_xray_hint": "推荐端口: 443。请确保未被其他 Web 服务器使用。", - "reinstall": "重新安装", - "uninstall_confirm": "确定卸载 {}?所有连接和配置都将丢失。", - "stop_container_confirm": "停止容器 {}?", - "start_container_confirm": "启动容器 {}?", - "stopping": "停止中", - "starting": "启动中", - "stopped": "已停止", - "started": "已启动", - "server_config": "服务端配置", - "add_connection": "添加连接", - "connection_name": "连接名称", - "connection_name_placeholder": "例如: iPhone, 笔记本电脑", - "assign_to_user": "分配给用户 (可选)", - "no_assign": "— 不分配 —", - "create": "创建", - "creating": "创建中...", - "connection_created": "连接 \"{}\" 已创建", - "delete_connection_confirm": "确定删除此连接?配置将失效。", - "connection_deleted": "连接已删除", - "config_tab": "📄 配置文件", - "vpn_key_tab": "🔑 VPN 密钥", - "qr_code_tab": "📱 二维码", - "copy_config": "📋 复制配置", - "download_conf": "⬇ 下载 .conf 文件", - "copy_key": "📋 复制密钥", - "vpn_link_hint": "将此链接粘贴到 AmneziaVPN 应用中即可自动配置", - "qr_hint": "使用 AmneziaVPN 应用扫描二维码", - "no_data": "无数据", - "start_btn": "▶ 启动", - "stop_btn": "⏹ 停止", - "config_btn": "⚙️ 配置", - "done": "完成", - "installing": "安装中...", - "start_install": "开始安装...", - "check_docker": "检查 Docker...", - "prepare_host": "准备环境...", - "build_container": "构建容器...", - "install_success": "协议安装成功!", - "install_error": "安装失败: ", - "qr_error": "无法生成二维码", - "users_title": "用户管理", - "search_placeholder": "搜索用户名、邮箱或 Telegram ID...", - "add_user": "添加用户", - "prev_page": "← 上一页", - "next_page": "下一页 →", - "page_info": "第 {} 页,共 {} 页", - "loading_users": "正在加载用户...", - "nothing_found": "未找到匹配项", - "search_empty_desc": "请尝试更改查询条件或创建新用户", - "username_label": "用户名", - "password_label": "密码", - "role_label": "角色", - "role_user_desc": "User — 仅查看自己的连接", - "role_support_desc": "Support — 服务器管理权限", - "role_admin_desc": "Admin — 全部权限", - "tg_id_label": "Telegram ID (可选)", - "email_label": "邮箱 (可选)", - "traffic_limit_label": "流量限制 (GB) (0 = 不限制)", - "description_label": "备注 (可选)", - "auto_conn_title": "自动创建连接 (可选)", - "server_label": "服务器", - "no_create_conn": "— 不自动创建 —", - "protocol_label": "协议", - "edit_user_title": "编辑用户: {}", - "edit_user_tg": "Telegram ID (可选)", - "edit_user_email": "邮箱 (可选)", - "edit_user_limit": "流量限制 (GB) (0 = 不限制)", - "new_password_hint": "新密码 (如不修改请留空)", - "op_type_label": "操作类型", - "create_new_conn": "创建新连接", - "link_existing_conn": "关联现有连接", - "select_existing_conn": "选择现有连接", - "existing_conn_hint": "仅显示尚未关联用户的连接", - "conn_name_panel": "连接名称 (面板显示)", - "user_conns_title": "用户连接列表", - "no_free_conns": "无可用空闲连接", - "select_connection": "请选择一个连接", - "linking": "关联中...", - "conn_linked": "连接关联成功", - "enabling": "启用中", - "disabling": "禁用中", - "user_enabled": "用户已启用", - "user_disabled": "用户已禁用", - "delete_user_confirm": "确定删除该用户?其所有连接都将被删除。", - "share_access": "分享访问权限", - "enable_public_access": "启用公开访问", - "share_password_label": "密码 (可选)", - "share_password_hint": "如设置,访问者需输入密码", - "guest_settings_title": "访客访问", - "guest_enable": "启用访客访问(无需注册)", - "guest_enable_hint": "登录页显示公开链接。访客可查看所选用户的连接,并可选择新建配置。", - "guest_link_label": "访客链接", - "guest_link_placeholder": "保存设置以生成链接", - "guest_regen_token": "重新生成链接", - "guest_regen_confirm": "重新生成访客链接?旧链接将失效。", - "guest_token_updated": "访客链接已更新", - "guest_user_label": "访客连接所属用户", - "guest_user_none": "— 选择用户 —", - "guest_user_hint": "创建普通用户(如 guest)并分配 VPN 连接。", - "guest_password_label": "访客密码(可选)", - "guest_password_keep": "留空以保留当前密码", - "guest_clear_password": "取消密码保护", - "guest_allow_create": "允许访客创建新配置", - "guest_access_btn": "以访客继续", - "guest_access_login_hint": "无需账号", - "guest_title": "访客访问", - "guest_subtitle": "无需注册即可获取 VPN 配置", - "guest_protected_desc": "此页面受密码保护。", - "guest_get_config": "获取配置", - "guest_get_config_hint": "将为您创建新的 VPN 配置", - "guest_not_found": "404 未找到", - "guest_not_found_desc": "访客访问已禁用或链接无效。", - "wrong_guest_password": "密码错误", - "invites_title": "邀请链接", - "invites_hint": "创建可分享的链接。对方可创建 VPN 配置,次数由您设定。", - "invites_empty": "暂无邀请链接", - "invites_empty_desc": "创建链接并分享 — 无需注册。", - "invite_create": "创建链接", - "invite_edit": "编辑链接", - "invite_active": "有效", - "invite_expired": "已过期", - "invite_exhausted": "次数用尽", - "invite_uses": "使用次数", - "invite_name_label": "链接名称", - "invite_max_uses_label": "最多配置数", - "invite_max_uses_hint": "此链接可创建配置的次数。0 = 不限。", - "invite_user_label": "配置归属用户", - "invite_user_hint": "创建的配置会挂到该面板用户下。", - "invite_user_required": "请选择用户", - "invite_password_label": "密码(可选)", - "invite_expires_label": "过期时间(可选)", - "invite_duration_label": "配置有效期(天)", - "invite_duration_hint": "从用户点击获取配置时开始计时。0 = 不过期。", - "invite_duration_short": "有效期", - "invite_duration_starts_hint": "获取后配置将有效 {} 天", - "invite_config_expires_at": "配置有效至:{}", - "invite_sub_tab": "订阅", - "days_short": "天", - "invite_clear_expires": "清除过期时间", - "invite_note_label": "备注(仅管理员)", - "invite_note_placeholder": "内部备注", - "invite_reset_used": "重置已用次数", - "invite_created": "链接已创建(URL 已复制)", - "invite_updated": "链接已更新", - "invite_deleted": "链接已删除", - "invite_delete_confirm": "删除此邀请链接?", - "invite_public_title": "邀请", - "invite_public_subtitle": "通过邀请链接获取 VPN 配置", - "invite_protected_desc": "此链接受密码保护。", - "invite_get_config_hint": "每次点击创建一份新配置并消耗一次额度", - "invite_uses_left": "剩余次数:{}", - "invite_uses_left_unlimited": "已创建:{}(不限)", - "invite_not_found": "404 未找到", - "invite_not_found_desc": "链接无效或已删除。", - "wrong_invite_password": "密码错误", - "public_link_label": "公开链接", - "disabled": "已禁用", - "out_of": "/", - "settings_title": "面板设置", - "appearance": "🎨 外观设置", - "title_label": "页面标题 (Title)", - "logo_label": "图标 (Emoji 或 字符)", - "subtitle_label": "副标题 (Subtitle)", - "captcha_title": "🔐 验证码", - "enable_captcha": "启用登录验证码", - "telegram_bot_title": "🤖 Telegram 机器人", - "bot_token_label": "Bot Token (来自 @BotFather)", - "bot_status": "状态", - "bot_running": "✅ 运行中", - "bot_stopped": "⏹ 已停止", - "bot_stop_btn": "⏹ 停止", - "bot_start_btn": "▶️ 启动", - "bot_hint": "修改 Token 后请保存,机器人将自动重启。用户需在个人资料中填写 Telegram ID。", - "api_docs_title": "🔌 API 文档", - "api_docs_hint": "使用这些接口了解面板功能", - "tunnels_title": "Tunnels", - "local_server": "本地服务器", - "tunnel_install_enable": "安装并启用", - "tunnel_enable": "启用隧道", - "tunnel_running": "隧道运行中", - "tunnel_stop": "停止", - "tunnel_stopping": "正在停止...", - "tunnel_stopped": "隧道已停止", - "tunnel_delete": "删除", - "tunnel_deleted": "隧道已删除", - "tunnel_delete_confirm": "停止并删除由面板管理的隧道二进制文件?", - "tunnel_starting": "正在启动隧道...", - "tunnel_started": "隧道已启动", - "tunnel_waiting_url": "隧道已启动,正在等待公网地址...", - "tunnel_no_public_url": "隧道启动后公网地址会显示在这里。", - "tunnels_hint": "快速隧道会将本地面板暴露到互联网。请使用强管理员密码,并仅与可信用户共享公网地址。", - "ngrok_authtoken_placeholder": "ngrok authtoken(某些账号可选)", - "import_users_title": "📤 导入用户", - "import_source_label": "导入 / 同步源", - "remnawave_url_label": "Remnawave 地址", - "api_key_label": "API 密钥", - "enable_sync": "开启同步", - "sync_hint": "将根据 Remnawave 数据自动创建、禁用和删除用户", - "sync_now_btn": "🔄 立即同步", - "delete_sync_btn": "🗑 删除同步数据", - "auto_create_conns": "自动创建连接", - "sync_server_label": "新连接的服务器", - "save_changes": "💾 保存修改", - "sync_success": "同步完成: {} 个用户已更新", - "sync_deleted": "已删除 {} 个同步用户", - "delete_sync_confirm": "确定要删除所有同步用户及其连接吗?此操作不可逆。", - "settings_saved": "设置已保存", - "bot_started": "机器人已启动", - "bot_stopped_msg": "机器人已停止", - "sync_running": "同步中...", - "deleting": "删除中...", - "my_connections_title": "我的连接", - "show_config": "📄 查看配置", - "no_connections_user_desc": "请联系管理员创建 VPN 连接", - "share_title": "访问连接", - "vpn_access": "VPN 访问", - "user_label_share": "用户", - "share_protected_desc": "此访问受密码保护。请输入密码以继续。", - "auth_success": "身份验证通过", - "loading_share_conns": "正在加载连接...", - "no_active_conns": "您目前没有活跃连接。", - "show_settings_btn": "🔑 显示设置", - "copied": "已复制!", - "active": "活跃", - "site_description": "Amnezia Web Panel — VPN 服务器及 AmneziaWG 协议管理工具", - "toggle_theme": "切换主题", - "copied_to_clipboard": "已复制到剪贴板", - "share_not_found": "404 页面未找到", - "share_not_found_desc": "链接无效或访问已被禁用。", - "invalid_captcha": "验证码错误", - "account_disabled": "账户已被禁用", - "invalid_login": "用户名或密码错误", - "user_exists": "该用户名已存在", - "cannot_delete_self": "您无法删除自己的账户", - "wrong_share_password": "密码错误", - "saving": "正在保存...", - "select_lang": "选择语言", - "lang_ru": "Русский", - "lang_en": "English", - "lang_fr": "Français", - "lang_zh": "中文 (Chinese)", - "lang_fa": "فارسی (Persian)", - "backup_title": "简易备份", - "download_backup": "下载 data.json", - "restore_backup": "从文件恢复", - "restore_confirm": "您确定吗?当前数据将被覆盖,应用程序将重新启动。", - "restore_success": "恢复成功!正在重启...", - "invalid_backup_file": "备份文件无效或结构错误", - "config_unavailable": "配置文件不可用", - "config_unavailable_desc": "此客户端是通过 Amnezia 原生应用创建的。\\n私钥仅存储在用户设备上,服务器无法恢复。", - "client_public_key": "客户端公钥:", - "management": "管理", - "server_management": "服务器管理", - "server_management_desc": "在您的机器上执行系统级别的操作。", - "check_server_services": "检查服务器服务", - "reboot_server": "重启服务器", - "clear_server": "清除服务器", - "remove_server": "从面板移除服务器", - "telemt_desc": "基于 MTProxy 的 Telegram 代理,支持高级混淆。", - "hysteria_desc": "Hysteria 2(QUIC/UDP,官方 tobyxdd/hysteria / apernet/hysteria),支持 Let's Encrypt TLS。安装时由管理员指定域名。", - "hysteria_domain": "域名", - "hysteria_email": "Let's Encrypt 邮箱", - "hysteria_port_hint": "Hysteria UDP 端口(默认 8998)。若 443 已被占用请换端口。", - "hysteria_install_hint": "安装前请将域名 A 记录指向本服务器。将自动启用 BBR 与 salamander 混淆。", - "hysteria_dns_hint": "安装前请创建此 DNS 记录:", - "hysteria_settings_title": "Hysteria 设置", - "hysteria_change_port": "更改端口", - "hysteria_port_change_hint": "UDP 监听端口。若提示 address already in use,请改用空闲端口(如 8443 或 8998)。", - "hysteria_settings_saved": "Hysteria 端口已更新", - "hysteria_ports_warning": "注意:稳定运行需要空闲的 TCP 端口 80 和 443(Let's Encrypt 使用 80,HTTPS/QUIC 使用 443)。", - "hysteria_renew_ssl": "续签 SSL", - "hysteria_ssl_change_hint": "更改域名会重新签发 Let's Encrypt 证书(需空闲端口 80)。", - "hysteria_ssl_required": "签发 SSL 需要域名和邮箱", - "naiveproxy_desc": "NaiveProxy(稳定)— Caddy + forwardproxy 的 HTTPS/HTTP2,Let's Encrypt TLS。客户端请用 Karing,不要用 v2rayN;其他客户端未测试。", - "naiveproxy_domain": "域名", - "naiveproxy_email": "Let's Encrypt 邮箱", - "naiveproxy_port_hint": "NaiveProxy 固定使用 TCP 443。签发证书需空闲 TCP 80。", - "naiveproxy_install_hint": "安装前请将域名 A 记录指向本服务器。需空闲 TCP 端口 80 和 443(Let's Encrypt 与 HTTPS 代理)。", - "naiveproxy_dns_hint": "安装前请创建此 DNS 记录:", - "naiveproxy_ports_warning": "注意:稳定运行需要空闲的 TCP 端口 80 和 443(Let's Encrypt 使用 80,HTTPS 代理使用 443)。", - "naiveproxy_client_hint": "稳定版。请勿使用 v2rayN。已确认 Karing 可用。其他客户端未测试。", - "server_ssl_domain": "SSL 域名(默认)", - "server_ssl_email": "SSL 邮箱(默认)", - "server_ssl_hint": "安装 Hysteria / NGINX 时自动填入(Let's Encrypt)。", - "container_logs": "容器日志", - "logs_btn": "📋 日志", - "logs_live": "实时", - "logs_reconnect": "重新连接…", - "no_logs": "暂无日志", - "container_status": "状态", - "exit_code": "退出码", - "dns_desc": "防止广告和保护隐私的个人 DNS 服务器。", - "dns_internal_hint": "The service runs on the internal network (172.29.172.254) without binding to host ports.", - "telemt_port_hint": "Port for the Telegram proxy (typically 443).", - "socks5_desc": "Single-account SOCKS5 proxy (3proxy) with username/password authentication.", - "socks5_username": "Username", - "socks5_password": "Password", - "socks5_password_placeholder": "Leave empty to auto-generate", - "socks5_password_hint": "If left empty, a random 16-character password will be generated.", - "socks5_port_hint": "TCP port the proxy will listen on. The default in the official Amnezia client is 38080.", - "socks5_change_settings": "Change settings", - "socks5_settings_title": "SOCKS5 — Connection settings", - "socks5_port_change_hint": "Changing the port recreates the container; existing sessions will be dropped.", - "socks5_settings_saved": "SOCKS5 settings saved", - "adguard_desc": "AdGuard Home — DNS-based ad blocker with a web admin UI.", - "adguard_mode": "Installation mode", - "adguard_mode_sidebyside": "Side-by-side", - "adguard_mode_sidebyside_hint": "runs alongside AmneziaDNS on a separate internal IP (172.29.172.253)", - "adguard_mode_replace": "Replace AmneziaDNS", - "adguard_mode_replace_hint": "removes amnezia-dns and takes its IP — VPN clients use AdGuard immediately", - "adguard_dns_port": "DNS port (53)", - "adguard_dns_port_hint": "Internal DNS port served to VPN clients via the docker network.", - "adguard_web_port": "Web UI port", - "adguard_dot_port": "DoT port", - "adguard_doh_port": "DoH port", - "adguard_expose_web": "Expose to internet", - "adguard_expose": "Expose to host", - "adguard_install_hint": "Initial AdGuard setup runs through its built-in wizard via the web UI after install.", - "adguard_internal_ip": "Internal IP", - "adguard_web_ui": "Admin URL", - "adguard_open_ui": "Open Web UI", - "edit_server_title": "Edit server", - "edit_keep_credential": "Leave empty to keep the current value", - "server_saved": "Server settings saved", - "servers_reordered": "Servers reordered", - "ping_checking": "Checking connectivity...", - "offline": "offline", - "api_tokens_title": "API tokens", - "api_tokens_hint": "Bearer tokens for external integrations. Send the token in the `Authorization: Bearer ` header. Tokens have admin-equivalent rights and stop working if their owner is disabled or demoted.", - "api_tokens_empty": "No API tokens yet. Create one to integrate the panel with an external service.", - "api_tokens_create": "Create token", - "api_tokens_create_title": "Create API token", - "api_tokens_name_label": "Name", - "api_tokens_name_placeholder": "e.g. Linear integration, CI bot, monitoring", - "api_tokens_name_hint": "Used to identify this token in the list. The token value itself is generated for you.", - "api_tokens_name_required": "Token name is required", - "api_tokens_created_title": "Token created", - "api_tokens_value_label": "Token value", - "api_tokens_warning_title": "This is the only time you'll see this token.", - "api_tokens_warning_body": "Copy it now and store it in your integration's secret manager. The panel keeps only a hash — if you lose this value you'll need to revoke and recreate the token.", - "api_tokens_revoke": "Revoke", - "api_tokens_revoke_confirm": "Revoke token \"{}\"? Any integration using it will stop working.", - "api_tokens_revoked": "Token revoked", - "api_tokens_created": "Created", - "api_tokens_last_used": "Last used", - "api_tokens_owner": "Owner", - "api_tokens_never": "never", - "coming_soon": "Coming soon", - "promo_star_cta": "Star us on GitHub", - "aivpn_subtitle": "AI-driven protocol selection that picks the right tunnel for the moment. Land it sooner — drop a star.", - "revproxy_title": "Reverse Proxy", - "revproxy_subtitle": "High-performance traffic obfuscation: hide your VPN endpoint behind a polished public-facing site, drop the DPI footprint.", - "warp_hint": "WARP routes this host outbound through Cloudflare. It does not create a public panel URL like Quick Tunnel or ngrok.", - "warp_install_hint": "Install the official Cloudflare WARP client first, then restart the panel.", - "warp_install_required": "Install WARP first", - "warp_connect": "Connect WARP", - "warp_disconnect": "Disconnect", - "warp_connected": "WARP connected", - "warp_disconnected": "WARP disconnected", - "warp_status_unknown": "WARP status is unknown.", - "warp_status_not_installed": "Cloudflare WARP CLI is not installed on this host.", - "warp_status_connected": "Cloudflare WARP is connected.", - "warp_status_connecting": "Cloudflare WARP is connecting.", - "warp_status_disconnected": "Cloudflare WARP is disconnected.", - "warp_status_not_registered": "Cloudflare WARP is installed but not registered yet. Connect will try to register automatically.", - "warp_status_service_unavailable": "Cloudflare WARP service is not available. Make sure warp-svc is running.", - "warp_status_error": "Cloudflare WARP returned an error.", - "install_starting": "Starting installation...", - "server_check_complete": "Check completed", - "server_connection_error": "Connection error", - "connections_load_error": "Error loading connections", - "connection_updated": "Connection updated", - "edit_connection": "Edit connection", - "enable_connection_progress": "Enabling connection...", - "disable_connection_progress": "Disabling connection...", - "connection_enabled": "Connection enabled", - "connection_disabled": "Connection disabled", - "gb": "GB", - "marketplace": "Templates", - "open_marketplace": "Open Templates", - "marketplace_desc": "Install protocol and service templates available for this server.", - "installed_apps": "Installed applications", - "installed_apps_hint": "Only installed protocols and services are shown here. Use Templates to add more.", - "no_installed_apps": "No installed applications yet", - "no_installed_apps_desc": "Open Templates to install protocols or services on this server.", - "install_another": "Install another", - "new_instance": "new instance", - "port_next_instance_hint": "Choose a free UDP port for the additional instance.", - "web_servers": "Web servers", - "nginx_desc": "NGINX web server with Let's Encrypt HTTPS certificate and editable index.html.", - "nginx_domain": "Domain", - "nginx_email": "Let's Encrypt email", - "nginx_port_hint": "HTTPS port exposed on the server. Port 80/TCP must be free and reachable for Let's Encrypt validation.", - "nginx_install_hint": "The domain must already point to this server. Port 80/TCP is used for HTTP-01 certificate validation.", - "site": "Site", - "site_editor": "Site editor", - "site_saved": "Site saved", - "nginx_dns_hint": "Create this DNS record before installation:", - "backup": "Backup", - "backups": "Backups", - "backup_desc": "Backups are created on the server and contain valuable protocol files needed to restore or recreate it later.", - "create_backup": "Create backup", - "creating_backup": "Creating backup...", - "backup_created": "Backup created", - "no_backups": "No backups yet", - "no_backups_desc": "Create the first backup for this protocol.", - "refresh": "Refresh", - "guest_allow_create_hint": "为访客用户创建配置(3x-ui VLESS 或所选服务器协议)。", - "invite_inbound_hint": "一次性选择所需的 VLESS 入站", - "invite_inbound_loading": "加载入站中…", - "invite_inbound_empty": "未找到 VLESS 入站", - "invite_inbound_need_xui": "请先在设置中配置 3x-ui", - "invite_inbound_required": "请选择 VLESS 入站", - "invite_sub_url_missing_title": "未设置订阅 URL", - "invite_sub_url_missing_hint": "在 设置 → 3x-ui 中填写订阅基础 URL,以便发放 /sub/… 配置。", - "xui_url_label": "3x-ui 地址", - "xui_url_hint": "包含协议、端口和 webBasePath(如有)", - "xui_sub_url_label": "订阅基础 URL", - "xui_sub_url_hint": "公共 /sub 基础地址,末尾不要斜杠(如 https://host:2096/sub)。", - "xui_api_token_label": "API Token(推荐)", - "xui_api_token_placeholder": "Settings → Security → API Token", - "xui_api_token_hint": "若已设置,将跳过用户名/密码登录", - "xui_username_label": "3x-ui 用户名", - "xui_password_label": "3x-ui 密码", - "xui_sync_hint": "3x-ui 客户端将按 email 同步为面板用户", - "xui_inbound_label": "VLESS 入站", - "xui_inbound_auto": "自动(第一个 VLESS 入站)", - "xui_inbound_hint": "创建 3x-ui VLESS 配置时使用。请先在 3x-ui 中创建 VLESS 入站。", - "xui_servers_title": "3x-ui 服务器", - "xui_servers_hint": "可添加多个 3x-ui 面板。创建配置时按名称选择服务器,并使用其订阅基础 URL。", - "xui_servers_empty": "暂无 3x-ui 服务器。请点击添加。", - "xui_servers_manage_hint": "面板与订阅地址在下方「3x-ui 服务器」中管理。", - "xui_server_add": "添加服务器", - "xui_server_name_label": "名称 / 描述", - "xui_server_name_hint": "显示在邀请与用户表单中(如 US、NL)。", - "xui_server_select_label": "3x-ui 服务器", - "xui_server_select_hint": "选择签发订阅 URL 的 3x-ui 面板。", - "xui_server_delete_confirm": "从面板删除此 3x-ui 服务器?" + "nav_servers": "服务器", + "nav_users": "用户列表", + "nav_invites": "邀请链接", + "nav_settings": "设置", + "nav_connections": "连接管理", + "nav_logout": "登出", + "theme_dark": "深色模式", + "theme_light": "浅色模式", + "back_to_servers": "← 返回服务器列表", + "server_check": "检查状态", + "server_checking": "正在检查...", + "protocols": "协议", + "install": "安装", + "uninstall": "卸载", + "run": "运行中", + "stop": "已停止", + "not_installed": "未安装", + "installed": "已安装", + "add_server": "添加服务器", + "no_servers": "尚未添加服务器", + "name": "名称", + "host": "地址", + "port": "端口", + "status": "状态", + "actions": "操作", + "edit": "编辑", + "delete": "删除", + "save": "保存", + "cancel": "取消", + "loading": "加载中...", + "copy": "复制", + "download": "下载", + "config": "配置", + "qr_code": "二维码", + "vpn_key": "VPN 密钥", + "clients": "客户端", + "traffic": "流量", + "limit": "限制", + "manage": "管理", + "no_protocols": "无协议", + "add_server_desc": "添加您的第一台 VPN 服务器。需要 SSH 凭据进行连接。", + "ssh_host": "主机 / IP", + "ssh_port": "SSH 端口", + "ssh_user": "用户名", + "ssh_password": "密码", + "ssh_key": "SSH 密钥", + "ssh_key_placeholder": "私钥 (Private Key)", + "ssh_key_hint": "粘贴 id_rsa 或 id_ed25519 文件的内容", + "connect": "连接", + "connecting": "正在连接...", + "server_added": "服务器添加成功", + "server_deleted": "服务器已删除", + "delete_confirm": "确定要删除此服务器吗?", + "success": "成功", + "error": "错误", + "info": "信息", + "login_title": "登录管理面板", + "username": "用户名", + "password": "密码", + "captcha": "验证码", + "captcha_placeholder": "输入图中字符", + "captcha_hint": "点击刷新验证码", + "login": "登录", + "logging_in": "登录中...", + "login_error": "登录失败", + "check_done": "检查完成", + "check_error": "连接错误", + "ssh_ok": "SSH 连接正常", + "docker_ok": "Docker 正常", + "docker_not_installed": "Docker 未安装", + "awg_desc": "基于 awg-go 的新版协议。支持 S3, S4 高级混淆。", + "awg_legacy_desc": "原始 AWG 版本。兼容旧版客户端。", + "xray_desc": "将流量伪装成普通网页流量 (XTLS-Reality),抗封锁能力强。", + "not_checked": "未检查", + "connections": "连接", + "add": "添加", + "loading_connections": "正在加载连接...", + "no_connections": "无连接", + "no_connections_desc": "添加首个连接以生成 VPN 配置文件", + "install_protocol": "安装协议", + "port_default_hint": "默认端口: 55424。请确保端口未被占用。", + "port_xray_hint": "推荐端口: 443。请确保未被其他 Web 服务器使用。", + "reinstall": "重新安装", + "uninstall_confirm": "确定卸载 {}?所有连接和配置都将丢失。", + "stop_container_confirm": "停止容器 {}?", + "start_container_confirm": "启动容器 {}?", + "stopping": "停止中", + "starting": "启动中", + "stopped": "已停止", + "started": "已启动", + "server_config": "服务端配置", + "add_connection": "添加连接", + "connection_name": "连接名称", + "connection_name_placeholder": "例如: iPhone, 笔记本电脑", + "assign_to_user": "分配给用户 (可选)", + "no_assign": "— 不分配 —", + "create": "创建", + "creating": "创建中...", + "connection_created": "连接 \"{}\" 已创建", + "delete_connection_confirm": "确定删除此连接?配置将失效。", + "connection_deleted": "连接已删除", + "config_tab": "📄 配置文件", + "vpn_key_tab": "🔑 VPN 密钥", + "qr_code_tab": "📱 二维码", + "copy_config": "📋 复制配置", + "download_conf": "⬇ 下载 .conf 文件", + "copy_key": "📋 复制密钥", + "vpn_link_hint": "将此链接粘贴到 AmneziaVPN 应用中即可自动配置", + "qr_hint": "使用 AmneziaVPN 应用扫描二维码", + "no_data": "无数据", + "start_btn": "▶ 启动", + "stop_btn": "⏹ 停止", + "config_btn": "⚙️ 配置", + "done": "完成", + "installing": "安装中...", + "start_install": "开始安装...", + "check_docker": "检查 Docker...", + "prepare_host": "准备环境...", + "build_container": "构建容器...", + "install_success": "协议安装成功!", + "install_error": "安装失败: ", + "qr_error": "无法生成二维码", + "users_title": "用户管理", + "search_placeholder": "搜索用户名、邮箱或 Telegram ID...", + "add_user": "添加用户", + "prev_page": "← 上一页", + "next_page": "下一页 →", + "page_info": "第 {} 页,共 {} 页", + "loading_users": "正在加载用户...", + "nothing_found": "未找到匹配项", + "search_empty_desc": "请尝试更改查询条件或创建新用户", + "username_label": "用户名", + "password_label": "密码", + "role_label": "角色", + "role_user_desc": "User — 仅查看自己的连接", + "role_support_desc": "Support — 服务器管理权限", + "role_admin_desc": "Admin — 全部权限", + "tg_id_label": "Telegram ID (可选)", + "email_label": "邮箱 (可选)", + "traffic_limit_label": "流量限制 (GB) (0 = 不限制)", + "description_label": "备注 (可选)", + "auto_conn_title": "自动创建连接 (可选)", + "server_label": "服务器", + "no_create_conn": "— 不自动创建 —", + "protocol_label": "协议", + "edit_user_title": "编辑用户: {}", + "edit_user_tg": "Telegram ID (可选)", + "edit_user_email": "邮箱 (可选)", + "edit_user_limit": "流量限制 (GB) (0 = 不限制)", + "new_password_hint": "新密码 (如不修改请留空)", + "op_type_label": "操作类型", + "create_new_conn": "创建新连接", + "link_existing_conn": "关联现有连接", + "select_existing_conn": "选择现有连接", + "existing_conn_hint": "仅显示尚未关联用户的连接", + "conn_name_panel": "连接名称 (面板显示)", + "user_conns_title": "用户连接列表", + "no_free_conns": "无可用空闲连接", + "select_connection": "请选择一个连接", + "linking": "关联中...", + "conn_linked": "连接关联成功", + "enabling": "启用中", + "disabling": "禁用中", + "user_enabled": "用户已启用", + "user_disabled": "用户已禁用", + "delete_user_confirm": "确定删除该用户?其所有连接都将被删除。", + "share_access": "分享访问权限", + "enable_public_access": "启用公开访问", + "share_password_label": "密码 (可选)", + "share_password_hint": "如设置,访问者需输入密码", + "guest_settings_title": "访客访问", + "guest_enable": "启用访客访问(无需注册)", + "guest_enable_hint": "登录页显示公开链接。访客可查看所选用户的连接,并可选择新建配置。", + "guest_link_label": "访客链接", + "guest_link_placeholder": "保存设置以生成链接", + "guest_regen_token": "重新生成链接", + "guest_regen_confirm": "重新生成访客链接?旧链接将失效。", + "guest_token_updated": "访客链接已更新", + "guest_user_label": "访客连接所属用户", + "guest_user_none": "— 选择用户 —", + "guest_user_hint": "创建普通用户(如 guest)并分配 VPN 连接。", + "guest_password_label": "访客密码(可选)", + "guest_password_keep": "留空以保留当前密码", + "guest_clear_password": "取消密码保护", + "guest_allow_create": "允许访客创建新配置", + "guest_access_btn": "以访客继续", + "guest_access_login_hint": "无需账号", + "guest_title": "访客访问", + "guest_subtitle": "无需注册即可获取 VPN 配置", + "guest_protected_desc": "此页面受密码保护。", + "guest_get_config": "获取配置", + "guest_get_config_hint": "将为您创建新的 VPN 配置", + "guest_not_found": "404 未找到", + "guest_not_found_desc": "访客访问已禁用或链接无效。", + "wrong_guest_password": "密码错误", + "invites_title": "邀请链接", + "invites_hint": "创建可分享的链接。对方可创建 VPN 配置,次数由您设定。", + "invites_empty": "暂无邀请链接", + "invites_empty_desc": "创建链接并分享 — 无需注册。", + "invite_create": "创建链接", + "invite_edit": "编辑链接", + "invite_active": "有效", + "invite_expired": "已过期", + "invite_exhausted": "次数用尽", + "invite_uses": "使用次数", + "invite_name_label": "链接名称", + "invite_max_uses_label": "最多配置数", + "invite_max_uses_hint": "此链接可创建配置的次数。0 = 不限。", + "invite_user_label": "配置归属用户", + "invite_user_hint": "创建的配置会挂到该面板用户下。", + "invite_user_required": "请选择用户", + "invite_password_label": "密码(可选)", + "invite_expires_label": "过期时间(可选)", + "invite_duration_label": "配置有效期(天)", + "invite_duration_hint": "从用户点击获取配置时开始计时。0 = 不过期。", + "invite_duration_short": "有效期", + "invite_duration_starts_hint": "获取后配置将有效 {} 天", + "invite_config_expires_at": "配置有效至:{}", + "invite_sub_tab": "订阅", + "days_short": "天", + "invite_clear_expires": "清除过期时间", + "invite_note_label": "备注(仅管理员)", + "invite_note_placeholder": "内部备注", + "invite_reset_used": "重置已用次数", + "invite_created": "链接已创建(URL 已复制)", + "invite_updated": "链接已更新", + "invite_deleted": "链接已删除", + "invite_delete_confirm": "删除此邀请链接?", + "invite_public_title": "邀请", + "invite_public_subtitle": "通过邀请链接获取 VPN 配置", + "invite_protected_desc": "此链接受密码保护。", + "invite_get_config_hint": "每次点击创建一份新配置并消耗一次额度", + "invite_uses_left": "剩余次数:{}", + "invite_uses_left_unlimited": "已创建:{}(不限)", + "invite_not_found": "404 未找到", + "invite_not_found_desc": "链接无效或已删除。", + "wrong_invite_password": "密码错误", + "public_link_label": "公开链接", + "disabled": "已禁用", + "out_of": "/", + "settings_title": "面板设置", + "appearance": "🎨 外观设置", + "title_label": "页面标题 (Title)", + "logo_label": "图标 (Emoji 或 字符)", + "subtitle_label": "副标题 (Subtitle)", + "captcha_title": "🔐 验证码", + "enable_captcha": "启用登录验证码", + "telegram_bot_title": "🤖 Telegram 机器人", + "bot_token_label": "Bot Token (来自 @BotFather)", + "bot_status": "状态", + "bot_running": "✅ 运行中", + "bot_stopped": "⏹ 已停止", + "bot_stop_btn": "⏹ 停止", + "bot_start_btn": "▶️ 启动", + "bot_hint": "修改 Token 后请保存,机器人将自动重启。用户需在个人资料中填写 Telegram ID。", + "api_docs_title": "🔌 API 文档", + "api_docs_hint": "使用这些接口了解面板功能", + "tunnels_title": "Tunnels", + "local_server": "本地服务器", + "tunnel_install_enable": "安装并启用", + "tunnel_enable": "启用隧道", + "tunnel_running": "隧道运行中", + "tunnel_stop": "停止", + "tunnel_stopping": "正在停止...", + "tunnel_stopped": "隧道已停止", + "tunnel_delete": "删除", + "tunnel_deleted": "隧道已删除", + "tunnel_delete_confirm": "停止并删除由面板管理的隧道二进制文件?", + "tunnel_starting": "正在启动隧道...", + "tunnel_started": "隧道已启动", + "tunnel_waiting_url": "隧道已启动,正在等待公网地址...", + "tunnel_no_public_url": "隧道启动后公网地址会显示在这里。", + "tunnels_hint": "快速隧道会将本地面板暴露到互联网。请使用强管理员密码,并仅与可信用户共享公网地址。", + "ngrok_authtoken_placeholder": "ngrok authtoken(某些账号可选)", + "import_users_title": "📤 导入用户", + "import_source_label": "导入 / 同步源", + "remnawave_url_label": "Remnawave 地址", + "api_key_label": "API 密钥", + "enable_sync": "开启同步", + "sync_hint": "将根据 Remnawave 数据自动创建、禁用和删除用户", + "sync_now_btn": "🔄 立即同步", + "delete_sync_btn": "🗑 删除同步数据", + "auto_create_conns": "自动创建连接", + "sync_server_label": "新连接的服务器", + "save_changes": "💾 保存修改", + "sync_success": "同步完成: {} 个用户已更新", + "sync_deleted": "已删除 {} 个同步用户", + "delete_sync_confirm": "确定要删除所有同步用户及其连接吗?此操作不可逆。", + "settings_saved": "设置已保存", + "bot_started": "机器人已启动", + "bot_stopped_msg": "机器人已停止", + "sync_running": "同步中...", + "deleting": "删除中...", + "my_connections_title": "我的连接", + "show_config": "📄 查看配置", + "no_connections_user_desc": "请联系管理员创建 VPN 连接", + "share_title": "访问连接", + "vpn_access": "VPN 访问", + "user_label_share": "用户", + "share_protected_desc": "此访问受密码保护。请输入密码以继续。", + "auth_success": "身份验证通过", + "loading_share_conns": "正在加载连接...", + "no_active_conns": "您目前没有活跃连接。", + "show_settings_btn": "🔑 显示设置", + "copied": "已复制!", + "active": "活跃", + "site_description": "Amnezia Web Panel — VPN 服务器及 AmneziaWG 协议管理工具", + "toggle_theme": "切换主题", + "copied_to_clipboard": "已复制到剪贴板", + "share_not_found": "404 页面未找到", + "share_not_found_desc": "链接无效或访问已被禁用。", + "invalid_captcha": "验证码错误", + "account_disabled": "账户已被禁用", + "invalid_login": "用户名或密码错误", + "user_exists": "该用户名已存在", + "cannot_delete_self": "您无法删除自己的账户", + "wrong_share_password": "密码错误", + "saving": "正在保存...", + "select_lang": "选择语言", + "lang_ru": "Русский", + "lang_en": "English", + "lang_fr": "Français", + "lang_zh": "中文 (Chinese)", + "lang_fa": "فارسی (Persian)", + "backup_title": "简易备份", + "download_backup": "下载 data.json", + "restore_backup": "从文件恢复", + "restore_confirm": "您确定吗?当前数据将被覆盖,应用程序将重新启动。", + "restore_success": "恢复成功!正在重启...", + "invalid_backup_file": "备份文件无效或结构错误", + "config_unavailable": "配置文件不可用", + "config_unavailable_desc": "此客户端是通过 Amnezia 原生应用创建的。\\n私钥仅存储在用户设备上,服务器无法恢复。", + "client_public_key": "客户端公钥:", + "management": "管理", + "server_management": "服务器管理", + "server_management_desc": "在您的机器上执行系统级别的操作。", + "check_server_services": "检查服务器服务", + "reboot_server": "重启服务器", + "clear_server": "清除服务器", + "remove_server": "从面板移除服务器", + "telemt_desc": "基于 MTProxy 的 Telegram 代理,支持高级混淆。", + "hysteria_desc": "Hysteria 2(QUIC/UDP,官方 tobyxdd/hysteria / apernet/hysteria),支持 Let\u0027s Encrypt TLS。安装时由管理员指定域名。", + "hysteria_domain": "域名", + "hysteria_email": "Let\u0027s Encrypt 邮箱", + "hysteria_port_hint": "Hysteria UDP 端口(默认 8998)。若 443 已被占用请换端口。", + "hysteria_install_hint": "安装前请将域名 A 记录指向本服务器。将自动启用 BBR 与 salamander 混淆。", + "hysteria_dns_hint": "安装前请创建此 DNS 记录:", + "hysteria_settings_title": "Hysteria 设置", + "hysteria_change_port": "更改端口", + "hysteria_port_change_hint": "UDP 监听端口。若提示 address already in use,请改用空闲端口(如 8443 或 8998)。", + "hysteria_settings_saved": "Hysteria 端口已更新", + "hysteria_ports_warning": "注意:稳定运行需要空闲的 TCP 端口 80 和 443(Let\u0027s Encrypt 使用 80,HTTPS/QUIC 使用 443)。", + "hysteria_renew_ssl": "续签 SSL", + "hysteria_ssl_change_hint": "更改域名会重新签发 Let\u0027s Encrypt 证书(需空闲端口 80)。", + "hysteria_ssl_required": "签发 SSL 需要域名和邮箱", + "naiveproxy_desc": "NaiveProxy(稳定)— Caddy + forwardproxy 的 HTTPS/HTTP2,Let\u0027s Encrypt TLS。客户端请用 Karing,不要用 v2rayN;其他客户端未测试。", + "naiveproxy_domain": "域名", + "naiveproxy_email": "Let\u0027s Encrypt 邮箱", + "naiveproxy_port_hint": "NaiveProxy 固定使用 TCP 443。签发证书需空闲 TCP 80。", + "naiveproxy_install_hint": "安装前请将域名 A 记录指向本服务器。需空闲 TCP 端口 80 和 443(Let\u0027s Encrypt 与 HTTPS 代理)。", + "naiveproxy_dns_hint": "安装前请创建此 DNS 记录:", + "naiveproxy_ports_warning": "注意:稳定运行需要空闲的 TCP 端口 80 和 443(Let\u0027s Encrypt 使用 80,HTTPS 代理使用 443)。", + "naiveproxy_client_hint": "稳定版。请勿使用 v2rayN。已确认 Karing 可用。其他客户端未测试。", + "server_ssl_domain": "SSL 域名(默认)", + "server_ssl_email": "SSL 邮箱(默认)", + "server_ssl_hint": "安装 Hysteria / NGINX 时自动填入(Let\u0027s Encrypt)。", + "container_logs": "容器日志", + "logs_btn": "📋 日志", + "logs_live": "实时", + "logs_reconnect": "重新连接…", + "no_logs": "暂无日志", + "container_status": "状态", + "exit_code": "退出码", + "dns_desc": "防止广告和保护隐私的个人 DNS 服务器。", + "dns_internal_hint": "The service runs on the internal network (172.29.172.254) without binding to host ports.", + "telemt_port_hint": "Port for the Telegram proxy (typically 443).", + "socks5_desc": "Single-account SOCKS5 proxy (3proxy) with username/password authentication.", + "socks5_username": "Username", + "socks5_password": "Password", + "socks5_password_placeholder": "Leave empty to auto-generate", + "socks5_password_hint": "If left empty, a random 16-character password will be generated.", + "socks5_port_hint": "TCP port the proxy will listen on. The default in the official Amnezia client is 38080.", + "socks5_change_settings": "Change settings", + "socks5_settings_title": "SOCKS5 — Connection settings", + "socks5_port_change_hint": "Changing the port recreates the container; existing sessions will be dropped.", + "socks5_settings_saved": "SOCKS5 settings saved", + "adguard_desc": "AdGuard Home — DNS-based ad blocker with a web admin UI.", + "adguard_mode": "Installation mode", + "adguard_mode_sidebyside": "Side-by-side", + "adguard_mode_sidebyside_hint": "runs alongside AmneziaDNS on a separate internal IP (172.29.172.253)", + "adguard_mode_replace": "Replace AmneziaDNS", + "adguard_mode_replace_hint": "removes amnezia-dns and takes its IP — VPN clients use AdGuard immediately", + "adguard_dns_port": "DNS port (53)", + "adguard_dns_port_hint": "Internal DNS port served to VPN clients via the docker network.", + "adguard_web_port": "Web UI port", + "adguard_dot_port": "DoT port", + "adguard_doh_port": "DoH port", + "adguard_expose_web": "Expose to internet", + "adguard_expose": "Expose to host", + "adguard_install_hint": "Initial AdGuard setup runs through its built-in wizard via the web UI after install.", + "adguard_internal_ip": "Internal IP", + "adguard_web_ui": "Admin URL", + "adguard_open_ui": "Open Web UI", + "edit_server_title": "Edit server", + "edit_keep_credential": "Leave empty to keep the current value", + "server_saved": "Server settings saved", + "servers_reordered": "Servers reordered", + "ping_checking": "Checking connectivity...", + "offline": "offline", + "api_tokens_title": "API tokens", + "api_tokens_hint": "Bearer tokens for external integrations. Send the token in the `Authorization: Bearer \u003ctoken\u003e` header. Tokens have admin-equivalent rights and stop working if their owner is disabled or demoted.", + "api_tokens_empty": "No API tokens yet. Create one to integrate the panel with an external service.", + "api_tokens_create": "Create token", + "api_tokens_create_title": "Create API token", + "api_tokens_name_label": "Name", + "api_tokens_name_placeholder": "e.g. Linear integration, CI bot, monitoring", + "api_tokens_name_hint": "Used to identify this token in the list. The token value itself is generated for you.", + "api_tokens_name_required": "Token name is required", + "api_tokens_created_title": "Token created", + "api_tokens_value_label": "Token value", + "api_tokens_warning_title": "This is the only time you\u0027ll see this token.", + "api_tokens_warning_body": "Copy it now and store it in your integration\u0027s secret manager. The panel keeps only a hash — if you lose this value you\u0027ll need to revoke and recreate the token.", + "api_tokens_revoke": "Revoke", + "api_tokens_revoke_confirm": "Revoke token \"{}\"? Any integration using it will stop working.", + "api_tokens_revoked": "Token revoked", + "api_tokens_created": "Created", + "api_tokens_last_used": "Last used", + "api_tokens_owner": "Owner", + "api_tokens_never": "never", + "coming_soon": "Coming soon", + "promo_star_cta": "Star us on GitHub", + "aivpn_subtitle": "AI-driven protocol selection that picks the right tunnel for the moment. Land it sooner — drop a star.", + "revproxy_title": "Reverse Proxy", + "revproxy_subtitle": "High-performance traffic obfuscation: hide your VPN endpoint behind a polished public-facing site, drop the DPI footprint.", + "warp_hint": "WARP routes this host outbound through Cloudflare. It does not create a public panel URL like Quick Tunnel or ngrok.", + "warp_install_hint": "Install the official Cloudflare WARP client first, then restart the panel.", + "warp_install_required": "Install WARP first", + "warp_connect": "Connect WARP", + "warp_disconnect": "Disconnect", + "warp_connected": "WARP connected", + "warp_disconnected": "WARP disconnected", + "warp_status_unknown": "WARP status is unknown.", + "warp_status_not_installed": "Cloudflare WARP CLI is not installed on this host.", + "warp_status_connected": "Cloudflare WARP is connected.", + "warp_status_connecting": "Cloudflare WARP is connecting.", + "warp_status_disconnected": "Cloudflare WARP is disconnected.", + "warp_status_not_registered": "Cloudflare WARP is installed but not registered yet. Connect will try to register automatically.", + "warp_status_service_unavailable": "Cloudflare WARP service is not available. Make sure warp-svc is running.", + "warp_status_error": "Cloudflare WARP returned an error.", + "install_starting": "Starting installation...", + "server_check_complete": "Check completed", + "server_connection_error": "Connection error", + "connections_load_error": "Error loading connections", + "connection_updated": "Connection updated", + "edit_connection": "Edit connection", + "enable_connection_progress": "Enabling connection...", + "disable_connection_progress": "Disabling connection...", + "connection_enabled": "Connection enabled", + "connection_disabled": "Connection disabled", + "gb": "GB", + "marketplace": "Templates", + "open_marketplace": "Open Templates", + "marketplace_desc": "Install protocol and service templates available for this server.", + "installed_apps": "Installed applications", + "installed_apps_hint": "Only installed protocols and services are shown here. Use Templates to add more.", + "no_installed_apps": "No installed applications yet", + "no_installed_apps_desc": "Open Templates to install protocols or services on this server.", + "install_another": "Install another", + "new_instance": "new instance", + "port_next_instance_hint": "Choose a free UDP port for the additional instance.", + "web_servers": "Web servers", + "nginx_desc": "NGINX web server with Let\u0027s Encrypt HTTPS certificate and editable index.html.", + "nginx_domain": "Domain", + "nginx_email": "Let\u0027s Encrypt email", + "nginx_port_hint": "HTTPS port exposed on the server. Port 80/TCP must be free and reachable for Let\u0027s Encrypt validation.", + "nginx_install_hint": "The domain must already point to this server. Port 80/TCP is used for HTTP-01 certificate validation.", + "site": "Site", + "site_editor": "Site editor", + "site_saved": "Site saved", + "nginx_dns_hint": "Create this DNS record before installation:", + "backup": "Backup", + "backups": "Backups", + "backup_desc": "Backups are created on the server and contain valuable protocol files needed to restore or recreate it later.", + "create_backup": "Create backup", + "creating_backup": "Creating backup...", + "backup_created": "Backup created", + "no_backups": "No backups yet", + "no_backups_desc": "Create the first backup for this protocol.", + "refresh": "Refresh", + "guest_allow_create_hint": "为访客用户创建配置(3x-ui VLESS 或所选服务器协议)。", + "invite_inbound_hint": "一次性选择所需的 VLESS 入站", + "invite_inbound_loading": "加载入站中…", + "invite_inbound_empty": "未找到 VLESS 入站", + "invite_inbound_need_xui": "请先在设置中配置 3x-ui", + "invite_inbound_required": "请选择 VLESS 入站", + "invite_sub_url_missing_title": "未设置订阅 URL", + "invite_sub_url_missing_hint": "在 设置 → 3x-ui 中填写订阅基础 URL,以便发放 /sub/… 配置。", + "xui_url_label": "3x-ui 地址", + "xui_url_hint": "包含协议、端口和 webBasePath(如有)", + "xui_sub_url_label": "订阅基础 URL", + "xui_sub_url_hint": "公共 /sub 基础地址,末尾不要斜杠(如 https://host:2096/sub)。", + "xui_api_token_label": "API Token(推荐)", + "xui_api_token_placeholder": "Settings → Security → API Token", + "xui_api_token_hint": "若已设置,将跳过用户名/密码登录", + "xui_username_label": "3x-ui 用户名", + "xui_password_label": "3x-ui 密码", + "xui_sync_hint": "3x-ui 客户端将按 email 同步为面板用户", + "xui_inbound_label": "VLESS 入站", + "xui_inbound_auto": "自动(第一个 VLESS 入站)", + "xui_inbound_hint": "创建 3x-ui VLESS 配置时使用。请先在 3x-ui 中创建 VLESS 入站。", + "xui_servers_title": "3x-ui 服务器", + "xui_servers_hint": "可添加多个 3x-ui 面板。创建配置时按名称选择服务器,并使用其订阅基础 URL。", + "xui_servers_empty": "暂无 3x-ui 服务器。请点击添加。", + "xui_servers_manage_hint": "面板与订阅地址在下方「3x-ui 服务器」中管理。", + "xui_server_add": "添加服务器", + "xui_server_name_label": "名称 / 描述", + "xui_server_name_hint": "显示在邀请与用户表单中(如 US、NL)。", + "xui_server_select_label": "3x-ui 服务器", + "xui_server_select_hint": "选择签发订阅 URL 的 3x-ui 面板。", + "xui_server_delete_confirm": "从面板删除此 3x-ui 服务器?", + "cascade_title": "Cascade (double VPN)", + "cascade_desc": "Clients connect to this entry server; internet exits through another server — to bypass country blocks.", + "cascade_hint": "ryderams-style AWG2 cascade: entry and exit must both have AmneziaWG/WireGuard. Clients keep using the ENTRY config only.", + "cascade_client_note": "Users connect only to the ENTRY server. Do not give them the exit cascade peer.", + "cascade_security_note": "Logic runs via panel SSH (no remote curl|bash). Based on ryderams/amneziawg-cascade approach.", + "cascade_entry_protocol": "Protocol on this server (entry)", + "cascade_exit_server": "Exit server", + "cascade_exit_server_none": "— Select server —", + "cascade_exit_protocol": "Protocol on exit server", + "cascade_enable": "Enable cascade", + "cascade_disable": "Disable cascade", + "cascade_working": "Setting up cascade…", + "cascade_disabling": "Disabling cascade…", + "cascade_enabled": "Cascade enabled", + "cascade_disabled": "Cascade disabled", + "cascade_off": "Off", + "cascade_on": "Active", + "cascade_configured_down": "Configured, tunnel down", + "cascade_exit_required": "Select an exit server", + "cascade_enable_confirm": "Enable cascade? A service client will be created on the exit server and a tunnel will be set up on this entry server.", + "cascade_disable_confirm": "Disable cascade on this server?", + "cascade_no_handshake": "Up, no handshake", + "cascade_settings_title": "Cascade settings (per step)", + "cascade_settings_desc": "If pages spin forever with no internet — keep all options on and click Enable cascade again.", + "cascade_opt_pin_exit": "Pin route to exit server", + "cascade_opt_pin_exit_hint": "Keeps the exit tunnel from looping through the VPN itself.", + "cascade_opt_remove_eth_masq": "Remove client NAT on eth0", + "cascade_opt_remove_eth_masq_hint": "Clients must not egress directly from the entry server.", + "cascade_opt_force_forward": "Force FORWARD between interfaces", + "cascade_opt_force_forward_hint": "Client traffic → cascade → exit.", + "cascade_opt_mss_clamp": "TCP MSS clamp", + "cascade_opt_mss_clamp_hint": "Fixes endless page loads on double VPN.", + "cascade_opt_wait_handshake": "Wait for exit handshake", + "cascade_opt_wait_handshake_hint": "Do not mark cascade active if the exit peer never answers.", + "cascade_verify_egress": "Verify egress IP", + "cascade_verify_egress_hint": "After handshake, curl ifconfig.me via the cascade Address; fail and roll back if empty.", + "cascade_opt_keep_dns": "Keep DNS from exit config", + "cascade_opt_keep_dns_hint": "Usually off — the client sets DNS itself.", + "cascade_opt_handshake_timeout": "Handshake timeout (sec)", + "cascade_opt_table_id": "Routing table id", + "cascade_opt_rule_priority": "ip rule priority", + "cascade_opt_allowed_ips": "AllowedIPs for entry→exit tunnel", + "cascade_opt_allowed_ips_hint": "Prefer 0.0.0.0/0 (IPv4); ::/0 often breaks double VPN.", + "cascade_opt_vpn_subnet": "Client subnet override", + "cascade_opt_vpn_subnet_hint": "Empty = auto-detect from server Address" }