mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-08-24 11:57:15 +00:00
effcccceac
* feat(amneziawg): add native AmneziaWG protocol backend AmneziaWG (WireGuard plus DPI-resistant obfuscation) needs no Docker here — it runs as a genuine kernel interface via awg-quick/awg, managed the same way internal/mtproto manages mtg: one Inbound row is one desired Instance, and a Manager reconciles running interfaces toward the database every 10s (internal/web/job/amneziawg_job.go) plus immediately after a client edit (applyLocalAmneziaWG). Clients reuse model.Client verbatim (the same PrivateKey/PublicKey/ PreSharedKey/AllowedIPs fields WireGuard already uses), so bulk operations, the QR/share-link modal and subscriptions come from the shared inbound infrastructure instead of a parallel implementation. internal/amneziawg owns the obfuscation param generator/validator (ported from coinman-dev/3ax-ui, upgraded to AmneziaWG 2.0's S3/S4 padding and I1 signature packet) and the exec wrapper around awg-quick/awg, with fingerprint-based reconcile (noop / reload-via- syncconf / full restart) mirroring mtproto.Manager so a same-protocol edit doesn't force an unnecessary interface bounce that would drop every peer's connection. Frontend and install.sh's DKMS/awg-tools setup are tracked separately; this is backend-only. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * feat(amneziawg): add frontend support and fix a Go->Zod generator gap Wires the amneziawg protocol through the panel UI the same way every other protocol is registered: a Zod settings schema (nested {server, clients}, matching the Go JSON exactly), the protocol enum, the inbound-form's per-protocol fields component and its tab-visibility allowlist, the default-settings factory, the client schema dispatcher, and the sniffing-capability exclusion (no Xray inbound exists for amneziawg, same as mtproto). Client key/allowedIPs fields are reused rather than duplicated: since AmneziaWG clients are wire-identical to WireGuard clients (same model.Client fields), ClientFormModal renders one shared field block for both, switching only the visible label by which protocol is active. The private-key input also gets a live public-key sync via a new useEffect, because unlike WireGuard's Xray-native inbound (which re-derives its public key at runtime and never stores one), AmneziaWG's server.publicKey is a real persisted field the Go backend reads directly — free-typing a new private key without this would silently save a mismatched keypair. Adds a downloadable per-client .conf (amneziawgConfig.ts, mirroring wireguardConfig.ts) with the obfuscation lines, and an InboundOption.AwgServer field on the Go side so the config builder gets the full server block in one round trip. Along the way, running tools/openapigen surfaced a real bug: it doesn't flatten anonymously-embedded Go structs the way encoding/json does, so ServerSettings embedding Obfuscation20 produced a Zod schema with a nested `obfuscation20` key that never matches the real wire JSON. Fixed by un-embedding (flat fields + an accessor method) and registering internal/amneziawg in the generator's own package list, which had been silently emitting a dangling schema reference. English and Russian translations are complete; the other 10 locale files still fall back to English for the new keys. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * feat(amneziawg): complete frontend parity for the Inbounds list page The Clients page (form, CRUD, QR/config) already worked from the prior commit; this closes the remaining gap on the Inbounds side and in a couple of protocol allowlists that a plain search for existing wireguard/mtproto handling turned up. lib/xray/inbound-link.ts gets amneziawg-specific link/config builders (genAmneziaWGLink/genAmneziaWGConfig, plus the *s fan-out variants) mirroring the wireguard ones — AmneziaWG has no legacy peers-array to fall back to, so these read settings.clients directly and add the obfuscation lines every client must share with the server. Wired into genInboundLinks generically, and into three consumers that call the wireguard builders directly rather than through that dispatcher: QrCodeModal, InboundInfoModal, and InboundsPage's bulk export. ClientInfoModal, ClientBulkAddModal, and the bulk attach/detach modals each had their own protocol allowlist that needed amneziawg added alongside wireguard/mtproto. Two real gaps surfaced by grepping every remaining 'wireguard' / Protocols.WIREGUARD hit in frontend/src rather than trusting the checklist was exhaustive: - useInbounds.ts's TRACKED_PROTOCOLS gates the deactive/depleted/ expiring/online client counts shown per inbound on the list page; without amneziawg those counts would silently read zero. - inbound-tag.ts is an explicit client-side mirror of the Go backend's port_conflict.go (the file says so itself: "Keep in sync"). It still only special-cased wireguard for UDP, so an amneziawg inbound would have fallen through to the TCP default and disagreed with the backend's own port-conflict math. Also finishes translating the AmneziaWG UI strings into the 11 locale files that were still falling back to English (ar-EG, es-ES, fa-IR, id-ID, ja-JP, pt-BR, tr-TR, uk-UA, vi-VN, zh-CN, zh-TW), matching en-US/ru-RU key-for-key (26 new keys, verified by count in every file). Not run anywhere: npm run typecheck / build. This machine has neither Node nor npm, so nothing here has compiled — reviewed by hand plus brace/paren balance checks and cross-referencing the generated Zod/TS types. Treat this as needing a real typecheck before shipping. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * docs(install): note that AmneziaWG kernel module install is still manual Tracked separately (not yet ported into this script) — see coinman-dev/3ax-ui's install_amneziawg for the reference approach (ppa:amnezia/ppa). Also serves as a real, path-filter-matching change to get the previous empty commit's CI trigger to actually fire — release.yml's push trigger is paths-scoped and an empty commit changes no files, so it never matched. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * feat(amneziawg): add a button to randomize obfuscation parameters Mirrors the existing key-regenerate button next to the private key field. Client-side randomization matches the ranges/constraints of GenerateObfuscation20's "default" preset (internal/amneziawg/params.go) closely enough for a form suggestion — the user can still hand-edit any field afterward. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * feat(install): auto-install the AmneziaWG DKMS module + amneziawg-tools Ports install_amneziawg from coinman-dev/3ax-ui's install.sh, adapted to this script's broader distro coverage and NONINTERACTIVE convention: - Ubuntu/Debian/Armbian: ppa:amnezia/ppa (primary, tested path), with a reachability pre-check for the Launchpad PPA host — often blocked by hosting providers, especially Russian VPS — so a flaky network skips the feature instead of hanging apt through several retries. - Fedora/RHEL-family, Arch/Manjaro/Parch: best-effort fallback to plain wireguard-tools (+ AUR amneziawg-dkms via yay/paru when available), with a manual-install pointer. - Everything else: manual-install pointer only. Also installs ndppd and persists IPv4/IPv6 forwarding (for the future IPv6/NDP phase, not yet wired into the panel) and adds a Secure Boot warning at the end of the run, since a DKMS-built module is unsigned and won't load while it's enabled — a common trap on cloud VPS images. Never fatal: the panel installs and runs fine either way, an AmneziaWG inbound just won't bring up its tunnel until the module is present. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * fix(amneziawg): resolve all 3 real CI failures (typecheck/lint/codegen) Found by checking the fork's Actions tab after the last two pushes — the release build passed (it doesn't run these checks) but the separate CI workflow caught three real issues: - golangci-lint (noctx): every internal/amneziawg/manager.go exec.Command call is now exec.CommandContext with a 30s timeout, so a hung awg-quick/awg invocation can't block the reconcile job indefinitely (mirrors internal/mtproto/process.go's own CommandContext usage). - tsc --noEmit: frontend/src/schemas/client.ts's hand-maintained InboundOptionSchema (used by the useClients hook, separate from the auto-generated one in generated/) never got an awgServer field added when the AmneziaWG frontend work was done — every read of inbound.awgServer.* in amneziawgConfig.ts was typing as {}. Added AwgServerOptionSchema, nested (not flattened like wg*) to match what amneziawgConfig.ts already expects. Also guarded server.publicKey in inbound-link.ts's genAmneziaWGLink against the schema's optional type. - codegen staleness: frontend/public/openapi.json is produced by a Node script (gen:api) this machine can't run; hand-applied the exact diff the CI failure log already showed (amneziawg protocol enum entry, ServerSettings schema, InboundOption.awgServer, one example payload), verified as valid JSON. Also confirmed independently by this run: install_amneziawg (previous commit) installed and loaded the DKMS module successfully on both amd64 and arm64 CI runners. The two "Deploy Smoke Tests" failures are unrelated to this change — this fork has only ever published the dev-latest pre-release, and GitHub's /releases/latest API deliberately excludes pre-releases, so the smoke test's no-argument install path (which resolves "latest") has nothing to find. Not a regression; needs an actual tagged release whenever that's wanted. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * feat(amneziawg): Phase 2a — IPv6 support + NDP proxy Adds native dual-stack IPv6 to AmneziaWG inbounds, ported from coinman-dev/3ax-ui's approach: - ServerSettings gets ipv6Enabled/ipv6Subnet/ipv6ExternalInterface; Instance carries the server's own IPv6 address (first host of the subnet) alongside its IPv4 one. - defaultAmneziaWGClients allocates an IPv6 host address per client (second AllowedIPs entry) when the server has IPv6 enabled, reusing allocateWireguardAddress — which needed a real fix along the way: it always suffixed "/32" regardless of address family, which is wrong for an IPv6 host address (needs /128). Now family-aware. - generateServerConfig's PostUp/PostDown gains IPv6 forward-accept rules, proxy_ndp sysctl, and one `ip -6 neigh add/del proxy` entry per enabled peer with an IPv6 address — the lightweight per-client method, not the ndppd-daemon whole-subnet method (not worth the config-file-management complexity at this scale; ndppd itself is still installed by install.sh in case that changes later). - ValidateIPv6Subnet rejects a malformed subnet before save. - Frontend: ipv6Enabled/ipv6Subnet/ipv6ExternalInterface fields on the AmneziaWG inbound form, EN+RU translations, openapi.json/generated/* regenerated (the latter via `go run ./tools/openapigen`, pure Go). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * fix(amneziawg): fill in IPv6 fields missed by the Phase 2a commit Two real gaps the CI caught (both new fields, both my miss): - inbound-defaults.ts's createDefaultAmneziawgInboundSettings() built a server object literal predating ipv6Enabled/ipv6Subnet/ ipv6ExternalInterface — AmneziawgServer's inferred type now requires them (zod .default() fields are non-optional post-parse), so this didn't typecheck at all. - openapi.json's ipv6Enabled property was missing the description the real generator attaches (the Go doc comment covering all three IPv6 fields is attached to the first one) — a one-line diff, but git diff --exit-code doesn't care how small. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * feat(amneziawg): Phase 2b — per-client port-forwarding Admins can now set a per-client ForwardedPorts string (e.g. "80, 443, 8000-8100") that gets DNAT'd + FORWARD'd to that peer's tunnel address via iptables rules in PostUp/PostDown, ported and simplified from coinman-dev/3ax-ui's shared/portfwd. Two decisions worth flagging for future readers: - The iptables --comment tag on each rule is awg-fwd-<fnv32a(email)>, not the raw client email. Email is admin/API-supplied free text that ends up embedded in a shell-executed PostUp/PostDown line; a hash can never carry a shell metacharacter through where raw interpolation could. - The reconcile manager gained a third fingerprint (portFwdFP, next to the existing structural/peers ones). `awg syncconf` only touches the WireGuard peer table — it never re-applies PostUp/PostDown iptables rules — so a port-forward-only change has to force a full awg-quick down+up bounce, same as a structural change, rather than the lighter sync a plain peer add/remove can use. Also fixes a real pre-existing bug found while wiring up IPv6 client allocation in the previous commit's spirit: allocateWireguardAddress always suffixed "/32" regardless of address family, which produced invalid host bits for IPv6 (needs "/128"). ForwardedPorts flows through model.Client -> model.ClientRecord (gorm column wg_forwarded_ports, auto-migrated) -> ToRecord/ToClient/ MergeClientRecord, mirroring the awgServer field's earlier lesson that new fields need checking against a second, hand-maintained persistence-layer struct. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * feat(amneziawg): route a client's traffic through Xray via the Routing page Every enabled AmneziaWG inbound gets its own Xray TPROXY bridge automatically, with no toggle to enable first: a loopback dokodemo-door inbound (sockopt.tproxy) tagged with the AmneziaWG inbound's own real tag, so it's already selectable in the existing Routing page's inbound-tag picker — the same trick the mtproto sidecar's own bridge already relies on (InboundService.GetInboundTags is a plain, protocol-blind SELECT over every inbound row's tag, no dedicated UI plumbing needed). internal/amneziawg's defaultPostUpDown TPROXYs every peer's traffic into that bridge unconditionally; the bridge's port is derived deterministically from the inbound's id (EgressPortForInbound) so the kernel-side reconcile loop and the Xray-config generator never need to negotiate a runtime value between them. injectAmneziawgEgress never generates a routing rule itself — whether a client's traffic goes anywhere beyond Xray's default routing is entirely up to whatever rules the admin adds through the existing Routing UI (pick the AmneziaWG inbound's tag as source, optionally a specific peer's IP via that page's own Source-IP field, and an outbound), exactly the same workflow as routing any other protocol. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * fix(amneziawg): recover orphaned interfaces after an ungraceful exit Two gaps left an AmneziaWG interface stuck outside the manager's control after a crash (kill -9/OOM/panic skips StopAll): - ensureRestart's teardown was gated on the in-memory `exists` map, which is always empty on a fresh process, so a survived interface never got interfaceDown before interfaceUp tried `ip link add` against a name the kernel already had — failing forever and never populating m.ifaces, so traffic accounting silently stopped and the inbound could never be removed. Gate on isInterfaceUp instead, which checks real kernel state rather than this process's own bookkeeping. - An inbound deleted from the database entirely while the panel was down has no entry in `desired` ever again, so it never reaches the per-id cleanup loop in Reconcile (which only walks m.ifaces). Add a one-time sweepOrphansLocked scan of configDir, mirroring mtproto.Manager.sweepOrphansLocked, that tears down and removes any leftover interface/config not in the current desired set. Found by the automated review on MHSanaei/3x-ui#6105 (Finding 1). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * i18n(amneziawg): backfill IPv6/obfuscation/port-forwarding keys in 11 locales Only en-US/ru-RU ever got these 9 keys as each AmneziaWG feature landed (the regenerate-obfuscation button, then Phase 2a's IPv6 fields, then Phase 2b's per-client ForwardedPorts) — the other 11 locale files were never backfilled, so i18next has been silently falling back to English for all of them since Phase 1. Cosmetic-only (never broke anything), but now closed for every shipped locale. * fix(amneziawg): resolve 7 Medium findings from the automated PR review Each is independently reproducible; fixed together since one review pass found all of them. - manager.go: the shared "ip rule add fwmark" policy route had no existence check, so it duplicated in "ip rule show" on every interface bounce (which hostRulesFingerprint forces on any client add/remove/ re-IP). Now checked via "ip rule list | grep -q ..." first. (Finding 2) - params.go: ExternalInterface, IPv6ExternalInterface, and subnetIp/ subnetCidr are interpolated unescaped into a shell-executed PostUp/ PostDown line, but only obfuscation and the IPv6 subnet were validated before save. Added ValidateInterfaceName (a strict charset+length pattern) and ValidateSubnetIPv4 (netip.ParsePrefix), wired into normalizeAmneziaWGSettings. (Finding 3) - amneziawg_job.go: IsAwgInstalled() existed but nothing ever called it, so a host without awg/awg-quick (the Docker image, RHEL, Arch, a failed install.sh PPA step) logged a reconcile failure every 10s forever. Now checked once an inbound actually needs it, warning once instead of spamming. (Finding 4) - client_inbound_apply.go: the WireGuard/AmneziaWG credential carry-forward (added so a metadata-only client edit doesn't rotate keys) never covered ForwardedPorts, so a partial edit -- an API call or Telegram-bot toggle that omits the field -- silently wiped a client's port-forwarding spec. Carried forward and written back the same way the key fields already are. (Finding 5) - manager.go: hostRulesFingerprint keyed each peer on its IPv4 address only, and structuralFingerprint omitted IPv6Enabled/IPv6ExternalInterface entirely, so an IPv6-only change could pick the syncconf reload path (which never re-runs PostUp, leaving a stale NDP-proxy entry) or be a complete no-op. Both fingerprints now cover the IPv6 fields. (Finding 6) - port_conflict.go: the AmneziaWG egress bridge (injectAmneziawgEgress) binds 127.0.0.1:63100+id with no collision check anywhere, since it isn't a database row the ordinary port-conflict query can see -- same blind spot the reserved Xray API port already has its own check for. Added the equivalent check for the AmneziaWG bridge port. (Finding 7) - install.sh: install_amneziawg ran unconditionally for every install/ update, building a DKMS kernel module and enabling host-wide IPv4/IPv6 forwarding whether or not the feature is ever used. Gated behind a new should_install_amneziawg (XUI_INSTALL_AMNEZIAWG=true/false, or an interactive y/N prompt defaulting to no). Also replaced the deprecated apt-key adv with a dedicated keyring + signed-by= on the Debian branch, and guarded its sources.list appends against duplication on a retried install. (Finding 8) Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * feat(amneziawg): make the Xray TPROXY bridge a per-inbound opt-in Addresses Finding 10 from the automated PR review: an always-on TPROXY bridge makes every AmneziaWG tunnel hard-depend on Xray being up (all traffic, including DNS, drops whenever Xray restarts), and forces a full awg-quick down+up bounce on any client add/remove/re-IP, permanently losing the syncconf fast path. Adds ServerSettings.RouteThroughXray (off by default): - defaultPostUpDown only emits the TPROXY/policy-route rules when it's on; a plain AmneziaWG tunnel now has zero Xray dependency out of the box. - structuralFingerprint covers it (toggling it changes whether PostUp/ PostDown contain any TPROXY rules at all -- structural, not a per-peer host-rule). hostRulesFingerprint's IPv4 tracking is now itself conditional on RouteThroughXray (and IPv6 tracking on IPv6Enabled), so an instance that never uses either keeps the syncconf fast path for a plain peer re-IP. - injectAmneziawgEgress only creates a bridge for inbounds that opted in; checkAmneziawgEgressConflict (the Finding-7 fix) now parses each candidate through InstanceFromInbound so a non-routed inbound's port is correctly never treated as reserved. - New inbound-level Switch in the AmneziaWG form; the actual outbound decision is still made entirely through the panel's stock Routing page, same as before -- only whether the bridge exists at all is now a choice. Translation keys added to all 13 locales in the same commit this time, not backfilled later (see Finding 9's lesson). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * fix(amneziawg): resolve 4 Low findings from the automated PR review - manager.go: serverAddress assumed subnetIp always ends in ".0"; a base like "10.8.1.5" was used verbatim as the server's own address, eventually colliding with peer allocation (which starts at .2 upward). Now derives the first host of the actual subnetIp/subnetCidr network via netip, matching serverAddressV6's own approach. A /32 base (no host bits at all) is still used as-is. (Finding 12, partial -- the /16 pool-widening half of this finding only exists on the upstream-pr/amneziawg branch's merged client_wireguard.go, not here; handled separately on that branch.) - manager.go: ensureLocked carried the previous per-peer traffic counters (`last`) forward even through a full restart, but awg-quick down+up resets the kernel's own counters to zero -- the next CollectTraffic computed a large negative delta (clamped to 0), silently discarding real traffic. Extracted the decision into nextTrafficBaseline: only a reload (syncconf) preserves the baseline. (Finding 13) - portfwd.go: exported ForwardedPortsInclude; inbound_amneziawg.go's new checkForwardedPortsConflict uses it to reject, at save time, a client's forwardedPorts that would DNAT the panel's own port or another enabled inbound's port to the tunnel client -- portForwardLines has no destination restriction, so this collision was previously silent. Wired into both the single-client update path and the add-client path (client_inbound_apply.go), plus normalizeAmneziaWGSettings for the whole-inbound save path. (Finding 14) - inbound.go: InboundOption.AwgServer sent the whole ServerSettings struct including PrivateKey to GetInboundOptions callers -- a shared, admin-wide dropdown-filling endpoint the frontend's own AwgServerOptionSchema never reads that field from. Redacted it before assigning. (Finding 11) Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * fix(amneziawg): don't widen the peer address pool past AmneziaWG's own subnet Completes Finding 12 from the automated PR review (the serverAddress half of this finding was already fixed on main and cherry-picked here). This half is specific to this branch: allocateWireguardAddress's /16 pool-widening fallback is an independent addition from upstream's own main that this branch inherited during the cherry-pick rebase -- it doesn't exist on the fork's own main at all, so this fix can't be cherry-picked the normal way and is committed directly here. Widening is safe for WireGuard's own Xray-native inbound (AllowedIPs isn't tied to a strict kernel interface subnet), but AmneziaWG's kernel interface Address is exactly the configured subnet -- an address allocated from the containing /16 once the /24 fills up would be silently unroutable. allocateWireguardAddress now takes an explicit allowWidening bool: WireGuard's own caller passes true (unchanged behavior), AmneziaWG's passes false (fails loudly on exhaustion instead). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * docs(docker): note that AmneziaWG doesn't work in this image Investigated: the image is Alpine-based, and AmneziaWG's own packaging (DKMS module + amneziawg-tools) doesn't target Alpine/musl at all -- unlike the Debian/Ubuntu/Fedora/Arch paths install.sh already handles, there's no package to apk add even with full host network/capabilities. The panel already degrades gracefully (IsAwgInstalled() logs one warning instead of retrying forever), so no code change is needed -- just made the reason explicit at the point where a user would reach for cap_add/ network_mode to try to work around it. * fix(sub): include amneziawg inbounds in subscription links getInboundsBySubId's SQL protocol allowlist never had 'amneziawg' added, so every AmneziaWG client was silently excluded from all three subscription formats (plain/individual links, JSON, Clash) and from the Telegram bot's QR/individual-link buttons, which fetch through the same path. genAmneziaWGLink itself was already fully implemented and already wired into GetLink's dispatch switch -- it just never got a chance to run. Same bug shape as the earlier TRACKED_PROTOCOLS frontend gap: a hardcoded protocol list one entry short. Found while investigating whether the Telegram bot needed AmneziaWG- specific client-management code -- it doesn't (the bot itself is fully protocol-agnostic), but this is the actual root cause of "can't share an AmneziaWG client's config via the bot." Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * fix(inbound): enforce node-eligibility server-side, not just in the UI Investigated multi-node interaction with AmneziaWG: the master's own reconcile (DesiredAmneziaWGInstances) and Xray config generation (injectAmneziawgEgress, the GenXrayInboundConfig protocol skip) all correctly filter on NodeID IS NULL, so a node-assigned AmneziaWG (or MTProto) inbound would never be managed by the master. But nothing stopped one from being created that way: NODE_ELIGIBLE_PROTOCOLS (frontend/src/pages/inbounds/form/InboundFormModal.tsx) only hides the node picker client-side -- a direct API call could set nodeId on an AmneziaWG inbound, which every node then reconciles as an ordinary local inbound (nodes run the identical binary, full cron suite included), leaving it running unmanaged and untracked by the master's own AmneziaWG bookkeeping. Added isNodeEligibleProtocol (inbound_protocol.go), mirroring the frontend's allowlist, and enforced it in both AddInbound (the actually exploitable path -- nodeId comes straight from the request) and UpdateInbound (defense in depth; NodeID is already restored from the stored row there before this check, so it mainly guards against a protocol change on an existing node-hosted inbound). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * fix(amneziawg): allow TPROXY-marked traffic through a default-deny INPUT chain TPROXY never rewrites a packet's own destination address, only the routing decision. A default-deny firewall whose INPUT chain sanity-checks "is this destination actually local" (UFW's ufw-not-local, via addrtype --dst-type LOCAL, is a concrete example) silently drops the redirected packet before Xray's socket ever sees it -- RouteThroughXray looked fully configured (TPROXY rule present and counting, Xray listening with IP_TRANSPARENT set) yet every peer's traffic vanished with no trace on either side. Adds an idempotent, never-torn-down "iptables -I INPUT 1 -m mark --mark <fwmark> -j ACCEPT" alongside the existing shared policy route, so this works regardless of which firewall manager owns the rest of the INPUT chain. * fix(frontend): give AmneziaWG the same UDP tag and its own tag color The Inbounds list only special-cased isWireguard/isHysteria for the "UDP" network badge, so an AmneziaWG row showed just the bare protocol tag with no transport badge next to it. Added the missing isAmneziawg flag (mirrors isWireguard exactly) and wired it into the same branch. Client-row protocol-color maps in ClientsPage/HostList had no amneziawg entry, silently falling back to grey -- ClientInfoModal already had amneziawg: 'yellow' from earlier work, these two just never got it. * feat(logs): show which AmneziaWG client an access-log line belongs to The dokodemo-door TPROXY bridge every AmneziaWG peer's traffic is routed through has no per-user identity, so Xray's own access log never carries an "email:" token for these lines -- the Access Logs modal showed a blank Email column for every in-*-udp row, even though every other protocol's rows show the client normally. The peer's decapsulated tunnel IP does survive as the log's "from" address, and that IP deterministically maps to exactly one configured peer. Builds a "<inbound tag>|<ip>" -> email index from the same AmneziaWG inbounds already parsed elsewhere (amneziawg.InstanceFromInbound), and fills in Email from it whenever the raw log line didn't have one. * fix(amneziawg): enable sniffing on the TPROXY bridge Domain-based Routing rules could never match RouteThroughXray traffic: an AmneziaWG peer resolves DNS itself, through the tunnel, before ever sending a packet, so the decapsulated traffic TPROXY hands to the bridge is already a bare destination IP with no domain name attached at the network layer. Every other inbound recovers this via sniffing (confirmed working for the stock wireguard inbound, which does have it configured); the bridge never got a sniffing block at all, so only tag/IP/network-based rules could ever match it -- any domain rule above it in the list was silently unreachable. * docs: add an AmneziaWG config page and list it as a supported protocol Closes the PR checklist gap: the feature shipped with zero mention on the docs site. Mirrors reality.mdx's structure (key settings, setup steps, config excerpt) and notes the Docker/multi-node/Telegram-bot caveats the PR itself is honest about not having confirmed. * fix: address the fresh review round on PR #6105 (8 findings) 1. hostRulesFingerprint didn't account for ForwardedPorts when RouteThroughXray was off, so re-IPing a peer with port-forwarding configured left stale DNAT rules pointing at an address the next peer could be handed. 2. Server/client config values (keys, email, I1) were never validated for control characters before being written into the generated .conf; a newline could smuggle a PostUp hook into awg-quick's parser. Added ValidateConfigValue at save time and a sanitizeConfigValue backstop at render time. 3. checkForwardedPortsConflict didn't scope to node_id IS NULL, so a port used only on a different node produced a false collision; also hoisted the panel-port/inbounds lookup out of the per-client loop (portConflictContext) so N clients cost one query, not N. 4. PostDown commands were ";"-joined and abort on the first failure; appendOrTrue makes teardown best-effort so an external firewall flush can't leave DNAT rules to accumulate across bounces. 5. The "ip rule list | grep -q" existence check could SIGPIPE under pipefail and re-add a duplicate rule; switched to grep -c >/dev/null. 6. Ported the vpn:// share-link format (base64url of the plain .conf text, matching the real AmneziaVPN app) onto this branch -- it had only ever landed on our own fork's main, so this PR branch was still on the old amneziawg://+query-params scheme our own docs no longer described. Also corrected the docs' install.sh claim (opt-in/ interactive, not automatic) and stale pre-opt-in comments in route_egress.go. 7. install.sh: Arch's ndppd install used pacman -Syu (full system upgrade) instead of -Sy like every other call in the script; and should_install_amneziawg re-prompted on every `x-ui update` even when awg was already installed. 8. CollectTraffic could clobber a concurrent restart's freshly-reset (empty) traffic baseline with stale pre-restart counters, since getPeerStats runs lock-free; now checks pointer identity before writing back. sweepOrphansLocked permanently disabled itself on a transient os.ReadDir failure instead of allowing a retry. go build/vet/test and frontend typecheck/lint/build/vitest all pass. * fix(install.sh): check the live sysctl value, not sysctl.conf text Reviewer feedback (cherts, PR #6105): grepping /etc/sysctl.conf for the setting name is unreliable -- many distros split sysctl config across /etc/sysctl.d/*.conf, and /etc/sysctl.conf can be a symlink into that directory, so the check can miss an already-active setting (harmless duplicate append) or match a disabled/commented line (forwarding silently stays off). Query the live value via `sysctl -n` instead, which is accurate regardless of which file set it. Applied the same fix to both the IPv6 and IPv4 checks for consistency. * fix: update inbound_amneziawg.go to the split buildInboundForLocalRuntime Same fork-only-file blind spot as the one caught on our own main after the 3.6.0 sync: upstream split buildRuntimeInboundForAPI into buildInboundForNodePush / buildInboundForLocalRuntime (part of the node-sync client-deletion fix,5bc81dfd), updating every call site it could see. This file doesn't exist upstream, so it kept calling the old name even after the branch merged in that commit. * fix(frontend): recognize AmneziaWG's vpn:// scheme in share-link labels The shared link-tag/label helper (used by the client info modal, QR modal, and subscription page) had no entry for the vpn:// scheme AmneziaWG links use, so it fell through to the generic fallback: a plain "Vpn" tag with no color, and an empty remark/port that made the row's title fall back to "Link N" instead of the inbound's actual name:port — unlike every other protocol, which shows its real tag and label. vpn:// links are base64url of a plain .conf text (matching the real AmneziaVPN app's own share-link format), not a structured URL, so there's no query string or #hash to read a remark/port from. Decode the payload and pull the remark/endpoint back out of the .conf text directly instead. * fix(xray): force a full restart for TPROXY inbounds, never hot-add them Real incident: an AmneziaWG inbound with RouteThroughXray enabled lost all internet on that connection after a migration. Root-caused on the live box -- iptables TPROXY counters were incrementing (packets correctly redirected to 127.0.0.1:63110), but nothing was actually listening there (ss showed nothing on that port) until a full `systemctl restart x-ui`, after which the bridge came up immediately. Xray-core's gRPC AddInbound reports success for a new sockopt.tproxy inbound (internal/amneziawg's own Xray egress bridge is the only kind this fork ever generates) but doesn't reliably bind a working listener for it outside of process startup -- the bridge silently never comes up, and RouteThroughXray traffic goes nowhere until the next full restart happens to occur for an unrelated reason. diffInbounds already has this exact defensive pattern for REALITY inbounds ("a gRPC remove+add does not reliably rebuild the REALITY authenticator"), just never extended to TPROXY, and only in the already-existing-then-changed branch -- the "brand new inbound" branch had no such guard at all, which is exactly the path a freshly-enabled RouteThroughXray bridge takes. Added inboundUsesTproxy and wired it into both branches. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * fix(amneziawg): flag Xray for resync when a peer edit changes qualifying state Real production bug, root-caused on iiadmin-vps: updateAmneziaWGInbound/ AddInbound/DelInbound only ever updated the kernel interface via amneziawg.GetManager() -- they never called SetNeedRestart the way every other protocol's mutation path does (client_crud.go, inbound.go, etc. all do). injectAmneziawgEgress's TPROXY bridge inbound depends on InstanceFromInbound finding at least one qualifying peer plus RouteThroughXray, so an edit that flips that (first peer added, last one removed, RouteThroughXray toggled on) previously required a full panel restart before the bridge actually got created, with no error anywhere: the kernel interface would handshake fine, but traffic redirected into the bridge's TPROXY port went nowhere because nothing was listening there. diffInbounds/inboundUsesTproxy already correctly force a full restart for a brand new TPROXY inbound (bdee0a20) -- that part was never the bug. The gap was entirely upstream: nothing ever told Xray a resync was even needed. * fix(clients): reject AllowedIPs already used on another WireGuard/AmneziaWG inbound defaultWireguardClients/defaultAmneziaWGClients only ever checked uniqueness against their own inbound's client list, so two inbounds sharing a subnet (same protocol or not) could silently hand out or accept the same address -- the exact scenario behind a real duplicate-IP incident where a WireGuard and an AmneziaWG client both ended up on the same address. otherTunnelAllowedIPs now collects every address already claimed on every other tunnel inbound and folds it into both the auto-allocation pool and the manual-entry collision check, naming the other inbound in the error when it fires. * fix(frontend): add the missing AmneziaWG config download on the sub page The subscription page already gave WireGuard links their own "Config" block (copy/download/QR of the actual .conf, via wireguardConfigFromLink reversing the wireguard:// query params) but had no equivalent for AmneziaWG's vpn:// links -- its isWireguardLink gate never matched them, and no reverse-parse helper existed for this page specifically. Every other surface (InboundInfoModal, ClientInfoModal, ClientQrModal) already had this parity; this was the one page that didn't. Fixed by adding amneziawgConfigFromLink (inbound-link.ts), simpler than its WireGuard counterpart since a vpn:// payload already *is* the plain .conf text -- just base64url-decode it, no query-param reconstruction needed -- and wiring it into SubPage.tsx alongside the existing WireGuard block, reusing the same pages.clients.amneziaWgConfig label the other three surfaces already use. * fix(xray): force a full restart for password-auth SOCKS5 hot-apply Real production incident: editing a client under an AmneziaWG inbound left its embedded SOCKS5 relay's settings byte-different (a new account list), and Xray's gRPC remove+add hot swap silently dropped the account for a peer whose email contained non-ASCII characters -- its tunnel kept handshaking fine but all its traffic got rejected at the SOCKS5 layer, while every other peer on the same relay was unaffected. A full restart (reading the same JSON straight from disk) always produced the correct account list. socks isn't in userDiffableProtocols (that only covers vless/vmess/trojan's clients+email shape, not accounts+user), so any settings drift on this inbound fell through to the generic remove+add path. Forces a restart instead, the same defensive choice already made for REALITY and TPROXY -- scoped to auth:"password" specifically so the other, noauth SOCKS5 bridges (panel/node/mtproto egress) keep the cheaper hot path. * Fix Attach reusing one identity's address across wg/awg inbounds ClientService.Attach deliberately copies one identity's stored AllowedIPs into every WireGuard/AmneziaWG inbound it's attached to in the same call, so the same person gets the same tunnel address on every protocol they use. Its loop calls addInboundClient once per inbound, and each of those independently computes otherTunnelAllowedIPs -- so by the second inbound in the batch, the first inbound's just-written copy of this identity's own address looked like a cross-inbound collision against itself. Real production symptom this caused: detaching then re-attaching a client to both wg and awg failed with "wireguard: allowedIPs entry X is already used by a client on inbound 'awg' (#N)" -- the exact address the identity is supposed to keep, rejected as if it belonged to someone else. Add a selfEmails exclusion to otherTunnelAllowedIPs and populate it from the client(s) being processed at the one real call site. Safe unconditionally: ClientRecord.Email is globally unique, so a match can only ever be this same identity's own entry on a sibling inbound, never a genuine different client's address. Reproduced the underlying mechanism live (manual entry correctly rejected as a cross-inbound collision; fresh auto-allocation correctly avoided a used address) before writing the fix, to confirm the guard itself works and the bug is specifically in how Attach's per-inbound calls interact with it. * Attach: allocate fresh when re-attaching with no active tunnel The previous fix (82cc69f5) made Attach's own address-reuse correctly not collide with itself across inbounds -- but it still always reused an identity's stored AllowedIPs verbatim, even when that identity currently has zero WireGuard/AmneziaWG attachments at all. A real report from testing this live: an identity fully detached from both its wg and awg inbounds, then re-attached, got its old address back even though several lower addresses were free -- because nothing about being fully detached ever cleared the stored value Attach copies from. Add hasTunnelAttachment, checked once against the identity's CURRENT inbound set before Attach's loop runs: if none of its current inbounds is WireGuard/AmneziaWG, clear the stored AllowedIPs so this attach allocates fresh (matching what a brand-new client would get) instead of resurrecting an address nothing reserves anymore. Left alone when the identity already has an active tunnel elsewhere, so extending it to a second protocol still keeps a consistent address. * Fix TestOtherTunnelAllowedIPsExcludesSelfEmail's own test setup CI caught this: the "genuinely different client" (other@wg) was seeded onto the SAME inbound passed as excludeID, which otherTunnelAllowedIPs already excludes entirely regardless of the selfEmails fix -- so the assertion that its address is still reported could never have passed, proving nothing either way. Move it onto the sibling inbound alongside shared@id, which is what the test actually needs to exercise (two clients on one sibling, one excluded by email, one not). * Attach: never inherit an address that doesn't fit the target inbound hasTunnelAttachment (from the earlier fix, commit 51067f16) only asked "does this identity have ANY tunnel attachment", treating that as license to reuse its stored address verbatim on every inbound being attached. Real production case this missed: an identity's stored address came from WireGuard's own fallback subnet (10.0.0.0/24, used when that inbound has no other clients to infer a base from), then got attached to a second, AmneziaWG inbound configured for a completely different subnet (10.8.1.0/24). defaultAmneziaWGClients's already-set-AllowedIPs branch only checks for collisions, never subnet membership, so the mismatched address was accepted silently -- producing a peer that can never actually connect, since an AmneziaWG address must fall inside the kernel interface's own configured subnet to be routable at all. Add addressesFitAmneziaWGInbound, checked per inbound inside Attach's loop: if the inherited address doesn't fit the SPECIFIC inbound being attached, clear it just for that one so it gets a fresh, valid allocation instead, while other already-attached inbounds keep their existing values. WireGuard has no equivalent strict subnet requirement (allocateWireguardAddress can widen to a fallback pool for it), so this only ever constrains AmneziaWG targets. * Give WireGuard an explicit, admin-configurable subnet field WireGuard previously had no configurable subnet at all -- only an implicit one, either inferred from existing clients' own addresses (wireguardAllocationBase) or a hardcoded 10.0.0.0/24 fallback when none exist yet. AmneziaWG, by contrast, has always had a real server.subnetIp/subnetCidr field in its settings, editable in the UI. User request: give WireGuard the same treatment. Backend: explicitWireguardSubnetBase reads an optional subnetIp/ subnetCidr pair from the inbound's own settings JSON (mirroring AmneziaWG's defaultAmneziaWGSubnetBases). defaultWireguardClients checks it first; only when unset does it fall back to today's inference-from-existing-clients behavior, so an inbound saved before this field existed keeps working exactly as it always has. Frontend: subnetIp/subnetCidr added to WireguardInboundSettingsSchema and the inbound form (mirroring AmneziaWG's own field layout/labels), with a real default (10.0.0.0/24, the same value the backend already fell back to) seeded for newly created inbounds so the field starts populated and editable rather than blank. Translated across all 13 locales. This also structurally closes the class of bug fixed in 82cc69f5/291c47b3: with wg and awg subnets explicit and independently controllable, an admin who wants matching addresses across both protocols can configure them to actually agree, instead of one silently inheriting the other's incompatible range. * Split the client edit form's AllowedIPs into per-protocol fields A client attached to both WireGuard and AmneziaWG shared one AllowedIPs form field with a dynamically-switching label, so its two genuinely different addresses could never both be shown or edited correctly. Worse, Update/Create broadcast that one shared value to every attached wg/awg inbound with no subnet-fit check, so an ordinary edit save could silently overwrite one protocol's address with the other's -- the same bug class already fixed for Attach, but reachable from any client edit. model.Client gains an optional AllowedIPsByInbound map so a caller can send distinct values per inbound; Update/Create honor it and, when it's absent, clear a shared value that doesn't fit an AmneziaWG inbound's own subnet instead of writing it through. A new TunnelAllowedIPsByInbound read path feeds the real per-inbound address to the client edit form via GET, which now renders two separate, correctly-labeled fields whenever both protocols are attached (unchanged single dynamic field otherwise). * Regenerate openapi.json for the new allowedIPsByInbound field Follow-up to 878ee839: gen:zod (frontend/src/generated) was already regenerated and committed, but gen:api (frontend/public/openapi.json) wasn't, so CI's codegen drift check failed. * Fix build breakage from merging upstream main: Update() gained a limitHwid param Two of our own AllowedIPs tests (not present upstream, so the merge never flagged them as conflicting) still called the old 3-arg Update(inboundSvc, id, client) -- upstream's hardware-ID-limit feature added a required limitHwid parameter that every other caller in this package already passes. Also drop createDefaultInboundSettings from InboundsPage.tsx: the merge conflict resolution kept the import, but upstream's clone-payload refactor (buildClonePayload, inbound-clone.ts) already calls it internally now -- this file doesn't need it directly anymore. * Fix real bug: AmneziaWG clients rejected as "empty client ID" in 3 places Three switch statements on inbound.Protocol handle "wireguard" explicitly (checking client.PublicKey) but fall through to the default case for "amneziawg" (checking client.ID, which AmneziaWG clients never set -- they use PublicKey/Email like WireGuard, not the VMess/VLESS UUID field). This is what the 4 AllowedIPs tests were actually catching: UpdateInboundClient's newClientId derivation hit this same default branch, so every Update() on an AmneziaWG client returned "empty client ID" before ever reaching the AllowedIPs logic being tested. Fixed by adding "amneziawg" alongside "wireguard" in each switch: addInboundClient's per-client validation, UpdateInboundClient's newClientId derivation, and AddInbound's per-client validation (the third one wasn't hit by these tests, but has the identical bug -- creating a brand-new AmneziaWG inbound with a client attached would fail the same way). * refactor(amneziawg): rename Obfuscation20 to Obfuscation31, drop the dead mobile preset Mechanical rename ahead of the AmneziaWG 3.1 parameter work: the type, generator and prose all said 2.0, and the "mobile" generator preset was reachable only from its own test. No behavior change. * feat(amneziawg): AmneziaWG 3.1 obfuscation parameters (backend + generated schemas) Adds the 3.1 parameter surface to the inbound settings and both Go config emitters: I2-I5 signature packets, HeaderProtectionKey (base64 32-byte, shared server<->client), ContentPaddingAddition, the five handshake-timing randomization ranges (RekeyAfterTime/RekeyTimeout/RejectAfterTime/ KeepaliveTimeout/MaxHandshakeAttempts), and the RandomTrailers/ DisableCookies switches. Freshly generated sets fill everything except I2-I5 (matching Amnezia's own generator) with jittered ranges bracketing WireGuard's stock timing constants; every reject window starts >= 30s above the rekey window by construction. Empty fields stay off the wire, so blanking a field disables just that feature. Validation generalizes the H1-H4 range checker for the new uint32-range fields, requires min 1 on timers, cross-checks rekey-vs-reject, and demands a real 32-byte base64 header-protection key. The manager warns once per process when the installed awg tools predate 3.1 but an inbound uses 3.1 parameters (awg-quick rejects unknown keys with a generic error otherwise); apply still proceeds. Requires amneziawg-tools v3.1.20260812+ / module or amneziawg-go v3.1.20260814+ on the host. * feat(amneziawg): emit and randomize 3.1 parameters in the frontend Both client-config emitters (the vpn:// link builder and the clients-page .conf builder) now carry the 3.1 [Interface] lines in the same order as the Go emitters. The obfuscation randomizer moves out of InboundFormModal into a shared lib/xray/amneziawg-obfuscation.ts that also fills the new fields, and createDefaultAmneziawgInboundSettings switches from static values to that generator — a fresh inbound now really gets the unique fingerprint the docs promise instead of the same jc=5/jmin=10 set on every install. Schema parse-time defaults for the new fields stay ''/false on purpose: real values come only from the generator, so resaving an inbound never mutates its stored parameters. A new parity test pins the hand-written AmneziawgServerSchema to the generated ServerSettings key set, so a field added on one side can no longer silently vanish from configs. * feat(amneziawg): 3.1 form fields and translations Inbound form gains inputs for I2-I5, HeaderProtectionKey (filled by the existing obfuscation Regenerate button), ContentPaddingAddition, the five timing ranges, and the RandomTrailers/DisableCookies switches; the MTU input picks up the min=1 its schema already enforced. All 13 locales get the 19 new keys and drop the "2.0" branding from the s3/s4/i1 labels. * docs(amneziawg): document 3.1 parameters; install.sh kernel/version notes The AmneziaWG page's obfuscation section moves from the 2.0 to the 3.1 parameter set: table rows for I2-I5, HeaderProtectionKey, ContentPaddingAddition, the timing-randomization ranges and the RandomTrailers/DisableCookies switches, a requirements callout (tools v3.1.20260812+, module/awg-go v3.1.20260814+, Linux 6.7+ for the DKMS path), and a sample client .conf that matches what the panel actually emits (including the DNS defaults and PersistentKeepalive it always had). install.sh warns before a DKMS build on a pre-6.7 kernel and after any install that left pre-3.1 amneziawg-tools on PATH. Also updates the hosts API operation paths ({id} -> {groupId}) in the stale ru/zh/fa reference pages: syncing docs/public/openapi.json for the new AmneziaWG schema fields surfaced that rename, which had never been copied over, and the docs build fails on paths missing from the spec. * fix(amneziawg): reject control characters and canonicalize 3.1 range values Adversarial review of the 3.1 work surfaced a validation gap: base64.DecodeString silently ignores CR/LF, so a header-protection key that picked up a line wrap in transit decoded to a valid 32 bytes, passed validation, and was emitted verbatim into every client config — where the orphan second line breaks the import while the server (whose emitter strips control chars) keeps running with the correct key. The key and range validators now reject control characters outright. Also from the same review: range values are canonicalized on save ("110 - 140" -> "110-140", whitespace-only collapses to feature-off, closing a case where the server conf rendered an invalid blank-value line the client emitters omitted); the rekey/reject invariant is now enforced against WireGuard's 120s/180s defaults when only one side is set; and the structural fingerprint joins on "\n" instead of "|", which is a legal I1-I5 character and made adjacent free-text fields join-ambiguous. * fix(install): resolve latest release tag via web redirect to dodge API rate limits The non-interactive install smoke test resolved the release version through the unauthenticated GitHub API (api.github.com/.../releases/latest), which allows only 60 requests/hour per IP. The test installs twice in one run, and on shared CI runner IPs the second call gets rate-limited, returns no tag_name, and install.sh treats an empty version as fatal (exit 1) — the same "Failed to fetch x-ui version" real users hit behind CGNAT/shared addresses. resolve_latest_tag() now reads the tag from the github.com releases/latest web redirect (not subject to the API rate limit), falling back to the API only if the redirect yields nothing. Verified with the real deploy/test/smoke-noninteractive.sh (two installs, both green). * fix(amneziawg): three review findings on #6105, plus a comment trim 1. A peer's allowedIPs reached the generated .conf unvalidated and unsanitized, unlike email/publicKey/preSharedKey which normalizeAmneziaWGSettings already guards. A newline in an entry let a following "[Interface]" re-open the interface section, whose "PostUp = ..." awg-quick then runs as root on the next apply. Reproduced end to end against generateServerConfig. The save path now rejects and canonicalizes through normalizeWireguardAllowedIPs, and the render path sanitizes as a backstop for rows predating the validation (an upgrade, a restored backup, a direct DB edit). H1-H4 get the same render-time sanitize, and the two NIC name fields a plausibility check, since stripping control characters alone would still let a shell metacharacter into a root-executed PostUp line. 2. EgressPortForInbound is 63100 + inbound id, so an id past 2435 derives a port above 65535 -- and Xray rejects the whole generated config over one invalid port, taking every other protocol down with it. It now reports ok=false past the range, and both the Xray bridge and its TPROXY rules are skipped instead of emitting an impossible port. 3. The downloadable AmneziaWG .conf read ClientRecord.allowedIPs, a single shared column that holds the WireGuard address for an identity attached to both protocols -- the exact ambiguity tunnelAllowedIPs was added to resolve for the edit form. The info and QR modals already hydrate that field, so they now pass this inbound's own address to the builder. Also trims the comment blocks in the files touched here to the 2-line guidance in CLAUDE.md: internal/amneziawg alone carried 423 comment lines in over-long blocks against 118 for the comparable internal/mtproto, and is now at 110. Every non-obvious constraint is kept (the kernel S1/S2 rule, why PostDown is best-effort, why grep -c and not -q, why the fingerprints split three ways); the narration is gone. Two hot_diff.go comments pointed at an internal/amneziawgnet package and an injectAmneziawgnetSocks function that exist nowhere in the tree; the checks themselves are unchanged. * feat(logs): add an AmneziaWG log view to the overview The overview has an access-log view for Xray but nothing for AmneziaWG, so when a tunnel misbehaves there is no way to see it from the panel at all. A kernel tunnel logs no per-request lines, so the equivalent view is built from the two things it does expose: - Live per-peer activity from `awg show <iface> dump`, joined to the client email through the desired peer set: last handshake, endpoint, allowed IPs, cumulative transfer and online state, newest handshake first. - The panel's own AmneziaWG event lines (interface up/down, awg-quick failures, the pre-3.1 tools warning), which are what actually explain a peer being absent from the table. POST /panel/api/server/amneziawglogs/:count serves both, with the same count + filter contract GetXrayLogs uses, and the modal mirrors XrayLogModal's toolbar, auto-update, mobile cards and download. The action-bar button is gated on a new status.amneziawg.configured, which stays true while an inbound exists but its interface is down -- exactly when the event lines matter. Verified against a running panel: the endpoint returns the peer table and real event lines ("awg/awg-quick not found on PATH", "create config dir: permission denied"), and count and filter both narrow as documented. One of those lines surfaced a Debugf that had been rendering as "for inbound1:amneziawg:"; fixed here since it is now user-visible. * fix(amneziawg): stop double-counting a routed inbound's traffic injectAmneziawgEgress tags its Xray bridge with the AmneziaWG inbound's own tag, so the stock Routing page can target it. Xray therefore reports that bridge's bytes under the inbound's tag, and XrayTrafficJob feeds them to AddTraffic -- which accumulates -- on top of the same bytes AmneziaWGJob already reported from `awg show dump`. An inbound with routeThroughXray on counted roughly twice its real traffic, which also inflates the quota checks that read the same counters. The awg counters are the complete measure: every peer, whether or not TPROXY routed it, and the same wire bytes the per-client totals are built from, so they stay and the Xray rows are dropped. Per-client stats were never affected -- a dokodemo-door bridge has no per-user identity, so Xray emits no user>>>email rows for it. Filtering happens before every consumer, so the DB totals, the external traffic inform and the dashboard's live speed all read one source per inbound. The set of bridge tags now comes from a predicate shared with injectAmneziawgEgress itself, with a test that pins the two together -- naming one tag too few doubles the traffic again, one too many makes real traffic vanish. * fix(amneziawg): align the three .conf emitters on one peer field order The panel builds an AmneziaWG client .conf in three independent places, and they disagreed: buildAmneziaWGClientConfig put PresharedKey right after PublicKey (wg-quick(8)'s own order, and what both WireGuard emitters on the clients side already use), while genAmneziaWGConfig and the Go amneziaWGConfigText put it after Endpoint. A user comparing a subscription link against a downloaded .conf sees the difference immediately, and the generators are exactly the kind of parallel implementation CLAUDE.md warns about drifting. Moves the two outliers onto the wg-quick order. Also drops the stray trailing newline that only appeared when PersistentKeepalive was set, so a config now always ends on its last set field whichever that is -- the same shape all three emitters produce for the same client. Parsing is unaffected either way (the format is order-insensitive, and the AmneziaVPN app reads it as a flat key-value bag), so this changes only the rendered text. Adds a test on each side that pins the peer block's field order, since nothing previously asserted it. * refactor(amneziawg): switch to the embedded amneziawg-go/gVisor architecture Replaces the kernel-module (DKMS) + awg-quick + TPROXY backend with the fork's own embedded design: amneziawg-go runs in-process over a userspace gVisor netstack, and each peer's decapsulated traffic relays into its own loopback Xray SOCKS5 inbound, so Xray's native stats/sniffing/routing work for free instead of through hand-rolled bridges. No kernel module, no DKMS, no Secure Boot conflicts, works the same in a container as on bare metal. - internal/amneziawgnet: new package (Device/UAPI, gVisor netstack, TCP/UDP forwarding, SOCKS5 relay, peer identity, IPv6 host-alias egress identity, per-client port-forwarding) - amneziawg-go v3.1.20260814 + gvisor. - internal/amneziawg: keep the reusable protocol-shape types/validation (Instance/Peer/Obfuscation, InstanceFromInbound); drop the OS-shellout half (awg-quick, TPROXY policy routing, NDP proxy, peer-stats parsing). - internal/web/service: rewire the 5 integration points (job, runtime, client-apply, web shutdown, xray config) from the old manager to the new one; the AmneziaWG log view is rebuilt on the embedded Device's own UAPI dump (extended to carry endpoint/AllowedIPs) instead of `awg show dump`. - install.sh: drop DKMS/ndppd/TPROXY/Secure-Boot installer code (~250 lines) - an entire recurring class of installer fragility goes away. - frontend: drop the now-meaningless routeThroughXray toggle (the relay is always on); keep the field in the Zod schema, unexposed, so it isn't silently stripped from stored settings on next save - two regression tests deliberately depend on the Go struct still carrying it. - docs/i18n: rewrite amneziawg.mdx for the new architecture; drop the dead routeThroughXray translation keys across all 13 locales. Real production throughput (embedded core datapath, isolated bench, same box the kernel-module path was measured on): ~296 Mbit/s up, ~640 Mbit/s down, vs. 414.69 MB/s (~3.3 Gbit/s) for the kernel module on the same hardware - a real gap, tempered by this being single-stream/no-SOCKS5-hop and most VPN traffic being latency-bound rather than throughput-saturating. * fix(amneziawg): restore the branch's own Obfuscation31 shape + 2 CodeQL findings The previous push's wholesale-copy of types.go/params.go from the fork's main branch pulled in that branch's own independent (and incompatible) naming for the same AWG 3.1 feature set: Obfuscation20/GenerateObfuscation20 instead of this branch's already-shipped Obfuscation31/GenerateObfuscation31, and a missing CanonicalizeUintRange -- broke every Go CI job (the whole matrix fails to compile when any one package doesn't, which is why govulncheck/ golangci/postgres-durable-first/race all failed identically, not just go-test). Restores params.go/params_test.go verbatim from this branch's own last commit (a strict superset of validation: it already cross-checks rekey vs. reject timing windows, which the copied version never did) and folds the 3.0/3.1 fields (HeaderProtectionKey, ContentPaddingAddition, the 5 timing fields, RandomTrailers/DisableCookies) into Obfuscation31 itself, matching the original struct exactly instead of as separate top-level Instance fields. instance.go, the two amneziawgnet call sites, and 7 amneziawgnet test files updated to match. Also drops the one test (sanitizeConfigValue) that only ever served the retired kernel-module .conf writer -- correctly not ported, so the test testing it shouldn't have been copied either. Also fixes 2 CodeQL findings the same push surfaced: a clamped uint64->int64 conversion for the new log view's live byte counters (server.go), and an unneeded len+len sum feeding a slice pre-size in the v6-egress outbound merge (xray.go) -- append already grows correctly without it. * chore(amneziawg): regenerate frontend schemas for updated doc comments npm run gen was missed after the previous commit's types.go doc-comment edits (Obfuscation20 -> Obfuscation31, ValidateHeaderProtection -> ValidateObfuscation in the prose) -- openapigen bakes those comments into the generated schema's description field, so the committed frontend/src/generated/schemas.ts and openapi.json still had the old wording. codegen's git-diff-exit-code check caught it correctly. * fix(amneziawg): narrow 2 test fixtures that collided with MaxForwardedPorts TestCheckForwardedPortsConflict_CollidesWithEnabledInboundPort and ..._NoCollisionWhenPortsDontOverlap used "8000-8100"/"9000-9100" as their ForwardedPorts fixture -- 101 ports each, one over MaxForwardedPorts (100). The cap check (checkForwardedPortsConflict, added this session alongside the SOCKS-phantom-port check) fires first, so both tests got "more than 100 forwarded ports" instead of ever reaching the collision logic they're actually testing. The cap itself has its own dedicated boundary test already; these two just needed a narrower range that still covers/misses port 8080 as intended -- 8075-8085 and 9075-9085, 11 ports each. * fix(amneziawg): checkAmneziawgnetSocksConflict had no receiver in its new home My merge-conflict resolution kept this as a method call (s.checkAmneziawgnetSocksConflict) inside checkPortConflictTx, a plain function with no *InboundService receiver -- upstream's #6225 fix moved the port-conflict check out of the (s *InboundService) method and into this new tx-scoped free function, and I didn't notice the call site needed to change shape too. CI caught it immediately (undefined: s); nothing in this specific package can be locally verified past internal/database's own unrelated, pre-existing CGO build issue on this dev machine. Since the signature had to change either way, folded in the fix already flagged as a separate follow-up: checkAmneziawgnetSocksConflict now takes the caller's db handle instead of fetching its own via database.GetDB(), so it actually runs inside the same serialized transaction #6225 introduced -- previously it sat right next to that race fix without benefiting from it. * fix: address the review findings on the embedded AmneziaWG PR 5 blocking findings: - Floor S3/S4 at 12 in both obfuscation generators (Go and frontend) and reject a hand-edited value below that when HeaderProtectionKey is set -- IpcSet requires it, and ~39% of previously-generated sets violated it silently. - Guard PrivateKey/PrimaryDNS/SecondaryDNS/remark against newline injection in the AmneziaWG .conf builder (both the Go subscription-link path and the frontend downloadable-config path) -- unguarded, any of them could inject an arbitrary config line into a subscriber's client. - Bound the derived AmneziaWG SOCKS relay port to <= 65535 once an inbound's id is known, and check the reverse direction (does the relay port collide with an existing inbound's port) on both create and update -- previously only port -> relay collisions were checked, not relay -> port. - Gate injectAmneziawgV6Egress on the same V6AliasesActive predicate desiredV6Aliases already uses, so the two can't disagree about whether a peer's IPv6 identity is actually active at the OS level. 2 minor findings: - Fix the forwarded-ports cap check's off-by-one (a spec covering exactly the cap was rejected as if it were over it). - Correct docker-compose.yml's stale comment describing the retired DKMS/kernel-module architecture. * chore: retrigger CI build (armv5) failed on a transient Go module proxy network error (INTERNAL_ERROR stream reset on sagernet/sing), unrelated to this PR's changes. * docs: fix doc comments still describing the retired DKMS/awg-quick design A few doc comments (and one illustrative test log line) survived the embedded-architecture cutover unchanged and now contradict the code they sit next to: - internal/amneziawg/types.go's package comment claimed this package still owns a Manager that reconciles OS-level interfaces via awg-quick/DKMS -- that Manager was removed; the reconcile loop lives in internal/amneziawgnet now, and this package is protocol-shape-only. - internal/amneziawg/params.go's ValidateObfuscation/ValidateConfigValue comments cited "awg-quick up" / "awg-quick executes as root" as the reason to validate -- the server itself never calls awg-quick in this architecture; the same value still reaches a real rendered .conf that a client app or an admin's own awg-quick CLI applies downstream, so the validation is still warranted, just for a different consumer. Mirrored the same fix in inbound_amneziawg.go's matching comment and its test's comment. - internal/amneziawgnet/manager.go's Manager doc comments (x3) pointed readers at "internal/amneziawg.Manager" for comparison -- that type no longer exists in this diff at all. Repointed at internal/mtproto.Manager, the pattern this was actually modeled on and the one that's still real. - Swapped one test's illustrative "awg-quick up awg2 failed" log line for a message shaped like this architecture's actual amneziawgnet logging, so a reader skimming the test doesn't wonder whether the server still shells out to awg-quick. No behavior change. * fix(docs): re-run codegen for xray-settings.mdx after conflict merge The automated conflict-resolution hand-merge for this generated file was content-correct but didn't byte-match a real regen (different YAML long-string folding style). Re-ran npm run gen + docs' gen:api and kept that canonical output instead. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * fix(amneziawg): drop the dead access-log email backfill amneziawgEmailIndex keyed peers by "<tag>|<tunnel IP>", a scheme built for the retired TPROXY bridge where the peer's decapsulated tunnel address survived as the access log's from-address. The embedded architecture relays through a loopback SOCKS5 dial, so every AmneziaWG log line's from-address is 127.0.0.1:<ephemeral> and the lookup could never match: the index was rebuilt on every log view just to miss. Remove the index, its GetXrayLogs wiring and its test. If per-line emails are wanted back, the relay would have to publish a local-port->email registry for the viewer to resolve loopback sources. * fix(api): generate AmneziaWGLogs/PeerActivity schemas instead of hand-writing them The amneziawglogs endpoint's response structs were missing from openapigen's StructAllow, so they were silently absent from every generated schema/example, the endpoints.ts entry carried a hand-written response, and AmneziaWGLogModal.tsx duplicated the shapes as local interfaces - the exact drift the allowlist rule exists to prevent. Allowlist both structs with example tags, point the endpoint at the generated schema, import the generated types in the modal, and sync docs/public/openapi.json. * chore(amneziawg): drop the unreferenced quiccapture package Nothing imports internal/amneziawg/quiccapture and no route exposes it; its package doc justifies the code as a port of frontend/src/lib/xray/i1Generators.ts, which does not exist in this repository, and promises an API round-trip that also does not exist. 1,110 lines of unreachable code with misleading provenance claims. Revert this commit to bring the package back when the live-capture I1 feature and its frontend counterpart actually land. * fix(clients): re-run cross-inbound conflict checks on the serialized writer The new client-level checks - cross-inbound AllowedIPs collisions and AmneziaWG forwardedPorts conflicts - read a fresh DB snapshot, decide, and only then enter runSerializedTx, while lockInbound only serializes writers on the SAME inbound. Two concurrent client creates on two different tunnel inbounds both passed the read and both committed, yielding two peers with one address: the exact check-then-claim race81cfd857(#6225) closed for AddInbound, which this PR's own checkAmneziawgnetSocksReverseConflict already cites. Keep the pre-tx pass for fail-fast UX and re-validate inside the transaction, where the single writer makes the answer authoritative. The race test drives two goroutines at two inbounds and demands exactly one winner; it fails with committed=2 when the in-tx re-check is removed. * fix(amneziawg): hot-apply depletion disables like mtproto does applyTrafficMutationBatch special-cases MTProto so a quota/expiry depletion cuts the sidecar immediately, but AmneziaWG fell through to runtime AddUser/RemoveUser - explicit no-ops for this protocol - so a depleted peer kept tunneling until the next 10s reconcile tick. Route it through applyLocalAmneziaWG, whose own contract (re-read committed settings, filter depleted clients, push to the interface) is exactly this case; the comment claiming it mirrors applyLocalMtproto is now true for the depletion path too. * fix(amneziawg): persist cleared DNS fields instead of resurrecting defaults PrimaryDNS/SecondaryDNS marshaled with omitempty, so clearing them persisted settings with no key at all - and the frontend re-parses stored settings through a Zod schema whose .default('8.8.8.8') / .default('8.8.4.4') fire on missing keys, silently repopulating the form on every load and re-persisting the defaults on the next save. Blank is a documented, meaningful state (no DNS line in client configs); drop omitempty so a cleared value survives the round-trip. The regression test normalizes a server block with cleared DNS and fails when the keys are dropped. * fix(amneziawg): accept cleared numeric obfuscation/subnet fields in the form AntD InputNumber emits null when cleared, Zod .default() only replaces undefined, and unlike wireguard.ts - whose optionalClearedInt comment documents exactly this failure mode - the AmneziaWG schema declared subnetCidr and jc/jmin/jmax/s1-s4 as bare z.number() defaults. Clearing any of the eight fields made safeParse reject the null and block the save until the user retyped a value. Absorb null into undefined while keeping each field's schema default, so a cleared field refills its documented default and legacy blobs with absent keys behave as before. * fix(amneziawg): guard the third .conf emitter against newline injection The review-round fix added the newline guard to amneziaWGConfigText (Go) and buildAmneziaWGClientConfig, but genAmneziaWGConfig in inbound-link.ts - the third of the three emitters its own comment says must not drift - still rendered privateKey/primaryDns/secondaryDns/remark unescaped, so a newline there injected a config line (e.g. a rogue PostUp) into the inbound form's downloaded .conf. Add the same guard, plus the regression tests the original fix shipped without: all four fields on the Go and both frontend emitters go red if any guard is removed. * test(amneziawg): pin the S3/S4 floors the TS drift guard claims to mirror The test's docstring says it mirrors internal/amneziawg/params_test.go, but it asserted S3>=8/S4>=4 while the Go test and both generators pin 12/12 - the floor ValidateObfuscation enforces whenever a header protection key is set, which this generator always sets. A regression narrowing the TS floors into 8-11/4-11 would have passed the drift guard and produced configs the backend rejects on save. * docs: restore the pia repo-map entry and document the AmneziaWG subsystem Merging main dropped CLAUDE.md's internal/pia/ bullet (added by #6272) while resolving the repo-map conflict - the package itself is untouched. Restore it, add the missing map entries for the two packages this branch introduces (internal/amneziawg/, internal/amneziawgnet/), bump the cron count, and give amneziawg_job its row in architecture.md's 5.4 table. * chore(amneziawg): correct comments stranded by the architecture pivot ae77c7e9's cutover to the embedded gVisor path deleted the kernel-module code but left several comments describing it in the present tense: hot_diff.go cited the removed service.amneziawgEgressStreamSettings and wrongly claimed AmneziaWG is the only sockopt.tproxy source (tunnel's TProxy mode is the live one the guard protects), socks_config.go pointed at the deleted EgressBasePort/EgressPortForInbound, manager.go referred to the deleted Manager and its fingerprinting as live code, web.go's cron registration claimed the job scrapes traffic (its own doc says it does not), and types.go capped ContentPaddingAddition at uint16 when validation and upstream both use uint32. * style(lint): satisfy gofumpt/goimports so make verify is green json_service.go's two 'Tag: "proxy"}' literals came in with main's owncc245a90formatting commit and fail the repo's gofumpt gate for everyone; the import grouping in inbound_amneziawg.go is from the serialized-writer fix on this branch. --------- Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com> Co-authored-by: Sanaei <ho3ein.sanaei@gmail.com> Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com>
2253 lines
120 KiB
JSON
2253 lines
120 KiB
JSON
{
|
||
"username": "使用者名稱",
|
||
"password": "密碼",
|
||
"login": "登入",
|
||
"confirm": "確定",
|
||
"cancel": "取消",
|
||
"close": "關閉",
|
||
"save": "儲存",
|
||
"logout": "登出",
|
||
"create": "建立",
|
||
"add": "新增",
|
||
"remove": "移除",
|
||
"update": "更新",
|
||
"copy": "複製",
|
||
"copied": "已複製",
|
||
"more": "更多",
|
||
"download": "下載",
|
||
"regenerate": "重新產生",
|
||
"jsonEditor": "JSON 編輯器",
|
||
"downloadImage": "下載圖片",
|
||
"sort": "排序",
|
||
"remark": "備註",
|
||
"enable": "啟用",
|
||
"protocol": "協議",
|
||
"search": "搜尋",
|
||
"filter": "篩選",
|
||
"all": "全部",
|
||
"from": "從",
|
||
"to": "到",
|
||
"done": "完成",
|
||
"loading": "載入中...",
|
||
"refresh": "重新整理",
|
||
"clear": "清除",
|
||
"second": "秒",
|
||
"minute": "分鐘",
|
||
"hour": "小時",
|
||
"day": "天",
|
||
"check": "檢視",
|
||
"indefinite": "無限期",
|
||
"unlimited": "無限制",
|
||
"none": "無",
|
||
"qrCode": "二維碼",
|
||
"info": "更多資訊",
|
||
"edit": "編輯",
|
||
"delete": "刪除",
|
||
"reset": "重置",
|
||
"noData": "無數據。",
|
||
"copySuccess": "複製成功",
|
||
"sure": "確定",
|
||
"encryption": "加密",
|
||
"transmission": "傳輸",
|
||
"host": "主機",
|
||
"path": "路徑",
|
||
"camouflage": "混淆",
|
||
"status": "狀態",
|
||
"enabled": "開啟",
|
||
"disabled": "關閉",
|
||
"depleted": "耗盡",
|
||
"depletingSoon": "即將耗盡",
|
||
"offline": "離線",
|
||
"online": "上線",
|
||
"domainName": "域名",
|
||
"monitor": "監聽",
|
||
"certificate": "憑證",
|
||
"fail": "失敗",
|
||
"comment": "評論",
|
||
"success": "成功",
|
||
"lastOnline": "上次上線",
|
||
"lastSubFetch": "上次取得訂閱",
|
||
"getVersion": "獲取版本",
|
||
"install": "安裝",
|
||
"clients": "客戶端",
|
||
"usage": "使用情況",
|
||
"twoFactorCode": "代碼",
|
||
"remained": "剩餘",
|
||
"security": "安全",
|
||
"emptyDnsDesc": "未添加 DNS 伺服器。",
|
||
"emptyFakeDnsDesc": "未添加 Fake DNS 伺服器。",
|
||
"emptyBalancersDesc": "未添加負載平衡器。",
|
||
"somethingWentWrong": "發生錯誤",
|
||
"subscription": {
|
||
"title": "訂閱資訊",
|
||
"subId": "訂閱 ID",
|
||
"status": "狀態",
|
||
"downloaded": "已下載",
|
||
"uploaded": "已上傳",
|
||
"expiry": "到期",
|
||
"totalQuota": "總配額",
|
||
"individualLinks": "個別連結",
|
||
"active": "啟用",
|
||
"inactive": "停用",
|
||
"unlimited": "無限制",
|
||
"noExpiry": "無到期",
|
||
"copyAllConfigs": "複製全部配置",
|
||
"copyAllConfigsCopied": "已複製全部配置",
|
||
"email": "電子郵件"
|
||
},
|
||
"menu": {
|
||
"theme": "主題",
|
||
"dashboard": "系統狀態",
|
||
"inbounds": "入站",
|
||
"clients": "客戶端",
|
||
"groups": "群組",
|
||
"nodes": "節點",
|
||
"settings": "面板設定",
|
||
"xray": "Xray 設定",
|
||
"routing": "路由",
|
||
"outbounds": "出站",
|
||
"apiDocs": "API 文件",
|
||
"donate": "捐贈",
|
||
"hosts": "主機",
|
||
"docs": "文件",
|
||
"openMenu": "開啟選單",
|
||
"pinSidebar": "固定側邊欄",
|
||
"unpinSidebar": "取消固定側邊欄",
|
||
"subFormats": "Sub Formats"
|
||
},
|
||
"pages": {
|
||
"login": {
|
||
"hello": "你好",
|
||
"title": "歡迎",
|
||
"loginAgain": "登入時效已過,請重新登入",
|
||
"toasts": {
|
||
"invalidFormData": "資料格式錯誤",
|
||
"emptyUsername": "請輸入使用者名稱",
|
||
"emptyPassword": "請輸入密碼",
|
||
"wrongUsernameOrPassword": "用戶名、密碼或雙重驗證碼無效。",
|
||
"successLogin": "您已成功登入您的帳戶。"
|
||
}
|
||
},
|
||
"index": {
|
||
"cpu": "CPU",
|
||
"swap": "交換空間",
|
||
"storage": "儲存",
|
||
"memory": "記憶體",
|
||
"xrayStatus": "Xray",
|
||
"stopXray": "停止",
|
||
"restartXray": "重新啟動",
|
||
"xraySwitch": "版本",
|
||
"xrayUpdates": "Xray 更新",
|
||
"xraySwitchClickDesk": "請謹慎選擇,因為較舊版本可能與當前配置不相容",
|
||
"updatePanel": "更新面板",
|
||
"panelUpdateDesc": "這將把 3X-UI 更新到最新版本並重新啟動面板服務。",
|
||
"currentPanelVersion": "目前面板版本",
|
||
"latestPanelVersion": "最新面板版本",
|
||
"panelUpToDate": "面板已是最新",
|
||
"devChannel": "開發通道",
|
||
"devChannelWarning": "開發版會追蹤 main 的每次提交,並非穩定版本,且無法自動降級。",
|
||
"currentCommit": "目前提交",
|
||
"latestCommit": "最新提交",
|
||
"updateChannelChanged": "更新通道已切換",
|
||
"xrayStatusUnknown": "未知",
|
||
"xrayStatusRunning": "運行中",
|
||
"xrayStatusStop": "停止",
|
||
"xrayStatusError": "錯誤",
|
||
"systemHistoryTitle": "系統歷史",
|
||
"historyTitleCpu": "CPU 使用率",
|
||
"historyTitleMem": "記憶體使用率",
|
||
"historyTitleNetwork": "網路頻寬",
|
||
"historyTitlePackets": "網路封包",
|
||
"historyTitleDisk": "磁碟 I/O",
|
||
"historyTitleOnline": "線上用戶端",
|
||
"historyTitleLoad": "系統平均負載(1 分鐘 / 5 分鐘 / 15 分鐘)",
|
||
"historyTitleConnections": "使用中的連線 (TCP / UDP)",
|
||
"historyTitleDiskUsage": "磁碟空間使用率",
|
||
"historyTabBandwidth": "頻寬",
|
||
"historyTabPackets": "封包",
|
||
"historyTabDisk": "磁碟 I/O",
|
||
"historyTabOnline": "線上",
|
||
"historyTabLoad": "負載",
|
||
"historyTabConnections": "連線數",
|
||
"historyTabDiskUsage": "磁碟使用量",
|
||
"xrayMetricsTitle": "Xray 指標",
|
||
"xrayTitleHeap": "已配置的堆積記憶體",
|
||
"xrayTitleSys": "向作業系統保留的記憶體",
|
||
"xrayTitleObjects": "存活的堆積物件",
|
||
"xrayTitleGcCount": "已完成的 GC 週期",
|
||
"xrayTitleGcPause": "GC 暫停時間",
|
||
"xrayTitleObservatory": "出站連線健康狀態",
|
||
"xrayTabHeap": "堆積",
|
||
"xrayTabSys": "系統",
|
||
"xrayTabObjects": "物件",
|
||
"xrayTabGcCount": "GC 次數",
|
||
"xrayTabGcPause": "GC 暫停",
|
||
"xrayTabObservatory": "觀測站",
|
||
"xrayMetricsDisabled": "未設定 Xray 指標端點",
|
||
"xrayMetricsHint": "在 xray 設定中加入頂層 metrics 區塊,tag 為 metrics_out,listen 為 127.0.0.1:11111,然後重啟 xray。",
|
||
"xrayObservatoryEmpty": "尚無 Observatory 資料",
|
||
"xrayObservatoryHint": "在 xray 設定中加入 observatory 區塊,列出要探測的出站 tag,然後重啟 xray。",
|
||
"xrayObservatoryTagPlaceholder": "選擇出站",
|
||
"xrayObservatoryAlive": "在線",
|
||
"xrayObservatoryDead": "離線",
|
||
"xrayObservatoryLastSeen": "最後在線",
|
||
"xrayObservatoryLastTry": "最後嘗試",
|
||
"connectionCount": "連線數",
|
||
"ipAddresses": "IP 地址",
|
||
"toggleIpVisibility": "切換 IP 可見性",
|
||
"overallSpeed": "整體速度",
|
||
"upload": "上傳",
|
||
"download": "下載",
|
||
"sent": "已發送",
|
||
"received": "已接收",
|
||
"xraySwitchVersionDialog": "您確定要變更 Xray 版本嗎?",
|
||
"xraySwitchVersionDialogDesc": "這將會把 Xray 版本變更為 #version#。",
|
||
"xraySwitchVersionPopover": "Xray 更新成功",
|
||
"panelUpdateDialog": "您確定要更新面板嗎?",
|
||
"panelUpdateDialogDesc": "這將把 3X-UI 更新到 #version# 並重新啟動面板服務。",
|
||
"panelUpdateStartedPopover": "面板更新已開始",
|
||
"panelUpdateFailedTitle": "面板更新失敗",
|
||
"panelUpdateFailedDesc": "更新未成功完成。請檢查伺服器日誌,或在命令列執行「x-ui update」。",
|
||
"panelUpdateUnknownTitle": "無法確認更新是否已完成",
|
||
"panelUpdateUnknownDesc": "面板未能及時回報結果。請重新整理以檢查目前版本,或檢查伺服器日誌。",
|
||
"geofileUpdateDialog": "您確定要更新地理檔案嗎?",
|
||
"geofileUpdateDialogDesc": "這將更新 #filename# 檔案。",
|
||
"geofilesUpdateDialogDesc": "這將更新所有文件。",
|
||
"geofilesUpdateAll": "全部更新",
|
||
"geofileUpdatePopover": "地理檔案更新成功",
|
||
"geodataTitle": "Geodata 自動更新",
|
||
"geodataHint": "Xray 會按排程下載這些檔案並熱重載,無需重啟。URL 必須為 HTTPS。檔案必須已存在於 bin 目錄中,Xray 才能對其更新。",
|
||
"geodataCron": "更新排程 (cron)",
|
||
"geodataOutbound": "透過出站下載(可選)",
|
||
"geodataFile": "檔案名稱",
|
||
"geodataAddFile": "新增檔案",
|
||
"geodataSaveRestart": "儲存並重啟 Xray",
|
||
"geodataConfirmTitle": "儲存 geodata 設定?",
|
||
"geodataConfirmContent": "將更新 Xray 設定範本並重啟 Xray。",
|
||
"geodataInvalidUrl": "每個檔案都需要 HTTPS 位址。",
|
||
"geodataInvalidFile": "檔案名稱必須是純檔名,例如 geosite_custom.dat(不能包含路徑)。",
|
||
"geodataInvalidCron": "Cron 必須為 5 段,例如 0 4 * * *",
|
||
"geodataEmpty": "尚未設定檔案。路由規則中可透過 ext:geosite_custom.dat:category 引用檔案。",
|
||
"dontRefresh": "安裝中,請勿重新整理此頁面",
|
||
"logs": "記錄",
|
||
"accessLogs": "存取記錄",
|
||
"autoUpdate": "自動更新",
|
||
"amneziawgLogs": "AmneziaWG 日誌",
|
||
"amneziawgHandshake": "最後握手",
|
||
"amneziawgInterface": "網路介面",
|
||
"amneziawgInbound": "入站",
|
||
"amneziawgEndpoint": "端點",
|
||
"amneziawgIdle": "閒置",
|
||
"amneziawgEvents": "事件",
|
||
"amneziawgNoPeers": "目前沒有活躍的 AmneziaWG 對端",
|
||
"amneziawgNoEvents": "尚未記錄 AmneziaWG 事件",
|
||
"config": "配置",
|
||
"backupTitle": "備份和恢復",
|
||
"exportDatabase": "備份",
|
||
"exportDatabaseDesc": "點擊下載包含當前資料庫備份的 .db 文件到您的設備。同一檔案也可以還原到執行 PostgreSQL 的面板中。",
|
||
"importDatabase": "恢復",
|
||
"importDatabaseDesc": "點擊選擇並上傳設備中的 .db 備份或遷移匯出檔(.dump)以還原資料庫。",
|
||
"importDatabaseSuccess": "資料庫匯入成功",
|
||
"importDatabaseError": "匯入資料庫時發生錯誤",
|
||
"readDatabaseError": "讀取資料庫時發生錯誤",
|
||
"getDatabaseError": "檢索資料庫時發生錯誤",
|
||
"getConfigError": "檢索設定檔時發生錯誤",
|
||
"backupPostgresNote": "此面板執行於 PostgreSQL 上。「備份」會下載一個 pg_dump 封存檔(.dump),「還原」會透過 pg_restore 重新載入。「還原」也接受 SQLite 資料庫(.db)或 SQLite 遷移匯出檔,並將其資料匯入 PostgreSQL。伺服器需要安裝 PostgreSQL 用戶端工具(pg_dump 與 pg_restore)。",
|
||
"exportDatabasePgDesc": "點擊將目前資料庫的 PostgreSQL 傾印(.dump)下載到您的裝置。",
|
||
"importDatabasePgDesc": "點擊選擇並上傳 PostgreSQL 備份(.dump)、SQLite 資料庫(.db)或 SQLite 遷移匯出檔以還原您的資料庫。此操作將取代所有目前的資料。",
|
||
"migrationDownload": "下載遷移檔案",
|
||
"migrationDownloadPgDesc": "點擊下載由 PostgreSQL 資料建立的 .db SQLite 資料庫,可用於在 SQLite 上執行本面板。",
|
||
"avg": "均值",
|
||
"peak": "峰值",
|
||
"free": "剩餘",
|
||
"openSockets": "開啟的通訊端",
|
||
"throughputSub": "網路介面總計",
|
||
"avgWindow": "視窗均值",
|
||
"healthWarm": "{list} — 偏高",
|
||
"healthCritical": "{list} — 危險",
|
||
"panel": "面板",
|
||
"threads": "執行緒",
|
||
"uptime": "執行時間",
|
||
"logLevelDebug": "Debug",
|
||
"logLevelInfo": "Info",
|
||
"logLevelNotice": "Notice",
|
||
"logLevelWarning": "Warning",
|
||
"logLevelError": "Error",
|
||
"accessDirect": "DIRECT",
|
||
"accessBlocked": "BLOCKED",
|
||
"accessProxy": "PROXY",
|
||
"importKeepHostSettings": "保留本機設定",
|
||
"importKeepHostSettingsDesc": "保留本面板的監聽位址、連接埠、基礎路徑、憑證與節點身分,而不是使用上傳檔案中的值。"
|
||
},
|
||
"inbounds": {
|
||
"totalDownUp": "總上傳 / 下載",
|
||
"totalUsage": "總用量",
|
||
"inboundCount": "入站數量",
|
||
"operate": "選單",
|
||
"enable": "啟用",
|
||
"remark": "備註",
|
||
"node": "節點",
|
||
"deployTo": "部署到",
|
||
"localPanel": "本機面板",
|
||
"fallbacks": {
|
||
"title": "Fallbacks",
|
||
"empty": "尚未新增回落",
|
||
"add": "新增回落",
|
||
"pickInbound": "選擇一個入站",
|
||
"matchAny": "任何",
|
||
"destPlaceholder": "自動(子入站 listen:port)",
|
||
"needsTls": "在「安全」分頁選擇 TLS 或 Reality 後即可設定回落(僅限 RAW 上的 VLESS/Trojan)。"
|
||
},
|
||
"protocol": "協議",
|
||
"port": "連接埠",
|
||
"portMap": "連接埠對應",
|
||
"traffic": "流量",
|
||
"speed": "速度",
|
||
"expireDate": "到期時間",
|
||
"createdAt": "建立時間",
|
||
"updatedAt": "更新時間",
|
||
"resetTraffic": "重置流量",
|
||
"addInbound": "新增入站",
|
||
"generalActions": "通用操作",
|
||
"modifyInbound": "修改入站",
|
||
"deleteConfirmTitle": "刪除入站「{remark}」?",
|
||
"deleteConfirmContent": "將刪除此入站及其所有客戶端。此操作無法復原。",
|
||
"resetConfirmTitle": "重置「{remark}」的流量?",
|
||
"resetConfirmContent": "將此入站的上/下行計數器歸零。",
|
||
"selectedCount": "已選 {count} 項",
|
||
"selectAll": "全選",
|
||
"bulkDeleteConfirmTitle": "刪除 {count} 個入站?",
|
||
"bulkDeleteConfirmContent": "將刪除所選入站及其所有客戶端。此操作無法復原。",
|
||
"cloneConfirmTitle": "複製入站「{remark}」?",
|
||
"cloneConfirmContent": "使用新連接埠和空客戶端清單建立副本。",
|
||
"delAllClients": "刪除所有客戶端",
|
||
"delAllClientsConfirmTitle": "從「{remark}」中刪除全部 {count} 個客戶端?",
|
||
"delAllClientsConfirmContent": "從此入站中移除每個客戶端並捨棄其流量記錄。入站本身將保留。此操作無法復原。",
|
||
"attachClients": "附加客戶端到…",
|
||
"addClientsToGroup": "將客戶端加入群組…",
|
||
"attachClientsTitle": "從「{remark}」附加客戶端",
|
||
"attachClientsDesc": "將相同的 {count} 個客戶端(相同 UUID/密碼與共享流量)附加到選定入站。它們仍保留於此入站。",
|
||
"attachClientsTargets": "目標入站",
|
||
"attachClientsNoTargets": "沒有可附加的其他相容入站。",
|
||
"attachClientsResult": "已附加 {attached},已略過 {skipped}。",
|
||
"attachClientsResultMixed": "已附加 {attached},已略過 {skipped},錯誤 {errors}。",
|
||
"attachClientsSelectLabel": "要附加的客戶端",
|
||
"attachClientsSearchPlaceholder": "搜尋電子郵件或備註",
|
||
"attachClientsStatusDisabled": "已停用",
|
||
"attachClientsSelectedCount": "已選 {selected}/{total}",
|
||
"attachExistingClients": "附加現有客戶端…",
|
||
"attachExistingTitle": "將現有客戶端附加到「{remark}」",
|
||
"attachExistingDesc": "將現有客戶端(可用 {count} 個)附加到此入站 — 相同 UUID/密碼與共享流量。已在此入站的客戶端將被略過。",
|
||
"attachExistingNoClients": "尚無客戶端。請先建立客戶端,然後在此附加。",
|
||
"attachExistingStatusAttached": "已附加",
|
||
"detachClients": "分離客戶端",
|
||
"detachClientsTitle": "從「{remark}」分離客戶端",
|
||
"detachClientsDesc": "僅從此入站移除選取的客戶端。客戶端記錄保留(用 Delete 完全移除)。來源共有 {count} 個客戶端。",
|
||
"detachClientsResult": "已分離 {detached},已略過 {skipped}。",
|
||
"detachClientsResultMixed": "已分離 {detached},已略過 {skipped},錯誤 {errors}。",
|
||
"detachClientsSelectLabel": "要分離的客戶端",
|
||
"exportLinksTitle": "匯出入站連結",
|
||
"exportSubsTitle": "匯出訂閱連結",
|
||
"exportAllLinksTitle": "匯出所有入站連結",
|
||
"exportAllSubsTitle": "匯出所有訂閱連結",
|
||
"exportAllLinksFileName": "所有入站",
|
||
"exportAllSubsFileName": "所有入站-Subs",
|
||
"inboundJsonTitle": "入站 JSON",
|
||
"resetTrafficContent": "確定要重置流量嗎?",
|
||
"copyLink": "複製連結",
|
||
"address": "地址",
|
||
"network": "網路",
|
||
"destinationPort": "目標埠",
|
||
"targetAddress": "目標地址",
|
||
"monitorDesc": "留空表示監聽所有 IP",
|
||
"meansNoLimit": "= 無限制。(單位: GB)",
|
||
"totalFlow": "總流量",
|
||
"leaveBlankToNeverExpire": "留空表示永不過期",
|
||
"certificatePath": "檔案路徑",
|
||
"certificateContent": "檔案內容",
|
||
"publicKey": "公鑰",
|
||
"privatekey": "私鑰",
|
||
"client": "客戶",
|
||
"export": "匯出連結",
|
||
"clone": "複製",
|
||
"resetAllTraffic": "重置所有入站流量",
|
||
"resetAllTrafficTitle": "重置所有入站流量",
|
||
"resetAllTrafficContent": "確定要重置所有入站流量嗎?",
|
||
"email": "電子郵件",
|
||
"IPLimit": "IP 限制",
|
||
"IPLimitlog": "IP 日誌",
|
||
"IPLimitlogclear": "清除日誌",
|
||
"setDefaultCert": "從面板設定證書",
|
||
"setDefaultCertEmpty": "面板尚未設定憑證。請先在「設定」中設定。",
|
||
"streamTab": "傳輸",
|
||
"securityTab": "安全",
|
||
"sniffingTab": "嗅探",
|
||
"sniffingMetadataOnly": "僅中繼資料",
|
||
"sniffingRouteOnly": "僅路由",
|
||
"sniffingIpsExcluded": "排除的 IP",
|
||
"sniffingDomainsExcluded": "排除的網域",
|
||
"decryption": "解密",
|
||
"encryption": "加密",
|
||
"vlessAuthX25519": "X25519 (native)",
|
||
"vlessAuthMlkem768": "ML-KEM-768 (native)",
|
||
"vlessAuthX25519Xorpub": "X25519 (xorpub)",
|
||
"vlessAuthX25519Random": "X25519 (random)",
|
||
"vlessAuthMlkem768Xorpub": "ML-KEM-768 (xorpub)",
|
||
"vlessAuthMlkem768Random": "ML-KEM-768 (random)",
|
||
"vlessAuthCustom": "自訂",
|
||
"vlessAuthSelected": "已選擇:{auth}",
|
||
"vlessAuthGenerate": "生成密鑰",
|
||
"vlessAuthGenerateButton": "生成",
|
||
"advanced": {
|
||
"title": "入站 JSON 部分",
|
||
"subtitle": "完整入站 JSON 以及針對 settings、sniffing 和 streamSettings 的專用編輯器。",
|
||
"all": "全部",
|
||
"allHelp": "在單一編輯器中編輯包含所有欄位的完整入站物件。",
|
||
"settings": "設定",
|
||
"settingsHelp": "Xray settings 區塊包裝:",
|
||
"sniffing": "Sniffing",
|
||
"sniffingHelp": "Xray sniffing 區塊包裝:",
|
||
"stream": "Stream",
|
||
"streamHelp": "Xray stream 區塊包裝:"
|
||
},
|
||
"subSortIndex": "訂閱排序",
|
||
"inboundInfo": "入站資訊",
|
||
"exportInbound": "匯出入站規則",
|
||
"import": "匯入",
|
||
"importInbound": "匯入入站規則",
|
||
"periodicTrafficResetTitle": "流量重置",
|
||
"periodicTrafficResetDay": "每月重置日",
|
||
"periodicTrafficReset": {
|
||
"never": "從不",
|
||
"daily": "每日",
|
||
"weekly": "每週",
|
||
"monthly": "每月",
|
||
"hourly": "每小時"
|
||
},
|
||
"toasts": {
|
||
"obtain": "獲取",
|
||
"updateSuccess": "更新成功",
|
||
"logCleanSuccess": "日誌已清除",
|
||
"inboundUpdateSuccess": "入站連接已成功更新",
|
||
"inboundCreateSuccess": "入站連接已成功建立",
|
||
"bulkDeleted": "已刪除 {count} 個入站",
|
||
"bulkDeletedMixed": "已刪除 {ok} 個,失敗 {failed} 個",
|
||
"clonedMany": "已複製 {count} 個入站",
|
||
"clonedMixed": "已複製 {ok} 個,失敗 {failed} 個",
|
||
"inboundDeleteSuccess": "入站連接已成功刪除",
|
||
"inboundClientAddSuccess": "已新增入站客戶端",
|
||
"inboundClientDeleteSuccess": "入站客戶端已刪除",
|
||
"inboundClientUpdateSuccess": "入站客戶端已更新",
|
||
"savedNodeOfflineWillSync": "已在本機儲存。某個支撐節點離線或已停用——重新連線後將同步此變更。",
|
||
"resetAllClientTrafficSuccess": "客戶端所有流量已重置",
|
||
"resetAllTrafficSuccess": "所有流量已重置",
|
||
"resetInboundClientTrafficSuccess": "流量已重置",
|
||
"resetInboundTrafficSuccess": "入站流量已重置",
|
||
"trafficGetError": "取得流量資料時發生錯誤",
|
||
"getNewX25519CertError": "取得 X25519 憑證時發生錯誤。",
|
||
"getNewmldsa65Error": "取得 mldsa65 憑證時發生錯誤。",
|
||
"getNewVlessEncError": "取得 VlessEnc 憑證時發生錯誤。",
|
||
"scanRealityTargetError": "掃描 REALITY 目標失敗。",
|
||
"scanRealityTargetFeasible": "目標可用 — 已填入目標與 SNI。",
|
||
"scanRealityTargetNotFeasible": "目標可達,但不適用於 REALITY。",
|
||
"scanRealityTargetPrivate": "目標可用,但位於內網/本機網路中。",
|
||
"invalidClientField": "用戶端 {client}:欄位 {field} — {reason}",
|
||
"invalidField": "{field} — {reason}",
|
||
"moreIssues": "{message} (另有 {count} 項)"
|
||
},
|
||
"form": {
|
||
"moveUp": "上移",
|
||
"moveDown": "下移",
|
||
"addAll": "全部新增",
|
||
"addAllFallbackTooltip": "為每個尚未連線的符合條件入站新增一個 fallback 列",
|
||
"peers": "Peers",
|
||
"addPeer": "新增 peer",
|
||
"keepAlive": "Keep-alive",
|
||
"autoSystemRoutesTooltip": "僅 Windows。CIDR 會自動加入系統路由表,使匹配的流量通過 TUN。",
|
||
"autoOutboundsInterface": "自動出站介面",
|
||
"autoOutboundsInterfaceTooltip": "出站流量的實體介面。使用 'auto' 進行偵測;設定 Auto system routes 時自動啟用。",
|
||
"rewriteAddress": "改寫地址",
|
||
"rewritePort": "改寫連接埠",
|
||
"allowedNetwork": "允許的網路",
|
||
"followRedirect": "跟隨重新導向",
|
||
"accounts": "帳號",
|
||
"allowTransparent": "允許透明",
|
||
"encryptionMethod": "加密方法",
|
||
"fakeTlsDomain": "FakeTLS 網域 (SNI)",
|
||
"mtprotoSecret": "金鑰",
|
||
"mtgDomainFrontingIp": "網域前置 IP",
|
||
"mtgDomainFrontingPort": "網域前置連接埠",
|
||
"mtgDomainFrontingProxyProtocol": "網域前置 PROXY 協定",
|
||
"mtgDomainFrontingHint": "mtg 轉發非 Telegram 流量的目標——例如你的 NGINX 偽裝站台。IP 留空則透過 DNS 解析 FakeTLS 網域;預設連接埠為 443。",
|
||
"mtgProxyProtocolListener": "接受 PROXY 協定(監聽器)",
|
||
"mtgPreferIp": "IP 偏好",
|
||
"mtgDebug": "除錯日誌",
|
||
"mtgRouteThroughXray": "透過 Xray 路由",
|
||
"mtgRouteThroughXrayHint": "讓此代理的 Telegram 流量經過 Xray,以套用您的路由規則。mtg 附屬程序會透過帶有此入站標籤的本機 SOCKS 橋接出站;在路由分頁中引用該標籤即可設定進階規則。",
|
||
"mtgRouteOutbound": "出站",
|
||
"mtgRouteOutboundHint": "選填。強制 Telegram 流量經由此出站(或負載平衡器)送出。留空則由您的路由規則決定。",
|
||
"mtgRouteOutboundPlaceholder": "使用路由規則",
|
||
"mtprotoFakeTlsDomainHint": "產生新用戶端金鑰時使用的預設 FakeTLS 網域。每個用戶端可使用各自的網域。",
|
||
"mtgThrottleMaxConnections": "最大連線數",
|
||
"mtgThrottleMaxConnectionsHint": "以公平分配限制所有使用者的並行連線數。0 表示不限制。",
|
||
"mtgAdTagInvalid": "廣告標籤必須為 32 位十六進位字元。",
|
||
"mtgPublicIpv4": "公網 IPv4",
|
||
"mtgPublicIpv6": "公網 IPv6",
|
||
"mtgPublicIpHint": "本伺服器可存取的公網位址,供廣告標籤的中間代理使用。留空則由 mtg 自動偵測。",
|
||
"visionTestseed": "Vision testseed",
|
||
"version": "版本",
|
||
"udpIdleTimeout": "UDP 閒置逾時 (s)",
|
||
"masquerade": "偽裝",
|
||
"type": "類型",
|
||
"upstreamUrl": "Upstream URL",
|
||
"rewriteHost": "改寫 Host",
|
||
"skipTlsVerify": "略過 TLS 驗證",
|
||
"directory": "目錄",
|
||
"statusCode": "狀態碼",
|
||
"body": "Body",
|
||
"headers": "標頭",
|
||
"proxyProtocol": "Proxy Protocol",
|
||
"requestVersion": "請求版本",
|
||
"requestMethod": "請求方法",
|
||
"requestPath": "請求路徑",
|
||
"requestHeaders": "請求標頭",
|
||
"responseVersion": "回應版本",
|
||
"responseStatus": "回應狀態",
|
||
"responseReason": "回應原因",
|
||
"responseHeaders": "回應標頭",
|
||
"heartbeatPeriod": "心跳週期",
|
||
"serviceName": "服務名稱",
|
||
"authority": "Authority",
|
||
"multiMode": "多模式",
|
||
"maxBufferedUpload": "最大緩衝上傳",
|
||
"maxUploadSize": "最大上傳大小 (位元組)",
|
||
"streamUpServer": "Stream-Up 伺服器",
|
||
"serverMaxHeaderBytes": "伺服器最大標頭位元組",
|
||
"paddingBytes": "Padding 位元組",
|
||
"uplinkHttpMethod": "Uplink HTTP 方法",
|
||
"paddingObfsMode": "Padding 混淆模式",
|
||
"paddingKey": "Padding Key",
|
||
"paddingHeader": "Padding Header",
|
||
"paddingPlacement": "Padding 位置",
|
||
"paddingMethod": "Padding 方法",
|
||
"sessionPlacement": "Session 位置",
|
||
"sessionKey": "Session Key",
|
||
"sessionIDTable": "工作階段 ID 字元表",
|
||
"sessionIDTableHint": "產生工作階段 ID 使用的字元集:預定義名稱(ALPHABET、Base62、hex、number 等)或字面 ASCII 字串。留空則使用 xray-core 預設值。",
|
||
"sessionIDLength": "工作階段 ID 長度",
|
||
"sessionIDLengthHint": "產生工作階段 ID 的長度或範圍(如 8-16)。僅在設定了工作階段 ID 字元表時生效;最小值必須大於 0。",
|
||
"sequencePlacement": "Sequence 位置",
|
||
"sequenceKey": "Sequence Key",
|
||
"uplinkDataPlacement": "Uplink 資料位置",
|
||
"uplinkDataKey": "Uplink 資料 Key",
|
||
"noSseHeader": "無 SSE 標頭",
|
||
"ttiMs": "TTI (ms)",
|
||
"uplinkMbps": "上行 (MB/s)",
|
||
"downlinkMbps": "下行 (MB/s)",
|
||
"cwndMultiplier": "CWND 倍數",
|
||
"maxSendingWindow": "最大發送視窗",
|
||
"externalProxy": "外部代理",
|
||
"forceTls": "強制 TLS",
|
||
"fingerprint": "指紋",
|
||
"defaultOption": "預設",
|
||
"routeMark": "Route Mark",
|
||
"tcpKeepAliveInterval": "TCP Keep Alive 間隔",
|
||
"tcpKeepAliveIdle": "TCP Keep Alive Idle",
|
||
"tcpMaxSeg": "TCP Max Seg",
|
||
"tcpUserTimeout": "TCP User Timeout",
|
||
"tcpWindowClamp": "TCP Window Clamp",
|
||
"tcpWindowClampHint": "留 0 使用作業系統預設值。非零值會限制通告的 TCP 接收視窗;像 600 這樣的值(來自 Xray 文件範例)在高延遲連結上可能導致吞吐量驟降。",
|
||
"tcpFastOpen": "TCP Fast Open",
|
||
"multipathTcp": "Multipath TCP",
|
||
"penetrate": "Penetrate",
|
||
"v6Only": "僅 V6",
|
||
"tcpCongestion": "TCP Congestion",
|
||
"dialerProxy": "Dialer Proxy",
|
||
"trustedXForwardedFor": "信任的 X-Forwarded-For",
|
||
"trustedXForwardedForHint": "信任此請求標頭以取得真實用戶端 IP(例如 Cloudflare CDN 後的 CF-Connecting-IP)。僅在 WebSocket、HTTPUpgrade、XHTTP 和 gRPC 傳輸上生效。留空則忽略轉發標頭。",
|
||
"proxyProtocolHint": "接受 PROXY protocol 標頭,從上游 L4 隧道或中繼(HAProxy、gost、nginx-stream、Xray dokodemo-door)或 Cloudflare Spectrum 取得真實用戶端 IP。上游必須傳送 PROXY protocol。適用於 TCP、WebSocket、HTTPUpgrade 和 gRPC;不適用於 mKCP。",
|
||
"realClientIp": "真實用戶端 IP",
|
||
"realClientIpHint": "當流量透過 CDN 或中繼到達此入站時,取得訪客的真實 IP,而非記錄中間節點的位址。選擇一個預設以自動填入下方對應的 sockopt 欄位。這些欄位絕不會在訂閱中傳送給用戶端。",
|
||
"realClientIpPresetOff": "關閉 / 直連",
|
||
"realClientIpPresetCloudflare": "Cloudflare CDN",
|
||
"realClientIpPresetProxyProtocol": "L4 中繼 / Spectrum (PROXY)",
|
||
"realClientIpTrustedHeaderTransportWarn": "Trusted X-Forwarded-For 僅在 WebSocket、HTTPUpgrade 和 XHTTP 上生效。在目前的傳輸上此標頭將被忽略。",
|
||
"realClientIpProxyProtocolTransportWarn": "PROXY protocol 不支援此傳輸(mKCP)。請使用 TCP/RAW、WebSocket、HTTPUpgrade、gRPC 或 XHTTP。",
|
||
"addressPortStrategy": "地址+連接埠策略",
|
||
"tryDelayMs": "嘗試延遲 (ms)",
|
||
"prioritizeIPv6": "IPv6 優先",
|
||
"interleave": "Interleave",
|
||
"maxConcurrentTry": "最大並發嘗試",
|
||
"customSockopt": "自訂 sockopt",
|
||
"addCustomOption": "新增自訂選項",
|
||
"serverNameIndication": "SNI",
|
||
"cipherSuites": "Cipher Suites",
|
||
"autoOption": "自動",
|
||
"minMaxVersion": "最小/最大版本",
|
||
"rejectUnknownSni": "拒絕未知 SNI",
|
||
"disableSystemRoot": "停用系統根",
|
||
"sessionResumption": "工作階段恢復",
|
||
"oneTimeLoading": "一次性載入",
|
||
"usageOption": "使用選項",
|
||
"buildChain": "建立憑證鏈",
|
||
"echKey": "ECH key",
|
||
"echConfig": "ECH 設定",
|
||
"pinnedPeerCertSha256": "釘選對端憑證 SHA-256",
|
||
"pinnedPeerCertSha256Tip": "對端憑證的 SHA-256 雜湊(十六進位字串,如 e8e2d3…),以逗號分隔。僅面板使用 — 不寫入伺服器的 xray 設定,但會包含在分享連結中,以便用戶端釘選憑證。",
|
||
"pinnedPeerCertSha256Placeholder": "十六進位雜湊,以逗號分隔",
|
||
"getNewEchCert": "取得新 ECH 憑證",
|
||
"show": "顯示",
|
||
"xver": "Xver",
|
||
"target": "目標",
|
||
"maxTimeDiff": "最大時間差 (ms)",
|
||
"minClientVer": "最小客戶端版本",
|
||
"maxClientVer": "最大客戶端版本",
|
||
"minClientVerHint": "留空不等於不限制:Xray-core 會改用所執行核心版本的內建最低值(目前版本為 26.3.27),拒絕自報版本較低的客戶端——包括 Mihomo、sing-box 等第三方核心。填 1.0.0 可放行它們,代價是允許過時的 TLS 指紋。",
|
||
"maxClientVerHint": "留空表示無上限。若填寫,不得低於實際生效的下限——最小客戶端版本,該欄位留空時則為 Xray-core 的內建最低值——否則所有客戶端都會被拒絕。",
|
||
"clientVerInvalid": "客戶端版本須為最多三段以點分隔的數字,每段 0-255(例如 26.3.27)",
|
||
"maxClientVerBelowMin": "最大客戶端版本不得低於最小客戶端版本",
|
||
"shortIds": "Short IDs",
|
||
"realityTargetHint": "必填。必須包含連接埠(例如 example.com:443)。沒有連接埠時 Xray-core 將無法啟動。",
|
||
"realityTargetRequired": "REALITY 目標為必填項",
|
||
"realityTargetNeedsPort": "REALITY 目標必須包含連接埠(例如 example.com:443)",
|
||
"realityTargetInvalidPort": "REALITY 目標的連接埠無效",
|
||
"scan": "掃描",
|
||
"findTargets": "尋找目標",
|
||
"scanModalTitle": "REALITY 目標掃描器",
|
||
"scanModalDesc": "驗證某個網域,或掃描 IP / CIDR 範圍,從憑證中探索新的 REALITY 目標。留空則探測常用候選。",
|
||
"scanDiscoverPlaceholder": "IP、CIDR 或網域 — 留空使用常用候選",
|
||
"scanStatus": "狀態",
|
||
"scanFeasible": "可用",
|
||
"scanNotFeasible": "不可用",
|
||
"scanCurve": "金鑰交換",
|
||
"scanCert": "憑證",
|
||
"scanCertInvalid": "不受信任",
|
||
"scanCertExpiry": "憑證有效期限",
|
||
"scanSniUsed": "使用的 SNI",
|
||
"scanPrivateNote": "已透過內網/本機網路檢測 — 此位址無法從網際網路存取。",
|
||
"scanPrivateConfirmTitle": "目標位於本機網路",
|
||
"scanPrivateConfirmContent": "「{target}」解析到內網或回環位址。本次檢測將僅為此探測略過面板的 SSRF 防護。是否繼續?",
|
||
"scanLatency": "延遲",
|
||
"scanUse": "使用",
|
||
"scanRescan": "重新掃描",
|
||
"spiderX": "SpiderX",
|
||
"spiderXHint": "各客戶端的種子——面板據此為每個客戶端衍生唯一的 spx 路徑;重新產生可輪換所有客戶端的路徑",
|
||
"getNewCert": "取得新憑證",
|
||
"mldsa65Seed": "mldsa65 Seed",
|
||
"mldsa65Verify": "mldsa65 Verify",
|
||
"getNewSeed": "取得新 Seed",
|
||
"echSockopt": "ECH Sockopt",
|
||
"echSockoptTip": "Xray 用來取得 ECH 設定清單的連線之通訊端選項(例如讓查詢透過 dialerProxy 出站)。停用則使用預設值。",
|
||
"curvePreferences": "曲線偏好",
|
||
"curvePreferencesTip": "依偏好順序限制伺服器提供的 TLS 金鑰交換曲線(例如 X25519MLKEM768、X25519)。留空則使用 Xray-core 預設值。",
|
||
"masterKeyLog": "主金鑰日誌",
|
||
"masterKeyLogTip": "寫入 TLS 主金鑰的路徑(SSLKEYLOGFILE 格式),用於以 Wireshark 除錯。正式環境請留空——擁有該檔案的任何人都能解密流量。",
|
||
"verifyPeerCertByName": "依名稱驗證對端憑證",
|
||
"verifyPeerCertByNameTip": "要求客戶端以此名稱(而非 SNI)驗證伺服器憑證。以逗號分隔多個名稱。僅面板使用——會包含在分享連結中(vcn)。這是 allowInsecure 的新式替代方案,Xray 已於 2026-06-01 之後移除 allowInsecure。",
|
||
"pinFromCert": "從此入站的憑證填入",
|
||
"pinFromRemote": "透過 ping SNI 取得雜湊值(xray tls ping)",
|
||
"pinFromRemoteNoSni": "請先設定 SNI(serverName)才能 ping 遠端憑證。",
|
||
"pinFromRemoteFailed": "無法取得遠端憑證雜湊值。",
|
||
"limitFallback": "限制 Fallback",
|
||
"limitFallbackUpload": "限制 Fallback 上傳",
|
||
"limitFallbackDownload": "限制 Fallback 下載",
|
||
"afterBytes": "起算位元組",
|
||
"afterBytesTip": "讓 fallback 以全速傳輸此數量的位元組後,再開始限速。0 = 從第一個位元組起即限速。",
|
||
"bytesPerSec": "每秒位元組",
|
||
"bytesPerSecTip": "在達到門檻後套用於 fallback 流量的速度上限(位元組/秒),使探測無法將你的伺服器當成通往目標的免費頻寬。0 = 不限制(停用此方向)。",
|
||
"burstBytesPerSec": "每秒突發位元組",
|
||
"burstBytesPerSecTip": "允許短暫超出穩定速率的額度(token-bucket 大小)。若低於「每秒位元組」,則會提升至與其相同。",
|
||
"listenHelp": "也可以填寫 Unix socket 路徑(例如 /run/xray/in.sock),或以 @ 為前綴的抽象通訊端名稱(例如 @xray/in.sock),以使用通訊端而非 TCP 連接埠監聽——此時請將連接埠設為 0。",
|
||
"shareAddrStrategy": "分享地址策略",
|
||
"shareAddrStrategyHelp": "控制匯出分享連結、QR Code 和訂閱輸出時寫入哪個地址。",
|
||
"shareAddr": "自訂分享地址",
|
||
"shareAddrHelp": "僅在分享地址策略為自訂時使用。填寫不帶協定和連接埠的網域或 IP。",
|
||
"subSortIndex": "訂閱排序",
|
||
"subSortIndexHelp": "此入站的連結在訂閱輸出(訂閱頁面和客戶端應用)中的位置。數值越小越靠前;數值相同時保持建立順序。不影響面板中的入站清單。",
|
||
"disableFlow": "停用 XTLS flow",
|
||
"disableFlowHelp": "讓此入站略過自動注入 xtls-rprx-vision,即使其傳輸支援 flow(例如啟用 VLESS 加密的通道化 XHTTP 入站)。用戶端在同一訂閱中的其他可用入站上仍保留 Vision。僅限 VLESS。",
|
||
"shareAddrStrategyOptions": {
|
||
"node": "節點地址",
|
||
"listen": "入站監聽地址",
|
||
"custom": "自訂"
|
||
}
|
||
},
|
||
"info": {
|
||
"mode": "模式",
|
||
"grpcServiceName": "grpc serviceName",
|
||
"grpcMultiMode": "grpc multiMode",
|
||
"interfaceName": "介面名稱",
|
||
"mtu": "MTU",
|
||
"gateway": "Gateway",
|
||
"dns": "DNS",
|
||
"outboundsInterface": "出站介面",
|
||
"autoSystemRoutes": "自動系統路由",
|
||
"followRedirect": "FollowRedirect",
|
||
"auth": "認證",
|
||
"noKernelTun": "非核心 TUN",
|
||
"keepAlive": "Keep alive",
|
||
"peerNumber": "Peer {n}",
|
||
"peerNumberConfig": "Peer {n} 設定"
|
||
},
|
||
"sniffingDestOverride": "目標覆寫"
|
||
},
|
||
"clients": {
|
||
"tabBasics": "基本",
|
||
"tabCredentials": "認證資訊",
|
||
"tabLinks": "連結",
|
||
"wireguardConfig": "WireGuard 設定",
|
||
"config": "設定",
|
||
"linksHint": "新增第三方分享連結和遠端訂閱網址,將其包含在此用戶端的訂閱中。",
|
||
"addExternalLink": "新增外部連結",
|
||
"addExternalSubscription": "新增外部訂閱",
|
||
"noExternalLinks": "尚無外部連結。",
|
||
"noExternalSubscriptions": "尚無外部訂閱。",
|
||
"namePrefix": "名稱前綴",
|
||
"lastFetchAt": "最後拉取",
|
||
"lastFetchError": "拉取失敗",
|
||
"neverFetched": "尚未拉取",
|
||
"submitEdit": "儲存變更",
|
||
"clientCount": "客戶端數量",
|
||
"bulk": "批次新增",
|
||
"selectAll": "全選",
|
||
"clearAll": "全部清除",
|
||
"method": "方法",
|
||
"first": "首個",
|
||
"last": "末位",
|
||
"ipLog": "IP 日誌",
|
||
"prefix": "前綴",
|
||
"postfix": "後綴",
|
||
"delayedStart": "首次使用後開始",
|
||
"expireDays": "時長 (天)",
|
||
"renew": "自動續期",
|
||
"renewDesc": "到期後自動續期。(0 = 停用) (單位: 天)",
|
||
"renewDays": "自動續期 (天)",
|
||
"searchPlaceholder": "搜尋電子郵件、備註、sub ID、UUID、密碼、auth、Telegram ID…",
|
||
"filterTitle": "篩選客戶端",
|
||
"clearAllFilters": "清除全部",
|
||
"filters": {
|
||
"nodes": "節點",
|
||
"localPanel": "本機(此面板)"
|
||
},
|
||
"showingCount": "顯示 {shown} / {total}",
|
||
"sortOldest": "最舊優先",
|
||
"sortNewest": "最新優先",
|
||
"sortRecentlyUpdated": "最近更新",
|
||
"sortRecentlyOnline": "最近上線",
|
||
"sortEmailAZ": "電子郵件 A→Z",
|
||
"sortEmailZA": "電子郵件 Z→A",
|
||
"sortMostTraffic": "流量最多",
|
||
"sortHighestRemaining": "剩餘最多",
|
||
"sortExpiringSoonest": "即將到期",
|
||
"has": "擁有",
|
||
"hasNot": "不擁有",
|
||
"actions": "操作",
|
||
"totalGB": "流量上限 (GB)",
|
||
"totalGBDesc": "該客戶端的流量配額。0 = 不限制。",
|
||
"expiryTime": "到期時間",
|
||
"addClients": "新增客戶端",
|
||
"limitIp": "IP 限制",
|
||
"limitIpDesc": "最大同時連線 IP 數。0 = 不限制。",
|
||
"limitHwid": "HWID 限制",
|
||
"limitHwidDesc": "訂閱請求最多可註冊的裝置數。0 = 不限制。",
|
||
"hwidLog": "HWID 裝置",
|
||
"hwidDevice": "已註冊裝置",
|
||
"noHwids": "尚無 HWID 裝置",
|
||
"firstSeen": "首次出現",
|
||
"lastSeen": "最後出現",
|
||
"deleteHwid": "移除裝置",
|
||
"deleteHwidConfirm": "移除此裝置?下次取得訂閱時它將需要重新註冊。",
|
||
"hwidDeleted": "裝置已移除。",
|
||
"clearHwidsConfirm": "移除所有已註冊的裝置?每台裝置在下次取得訂閱時都需要重新註冊。",
|
||
"limitIpFail2banMissing": "未安裝 Fail2ban,無法實施 IP 限制。請從 x-ui 命令列選單安裝 Fail2ban 以啟用此選項。",
|
||
"limitIpFail2banWindows": "Windows 上不支援 Fail2ban,無法實施 IP 限制。",
|
||
"limitIpDisabled": "此伺服器已停用 IP 限制功能。",
|
||
"password": "密碼",
|
||
"passwordDesc": "僅 Trojan 與 Shadowsocks 用戶端使用;VLESS、VMess、Hysteria 和 WireGuard 會忽略此項。",
|
||
"subId": "訂閱 ID",
|
||
"online": "上線",
|
||
"email": "電子郵件",
|
||
"emailInvalidChars": "電子郵件不能包含空格、'/'、'\\' 或控制字元",
|
||
"subIdInvalidChars": "訂閱 ID 不能包含空格、'/'、'\\' 或控制字元",
|
||
"group": "群組",
|
||
"groupDesc": "用於將相關客戶端歸類的邏輯標籤(如團隊、客戶、地區)。可從工具列篩選。",
|
||
"groupPlaceholder": "如 customer-a",
|
||
"comment": "備註",
|
||
"traffic": "流量",
|
||
"speed": "速度",
|
||
"offline": "離線",
|
||
"addClient": "新增客戶端",
|
||
"qrCode": "QR 碼",
|
||
"clientInfo": "客戶端資訊",
|
||
"editClient": "編輯客戶端",
|
||
"client": "客戶端",
|
||
"enabled": "已啟用",
|
||
"remaining": "剩餘",
|
||
"duration": "時長",
|
||
"attachedInbounds": "關聯入站",
|
||
"selectInbound": "選擇一個或多個入站",
|
||
"selectAllInbounds": "全選",
|
||
"clearAllInbounds": "全部清除",
|
||
"noSubId": "此客戶端沒有 subId,無法產生共享連結。",
|
||
"noLinks": "沒有可共享的連結 — 請先將此客戶端關聯至支援協定的入站。",
|
||
"link": "連結",
|
||
"resetNotPossible": "請先將此客戶端關聯至入站。",
|
||
"resetAllTraffics": "重設所有客戶端流量",
|
||
"resetAllTrafficsTitle": "重設所有客戶端流量?",
|
||
"resetAllTrafficsContent": "所有客戶端的上下行計數器將歸零。配額與到期時間不受影響。此操作無法復原。",
|
||
"deleteConfirmTitle": "刪除客戶端 {email}?",
|
||
"deleteConfirmContent": "將從所有關聯入站中移除該客戶端並刪除其流量紀錄。此操作無法復原。",
|
||
"adjustSelected": "調整 ({count})",
|
||
"subLinksSelected": "訂閱連結 ({count})",
|
||
"addToGroupTitle": "將 {count} 個客戶端加入群組",
|
||
"addToGroupTooltip": "選擇現有群組或輸入新名稱。使用 Ungroup 操作從當前群組移除客戶端。",
|
||
"groupName": "群組名稱",
|
||
"addToGroupSuccessToast": "已將 {count} 個客戶端加入 {group}",
|
||
"ungroupSuccessToast": "已清除 {count} 個客戶端的群組",
|
||
"ungroup": "取消群組",
|
||
"ungroupConfirmTitle": "將 {count} 個客戶端從其群組中移除?",
|
||
"ungroupConfirmContent": "清除每個選取客戶端的群組標籤。客戶端本身保留(用 Delete 完全移除)。",
|
||
"addToGroup": "加入群組",
|
||
"attach": "附加",
|
||
"adjust": "調整",
|
||
"subLinks": "訂閱連結",
|
||
"enable": "啟用",
|
||
"disable": "停用",
|
||
"bulkEnableConfirmTitle": "啟用 {count} 個客戶端?",
|
||
"bulkEnableConfirmContent": "在每個已附加的入站上啟用所選的客戶端。配額已用盡或已過期的客戶端將被自動重新停用。",
|
||
"bulkDisableConfirmTitle": "停用 {count} 個客戶端?",
|
||
"bulkDisableConfirmContent": "在每個已附加的入站上停用所選的客戶端。他們將立即失去存取權限,但其記錄與流量將被保留。",
|
||
"selectedCount": "已選 {count} 項",
|
||
"attachToInboundsTitle": "將 {count} 個客戶端附加到入站",
|
||
"attachToInboundsDesc": "將選取的 {count} 個客戶端(相同 UUID/密碼與共享流量)附加到選定入站。它們保留現有附加關係。",
|
||
"attachToInboundsTargets": "目標入站",
|
||
"attachToInboundsNoTargets": "沒有可供附加的多用戶入站。",
|
||
"detach": "分離",
|
||
"detachFromInboundsTitle": "從入站分離 {count} 個客戶端",
|
||
"detachFromInboundsDesc": "從選定入站中移除選取的 {count} 個客戶端。客戶端未附加的配對會被靜默略過。客戶端記錄保留(用 Delete 完全移除)。",
|
||
"detachFromInboundsTargets": "要分離的入站",
|
||
"detachFromInboundsNoTargets": "沒有可用的多用戶入站。",
|
||
"detachFromInboundsResult": "已分離 {detached},已略過 {skipped}。",
|
||
"detachFromInboundsResultMixed": "已分離 {detached},已略過 {skipped},錯誤 {errors}。",
|
||
"subLinksTitle": "訂閱連結 ({count})",
|
||
"subLinkColumn": "訂閱 URL",
|
||
"subJsonLinkColumn": "訂閱 JSON URL",
|
||
"subLinksCopyAll": "全部複製",
|
||
"subLinksCopiedAll": "已複製 {count} 條連結",
|
||
"subLinksEmpty": "選取的客戶端皆無訂閱 ID。",
|
||
"subLinksDisabled": "訂閱服務已停用。",
|
||
"subLinksDisabledHint": "在面板設定 → 訂閱中啟用訂閱以產生連結。",
|
||
"bulkDeleteConfirmTitle": "刪除 {count} 個客戶端?",
|
||
"bulkDeleteConfirmContent": "每個所選客戶端都會從關聯的入站中被移除,其流量紀錄也會被刪除。此操作無法復原。",
|
||
"bulkAdjustTitle": "調整 {count} 個客戶端",
|
||
"bulkAdjustHint": "正值延長,負值減少。具有無限期限或流量的客戶端將跳過該欄位。",
|
||
"bulkAdjustNothing": "套用前請設定天數或流量。",
|
||
"addDays": "新增天數",
|
||
"addTrafficGB": "新增流量 (GB)",
|
||
"bulkFlow": "設定 flow",
|
||
"bulkFlowNoChange": "不變更",
|
||
"bulkFlowDisable": "停用(清除 flow)",
|
||
"delDepleted": "刪除已耗盡",
|
||
"delDepletedConfirmTitle": "刪除已耗盡的客戶端?",
|
||
"delDepletedConfirmContent": "刪除所有流量配額已用盡或已過期的客戶端。此操作無法復原。",
|
||
"exportClients": "匯出客戶端",
|
||
"importClients": "匯入客戶端",
|
||
"import": "匯入",
|
||
"delOrphans": "刪除未關聯的客戶端",
|
||
"delOrphansConfirmTitle": "刪除沒有入站的客戶端?",
|
||
"delOrphansConfirmContent": "移除所有未關聯任何入站的客戶端,連同其流量紀錄一併刪除。此操作無法復原。",
|
||
"auth": "認證",
|
||
"hysteriaAuth": "Hysteria 認證",
|
||
"hysteriaAuthDesc": "僅 Hysteria 用戶端使用的憑證。Trojan 與 Shadowsocks 請改用上方的「密碼」欄位。",
|
||
"uuid": "UUID",
|
||
"flow": "Flow",
|
||
"vmessSecurity": "VMess 加密",
|
||
"wireguardPrivateKey": "WireGuard 私鑰",
|
||
"wireguardPublicKey": "WireGuard 公鑰",
|
||
"wireguardPreSharedKey": "WireGuard 預共用金鑰",
|
||
"wireguardAllowedIPs": "WireGuard 允許的 IP",
|
||
"wireguardAllowedIPsHint": "留空則自動分配;多個條目用逗號分隔",
|
||
"amneziaWgPrivateKey": "AmneziaWG 私鑰",
|
||
"amneziaWgPublicKey": "AmneziaWG 公鑰",
|
||
"amneziaWgPreSharedKey": "AmneziaWG 預共用金鑰",
|
||
"amneziaWgAllowedIPs": "AmneziaWG 允許的 IP",
|
||
"amneziaWgAllowedIPsHint": "留空則自動分配;多個條目用逗號分隔",
|
||
"amneziaWgForwardedPorts": "轉發連接埠",
|
||
"amneziaWgForwardedPortsHint": "轉發到此客戶端的連接埠/範圍,例如 80, 443, 8000-8100。留空則不轉發。",
|
||
"amneziaWgConfig": "AmneziaWG 設定",
|
||
"mtprotoSecret": "MTProto 金鑰",
|
||
"mtprotoSecretHint": "該用戶端的 FakeTLS 金鑰。重新產生即可更換。",
|
||
"mtprotoAdTag": "廣告標籤(贊助頻道)",
|
||
"mtprotoAdTagHint": "選填的 32 位十六進位標籤,從 Telegram 代理註冊處取得。設定後,該用戶端會透過 Telegram 中間代理路由,贊助頻道會顯示在其聊天清單頂端。",
|
||
"reverseTag": "反向標籤",
|
||
"reverseTagPlaceholder": "選用 Reverse tag",
|
||
"telegramId": "Telegram 使用者 ID",
|
||
"telegramIdPlaceholder": "數字形式的 Telegram 使用者 ID (0 = 無)",
|
||
"ipLimit": "IP 限制",
|
||
"toasts": {
|
||
"deleted": "客戶端已刪除",
|
||
"trafficReset": "流量已重設",
|
||
"allTrafficsReset": "所有客戶端流量已重設",
|
||
"bulkDeleted": "已刪除 {count} 個客戶端",
|
||
"bulkDeletedMixed": "已刪除 {ok} 個,失敗 {failed} 個",
|
||
"bulkEnabled": "已啟用 {count} 個客戶端",
|
||
"bulkEnabledMixed": "已啟用 {ok} 個,失敗 {failed} 個",
|
||
"bulkDisabled": "已停用 {count} 個客戶端",
|
||
"bulkDisabledMixed": "已停用 {ok} 個,失敗 {failed} 個",
|
||
"bulkCreated": "已建立 {count} 個客戶端",
|
||
"bulkCreatedMixed": "已建立 {ok} 個,失敗 {failed} 個",
|
||
"bulkAdjusted": "已調整 {count} 個客戶端",
|
||
"bulkAdjustedMixed": "已調整 {ok} 個,跳過 {skipped} 個",
|
||
"delDepleted": "已刪除 {count} 個已耗盡的客戶端",
|
||
"delOrphans": "已刪除 {count} 個未關聯的客戶端",
|
||
"imported": "已匯入 {count} 個客戶端",
|
||
"importedMixed": "已匯入 {ok} 個,跳過 {failed} 個"
|
||
},
|
||
"renewMax": "最大續期次數",
|
||
"renewMaxDesc": "自動續期最多可觸發的次數,達到後用戶端將自然到期。填 0 表示不限制。補齊多個錯過的週期時,每個週期消耗一次續期。",
|
||
"renewOnDay": "按日期續期",
|
||
"renewOnDayDesc": "每個自然月的這一天午夜(依面板時區)續期,而不是每 N 天續期一次。若當月沒有該日期,則在當月最後一天續期。填 0 保持按天間隔模式。",
|
||
"renewsUsed": "已用續期次數"
|
||
},
|
||
"groups": {
|
||
"name": "名稱",
|
||
"clientCount": "客戶端",
|
||
"totalGroups": "群組總數",
|
||
"totalGroupedClients": "有群組的客戶端",
|
||
"trafficUsed": "已用流量",
|
||
"upload": "上傳",
|
||
"download": "下載",
|
||
"totalTraffic": "總流量",
|
||
"totalUpDown": "總上傳 / 下載",
|
||
"addGroup": "新增群組",
|
||
"createSuccess": "已建立群組「{name}」。",
|
||
"rename": "重新命名",
|
||
"renameTitle": "重新命名 {name}",
|
||
"renameCollision": "已存在名為「{name}」的群組。",
|
||
"renameSuccess": "已為 {count} 個客戶端重新命名群組。",
|
||
"deleteConfirmTitle": "刪除群組 {name}?",
|
||
"deleteConfirmContent": "這將刪除群組並清除 {count} 個客戶端的標籤。客戶端本身不會被刪除。",
|
||
"deleteSuccess": "已清除 {count} 個客戶端的群組。",
|
||
"resetTraffic": "重置流量",
|
||
"resetConfirmTitle": "重置群組 {name} 的流量?",
|
||
"resetConfirmContent": "這只會將此群組的流量計數器歸零,不影響各個客戶端的計數器。",
|
||
"resetSuccess": "已重置群組 {name} 的流量。",
|
||
"adjustSuccess": "已調整 {name} 中的 {count} 個客戶端。",
|
||
"emptyForAction": "此群組尚無客戶端。",
|
||
"deleteGroupOnly": "刪除群組(保留客戶端)",
|
||
"deleteClients": "刪除群組中的客戶端",
|
||
"deleteClientsConfirmTitle": "刪除 {name} 中的所有客戶端?",
|
||
"deleteClientsConfirmContent": "這將永久刪除 {count} 個客戶端及其流量記錄。群組標籤亦會被清除。此操作無法復原。",
|
||
"deleteClientsSuccess": "已刪除 {count} 個客戶端。",
|
||
"deleteClientsMixed": "已刪除 {ok},已略過 {failed}",
|
||
"addToGroup": "新增客戶端…",
|
||
"addToGroupTitle": "將客戶端加入群組「{name}」",
|
||
"addToGroupDesc": "選擇要加入此群組的客戶端。保留其現有入站附加;僅更改群組標籤。已在此群組中的客戶端不會列出。",
|
||
"addToGroupEmpty": "沒有其他可加入的客戶端。",
|
||
"addToGroupResult": "已將 {count} 個客戶端加入 {name}。",
|
||
"removeFromGroup": "移除客戶端…",
|
||
"removeFromGroupTitle": "從群組「{name}」移除客戶端",
|
||
"removeFromGroupDesc": "選擇要從此群組移除的成員。客戶端本身保留(用「刪除群組中的客戶端」完全移除)。",
|
||
"removeFromGroupResult": "已從 {name} 移除 {count} 個客戶端。"
|
||
},
|
||
"nodes": {
|
||
"addNode": "新增節點",
|
||
"editNode": "編輯節點",
|
||
"totalNodes": "節點總數",
|
||
"onlineNodes": "上線",
|
||
"offlineNodes": "離線",
|
||
"avgLatency": "平均延遲",
|
||
"name": "名稱",
|
||
"namePlaceholder": "例如:de-frankfurt-1",
|
||
"addressPlaceholder": "panel.example.com 或 1.2.3.4",
|
||
"remark": "備註",
|
||
"scheme": "協議",
|
||
"address": "位址",
|
||
"port": "連接埠",
|
||
"basePath": "基礎路徑",
|
||
"apiToken": "API 權杖",
|
||
"apiTokenPlaceholder": "遠端面板設定頁中的權杖",
|
||
"apiTokenHint": "遠端面板在 安全設定 → API 權杖 中顯示其 API 權杖。",
|
||
"apiTokenKeepHint": "留空以保留目前的權杖",
|
||
"allowPrivateAddress": "允許私有地址",
|
||
"allowPrivateAddressHint": "僅對私有網路或 VPN 上的節點啟用。",
|
||
"outboundTag": "連線出站",
|
||
"outboundTagHint": "透過選定的 Xray 出站路由此節點的面板 API 流量。系統會自動將迴環橋接入站加入執行中的設定並即時套用。留空表示直接連線。",
|
||
"outboundTagPlaceholder": "直接連線",
|
||
"inboundSyncMode": "入站匯入",
|
||
"inboundSyncModeHint": "選擇要從此節點匯入的入站。現有節點預設匯入所有入站。",
|
||
"allInbounds": "所有入站",
|
||
"selectedInbounds": "選取的入站",
|
||
"inboundTags": "入站",
|
||
"inboundTagsHint": "依入站標籤比對。空白選取不會匯入任何入站。",
|
||
"inboundTagsPlaceholder": "載入並選取入站",
|
||
"loadInbounds": "從節點載入入站",
|
||
"inboundsLoaded": "已載入 {{count}} 個入站",
|
||
"inboundsLoadFailed": "載入入站失敗",
|
||
"enable": "已啟用",
|
||
"status": "狀態",
|
||
"cpu": "CPU",
|
||
"mem": "記憶體",
|
||
"netUp": "網路上行 (KB/s)",
|
||
"netDown": "網路下行 (KB/s)",
|
||
"uptime": "運行時間",
|
||
"latency": "延遲",
|
||
"lastHeartbeat": "上次心跳",
|
||
"xrayVersion": "Xray 版本",
|
||
"panelVersion": "面板版本",
|
||
"actions": "操作",
|
||
"probe": "立即探測",
|
||
"updatePanel": "更新面板",
|
||
"updateSelected": "更新所選 ({count})",
|
||
"updateAvailable": "有可用更新",
|
||
"updateConfirmTitle": "將 {count} 個節點更新到最新版本?",
|
||
"updateConfirmContent": "每個所選節點會下載最新版本並重新啟動。僅更新已啟用且在線的節點。",
|
||
"updateDevChannel": "更新到開發通道(最新提交)",
|
||
"testConnection": "測試連線",
|
||
"connectionOk": "連線正常 ({ms} ms)",
|
||
"connectionFailed": "連線失敗",
|
||
"never": "從未",
|
||
"justNow": "剛剛",
|
||
"subNode": "子節點",
|
||
"subNodeTip": "唯讀:透過 {parent} 連接的下游節點。請在 {parent} 自己的面板中管理。",
|
||
"deleteConfirmTitle": "刪除節點「{name}」?",
|
||
"deleteConfirmContent": "這將停止監控該節點。遠端面板本身不受影響。",
|
||
"statusValues": {
|
||
"online": "上線",
|
||
"offline": "離線",
|
||
"unknown": "未知",
|
||
"xrayError": "Xray 錯誤",
|
||
"xrayStopped": "已停止"
|
||
},
|
||
"toasts": {
|
||
"list": "載入節點失敗",
|
||
"obtain": "載入節點失敗",
|
||
"add": "新增節點",
|
||
"update": "更新節點",
|
||
"delete": "刪除節點",
|
||
"deleted": "節點已刪除",
|
||
"test": "測試連線",
|
||
"fillRequired": "名稱、位址、埠與 API 權杖為必填",
|
||
"probeFailed": "探測失敗",
|
||
"updateStarted": "已開始更新面板",
|
||
"updateResult": "已在 {ok} 個節點上觸發更新,{failed} 個失敗",
|
||
"updateNoneEligible": "請至少選擇一個在線且已啟用的節點",
|
||
"saveMtls": "儲存節點 mTLS",
|
||
"reloadMtls": "Reload master mTLS credential"
|
||
},
|
||
"tlsVerifyMode": "TLS 驗證",
|
||
"tlsVerifyModeHint": "面板如何驗證節點的 HTTPS 憑證。釘選或略過用於自簽憑證(僅 https 節點)。",
|
||
"tlsVerify": "驗證(預設 CA)",
|
||
"tlsPin": "釘選憑證(SHA-256)",
|
||
"tlsSkip": "略過驗證",
|
||
"tlsMtls": "雙向 TLS(用戶端憑證)",
|
||
"mtlsFormHint": "此節點使用用戶端憑證對面板進行驗證。請從「節點 mTLS」區域複製本面板的 CA 到該節點,設定其受信任的 CA,然後重新啟動該節點。",
|
||
"mtls": {
|
||
"title": "節點 mTLS",
|
||
"intro": "雙向 TLS 在節點間呼叫的 API 權杖之上增加用戶端憑證驗證。此為選用項目:留空則僅使用權杖驗證。",
|
||
"copyCa": "複製此面板的 CA",
|
||
"copyCaHint": "將此 CA 提供給本面板管理的節點,然後將它們的 TLS 驗證設定為雙向 TLS。",
|
||
"caCopied": "CA 憑證已複製到剪貼簿",
|
||
"caFailed": "取得 CA 憑證失敗",
|
||
"trustLabel": "受信任的上層 CA",
|
||
"trustHint": "當本面板自身作為節點時,將管理它的面板的 CA 貼到此處以要求其用戶端憑證。重新啟動面板後生效。",
|
||
"trustPlaceholder": "-----BEGIN CERTIFICATE-----",
|
||
"save": "儲存受信任的 CA",
|
||
"saved": "受信任的 CA 已儲存 — 重新啟動面板後生效"
|
||
},
|
||
"tlsSkipWarning": "略過驗證會失去對中間人攻擊的防護,API 權杖可能被攔截。建議改用釘選憑證。",
|
||
"pinnedCert": "釘選憑證的 SHA-256",
|
||
"pinnedCertHint": "節點憑證的 SHA-256(base64 或 hex)。點選「取得」可立即從節點讀取。",
|
||
"pinnedCertPlaceholder": "base64 或 hex 的 SHA-256",
|
||
"fetchPin": "取得",
|
||
"pinFetched": "已取得節點目前憑證",
|
||
"pinFetchFailed": "無法取得憑證"
|
||
},
|
||
"settings": {
|
||
"defaultTag": "預設",
|
||
"title": "面板設定",
|
||
"save": "儲存",
|
||
"infoDesc": "此處的所有更改都需要儲存並重啟面板才能生效",
|
||
"restartPanel": "重新啟動面板",
|
||
"restartPanelDesc": "確定要重啟面板嗎?若重啟後無法訪問面板,請前往伺服器檢視面板日誌資訊",
|
||
"restartPanelSuccess": "面板已成功重新啟動",
|
||
"actions": "操作",
|
||
"resetDefaultConfig": "重置為預設配置",
|
||
"panelSettings": "常規",
|
||
"securitySettings": "安全設定",
|
||
"securityWarnings": "安全警告",
|
||
"panelExposed": "您的面板可能已暴露:",
|
||
"warnHttp": "面板透過明文 HTTP 提供服務 — 生產環境請設定 TLS。",
|
||
"warnDefaultPort": "預設連接埠 2053 廣為人知 — 請更改為隨機連接埠。",
|
||
"warnDefaultBasePath": "預設根路徑 \"/\" 廣為人知 — 請更改為隨機路徑。",
|
||
"warnDefaultSubPath": "預設訂閱路徑 \"/sub/\" 廣為人知 — 請更改。",
|
||
"warnDefaultJsonPath": "預設 JSON 訂閱路徑 \"/json/\" 廣為人知 — 請更改。",
|
||
"TGBotSettings": "Telegram 機器人",
|
||
"panelListeningIP": "面板監聽 IP",
|
||
"panelListeningIPDesc": "預設留空監聽所有 IP",
|
||
"panelListeningDomain": "面板監聽域名",
|
||
"panelListeningDomainDesc": "預設情況下留空以監視所有域名和 IP 地址",
|
||
"panelPort": "面板監聽埠",
|
||
"panelPortDesc": "重啟面板生效",
|
||
"publicKeyPath": "面板證書公鑰檔案路徑",
|
||
"publicKeyPathDesc": "填寫一個 '/' 開頭的絕對路徑",
|
||
"privateKeyPath": "面板證書金鑰檔案路徑",
|
||
"privateKeyPathDesc": "填寫一個 '/' 開頭的絕對路徑",
|
||
"panelUrlPath": "URI 路徑",
|
||
"panelUrlPathDesc": "必須以 '/' 開頭,以 '/' 結尾",
|
||
"pageSize": "分頁大小",
|
||
"pageSizeDesc": "定義入站表的頁面大小。設定 0 表示禁用",
|
||
"panelOutbound": "面板流量出站",
|
||
"panelOutboundDesc": "透過此 Xray 出站路由面板自身的請求(面板/Xray 版本檢查與下載、Telegram、一般 geo 檔案更新),以繞過伺服器端對 GitHub/Telegram 的過濾。本地橋接入站會自動加入執行中的設定並即時生效。Xray 原生的 Geodata 自動更新不受影響,它有自己的下載出站。留空表示直連。",
|
||
"panelOutboundPh": "直連",
|
||
"datepicker": "日期選擇器",
|
||
"datepickerPlaceholder": "選擇日期",
|
||
"datepickerDescription": "選擇器日曆類型指定到期日期",
|
||
"oldUsername": "原使用者名稱",
|
||
"currentPassword": "原密碼",
|
||
"newUsername": "新使用者名稱",
|
||
"newPassword": "新密碼",
|
||
"telegramBotEnable": "啟用 Telegram 機器人",
|
||
"telegramBotEnableDesc": "啟用 Telegram 機器人功能",
|
||
"telegramToken": "Telegram Token",
|
||
"telegramTokenDesc": "從 '{'@'}BotFather' 獲取的 Telegram 機器人令牌",
|
||
"telegramProxy": "SOCKS 代理",
|
||
"telegramProxyDesc": "啟用 SOCKS5 代理連線到 Telegram(根據指南調整設定)",
|
||
"telegramAPIServer": "Telegram API 伺服器",
|
||
"telegramAPIServerDesc": "要使用的 Telegram API 伺服器。留空以使用預設伺服器。",
|
||
"telegramChatId": "管理員聊天 ID",
|
||
"telegramChatIdDesc": "Telegram 管理員聊天 ID (多個以逗號分隔)(可通過 {'@'}userinfobot 獲取,或在機器人中使用 '/id' 命令獲取)",
|
||
"telegramNotifyTime": "通知時間",
|
||
"telegramNotifyTimeDesc": "Telegram 機器人傳送週期性報告的頻率。選擇預設間隔,或選擇「自訂」以輸入 crontab 運算式。",
|
||
"notifyTime": {
|
||
"every": "@every — 依間隔重複",
|
||
"hourly": "@hourly — 每小時",
|
||
"daily": "@daily — 每天 00:00",
|
||
"weekly": "@weekly — 每週",
|
||
"monthly": "@monthly — 每月",
|
||
"custom": "自訂 (crontab)",
|
||
"seconds": "秒",
|
||
"minutes": "分鐘",
|
||
"hours": "小時",
|
||
"interval": "間隔",
|
||
"unit": "單位"
|
||
},
|
||
"tgNotifyBackup": "資料庫備份",
|
||
"tgNotifyBackupDesc": "傳送帶有報告的資料庫備份檔案",
|
||
"tgNotifyLogin": "登入通知",
|
||
"tgNotifyLoginDesc": "當有人試圖登入你的面板時顯示使用者名稱、IP 地址和時間",
|
||
"sessionMaxAge": "會話時長",
|
||
"sessionMaxAgeDesc": "保持登入狀態的時長(單位:分鐘)",
|
||
"expireTimeDiff": "到期通知閾值",
|
||
"expireTimeDiffDesc": "達到此閾值時,將收到有關到期時間的通知(單位:天)",
|
||
"trafficDiff": "流量耗盡閾值",
|
||
"trafficDiffDesc": "達到此閾值時,將收到有關流量耗盡的通知(單位:GB)",
|
||
"tgNotifyCpu": "CPU 負載通知閾值",
|
||
"tgNotifyCpuDesc": "CPU 負載超過此閾值時,將收到通知(單位:%)",
|
||
"timeZone": "時區",
|
||
"timeZoneDesc": "定時任務將按照該時區的時間執行",
|
||
"subSettings": "訂閱設定",
|
||
"subEnable": "啟用訂閱服務",
|
||
"subEnableDesc": "啟用訂閱服務功能",
|
||
"subJsonEnable": "獨立啟用/停用 JSON 訂閱端點。",
|
||
"subJsonEnableTitle": "JSON 訂閱",
|
||
"subClashEnableTitle": "Clash / Mihomo 訂閱",
|
||
"subFormatsTipTitle": "特定格式的訂閱設定",
|
||
"subFormatsTipDesc": "分別設定 JSON 和 Clash / Mihomo 的 URL 路徑、反向代理 URL 與用戶端自動識別。",
|
||
"subFormatsTipAction": "開啟訂閱格式",
|
||
"subJsonAutoDetect": "自動識別 Xray JSON 用戶端",
|
||
"subJsonAutoDetectDesc": "啟用後,使用標準訂閱 URL 的已識別相容用戶端將自動取得 Xray JSON 設定陣列。其他用戶端繼續取得原始/Base64 回應。需要啟用 JSON 訂閱並重新啟動面板才能生效。",
|
||
"subJsonAlwaysArray": "始終回傳 JSON 陣列",
|
||
"subJsonAlwaysArrayDesc": "即使只有一個設定,也將明確 JSON 訂閱端點回傳為陣列,以符合 XTLS 訂閱標準。自動識別的 JSON 回應始終使用陣列。停用時保留舊版單一物件回應。",
|
||
"subJsonUserAgentRegex": "Xray JSON User-Agent 正規表示式",
|
||
"subJsonUserAgentRegexDesc": "用於與用戶端 User-Agent 進行比對,以便在標準訂閱 URL 上自動選擇 Xray JSON 格式的 Go RE2 正規表示式。預設留空,因此在為要服務的用戶端設定規則之前,自動辨識會保持關閉。其他用戶端繼續取得原始/Base64 回應。變更後請重新啟動面板。",
|
||
"subClashAutoDetect": "自動識別 Clash/Mihomo 用戶端",
|
||
"subClashAutoDetectDesc": "啟用後,使用標準訂閱 URL 的已識別 Clash/Mihomo 用戶端將自動取得 Clash YAML。瀏覽器仍會顯示訂閱頁面,其他用戶端繼續取得原始/Base64 回應,獨立的 JSON 和 Clash URL 仍可使用。需要啟用 Clash/Mihomo 訂閱並重新啟動面板才能生效。",
|
||
"subClashUserAgentRegex": "Clash/Mihomo User-Agent 正規表示式",
|
||
"subClashUserAgentRegexDesc": "用於與用戶端 User-Agent 進行比對,以便在標準訂閱 URL 上識別 Clash/Mihomo 用戶端的 Go RE2 正規表示式。留空則使用預設規則。變更後請重新啟動面板。",
|
||
"subTitle": "訂閱標題",
|
||
"subTitleDesc": "在 VPN 客戶端中顯示的標題。支援用戶端身分權杖:{{EMAIL}}、{{ID}}、{{SHORT_ID}}、{{SUB_ID}}、{{TELEGRAM_ID}}。",
|
||
"subSupportUrl": "支援連結",
|
||
"subSupportUrlDesc": "VPN 用戶端中顯示的技術支援連結。支援用戶端身分權杖:{{EMAIL}}、{{ID}}、{{SHORT_ID}}、{{SUB_ID}}、{{TELEGRAM_ID}}。",
|
||
"subProfileUrl": "個人資料連結",
|
||
"subProfileUrlDesc": "VPN 用戶端中顯示的網站連結。支援用戶端身分權杖:{{EMAIL}}、{{ID}}、{{SHORT_ID}}、{{SUB_ID}}、{{TELEGRAM_ID}}。",
|
||
"subAnnounce": "公告",
|
||
"subAnnounceDesc": "VPN 用戶端中顯示的公告文字。支援用戶端身分權杖:{{EMAIL}}、{{ID}}、{{SHORT_ID}}、{{SUB_ID}}、{{TELEGRAM_ID}}。",
|
||
"subThemeDir": "訂閱主題目錄",
|
||
"subThemeDirDesc": "包含自訂訂閱頁面範本 (index.html/sub.html) 的資料夾的絕對路徑(例如 /etc/3x-ui/sub_templates/my-theme/)。留空則使用預設頁面。",
|
||
"subThemeDirDocs": "範本指南 ↗",
|
||
"subEnableRouting": "啟用路由",
|
||
"subEnableRoutingDesc": "在 VPN 用戶端中啟用路由的全域設定。(僅限 Happ)",
|
||
"subRoutingRules": "路由規則",
|
||
"subRoutingRulesDesc": "貼上現成的 happ:// 深層連結或一個固定 HTTPS URL。面板會在背景更新遠端規則並保留最後一個有效值,因此訂閱請求不需等待遠端來源。(僅限 Happ)",
|
||
"subHideSettings": "隱藏伺服器設定",
|
||
"subHideSettingsDesc": "在 VPN 用戶端中隱藏查看和編輯伺服器配置的功能。(僅限 Happ)",
|
||
"subIncyEnableRouting": "啟用路由",
|
||
"subIncyEnableRoutingDesc": "為 Incy 用戶端將路由設定檔注入訂閱內容中。(僅限 Incy)",
|
||
"subIncyRoutingRules": "路由規則",
|
||
"subIncyRoutingRulesDesc": "貼上現成的 incy:// 深層連結或指向 JSON 的固定 HTTPS URL。Incy 會建立 autorouting 設定並自動更新。(僅限 Incy)",
|
||
"subClashEnableRouting": "啟用路由",
|
||
"subClashEnableRoutingDesc": "在產生的 YAML 訂閱中包含 Clash/Mihomo 全域路由規則。",
|
||
"subClashRoutingRules": "全域路由規則",
|
||
"subClashRoutingRulesDesc": "貼上規則/YAML 或一個固定 HTTPS URL。面板會在背景更新,只匯入代理群組、規則提供者與規則,並保留面板產生的 VPN 節點及最後一個有效值。",
|
||
"subListen": "監聽 IP",
|
||
"subListenDesc": "訂閱服務監聽的 IP 地址(留空表示監聽所有 IP)",
|
||
"subPort": "監聽埠",
|
||
"subPortDesc": "訂閱服務監聽的埠號(必須是未使用的埠)。當下方「反向代理 URI」為空時,也會用來產生面板中顯示的訂閱連結/QR 碼——如果訂閱是透過反向代理的其他埠存取的,請改為設定「反向代理 URI」。",
|
||
"subCertPath": "公鑰路徑",
|
||
"subCertPathDesc": "訂閱服務使用的公鑰檔案路徑(以 '/' 開頭)",
|
||
"subKeyPath": "私鑰路徑",
|
||
"subKeyPathDesc": "訂閱服務使用的私鑰檔案路徑(以 '/' 開頭)",
|
||
"subPath": "URI 路徑",
|
||
"subPathDesc": "訂閱服務使用的 URI 路徑(以 '/' 開頭,以 '/' 結尾)",
|
||
"subDomain": "監聽域名",
|
||
"subDomainDesc": "訂閱服務監聽的域名(留空表示監聽所有域名和 IP)。當「反向代理 URI」為空時,也會作為顯示的訂閱連結的備援域名——如果面板和訂閱透過不同的域名存取(例如位於反向代理之後),請設定「反向代理 URI」。",
|
||
"subUpdates": "更新間隔",
|
||
"subUpdatesDesc": "客戶端應用中訂閱 URL 的更新間隔(單位:小時)",
|
||
"subEncrypt": "編碼",
|
||
"subEncryptDesc": "訂閱服務返回的內容將採用 Base64 編碼",
|
||
"subURI": "反向代理 URI",
|
||
"subURIDesc": "用於訂閱連結和 QR 碼的完整基礎 URL(scheme://域名[:埠]/路徑/),會取代監聽域名/監聽埠使用。當訂閱透過反向代理存取,或使用與上面不同的域名/埠存取時,請設定此項。",
|
||
"externalTrafficInformEnable": "外部交通通知",
|
||
"externalTrafficInformEnableDesc": "每次流量更新時通知外部 API。",
|
||
"externalTrafficInformURI": "外部流量通知 URI",
|
||
"externalTrafficInformURIDesc": "流量更新將會傳送到此 URI",
|
||
"restartXrayOnClientDisable": "用戶自動停用後重新啟動 Xray",
|
||
"restartXrayOnClientDisableDesc": "當用戶因到期或流量上限而被自動停用時,重新啟動 Xray。",
|
||
"fragment": "分片",
|
||
"fragmentDesc": "啟用 TLS hello 資料包分片",
|
||
"fragmentSett": "設定",
|
||
"noisesDesc": "啟用 Noises.",
|
||
"noisesSett": "Noises 設定",
|
||
"trustedProxyCidrs": "信任代理 CIDR",
|
||
"trustedProxyCidrsDesc": "允許設定轉發 host、proto 與客戶端 IP 標頭的 IP/CIDR(逗號分隔)。",
|
||
"ldap": {
|
||
"enable": "啟用 LDAP 同步",
|
||
"host": "LDAP host",
|
||
"port": "LDAP 連接埠",
|
||
"useTls": "使用 TLS (LDAPS)",
|
||
"skipTlsVerify": "略過 TLS 憑證驗證",
|
||
"skipTlsVerifyDesc": "不安全 — 停用伺服器憑證驗證。僅用於內部/不受信任的 CA。",
|
||
"bindDn": "Bind DN",
|
||
"passwordConfigured": "已設定;留空以保留目前密碼。",
|
||
"passwordUnconfigured": "未設定。",
|
||
"passwordPlaceholder": "已設定 - 輸入新值以取代",
|
||
"baseDn": "Base DN",
|
||
"userFilter": "使用者篩選",
|
||
"userAttr": "使用者屬性 (username/email)",
|
||
"vlessField": "VLESS flag 屬性",
|
||
"flagField": "通用 flag 屬性 (選用)",
|
||
"flagFieldDesc": "若設定,將覆寫 VLESS flag — 如 shadowInactive。",
|
||
"truthyValues": "Truthy 值",
|
||
"truthyValuesDesc": "以逗號分隔;預設: true,1,yes,on",
|
||
"invertFlag": "反轉 flag",
|
||
"invertFlagDesc": "當屬性表示已停用時啟用 (如 shadowInactive)。",
|
||
"syncSchedule": "同步排程",
|
||
"syncScheduleDesc": "類 cron 字串,如 @every 1m",
|
||
"inboundTags": "入站標籤",
|
||
"inboundTagsDesc": "允許 LDAP 同步自動建立或刪除客戶端的入站。",
|
||
"noInbounds": "未找到入站。請先在「入站」中建立。",
|
||
"autoCreate": "自動建立客戶端",
|
||
"autoDelete": "自動刪除客戶端",
|
||
"defaultTotalGb": "預設總流量 (GB)",
|
||
"defaultExpiryDays": "預設到期 (天)",
|
||
"defaultIpLimit": "預設 IP 限制"
|
||
},
|
||
"subFormats": {
|
||
"finalMask": "Final Mask",
|
||
"finalMaskDesc": "將 Xray finalmask TCP/UDP 遮罩和 QUIC 參數注入每個產生的 Xray JSON 設定。需要支援 Xray JSON 訂閱的用戶端應用程式和較新的 Xray 核心。",
|
||
"packets": "封包",
|
||
"length": "長度",
|
||
"interval": "間隔",
|
||
"maxSplit": "最大分割",
|
||
"noises": "雜訊",
|
||
"noiseItem": "雜訊 №{n}",
|
||
"type": "類型",
|
||
"packet": "封包",
|
||
"delayMs": "延遲 (ms)",
|
||
"applyTo": "套用至",
|
||
"addNoise": "+ 雜訊",
|
||
"concurrency": "並發",
|
||
"xudpConcurrency": "xudp 並發",
|
||
"xudpUdp443": "xudp UDP 443"
|
||
},
|
||
"mux": "Mux",
|
||
"muxDesc": "在已建立的資料流內傳輸多個獨立的資料流",
|
||
"muxSett": "複用器設定",
|
||
"direct": "直接連線",
|
||
"directDesc": "直接與特定國家的域或 IP 範圍建立連線",
|
||
"notifications": "通知",
|
||
"certs": "證書",
|
||
"externalTraffic": "外部流量",
|
||
"dateAndTime": "日期和時間",
|
||
"proxyAndServer": "代理和伺服器",
|
||
"intervals": "間隔",
|
||
"information": "資訊",
|
||
"profile": "資料",
|
||
"language": "語言",
|
||
"telegramBotLanguage": "Telegram 機器人語言",
|
||
"security": {
|
||
"admin": "管理員憑證",
|
||
"twoFactor": "雙重驗證",
|
||
"twoFactorEnable": "啟用 2FA",
|
||
"twoFactorEnableDesc": "增加額外的驗證層以提高安全性。",
|
||
"twoFactorModalSetTitle": "啟用雙重認證",
|
||
"twoFactorModalDeleteTitle": "停用雙重認證",
|
||
"twoFactorModalSteps": "要設定雙重認證,請執行以下步驟:",
|
||
"twoFactorModalFirstStep": "1. 在認證應用程式中掃描此 QR 碼,或複製 QR 碼附近的令牌並貼到應用程式中",
|
||
"twoFactorModalSecondStep": "2. 輸入應用程式中的驗證碼",
|
||
"twoFactorModalRemoveStep": "輸入應用程式中的驗證碼以移除雙重認證。",
|
||
"twoFactorModalChangeCredentialsTitle": "更改憑證",
|
||
"twoFactorModalChangeCredentialsStep": "輸入應用程式中的代碼以更改管理員憑證。",
|
||
"twoFactorModalSetSuccess": "雙重身份驗證已成功建立",
|
||
"twoFactorModalDeleteSuccess": "雙重身份驗證已成功刪除",
|
||
"twoFactorModalError": "驗證碼錯誤",
|
||
"show": "顯示",
|
||
"hide": "隱藏",
|
||
"apiTokenNew": "新增令牌",
|
||
"apiTokenName": "名稱",
|
||
"apiTokenNamePlaceholder": "例如 central-panel-a",
|
||
"apiTokenNameRequired": "名稱必填",
|
||
"apiTokenEmpty": "尚無令牌 — 建立一個以認證機器人或遠端面板。",
|
||
"apiTokenDeleteWarning": "使用此令牌的任何呼叫方將立即無法認證。",
|
||
"apiTokenCreatedTitle": "權杖已建立",
|
||
"apiTokenCreatedNotice": "請立即複製此權杖。基於安全考量,它不會以可讀形式儲存,也不會再次顯示。"
|
||
},
|
||
"toasts": {
|
||
"modifySettings": "參數已更改。",
|
||
"getSettings": "取得參數時發生錯誤",
|
||
"modifyUserError": "變更管理員憑證時發生錯誤。",
|
||
"modifyUser": "您已成功變更管理員憑證。",
|
||
"originalUserPassIncorrect": "原使用者名稱或原密碼錯誤",
|
||
"userPassMustBeNotEmpty": "新使用者名稱和新密碼不能為空",
|
||
"getOutboundTrafficError": "取得出站流量錯誤",
|
||
"resetOutboundTrafficError": "重設出站流量錯誤"
|
||
},
|
||
"smtpSettings": "SMTP 設定",
|
||
"smtpEnable": "啟用電子郵件通知",
|
||
"smtpEnableDesc": "透過 SMTP 啟用電子郵件通知",
|
||
"smtpHost": "SMTP 主機",
|
||
"smtpHostDesc": "SMTP 伺服器主機名稱(例如 smtp.gmail.com)",
|
||
"smtpPort": "SMTP 連接埠",
|
||
"smtpPortDesc": "SMTP 伺服器連接埠(預設:587)",
|
||
"smtpUsername": "SMTP 使用者名稱",
|
||
"smtpUsernameDesc": "SMTP 驗證使用者名稱",
|
||
"smtpFrom": "寄件者位址 (From)",
|
||
"smtpFromDesc": "郵件 From 標頭使用的位址。留空則使用使用者名稱。",
|
||
"smtpFromName": "寄件者名稱 (From)",
|
||
"smtpFromNameDesc": "From 標頭中顯示在位址前的可選顯示名稱。",
|
||
"smtpPassword": "SMTP 密碼",
|
||
"smtpPasswordDesc": "SMTP 驗證密碼",
|
||
"smtpTo": "收件人",
|
||
"smtpToDesc": "以逗號分隔的收件人電子郵件地址",
|
||
"emailSettings": "電子郵件",
|
||
"emailNotifications": "通知",
|
||
"smtpEventBusNotify": "電子郵件事件通知",
|
||
"smtpEventBusNotifyDesc": "選擇觸發電子郵件通知的事件",
|
||
"tgEventBusNotify": "Telegram 事件通知",
|
||
"tgEventBusNotifyDesc": "選擇觸發 Telegram 通知的事件",
|
||
"testSmtp": "傳送測試郵件",
|
||
"testTgBot": "傳送測試訊息",
|
||
"eventGroupOutbound": "出站",
|
||
"eventGroupXray": "Xray 核心",
|
||
"eventGroupSystem": "系統",
|
||
"eventGroupSecurity": "安全性",
|
||
"eventGroupNode": "節點",
|
||
"eventOutboundDown": "中斷",
|
||
"eventOutboundUp": "恢復",
|
||
"eventXrayCrash": "當機",
|
||
"eventNodeDown": "離線",
|
||
"eventNodeUp": "上線",
|
||
"eventCPUHigh": "CPU 偏高(%)",
|
||
"requestFailed": "請求失敗",
|
||
"smtpEncryption": "加密",
|
||
"smtpEncryptionDesc": "SMTP 連線加密方式",
|
||
"smtpEncryptionNone": "無(純文字)",
|
||
"smtpEncryptionStartTLS": "STARTTLS",
|
||
"smtpEncryptionTLS": "TLS(隱含)",
|
||
"smtpStageConnect": "連線",
|
||
"smtpStageAuth": "驗證",
|
||
"smtpStageSend": "傳送",
|
||
"smtpTestSuccess": "測試郵件已成功傳送",
|
||
"smtpHostNotConfigured": "尚未設定 SMTP 主機",
|
||
"smtpNoRecipients": "尚未設定收件人",
|
||
"smtpFromNotConfigured": "未設定 SMTP 寄件人地址",
|
||
"eventLoginAttempt": "登入嘗試",
|
||
"telegramTokenConfigured": "已設定;留空以保留目前的權杖。",
|
||
"telegramTokenPlaceholder": "已設定 - 輸入新權杖以取代",
|
||
"smtpPasswordConfigured": "已設定;留空以保留目前的密碼。",
|
||
"smtpPasswordPlaceholder": "已設定 - 輸入新密碼以取代",
|
||
"smtpNotInitialized": "SMTP 尚未初始化",
|
||
"tgBotNotEnabled": "Telegram 機器人未啟用",
|
||
"tgTestFailed": "Telegram 測試失敗",
|
||
"tgTestSuccess": "測試訊息已傳送至 Telegram",
|
||
"tgBotNotRunning": "Telegram 機器人未執行",
|
||
"smtpErrorAuth": "驗證失敗 — 請檢查使用者名稱和密碼",
|
||
"smtpErrorStarttls": "伺服器需要 STARTTLS — 請變更加密類型",
|
||
"smtpErrorTls": "伺服器需要 TLS — 請變更加密類型",
|
||
"smtpErrorRefused": "連線遭拒 — 請檢查主機和連接埠",
|
||
"smtpErrorTimeout": "連線逾時 — 無法連線至主機",
|
||
"smtpErrorRelay": "伺服器拒絕從此地址傳送",
|
||
"smtpErrorEof": "連線已被伺服器關閉",
|
||
"smtpErrorUnknown": "SMTP 錯誤:{{ .Error }}",
|
||
"eventMemoryHigh": "記憶體使用率高 (%)",
|
||
"remarkTemplate": "備註範本",
|
||
"remarkTemplateDesc": "設定後,這將取代每個訂閱連結的備註模型——使用變數標記撰寫您自己的格式(使用按鈕來插入)。留空則使用上方的模型。",
|
||
"subShowIdentityOnAllLinks": "在每個連結上顯示身分",
|
||
"subShowIdentityOnAllLinksDesc": "啟用後,{{EMAIL}} 與 {{USERNAME}} 會保留在每條訂閱正文備註中。用量相關變數仍僅出現在第一條連結。",
|
||
"validation": {
|
||
"pathLeadingSlash": "路徑必須以 / 開頭"
|
||
},
|
||
"secretClear": "清除",
|
||
"secretClearUndo": "復原清除",
|
||
"calendarGregorian": "Gregorian (Standard)",
|
||
"calendarJalalian": "Jalalian (شمسی)",
|
||
"ipLimitAllowlist": "IP 限制白名單",
|
||
"ipLimitAllowlistDesc": "IP 限制永遠不會計入也不會封鎖的位址與網段,避免辦公室或校園的共用位址耗盡客戶端的額度。IP/CIDR(逗號分隔)。",
|
||
"subBalancers": {
|
||
"menu": "訂閱平衡器",
|
||
"title": "訂閱平衡器",
|
||
"add": "新增平衡器",
|
||
"desc": "每個啟用的平衡器會作為額外設定加入 JSON 訂閱,自動從所選入站的端點中挑選最佳節點(用戶端設定中的 routing.balancers + burstObservatory)。",
|
||
"remark": "備註",
|
||
"remarkPlaceholder": "自動 · 最快",
|
||
"strategy": "策略",
|
||
"strategyLeastLoad": "最小負載",
|
||
"strategyLeastPing": "最低延遲",
|
||
"strategyRandom": "隨機",
|
||
"strategyRoundRobin": "輪詢",
|
||
"sortOrder": "順序",
|
||
"sortOrderHelp": "在訂閱列表中的位置,與入站順序交錯排列;序號相同時排在入站之後。",
|
||
"inbounds": "入站",
|
||
"inboundsCount": "{count} 入站",
|
||
"enabled": "啟用",
|
||
"empty": "尚無平衡器",
|
||
"deleteConfirm": "確定刪除此平衡器?",
|
||
"errRemarkRequired": "請填寫備註",
|
||
"errInboundsRequired": "請至少選擇一個入站",
|
||
"errSortOrder": "順序必須為不小於 1 的整數",
|
||
"toasts": {
|
||
"list": "列出訂閱平衡器失敗",
|
||
"create": "建立訂閱平衡器失敗",
|
||
"update": "更新訂閱平衡器失敗",
|
||
"delete": "刪除訂閱平衡器失敗",
|
||
"invalidId": "無效的 id"
|
||
},
|
||
"tabBalancers": "負載均衡",
|
||
"tabObservatory": "觀測器",
|
||
"observatory": {
|
||
"title": "平衡器探活",
|
||
"desc": "寫入每個 leastPing/leastLoad 平衡器設定檔的 burstObservatory 探活參數。random/roundRobin 不產生探活。以面板級 JSON 訂閱設定儲存。",
|
||
"destination": "探活 URL",
|
||
"destinationDesc": "用戶端探測每個成員出站的位址。",
|
||
"connectivity": "連通性 URL",
|
||
"connectivityDesc": "選用位址,檢查成員能否到達探活目標。留空則跳過。",
|
||
"interval": "探活間隔",
|
||
"intervalDesc": "探活輪次之間的時間,例如 1m。",
|
||
"timeout": "探活逾時",
|
||
"timeoutDesc": "單次探活逾時,例如 5s。",
|
||
"sampling": "取樣",
|
||
"samplingDesc": "用於穩定度平均的連續探活次數。",
|
||
"httpMethod": "HTTP 方法",
|
||
"httpMethodDesc": "探活請求使用的 HTTP 方法。",
|
||
"note": "leastPing/leastLoad 平衡器始終帶有 burstObservatory。此開關自訂其探活參數 — 關閉以使用內建預設值。變更在面板重啟後生效。"
|
||
}
|
||
}
|
||
},
|
||
"xray": {
|
||
"save": "儲存",
|
||
"restartSuccess": "Xray 已成功重新啟動",
|
||
"stopSuccess": "Xray 已成功停止",
|
||
"restartError": "重新啟動 Xray 時發生錯誤。",
|
||
"stopError": "停止 Xray 時發生錯誤。",
|
||
"importRules": "匯入規則",
|
||
"exportRules": "匯出規則",
|
||
"importOutbounds": "匯入出站",
|
||
"exportOutbounds": "匯出出站",
|
||
"importInvalidJson": "無效的 JSON——預期為陣列或含有相符鍵的物件。",
|
||
"metricsListen": "Metrics 端點",
|
||
"metricsListenDesc": "在此 address:port 上公開 Xray 的 Prometheus 風格 metrics(例如 127.0.0.1:11111)。留空則停用。請綁定 localhost 並以反向代理轉發——此端點未經驗證。",
|
||
"metricsTag": "Metrics 標籤",
|
||
"basicTemplate": "基礎配置",
|
||
"advancedTemplate": "高階配置",
|
||
"generalConfigs": "常規配置",
|
||
"generalConfigsDesc": "這些選項將決定常規配置",
|
||
"logConfigs": "記錄",
|
||
"logConfigsDesc": "日誌可能會影響伺服器的效能,建議僅在需要時啟用",
|
||
"basicRouting": "基本路由",
|
||
"blockConnectionsConfigsDesc": "這些選項將根據特定的請求國家阻止流量。",
|
||
"directConnectionsConfigsDesc": "直接連線確保特定的流量不會通過其他伺服器路由。",
|
||
"blockips": "阻止 IP",
|
||
"blockdomains": "阻止域名",
|
||
"directips": "直接 IP",
|
||
"directdomains": "直接域名",
|
||
"ipv4Routing": "IPv4 路由",
|
||
"ipv4RoutingDesc": "此選項將僅通過 IPv4 路由到目標域",
|
||
"Template": "高階 Xray 配置模板",
|
||
"TemplateDesc": "最終的 Xray 配置檔案將基於此模板生成",
|
||
"FreedomStrategy": "Freedom 協議策略",
|
||
"FreedomStrategyDesc": "設定 Freedom 協議中網路的輸出策略",
|
||
"FreedomHappyEyeballs": "Freedom Happy Eyeballs (IPv4/IPv6)",
|
||
"FreedomHappyEyeballsDesc": "為直連(freedom)出站啟用雙協定堆疊撥號——在同時具備 IPv4 與 IPv6 的出口伺服器上很有用。",
|
||
"FreedomHappyEyeballsTryDelayDesc": "嘗試備用位址族之前等待的毫秒數。150–250 毫秒是不錯的起點。",
|
||
"RoutingStrategy": "配置路由域策略",
|
||
"RoutingStrategyDesc": "設定 DNS 解析的整體路由策略",
|
||
"outboundTestUrl": "出站測試 URL",
|
||
"outboundTestUrlDesc": "測試出站連線時使用的 URL",
|
||
"Torrent": "遮蔽 BitTorrent 協議",
|
||
"Inbounds": "入站",
|
||
"Outbounds": "出站",
|
||
"Balancers": "負載均衡",
|
||
"balancerTagRequired": "標籤為必填",
|
||
"balancerSelectorRequired": "至少選擇一個出站",
|
||
"balancerLive": "目前目標",
|
||
"balancerOverride": "強制指定",
|
||
"balancerOverridePh": "自動(策略)",
|
||
"balancerLiveRefresh": "重新整理負載均衡器即時狀態",
|
||
"balancerNotRunning": "此負載均衡器在執行中的 Xray 未啟用 — 請先儲存變更或啟動 Xray",
|
||
"routeTester": "路由測試",
|
||
"routeTesterDesc": "向執行中的 Xray 查詢某個連線將使用哪個出站。不會傳送真實流量 — 結果直接來自即時路由引擎。",
|
||
"routeTesterDest": "網域或 IP",
|
||
"routeTesterPort": "埠號",
|
||
"routeTesterInbound": "入站",
|
||
"routeTesterProtocol": "嗅探協議",
|
||
"routeTesterTest": "測試路由",
|
||
"routeTesterMatchedOutbound": "匹配出站",
|
||
"routeTesterViaBalancer": "經由負載均衡器",
|
||
"routeTesterDefaultOutbound": "無路由規則匹配 — 流量將導向預設(第一個)出站。",
|
||
"Routings": "路由規則",
|
||
"completeTemplate": "全部",
|
||
"logLevel": "日誌級別",
|
||
"logLevelDesc": "錯誤日誌的日誌級別,用於指示需要記錄的資訊",
|
||
"accessLog": "訪問日誌",
|
||
"accessLogDesc": "訪問日誌的檔案路徑。特殊值 'none' 禁用訪問日誌",
|
||
"errorLog": "錯誤日誌",
|
||
"errorLogDesc": "錯誤日誌的檔案路徑。特殊值 'none' 禁用錯誤日誌",
|
||
"dnsLog": "DNS 日誌",
|
||
"dnsLogDesc": "是否啟用 DNS 查詢日誌",
|
||
"maskAddress": "隱藏地址",
|
||
"maskAddressDesc": "IP 地址掩碼,啟用時會自動替換日誌中出現的 IP 地址。",
|
||
"statistics": "統計",
|
||
"statsInboundUplink": "入站上傳統計",
|
||
"statsInboundDownlink": "入站下載統計",
|
||
"statsOutboundUplink": "出站上傳統計",
|
||
"statsOutboundDownlink": "出站下載統計",
|
||
"connectionLimits": "連線限制",
|
||
"connectionLimitsDesc": "使用者等級 0 的連線層級原則。留空則使用 Xray 的預設值。",
|
||
"connIdle": "閒置逾時",
|
||
"connIdleDesc": "連線閒置達到該秒數後將被關閉。在繁忙的伺服器上調低此值可更快釋放記憶體和檔案描述符(Xray 預設值:300)。",
|
||
"bufferSize": "緩衝區大小",
|
||
"bufferSizeDesc": "每個連線的內部緩衝區大小(KB)。在低記憶體伺服器上設為 0 可最大限度減少記憶體佔用(Xray 預設值取決於平台)。",
|
||
"bufferSizePlaceholder": "自動",
|
||
"seconds": "秒",
|
||
"rules": {
|
||
"source": "來源",
|
||
"dest": "目的地址",
|
||
"inbound": "入站",
|
||
"balancer": "負載均衡",
|
||
"useComma": "逗號分隔的項目"
|
||
},
|
||
"routing": {
|
||
"dragToReorder": "拖曳以重新排序"
|
||
},
|
||
"geoBrowser": {
|
||
"title": "geo 分類",
|
||
"openTooltip": "瀏覽 geo 分類",
|
||
"database": "資料庫",
|
||
"searchCategory": "搜尋分類",
|
||
"searchEntries": "在分類內篩選",
|
||
"selectFound": "勾選搜尋結果",
|
||
"selected": "已選 {count} 項",
|
||
"clearAll": "全部清除",
|
||
"apply": "套用",
|
||
"emptySelection": "勾選分類,它們將成為規則中的項目",
|
||
"pickCategory": "在左側選擇一個分類以查看其內容",
|
||
"noMatches": "找不到任何內容",
|
||
"noFiles": "Xray 目錄中沒有 geo 資料庫",
|
||
"noFilesHint": "Xray 下載 geosite.dat 與 geoip.dat 後即會出現",
|
||
"fileMeta": "{count} 個分類 · {size} · 更新於 {date}",
|
||
"entriesCount": "{count} 筆記錄",
|
||
"subnetsCount": "{count} 個網段",
|
||
"shownRange": "顯示第 {from}–{to} 項,共 {total} 項",
|
||
"loadFailed": "無法載入 geo 資料庫",
|
||
"checkFailed": "無法依據 geo 資料庫檢查這些值",
|
||
"parseFailed": "檔案已損毀或不是 geosite/geoip 資料庫",
|
||
"tooLarge": "檔案過大,無法瀏覽",
|
||
"unknownCategories": "資料庫中不存在:{tokens}",
|
||
"missingDatabase": "找不到資料庫檔案:{tokens} — 請在 Geodata 中新增",
|
||
"unknownAttribute": "找不到該屬性,規則不會比對到任何內容:{tokens}",
|
||
"invalidToken": "Xray 無法接受此寫法:{tokens}",
|
||
"wrongKind": "此欄位的資料庫類型不符:{tokens}"
|
||
},
|
||
"ruleForm": {
|
||
"sourceIps": "來源 IP",
|
||
"sourcePort": "來源連接埠",
|
||
"vlessRoute": "VLESS 路由",
|
||
"attributes": "屬性",
|
||
"value": "值",
|
||
"user": "使用者",
|
||
"userPlaceholder": "選擇使用者",
|
||
"userEmpty": "暫無可用使用者",
|
||
"userLoadError": "無法載入使用者",
|
||
"inboundTags": "入站標籤",
|
||
"outboundTag": "出站標籤",
|
||
"balancerTag": "均衡器標籤",
|
||
"balancerTagTooltip": "透過其中一個已設定的負載均衡器路由流量"
|
||
},
|
||
"outboundForm": {
|
||
"tagDuplicate": "該標籤已被其他出站使用",
|
||
"tagRequired": "標籤為必填",
|
||
"tagPlaceholder": "唯一標籤",
|
||
"localIpPlaceholder": "本地 IP",
|
||
"dialerProxyPlaceholder": "選擇要串接的出站",
|
||
"dialerProxyHint": "讓此出站透過另一個出站(以標籤指定)連線,以建立代理鏈。留空則直接連線。",
|
||
"targetStrategyHint": "連線前如何解析目標網域:AsIs(預設)原樣傳送,UseIP… 解析失敗時回退,ForceIP… 必須解析成功。",
|
||
"addressRequired": "地址為必填",
|
||
"portRequired": "連接埠為必填",
|
||
"optional": "選用",
|
||
"udpOverTcp": "UDP over TCP",
|
||
"uotVersion": "UoT 版本",
|
||
"inboundTag": "入站標籤",
|
||
"inboundTagPlaceholder": "用於路由規則的入站標籤",
|
||
"responseType": "回應類型",
|
||
"rewriteNetwork": "改寫網路",
|
||
"unchanged": "(未變更)",
|
||
"unchangedAddress": "(未變更) 如 1.1.1.1",
|
||
"rules": "規則",
|
||
"ruleN": "規則 {n}",
|
||
"action": "動作",
|
||
"redirect": "Redirect",
|
||
"finalRules": "最終規則",
|
||
"overrideXrayPrivateIp": "覆寫 Xray 預設的私有 IP 封鎖",
|
||
"blockDelay": "阻斷延遲 (ms)",
|
||
"reverseSniffing": "反向 sniffing",
|
||
"reserved": "保留",
|
||
"minUploadInterval": "最小上傳間隔 (ms)",
|
||
"maxUploadSizeBytes": "最大上傳大小 (位元組)",
|
||
"uplinkChunkSize": "Uplink chunk 大小",
|
||
"noGrpcHeader": "無 gRPC 標頭",
|
||
"maxConcurrency": "最大並發",
|
||
"maxConnections": "最大連線",
|
||
"maxReuseTimes": "最大重用次數",
|
||
"maxRequestTimes": "最大請求次數",
|
||
"maxReusableSecs": "最大可重用秒數",
|
||
"keepAlivePeriod": "keep alive 週期",
|
||
"authPassword": "認證密碼",
|
||
"visionTestpre": "Vision testpre",
|
||
"serverNamePlaceholder": "伺服器名稱",
|
||
"verifyPeerName": "驗證 peer 名稱",
|
||
"pinnedSha256": "Pinned SHA256",
|
||
"shortId": "Short ID",
|
||
"sockopts": "Sockopts",
|
||
"keepAliveInterval": "keep alive 間隔",
|
||
"markFwmark": "Mark (fwmark)",
|
||
"interface": "介面",
|
||
"proxyProtocol": "Proxy protocol",
|
||
"tcpUserTimeoutMs": "TCP user timeout (ms)",
|
||
"tcpKeepAliveIdleS": "TCP keep-alive idle (s)"
|
||
},
|
||
"outbound": {
|
||
"tag": "標籤",
|
||
"egress": "出口",
|
||
"egressHint": "執行 HTTP 測試以顯示出口 IP 與所在國家/地區。",
|
||
"outboundStatus": "出站狀態",
|
||
"sendThrough": "傳送通過",
|
||
"targetStrategy": "目標解析策略",
|
||
"modeRealDelay": "真實延遲",
|
||
"testModeTooltip": "TCP: 快速 dial-only 探測。HTTP: 透過 xray 的完整請求。真實延遲: 含建立連線的總耗時。",
|
||
"testAll": "全部測試",
|
||
"httpStatus": "HTTP 狀態",
|
||
"breakdownConnect": "代理連線",
|
||
"breakdownTls": "經由出站的 TLS",
|
||
"breakdownTtfb": "首位元組",
|
||
"country": "國家",
|
||
"server": "伺服器",
|
||
"city": "城市",
|
||
"allCities": "所有城市",
|
||
"moveToTop": "移到頂部"
|
||
},
|
||
"outboundSub": {
|
||
"manage": "訂閱",
|
||
"title": "出站訂閱",
|
||
"remark": "備註(選填)",
|
||
"remarkPlaceholder": "例如:香港節點",
|
||
"url": "訂閱 URL",
|
||
"urlPlaceholder": "https://...(base64 連結清單)",
|
||
"tagPrefix": "標籤前綴",
|
||
"tagPrefixPlaceholder": "hk-",
|
||
"interval": "更新間隔",
|
||
"hours": "時",
|
||
"minutes": "分",
|
||
"intervalHint": "預設為 10 分鐘。背景工作會頻繁檢查;每個訂閱只在自己的間隔到期後才重新抓取。",
|
||
"enabled": "啟用",
|
||
"allowPrivate": "允許私有位址",
|
||
"allowPrivateHint": "允許此訂閱的 URL 使用 localhost/區域網路(LAN)/私有 IP。基於安全考量預設為關閉,請僅在來源為受信任的本機時才啟用。",
|
||
"prepend": "置於手動出站之前",
|
||
"prependHint": "將此訂閱的出站排在您手動設定的出站之前,讓其中之一可成為預設出站。",
|
||
"preview": "預覽",
|
||
"previewEmpty": "在此 URL 找不到任何出站。",
|
||
"refreshAll": "全部重新整理",
|
||
"statusOk": "正常",
|
||
"toastUpdated": "訂閱已更新",
|
||
"addButton": "新增",
|
||
"active": "啟用中的訂閱",
|
||
"empty": "尚無訂閱。請從上方新增。",
|
||
"colRemark": "備註",
|
||
"colLastFetch": "上次抓取",
|
||
"colEnabled": "啟用",
|
||
"auto": "自動",
|
||
"never": "從不",
|
||
"refreshNow": "立即重新整理",
|
||
"deleteConfirm": "確定要刪除此訂閱嗎?",
|
||
"restartHint": "新增或重新整理後,請重新啟動 Xray(或等待下次自動重新載入),讓出站生效。",
|
||
"fromSubsTitle": "來自出站訂閱(唯讀)",
|
||
"fromSubsDesc": "從您啟用中的訂閱匯入。請於上方的「訂閱」面板中管理。",
|
||
"toastLoadFailed": "載入訂閱失敗",
|
||
"toastUrlRequired": "訂閱 URL 為必填",
|
||
"toastAdded": "訂閱已新增",
|
||
"toastAddFailed": "新增訂閱失敗",
|
||
"toastRefreshed": "已重新整理",
|
||
"toastRefreshFailed": "重新整理失敗",
|
||
"toastDeleted": "已刪除",
|
||
"toastDeleteFailed": "刪除失敗"
|
||
},
|
||
"pia": {
|
||
"menu": "PIA",
|
||
"username": "PIA 使用者名稱",
|
||
"password": "PIA 密碼",
|
||
"account": "帳號",
|
||
"region": "區域",
|
||
"allRegions": "全部區域",
|
||
"noServers": "所選國家沒有可用伺服器",
|
||
"outboundAdded": "已新增 PIA 出站",
|
||
"outboundUpdated": "已更新 PIA 出站",
|
||
"addedServers": "已新增的伺服器",
|
||
"alreadyAdded": "此伺服器已在出站清單中。若要換新金鑰請按 {reset}。",
|
||
"provisionFailed": "無法產生 PIA 出站,請重試。"
|
||
},
|
||
"tabBalancerSettings": "負載平衡設定",
|
||
"tabObservatory": "觀測器",
|
||
"observatory": {
|
||
"autoManaged": "觀測器會根據你的負載平衡器自動管理。可在下方調整探測方式;被觀測的出站會跟隨負載平衡器的選擇器。",
|
||
"emptyHint": "目前沒有作用中的連線觀測器。當你建立 Least Ping 或 Least Load 負載平衡器,或帶有 fallback 的 Random / Round-robin 負載平衡器時,會自動新增一個,讓依賴觀測器的負載平衡器能在選擇目標前檢查出站健康狀態。",
|
||
"mixedLegacy": "此設定同時包含 Observatory 與 Burst Observatory。Xray 只使用一個全域觀測器,因此不支援這種舊式混合狀態;儲存負載平衡器時會將其正規化為單一觀測器。",
|
||
"subjectSelector": "被觀測的出站",
|
||
"subjectSelectorDesc": "此觀測器探測的出站標籤。會根據你的負載平衡器自動管理。",
|
||
"probeURL": "探測 URL",
|
||
"probeURLDesc": "用於量測每個出站而請求的 URL,應回傳 HTTP 204。",
|
||
"probeInterval": "探測間隔",
|
||
"probeIntervalDesc": "每個出站的探測頻率,例如 30s、1m、2h45m。",
|
||
"enableConcurrency": "並行探測",
|
||
"enableConcurrencyDesc": "一次探測所有被觀測的出站,而非逐一探測。較快,但在網路上更明顯。",
|
||
"destination": "探測目標",
|
||
"destinationDesc": "用於量測每個出站而請求的 URL,應回傳 HTTP 204。",
|
||
"connectivity": "連線檢查",
|
||
"connectivityDesc": "選用的本機網路檢查 URL,僅在目標失敗後才嘗試。留空則略過。",
|
||
"interval": "探測間隔",
|
||
"intervalDesc": "每個出站兩次探測之間的平均時間,例如 1m。最小 10s。",
|
||
"timeout": "探測逾時",
|
||
"timeoutDesc": "判定探測失敗前的等待時間,例如 5s。",
|
||
"sampling": "取樣數量",
|
||
"samplingDesc": "為每個出站評分而保留的最近探測結果數量。",
|
||
"httpMethod": "HTTP 方法",
|
||
"httpMethodDesc": "探測所用的 HTTP 方法。",
|
||
"deleteAlsoObservatory": "這是最後一個使用 Observatory 的負載平衡器,因此它也會一併被移除。",
|
||
"deleteAlsoBurst": "這是最後一個使用 Burst Observatory 的負載平衡器,因此它也會一併被移除。"
|
||
},
|
||
"refCleanup": {
|
||
"header": "刪除此項也會更新你的路由:",
|
||
"ruleRemoved": "規則 {label} — 已移除(沒有剩餘出口)",
|
||
"ruleModified": "規則 {label} — 已保留(現使用 {keeps})",
|
||
"balancerRemoved": "負載平衡器 {tag} — 已移除(沒有剩餘目標)"
|
||
},
|
||
"balancer": {
|
||
"balancerStrategy": "策略",
|
||
"tag": "標籤",
|
||
"tagDuplicate": "該標籤已被其他均衡器使用",
|
||
"tagPlaceholder": "唯一均衡器標籤",
|
||
"selector": "選擇器",
|
||
"fallback": "備用負載平衡器",
|
||
"cycleTooltip": "循環: {path} → (回到 {start})",
|
||
"expected": "期望",
|
||
"expectedPlaceholder": "最佳節點數",
|
||
"maxRtt": "最大 RTT",
|
||
"tolerance": "容差",
|
||
"baselines": "基準值",
|
||
"costs": "成本設定",
|
||
"costMatch": "標籤比對模式",
|
||
"costValue": "權重",
|
||
"costRegexp": "正規表示式比對",
|
||
"balancerDeleteInUse": "無法刪除此負載平衡器 — 它被用作以下負載平衡器的備用:{names}",
|
||
"balancerFallbackCycle": "無法將此負載平衡器設定為備用 — 這會建立循環依賴。",
|
||
"balancerFallbackInfo": "流量將透過以下路徑路由:負載平衡器 → Loopback → 伺服器 → 目標負載平衡器 → 出站連線。這會增加一個經過伺服器的額外跳轉,可能會引入輕微延遲。",
|
||
"fallbackBalancerHint": "選擇另一個負載平衡器作為備用",
|
||
"reservedPrefix": "_bl_ 前綴保留給內部負載均衡器迴圈物件"
|
||
},
|
||
"wireguard": {
|
||
"secretKey": "金鑰",
|
||
"publicKey": "公鑰",
|
||
"subnetIp": "子網路",
|
||
"subnetCidr": "子網路 CIDR",
|
||
"allowedIPs": "允許的 IP",
|
||
"endpoint": "端點",
|
||
"domainStrategy": "域策略"
|
||
},
|
||
"amneziawg": {
|
||
"privateKey": "私鑰",
|
||
"publicKey": "公鑰",
|
||
"subnetIp": "子網路",
|
||
"subnetCidr": "子網路 CIDR",
|
||
"mtu": "MTU",
|
||
"primaryDns": "主要 DNS",
|
||
"secondaryDns": "次要 DNS",
|
||
"externalInterface": "外部網路介面",
|
||
"externalInterfaceHint": "用於 NAT(PostUp/PostDown)的主機網路介面。留空則自動偵測。",
|
||
"ipv6Enabled": "啟用 IPv6",
|
||
"ipv6Subnet": "IPv6 子網路",
|
||
"ipv6SubnetHint": "例如 fd86:ea04:1115::/64。啟用 IPv6 時必填。",
|
||
"ipv6ExternalInterface": "IPv6 外部網路介面",
|
||
"ipv6ExternalInterfaceHint": "用於 NDP 代理項目的主機網路介面。留空則使用外部網路介面。",
|
||
"obfuscation": "混淆參數",
|
||
"regenerateObfuscation": "重新產生",
|
||
"jc": "Jc(垃圾封包數量)",
|
||
"jmin": "Jmin(垃圾封包最小大小)",
|
||
"jmax": "Jmax(垃圾封包最大大小)",
|
||
"s1": "S1(init 封包填充大小)",
|
||
"s2": "S2(response 封包填充大小)",
|
||
"s3": "S3(cookie reply 填充)",
|
||
"s4": "S4(傳輸封包填充)",
|
||
"h1": "H1(魔術標頭)",
|
||
"h2": "H2(魔術標頭)",
|
||
"h3": "H3(魔術標頭)",
|
||
"h4": "H4(魔術標頭)",
|
||
"hHint": "單一整數或範圍。留空則使用經典預設值 1/2/3/4。",
|
||
"i1": "I1(簽章封包)",
|
||
"i1Hint": "可選的簽章封包。留空則不傳送。",
|
||
"i2": "I2(簽章封包)",
|
||
"i3": "I3(簽章封包)",
|
||
"i4": "I4(簽章封包)",
|
||
"i5": "I5(簽章封包)",
|
||
"headerProtectionKey": "HeaderProtectionKey(標頭保護)",
|
||
"headerProtectionKeyHint": "Base64 編碼的 32 位元組金鑰;必須與每個用戶端設定一致。留空則停用標頭保護。",
|
||
"contentPaddingAddition": "ContentPaddingAddition(內容填充)",
|
||
"contentPaddingAdditionHint": "單一整數或 low-high 位元組範圍,附加到內容封包。留空則停用。",
|
||
"rekeyAfterTime": "RekeyAfterTime(秒)",
|
||
"rekeyTimeout": "RekeyTimeout(秒)",
|
||
"rejectAfterTime": "RejectAfterTime(秒)",
|
||
"keepaliveTimeout": "KeepaliveTimeout(秒)",
|
||
"maxHandshakeAttempts": "MaxHandshakeAttempts",
|
||
"timingRangeHint": "單一整數或範圍。留空則使用 WireGuard 預設值。",
|
||
"maxHandshakeAttemptsHint": "放棄前的交握重試次數。留空則使用預設值。",
|
||
"randomTrailers": "RandomTrailers",
|
||
"randomTrailersHint": "在每個封包結尾附加隨機位元組。兩端都需要 AmneziaWG 3.1+。",
|
||
"disableCookies": "DisableCookies",
|
||
"disableCookiesHint": "永不傳送 cookie reply——消除一個 DPI 指紋,但會削弱抗洪泛能力。"
|
||
},
|
||
"tun": {
|
||
"userLevel": "用戶級別"
|
||
},
|
||
"nord": {
|
||
"accessToken": "存取權杖",
|
||
"privateKey": "私鑰",
|
||
"noServers": "未找到選定國家/地區的伺服器",
|
||
"noPublicKey": "選定的伺服器未公布 NordLynx 公鑰。",
|
||
"outboundAdded": "NordVPN 出站已新增",
|
||
"outboundUpdated": "NordVPN 出站已更新"
|
||
},
|
||
"warp": {
|
||
"changeIp": "更換 IP",
|
||
"changeIpSuccess": "WARP IP 更換成功!",
|
||
"autoUpdateIp": "自動更新 IP 位址",
|
||
"intervalDays": "間隔(天)",
|
||
"intervalDesc": "設為 0 停用。自動更換 IP 位址。",
|
||
"licenseError": "設定 WARP 授權失敗。",
|
||
"fetchFirst": "請先取得 WARP 設定。",
|
||
"createAccount": "建立 WARP 帳號",
|
||
"accessToken": "存取權杖",
|
||
"deviceId": "裝置 ID",
|
||
"licenseKey": "授權金鑰",
|
||
"privateKey": "私鑰",
|
||
"deleteAccount": "刪除帳號",
|
||
"settings": "設定",
|
||
"licenseKeyLabel": "WARP / WARP+ 授權金鑰",
|
||
"key": "金鑰",
|
||
"keyPlaceholder": "26 位 WARP+ 金鑰",
|
||
"accountInfo": "帳號資訊",
|
||
"deviceName": "裝置名稱",
|
||
"deviceModel": "裝置型號",
|
||
"deviceEnabled": "裝置已啟用",
|
||
"accountType": "帳號類型",
|
||
"role": "角色",
|
||
"warpPlusData": "WARP+ 資料",
|
||
"quota": "配額",
|
||
"usage": "使用",
|
||
"addOutbound": "新增出站"
|
||
},
|
||
"dns": {
|
||
"enable": "啟用 DNS",
|
||
"enableDesc": "啟用內建 DNS 伺服器",
|
||
"tag": "DNS 入站標籤",
|
||
"tagDesc": "此標籤將在路由規則中可用作入站標籤",
|
||
"clientIp": "客戶端 IP",
|
||
"clientIpDesc": "用於在 DNS 查詢期間通知伺服器指定的 IP 位置",
|
||
"disableCache": "禁用快取",
|
||
"disableCacheDesc": "禁用 DNS 快取",
|
||
"disableFallback": "禁用回退",
|
||
"disableFallbackDesc": "禁用回退 DNS 查詢",
|
||
"disableFallbackIfMatch": "匹配時禁用回退",
|
||
"disableFallbackIfMatchDesc": "當 DNS 伺服器的匹配域名列表命中時,禁用回退 DNS 查詢",
|
||
"enableParallelQuery": "啟用並行查詢",
|
||
"enableParallelQueryDesc": "啟用並行 DNS 查詢到多個伺服器以實現更快的解析",
|
||
"strategy": "查詢策略",
|
||
"strategyDesc": "解析域名的總體策略",
|
||
"add": "新增伺服器",
|
||
"edit": "編輯伺服器",
|
||
"domains": "網域",
|
||
"expectIPs": "預期 IP",
|
||
"unexpectIPs": "意外 IP",
|
||
"useSystemHosts": "使用系統 Hosts",
|
||
"useSystemHostsDesc": "使用已安裝系統的 hosts 檔案",
|
||
"serveStale": "提供過期結果",
|
||
"serveStaleDesc": "在背景重新整理時傳回過期的快取結果",
|
||
"serveExpiredTTL": "過期 TTL",
|
||
"serveExpiredTTLDesc": "過期快取項目的有效期(秒);0 = 永不過期",
|
||
"timeoutMs": "逾時 (毫秒)",
|
||
"skipFallback": "跳過回退",
|
||
"finalQuery": "最終查詢",
|
||
"hosts": "主機對應",
|
||
"hostsAdd": "新增 Host",
|
||
"hostsEmpty": "未定義任何 Host",
|
||
"hostsDomain": "網域 (例如 domain:example.com)",
|
||
"hostsValues": "IP 或網域 — 輸入後按 Enter",
|
||
"usePreset": "使用範本",
|
||
"dnsPresetTitle": "DNS 範本",
|
||
"dnsPresetFamily": "家庭",
|
||
"clearAll": "全部刪除",
|
||
"clearAllTitle": "刪除所有 DNS 伺服器?",
|
||
"clearAllConfirm": "此操作將從清單中刪除所有 DNS 伺服器,無法復原。",
|
||
"dnsLeakWarning": "DNS 可能透過 localhost、明文 UDP/TCP、本機模式 DoH/DoQ、回退查詢或 EDNS client IP 洩漏。重視隱私時請使用經路由的 DoH、在 hosts 固定解析器,並停用回退。"
|
||
},
|
||
"fakedns": {
|
||
"add": "新增假 DNS",
|
||
"ipPool": "IP 池子網",
|
||
"poolSize": "池大小"
|
||
},
|
||
"defaultOutbound": "預設出站",
|
||
"defaultOutboundDesc": "未符合任何路由規則的流量走此出站(清單中的第一個出站)。"
|
||
},
|
||
"hosts": {
|
||
"addHost": "新增 Host",
|
||
"editHost": "編輯 Host",
|
||
"selectInbound": "選擇一個入站",
|
||
"selectedCount": "已選 {count} 項",
|
||
"summary": {
|
||
"total": "總數",
|
||
"enabled": "已啟用",
|
||
"disabled": "已停用"
|
||
},
|
||
"moveUp": "上移",
|
||
"moveDown": "下移",
|
||
"bulkEnable": "啟用",
|
||
"bulkDisable": "停用",
|
||
"bulkDelete": "刪除",
|
||
"bulkDeleteConfirm": "刪除所選的 {count} 個 Host?",
|
||
"deleteConfirmTitle": "刪除 Host「{name}」?",
|
||
"sections": {
|
||
"basic": "基本",
|
||
"security": "安全",
|
||
"advanced": "進階",
|
||
"general": "一般",
|
||
"clash": "Clash (mihomo)"
|
||
},
|
||
"fields": {
|
||
"remark": "備註",
|
||
"serverDescription": "描述",
|
||
"inbound": "入站",
|
||
"address": "地址",
|
||
"port": "連接埠",
|
||
"endpoint": "端點",
|
||
"enable": "啟用",
|
||
"actions": "操作",
|
||
"security": "安全",
|
||
"sni": "SNI",
|
||
"overrideSniFromAddress": "使用地址作為 SNI",
|
||
"keepSniBlank": "保持 SNI 留空",
|
||
"hostHeader": "Host 標頭",
|
||
"path": "路徑",
|
||
"alpn": "ALPN",
|
||
"fingerprint": "指紋",
|
||
"pins": "釘選憑證 SHA-256",
|
||
"verifyPeerCertByName": "依名稱驗證對端憑證",
|
||
"allowInsecure": "允許不安全連線",
|
||
"echConfigList": "ECH 設定清單",
|
||
"muxParams": "Mux",
|
||
"sockoptParams": "Sockopt",
|
||
"finalMask": "Final Mask",
|
||
"vlessRoute": "VLESS 路由",
|
||
"mihomoIpVersion": "IP 版本",
|
||
"mihomoX25519": "Mihomo X25519",
|
||
"shuffleHost": "隨機排序 Host",
|
||
"tags": "標籤",
|
||
"nodeGuids": "節點",
|
||
"excludeFromSubTypes": "從格式中排除",
|
||
"inheritAddress": "繼承地址"
|
||
},
|
||
"hints": {
|
||
"address": "留空以繼承入站本身的地址。",
|
||
"port": "0 表示繼承入站的連接埠。",
|
||
"tags": "對終端使用者不可見;僅隨 RAW 訂閱傳送。只能使用大寫字母、數字、_ 和 :。",
|
||
"nodeGuids": "選擇由此 Host 解析而來的節點。僅為視覺上的指派。",
|
||
"serverDescription": "顯示於備註下方的選填註記。",
|
||
"allowInsecure": "略過 TLS 憑證驗證(allowInsecure / skip-cert-verify)。",
|
||
"vlessRoute": "嵌入 UUID 的單一 VLESS 路由值(0-65535),例如 443。留空表示無。",
|
||
"remark": "此 Host 的純文字標籤。僅當入站本身沒有備註時,才作為配置名稱顯示。"
|
||
},
|
||
"remarkVars": {
|
||
"title": "範本變數",
|
||
"intro": "點擊變數以加入。產生訂閱時會針對每個客戶端進行替換。",
|
||
"preview": "預覽",
|
||
"groups": {
|
||
"client": "客戶端",
|
||
"traffic": "流量",
|
||
"time": "時間與狀態",
|
||
"connection": "連線"
|
||
},
|
||
"descEMAIL": "客戶端電子郵件",
|
||
"descINBOUND": "入站本身的備註(配置名稱)",
|
||
"descHOST": "Host 備註",
|
||
"descID": "客戶端 UUID",
|
||
"descSHORT_ID": "UUID 的前 8 個字元",
|
||
"descTELEGRAM_ID": "客戶端的 Telegram ID(未設定則為空)",
|
||
"descSUB_ID": "訂閱 ID",
|
||
"descCOMMENT": "客戶端備註",
|
||
"descTRAFFIC_USED": "已用流量(易讀格式)",
|
||
"descTRAFFIC_LEFT": "剩餘流量(無限制時隱藏)",
|
||
"descTRAFFIC_TOTAL": "總流量(無限制時隱藏)",
|
||
"descTRAFFIC_USED_BYTES": "已用流量(位元組)",
|
||
"descTRAFFIC_LEFT_BYTES": "剩餘流量(位元組)",
|
||
"descTRAFFIC_TOTAL_BYTES": "總流量(位元組)",
|
||
"descUP": "上傳流量",
|
||
"descDOWN": "下載流量",
|
||
"descSTATUS": "active / expired / disabled / depleted",
|
||
"descSTATUS_EMOJI": "以表情符號表示的狀態(✅ ⏳ 🚫)",
|
||
"descDAYS_LEFT": "距到期天數(無限制時隱藏)",
|
||
"descTIME_LEFT": "剩餘時間(例如 12d 4h 30m)",
|
||
"descUSAGE_PERCENTAGE": "已用流量百分比(無限制時隱藏)",
|
||
"descEXPIRE_DATE": "到期日期(YYYY-MM-DD)",
|
||
"descJALALI_EXPIRE_DATE": "Jalali 曆的到期日期(YYYY/MM/DD)",
|
||
"descEXPIRE_UNIX": "到期時間(Unix 時間戳記,秒)",
|
||
"descCREATED_UNIX": "建立時間(Unix 時間戳記,秒)",
|
||
"descRESET_DAYS": "流量重置週期(天)",
|
||
"descRESET_DAY": "按月續期的日期",
|
||
"descPROTOCOL": "入站協定(VLESS、VMess、Trojan…)",
|
||
"descTRANSPORT": "傳輸網路(tcp、ws、grpc…)",
|
||
"descSECURITY": "傳輸安全(TLS、REALITY、NONE)"
|
||
},
|
||
"toasts": {
|
||
"list": "載入 Host 失敗",
|
||
"obtain": "載入 Host 失敗",
|
||
"add": "新增 Host",
|
||
"update": "更新 Host",
|
||
"delete": "刪除 Host",
|
||
"badTag": "無效的標籤",
|
||
"badVlessRoute": "請輸入 0 到 65535 之間的單一數字"
|
||
}
|
||
}
|
||
},
|
||
"tgbot": {
|
||
"keyboardClosed": "❌ 自定義鍵盤已關閉!",
|
||
"noResult": "❗ 沒有結果!",
|
||
"noQuery": "❌ 未找到查詢!請再次使用該命令!",
|
||
"wentWrong": "❌ 出了點問題!",
|
||
"noIpRecord": "❗ 沒有 IP 記錄!",
|
||
"noInbounds": "❗ 未找到入站!",
|
||
"unlimited": "♾ 無限(重置)",
|
||
"add": "添加",
|
||
"month": "月",
|
||
"months": "月",
|
||
"days": "天",
|
||
"hours": "小時",
|
||
"minutes": "分鐘",
|
||
"unknown": "未知",
|
||
"inbounds": "入站",
|
||
"clients": "客戶端",
|
||
"offline": "🔴 離線",
|
||
"online": "🟢 上線",
|
||
"commands": {
|
||
"unknown": "❗ 未知命令",
|
||
"pleaseChoose": "👇 請選擇:\r\n",
|
||
"help": "🤖 歡迎使用本機器人!它旨在為您提供來自伺服器的特定資料,並允許您進行必要的修改。\r\n\r\n",
|
||
"start": "👋 你好,<i>{{ .Firstname }}</i>。\r\n",
|
||
"welcome": "🤖 歡迎來到 <b>{{ .Hostname }}</b> 管理機器人。\r\n",
|
||
"status": "✅ 機器人正常執行!",
|
||
"usage": "❗ 請輸入要搜尋的文字!",
|
||
"getID": "🆔 您的 ID 為:<code>{{ .ID }}</code>",
|
||
"helpAdminCommands": "要重新啟動 Xray Core:\r\n<code>/restart</code>\r\n\r\n要搜尋客戶電子郵件:\r\n<code>/usage [電子郵件]</code>\r\n\r\n要搜尋入站(帶有客戶統計資料):\r\n<code>/inbound [備註]</code>\r\n\r\nTelegram 聊天 ID:\r\n<code>/id</code>",
|
||
"helpClientCommands": "要搜尋統計資料,請使用以下命令:\r\n<code>/usage [電子郵件]</code>\r\n\r\nTelegram 聊天 ID:\r\n<code>/id</code>",
|
||
"restartUsage": "\r\n\r\n<code>/restart</code>",
|
||
"restartSuccess": "✅ 操作成功!",
|
||
"restartFailed": "❗ 操作錯誤。\r\n\r\n<code>錯誤: {{ .Error }}</code>.",
|
||
"xrayNotRunning": "❗ Xray Core 未運行。",
|
||
"startDesc": "顯示主選單",
|
||
"helpDesc": "機器人幫助",
|
||
"statusDesc": "檢查機器人狀態",
|
||
"idDesc": "顯示您的 Telegram ID",
|
||
"usageDesc": "查看客戶端用量:/usage 郵箱",
|
||
"inboundDesc": "搜尋入站:/inbound 備註(管理員)",
|
||
"restartDesc": "重啟 Xray 核心(管理員)",
|
||
"clearallDesc": "重置所有客戶端流量(管理員)"
|
||
},
|
||
"messages": {
|
||
"cpuThreshold": "CPU 使用率為 {{ .Percent }}%,超過閾值 {{ .Threshold }}%",
|
||
"selectUserFailed": "❌ 使用者選擇錯誤!",
|
||
"userSaved": "✅ 電報使用者已儲存。",
|
||
"loginSuccess": "✅ 成功登入到面板。\r\n",
|
||
"loginFailed": "❗️ 面板登入失敗。\r\n",
|
||
"report": "🕰 定時報告:{{ .RunTime }}\r\n",
|
||
"datetime": "⏰ 日期時間:{{ .DateTime }}\r\n",
|
||
"hostname": "💻 主機: {{ .Hostname }}\r\n",
|
||
"version": "🚀 X-UI 版本:{{ .Version }}\r\n",
|
||
"xrayVersion": "📡 Xray 版本: {{ .XrayVersion }}\r\n",
|
||
"ipv6": "🌐 IPv6: {{ .IPv6 }}\r\n",
|
||
"ipv4": "🌐 IPv4: {{ .IPv4 }}\r\n",
|
||
"ip": "🌐 IP: {{ .IP }}\r\n",
|
||
"ips": "🔢 IPs:\r\n{{ .IPs }}\r\n",
|
||
"serverUpTime": "⏳ 伺服器執行時間:{{ .UpTime }} {{ .Unit }}\r\n",
|
||
"serverLoad": "📈 伺服器負載:{{ .Load1 }}, {{ .Load2 }}, {{ .Load3 }}\r\n",
|
||
"serverMemory": "📋 RAM: {{ .Current }}/{{ .Total }}\r\n",
|
||
"tcpCount": "🔹 TCP: {{ .Count }}\r\n",
|
||
"udpCount": "🔸 UDP: {{ .Count }}\r\n",
|
||
"traffic": "🚦 流量:{{ .Total }} (↑{{ .Upload }},↓{{ .Download }})\r\n",
|
||
"xrayStatus": "ℹ️ 狀態: {{ .State }}\r\n",
|
||
"username": "👤 使用者名稱:{{ .Username }}\r\n",
|
||
"reason": "❗️ 原因:{{ .Reason }}\r\n",
|
||
"time": "⏰ 時間:{{ .Time }}\r\n",
|
||
"inbound": "📍 入站: {{ .Remark }}\r\n",
|
||
"port": "🔌 連接埠: {{ .Port }}\r\n",
|
||
"expire": "📅 過期日期:{{ .Time }}\r\n",
|
||
"expireIn": "📅 剩餘時間:{{ .Time }}\r\n",
|
||
"active": "💡 啟用:{{ .Enable }}\r\n",
|
||
"enabled": "🚨 已啟用:{{ .Enable }}\r\n",
|
||
"online": "🌐 連線狀態:{{ .Status }}\r\n",
|
||
"lastOnline": "🔙 上次上線: {{ .Time }}\r\n",
|
||
"email": "📧 電子郵件: {{ .Email }}\r\n",
|
||
"upload": "🔼 上傳: ↑{{ .Upload }}\r\n",
|
||
"download": "🔽 下載: ↓{{ .Download }}\r\n",
|
||
"total": "📊 總計: ↑↓{{ .UpDown }} / {{ .Total }}\r\n",
|
||
"TGUser": "👤 電報使用者:{{ .TelegramID }}\r\n",
|
||
"exhaustedCount": "🚨 耗盡的 {{ .Type }} 數量:\r\n",
|
||
"onlinesCount": "🌐 線上客戶:{{ .Count }}\r\n",
|
||
"disabled": "🛑 禁用:{{ .Disabled }}\r\n",
|
||
"depleteSoon": "🔜 即將耗盡:{{ .Deplete }}\r\n\r\n",
|
||
"backupTime": "🗄 備份時間:{{ .Time }}\r\n",
|
||
"refreshedOn": "\r\n📋🔄 重新整理時間:{{ .Time }}\r\n\r\n",
|
||
"yes": "✅ 是的",
|
||
"no": "❌ 否",
|
||
"received_email": "📧📥 電子郵件已更新。",
|
||
"received_comment": "💬📥 評論已更新。",
|
||
"email_prompt": "📧 預設電子郵件: {{ .ClientEmail }}\n\n請輸入您的電子郵件。",
|
||
"comment_prompt": "💬 預設評論: {{ .ClientComment }}\n\n請輸入您的評論。",
|
||
"cancel": "❌ 程序已取消!\n\n您可以隨時使用 /start 重新開始。 🔄",
|
||
"error_add_client": "⚠️ 錯誤:\n\n {{ .error }}",
|
||
"using_default_value": "好的,我會使用預設值。 😊",
|
||
"incorrect_input": "您的輸入無效。\n短語應連續輸入,不能有空格。\n正確示例: aaaaaa\n錯誤示例: aaa aaa 🚫",
|
||
"AreYouSure": "你確定嗎?🤔",
|
||
"SuccessResetTraffic": "📧 電子郵件: {{ .ClientEmail }}\n🏁 結果: ✅ 成功",
|
||
"FailedResetTraffic": "📧 電子郵件: {{ .ClientEmail }}\n🏁 結果: ❌ 失敗 \n\n🛠️ 錯誤: [ {{ .ErrorMessage }} ]",
|
||
"FinishProcess": "🔚 所有客戶的流量重置已完成。",
|
||
"eventOutboundDown": "出站 {{ .Tag }} 已中斷",
|
||
"eventOutboundUp": "出站 {{ .Tag }} 已恢復",
|
||
"eventErrorDetail": "錯誤:{{ .Error }}",
|
||
"eventDelayDetail": "延遲:{{ .Delay }} 毫秒",
|
||
"eventXrayCrash": "Xray 已當機",
|
||
"eventXrayCrashError": "錯誤:{{ .Error }}",
|
||
"eventNodeDown": "節點 {{ .Name }} 已離線",
|
||
"eventNodeUp": "節點 {{ .Name }} 已上線",
|
||
"eventLoginFallback": "來自 {{ .Source }} 的登入失敗",
|
||
"memoryThreshold": "記憶體使用率 {{ .Percent }}% 超過閾值 {{ .Threshold }}%"
|
||
},
|
||
"buttons": {
|
||
"closeKeyboard": "❌ 關閉鍵盤",
|
||
"cancel": "❌ 取消",
|
||
"cancelReset": "❌ 取消重置",
|
||
"cancelIpLimit": "❌ 取消 IP 限制",
|
||
"confirmResetTraffic": "✅ 確認重置流量?",
|
||
"confirmClearIps": "✅ 確認清除 IP?",
|
||
"confirmRemoveTGUser": "✅ 確認移除 Telegram 使用者?",
|
||
"confirmToggle": "✅ 確認啟用/禁用使用者?",
|
||
"dbBackup": "獲取資料庫備份",
|
||
"serverUsage": "伺服器使用情況",
|
||
"getInbounds": "獲取入站資訊",
|
||
"depleteSoon": "即將耗盡",
|
||
"clientUsage": "獲取使用情況",
|
||
"onlines": "線上客戶端",
|
||
"commands": "命令",
|
||
"refresh": "🔄 重新整理",
|
||
"clearIPs": "❌ 清除 IP",
|
||
"removeTGUser": "❌ 移除 Telegram 使用者",
|
||
"selectTGUser": "👤 選擇 Telegram 使用者",
|
||
"selectOneTGUser": "👤 選擇一個 Telegram 使用者:",
|
||
"resetTraffic": "📈 重置流量",
|
||
"resetExpire": "📅 更改到期日期",
|
||
"ipLog": "🔢 IP 日誌",
|
||
"ipLimit": "🔢 IP 限制",
|
||
"setTGUser": "👤 設定 Telegram 使用者",
|
||
"toggle": "🔘 啟用/禁用",
|
||
"custom": "🔢 自訂",
|
||
"confirmNumber": "✅ 確認: {{ .Num }}",
|
||
"confirmNumberAdd": "✅ 確認新增:{{ .Num }}",
|
||
"limitTraffic": "🚧 流量限制",
|
||
"getBanLogs": "禁止日誌",
|
||
"allClients": "所有客戶",
|
||
"addClient": "新增客戶",
|
||
"submitDisable": "以停用方式送出 ☑️",
|
||
"submitEnable": "以啟用方式送出 ✅",
|
||
"use_default": "🏷️ 使用預設值",
|
||
"change_email": "⚙️📧 電子郵件",
|
||
"change_comment": "⚙️💬 評論",
|
||
"ResetAllTraffics": "重設所有流量",
|
||
"SortedTrafficUsageReport": "排序過的流量使用報告"
|
||
},
|
||
"answers": {
|
||
"successfulOperation": "✅ 成功!",
|
||
"errorOperation": "❗ 操作錯誤。",
|
||
"getInboundsFailed": "❌ 獲取入站資訊失敗。",
|
||
"getClientsFailed": "❌ 獲取客戶失敗。",
|
||
"canceled": "❌ {{ .Email }}:操作已取消。",
|
||
"clientRefreshSuccess": "✅ {{ .Email }}:客戶端重新整理成功。",
|
||
"IpRefreshSuccess": "✅ {{ .Email }}:IP 重新整理成功。",
|
||
"TGIdRefreshSuccess": "✅ {{ .Email }}:客戶端的 Telegram 使用者重新整理成功。",
|
||
"resetTrafficSuccess": "✅ {{ .Email }}:流量已重置成功。",
|
||
"setTrafficLimitSuccess": "✅ {{ .Email }}: 流量限制儲存成功。",
|
||
"expireResetSuccess": "✅ {{ .Email }}:過期天數已重置成功。",
|
||
"resetIpSuccess": "✅ {{ .Email }}:成功儲存 IP 限制數量為 {{ .Count }}。",
|
||
"clearIpSuccess": "✅ {{ .Email }}:IP 已成功清除。",
|
||
"getIpLog": "✅ {{ .Email }}:獲取 IP 日誌。",
|
||
"getUserInfo": "✅ {{ .Email }}:獲取 Telegram 使用者資訊。",
|
||
"removedTGUserSuccess": "✅ {{ .Email }}:Telegram 使用者已成功移除。",
|
||
"enableSuccess": "✅ {{ .Email }}:已成功啟用。",
|
||
"disableSuccess": "✅ {{ .Email }}:已成功禁用。",
|
||
"askToAddUserId": "未找到您的配置!\r\n請向管理員詢問,在您的配置中使用您的 Telegram 使用者 ChatID。\r\n\r\n您的使用者 ChatID:<code>{{ .TgUserID }}</code>",
|
||
"chooseClient": "為入站 {{ .Inbound }} 選擇一個客戶",
|
||
"chooseInbound": "選擇一個入站"
|
||
}
|
||
},
|
||
"email": {
|
||
"labelStatus": "狀態",
|
||
"labelOutbound": "出站",
|
||
"labelNode": "節點",
|
||
"labelError": "錯誤",
|
||
"labelDelay": "延遲",
|
||
"labelUsername": "使用者名稱",
|
||
"labelIP": "IP",
|
||
"labelReason": "原因",
|
||
"labelSource": "來源",
|
||
"statusCrashed": "已當機",
|
||
"statusHigh": "偏高",
|
||
"statusSuccess": "成功",
|
||
"statusFailed": "失敗",
|
||
"statusDown": "中斷",
|
||
"statusUp": "恢復"
|
||
}
|
||
}
|