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_hint') }}
+
{{ _('cascade_client_note') }}
+
{{ _('cascade_security_note') }}
+
+
+ {{ _('cascade_settings_title') }}
+ {{ _('cascade_settings_desc') }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
{{ _('cascade_working') }}
+
+
+
@@ -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"
}