Files
3x-ui/internal/web/translation/es-ES.json
T
Sanaei 5e1cb7693b Repo-wide self-correcting audit: 54 verified bug fixes (#5970)
* fix(email): resolve a name-addr smtpFrom into bare envelope address and display name

The save-time validator accepts any RFC 5322 address form, so a value
like '3x-ui Panel <panel(at)example.com>' passes validation, but Send and
TestConnection fed that raw string to MAIL FROM, which strict servers
reject with 501, and buildMessage mangled it into a quoted local part.
Parse the configured sender at the point of use: the envelope gets the
bare address and, when no explicit sender name is set, the display name
embedded in the setting is used for the From header.

* fix(email): report a missing sender address from the SMTP connection test

TestConnection skipped the empty-from guard that Send enforces, so with
no sender and no username configured the test issued the null reverse-path
and could report success against a lenient relay while every real
notification send kept failing with the missing-sender error. Guard the
test path the same way and surface a dedicated translated message.

* fix(sub): fall back to the raw subscription when an auto-detected format has no content

With format auto-detection enabled, a client whose User-Agent matched the
Clash or JSON regex was routed straight to that format handler. For a
subscription whose entries convert to neither format (an MTProto-only
subscription, for example) the handler returns an empty document and the
request ended as 404, breaking a URL that served the raw list before the
toggle. The auto-detect branches now serve the detected format only when
it produces content and otherwise continue to the raw response; the
explicit format endpoints keep answering 404 for empty documents.

* fix(node): match prefixed central tags when filtering a selected-mode node snapshot

FilterNodeSnapshot compared a node snapshot's inbound tags against the
raw selected-tag list with an exact match, while its two siblings
(SnapshotHasUnadoptedInbounds and the reconcile tagToCentral map) expand
each selected tag to both its bare node-side form and its n<id>- prefixed
central form. A panel-created node inbound is recorded in the selected
list under the central prefixed tag but reported by the node under the
bare tag, so the exact match dropped it from every snapshot and the
orphan sweep then deleted its central row one tick after creation. Expand
the allowed set with the same prefix flip the siblings use.

* fix(client): refuse a bulk quota reduction that would fall to or below zero

BulkAdjust clamped a client's new traffic limit with max(total+addBytes, 0).
Because 0 is the unlimited sentinel, reducing a client's quota by more than
it had left silently granted that client unlimited traffic. The sibling
expiry branch already refuses an over-reduction; mirror it for quota so the
adjustment is skipped with a clear reason instead of crossing the sentinel.

* fix(client): persist a bulk adjustment's applied field even when the sibling field is skipped

In a mixed BulkAdjust (both a days delta and a bytes delta), a per-field
planning skip such as "unlimited expiry" or "unlimited traffic" was recorded
in the same map that gated the client_traffics write. The applied field was
already written to the inbound JSON and the clients table, but the enforcement
row was left untouched, so the depletion job cut the client on the old limit
while the panel showed the new one. Gate the traffic-row write on an actual
inbound-processing failure rather than on any planning-phase skip note.

* fix(inbound): always create in AddInbound instead of overwriting a row whose id was posted

The add controller binds the inbound model's id form field and never clears
it, and AddInbound persisted with GORM Save, which updates in place when the
primary key is non-zero. A client that reused an existing id (for instance by
duplicating an inbound fetched from /get and changing the port) silently
overwrote that stored row instead of creating a new inbound. Zero the id at
the top of AddInbound, matching how it already zeroes the client-stat ids.

* fix(inbound): accept WireGuard clients when creating an inbound

AddInbound's per-client validation switch had cases for every protocol
except WireGuard, so a WireGuard client fell through to the default branch
that requires a non-empty id. WireGuard clients are keyed by their public
key and carry no id, so importing a WireGuard inbound or re-adding one to a
reconciling node was rejected with "empty client ID". Add a wireguard case
that validates the client key, mirroring addInboundClient.

* fix(client): stop holding the inbound-lock registry mutex while waiting on one inbound

lockInbound acquired the global registry mutex and then blocked on the
per-inbound mutex without releasing the registry first. A slow client
operation holding one inbound's mutex (for example a bulk delete pushing to
an unreachable node) made the next waiter park on that inbound while still
holding the registry mutex, which in turn blocked lockInbound for every
other inbound — freezing client mutations panel-wide. Release the registry
mutex before taking the per-inbound lock.

* fix(client): honor keepTraffic when deleting a client that is attached to inbounds

Delete, DeleteByEmail and BulkDelete all pass keepTraffic to their final
cleanup transaction, but each called the per-inbound delete helper with a
hardcoded false. That helper purges the client's traffic, IP and stat rows
before the gated cleanup runs, so keepTraffic=true still destroyed all
traffic history for any client actually attached to an inbound (the pinned
test only covered a record with no inbound mappings). Thread the caller's
keepTraffic through to the per-inbound helper at all three call sites.

* fix(inbound): defer a local MTProto inbound edit's sidecar push until after commit

UpdateInbound applied a local MTProto inbound change by calling the runtime
UpdateInbound (which stops/starts the mtg sidecar or talks to it) from inside
runSerializedTx. That runs process and network I/O on the single traffic-writer
goroutine while a DB transaction is open, so a slow sidecar stalls traffic
accounting and every concurrent client mutation, and a later step failing the
transaction leaves the sidecar ahead of the rolled-back row. Move the push into
the post-commit hook, matching the xray branch. Adds a SetLocalRuntimeOverride
test seam mirroring the existing node override so the deferral is regression
tested.

* fix(client): delete external-link rows when bulk-deleting clients

The single-client Delete path removes a client's client_external_links rows,
but BulkDelete (and the DelDepleted reaper that routes through it) deleted the
record, mappings and traffic while leaving the external-link rows keyed by the
now-dead client id, so they accumulated as orphans. Delete them in the same
cleanup transaction, keyed by client id like the single path.

* fix(inbound): request an xray restart when toggling a routed MTProto inbound

AddInbound, DelInbound and UpdateInbound all flag needRestart when an inbound
routes MTProto through xray, so the egress SOCKS bridge is regenerated. Only
SetInboundEnable's local path omitted it, so toggling a routed MTProto inbound
off then on left the bridge out of the running config while the sidecar dialed
its loopback port, blackholing that inbound until an unrelated restart. Flag the
restart on the local enable path too.

* fix(client): apply enable-by-email to every inbound a client is attached to

ToggleClientEnableByEmail (Telegram bot) and SetClientEnableByEmail (LDAP sync)
resolved a single inbound via the legacy client_traffics pointer and flipped
enable only there. A client attached to several inbounds kept connecting through
the siblings' running Xray after being disabled, and the next edit could
re-enable it everywhere from a stale sibling. Route both through the
applyClientFieldByEmail fan-out (the #5039 fix path) so the whole multi-inbound
identity is toggled at once, dropping the circular Set/Toggle dependency.

* fix(traffic): commit a traffic tick even when a best-effort maintenance helper fails

addTrafficLocked stages the inbound and client deltas, then runs three helpers
(auto-renew, disable depleted clients, disable depleted inbounds) that are meant
to log and continue. All three reused the function-scope err that the deferred
commit/rollback inspects, so the last helper's error decided the whole tick: a
failure in disableInvalidInbounds rolled back the already-staged traffic while
AddTraffic reported success, and because xray had already advanced its counter
baseline that traffic was lost for good. Give each best-effort helper its own
error variable so only a genuine staging failure rolls the tick back.

* fix(traffic): re-enable clients and serialize the write in Reset All Client Traffic

ClientService.ResetAllTraffics zeroed up/down but, unlike every sibling reset
path, never restored enable=true, so clients that had been auto-disabled for
exceeding their quota stayed cut with zero usage after a reset. It also wrote
client_traffics directly on the shared DB handle instead of through the serial
traffic writer, reintroducing the cross-transaction lock-order deadlock the
writer exists to prevent. Restore enable and run the reset inside
submitTrafficWrite within one transaction.

* fix(traffic): keep node reset propagation out of the serial traffic writer

ResetAllTraffics and ResetInboundTraffic performed their remote-node reset HTTP
calls inside submitTrafficWrite. Each call can block up to the remote timeout,
and Reset All Traffics loops every node serially, so the single traffic-writer
goroutine was held for seconds — long enough that the concurrent 5s traffic poll
timed out submitting its own write and dropped the deltas it had already drained
from xray. Do the DB reset inside the writer, then propagate to the nodes after
it returns, matching how the mtproto quota reset is already sequenced.

* fix(sub): stop the subscription from 500ing on valid-but-unusual stream settings

The raw share-link generators used unchecked type assertions and unguarded
array indexing: an empty Reality shortIds/serverNames array (random.Num(0)
panics), a tcp-http header with no request block or an empty request.path, a
grpc block missing its keys, empty stream settings, and a non-string Host
header all panicked mid-generation. Because getSubs loops every client's link
with no recover, one such client 500s the entire subscription for everyone. The
sibling JSON, Clash and frontend generators already guard these; make the raw
generators match with comma-ok assertions and length checks.

* fix(sub): tolerate a hysteria inbound without hysteriaSettings in the JSON subscription

genHy asserted stream["hysteriaSettings"].(map[string]any) without the comma-ok
form, so a hysteria inbound whose StreamSettings omit the hysteriaSettings key
(a valid, representable shape the raw generator renders fine) panicked and 500ed
the entire JSON subscription. Use comma-ok; the downstream reads already guard
each key, so a nil map degrades gracefully.

* fix(sub): emit the pinned peer cert sha256 in Clash subscriptions

The Clash stream builder computed tlsSettings["pin-sha256"] from the inbound's
pinnedPeerCertSha256, but applySecurity's tls case never copied it onto the
proxy, so it was written with no reader and silently dropped. Clash subscribers
lost certificate pinning while JSON subscribers kept it. Surface pin-sha256 on
the proxy in the tls case, matching the JSON emitter.

* fix(link): parse the snake_case and extra-blob xhttp fields when importing a share link

The panel's share-link emitters (Go and TS) carry advanced xhttp knobs as a
snake_case x_padding_bytes plus an extra=<json> payload, but the Go parser's
xhttp branch read only top-level camelCase params, so importing an xhttp link
via the outbound-subscription feature dropped xPaddingBytes, scMaxEachPostBytes
and the rest, silently reverting them to the stream defaults and producing a
non-working outbound. Mirror the TS parser: read the snake_case alias, merge the
extra JSON blob, then let explicit camelCase params win.

* fix(frontend): decode URL-safe base64 when parsing an imported share link

Base64.decode called window.atob directly, which rejects the base64url
alphabet (- and _) and unpadded input. But the panel's own share-link emitter
uses Base64.encode(x, true) (URL-safe, unpadded), and real SIP002 links do too,
so importing a Shadowsocks link whose method:password encodes with a - or _ threw,
fell back to the raw undecoded string, and produced a wrong method and garbage
password (the vmess parser shared the same limitation). Normalize base64url and
re-pad before atob so decode round-trips every emitted link.

* fix(link): honor the vmess ws path and hysteria2 vcn params on import

Two Go/TS parser parity gaps in the outbound share-link import path: parseVmess
only applied a ws link's path when the inner JSON also carried a host key, so a
generator that omits host dropped the path back to the default; and parseHysteria2
hardcoded verifyPeerCertByName to empty, ignoring the vcn param the panel emits,
so a hysteria2 outbound with a decoy SNI and a distinct cert name failed TLS
verification after import. The TS parser handles both; make the Go parser match.

* fix(ui): stop the sniffing form island from clobbering unrendered fields

antd's Form.useWatch only reports registered fields, so while the
sniffing toggle was off the island emitted { enabled: false } upward and
replaced the full Sniffing object in form state. Saving a VLESS reverse
outbound then crashed in sniffingToWire on the missing ipsExcluded
array; the loopback outbound and the inbound sniffing tab shared the
same hole. Watch the store with preserve: true so unrendered fields
keep their values, and seed a missing value from the schema defaults
instead of an empty cast.

* fix(sub): drop empty remark segments instead of leaving a stray separator

expandSegment dropped a "|" segment only when its tokens rendered the unlimited
mark, so a segment whose only token resolved to the empty string (a client with
no comment, an unlimited client's expiry date) was kept as bare decoration,
leaving a trailing "|" or a dangling emoji on every share link's remark. Drop a
token-bearing segment whenever none of its tokens produce a real value, while
still keeping pure-literal segments.

* fix(xray): keep source- and domains-scoped routing rules when an inbound is deleted

removeInboundTagFromRules drops a routing rule whose inboundTag list becomes
empty only if the rule has no other matcher, but routingMatcherKeys omitted
xray-core's canonical source and domains keys. A rule scoped by source or domains
(common in hand-authored or imported configs) therefore lost its whole body —
including a security-relevant block — when its single listed inbound was deleted,
instead of just having the tag trimmed. Recognize source and domains as live
matchers.

* fix(xray): guard RemoveUser against an uninitialized handler client

Every XrayAPI handler method returns an error when HandlerServiceClient is nil,
except RemoveUser, which dereferenced it directly. A depletion sweep runs Init
with the port ignored and, during a restart window where the fresh process's
api port is still 0, Init fails and leaves the client nil — so RemoveUser
panicked (recovered by the traffic writer, but re-thrown every poll) instead of
returning an error. Add the same nil guard the siblings have.

* fix(xray): do not revive a manually stopped Xray on a background restart

RestartXray cleared isManuallyStopped unconditionally at its top, so the @30s
pending-config cron (and warp/ldap/outbound reconcile jobs) that call
RestartXray(false) resurrected an Xray the admin had deliberately stopped —
unlike the crash-detector, which honors the manual-stop flag. Skip a non-forced
restart while the stop flag is set; only an explicit forced restart clears it.

* fix(xray): retry a failed pending-restart instead of dropping the config change

The 30s cron consumed the need-restart flag with IsNeedRestartAndSetFalse before
calling RestartXray and only logged a failure. If RestartXray failed early (a
transient GetXrayConfig DB error) the old process kept running the old config,
the crash detector saw a running process and never retried, and the flag stayed
cleared — so an admin's saved change silently never reached the core. Move the
consume/restart/retry into ApplyPendingRestart, which re-arms the flag on
failure so the next tick retries.

* fix(xray): synchronize the process version and apiPort fields

Start writes p.version and p.apiPort (via refreshVersion/refreshAPIPort) after
flipping the process to running, while GetXrayVersion and GetAPIPort read them
lock-free from the status and traffic poll goroutines. The struct mutex
deliberately excluded these fields, so a restart racing a poll was a real data
race — a torn read of the version string header can crash. Extend the mutex to
cover version and apiPort, doing the blocking version probe before taking the
lock.

* fix(settings): detect a wildcard listen collision between the web and sub ports

The web/sub same-port check compared the two listen addresses as raw strings, so
binding both on all interfaces with different spellings (webListen 0.0.0.0 vs an
empty subListen) slipped past validation and only failed at startup with an
opaque bind error. Treat any wildcard listen ('', 0.0.0.0, ::) as overlapping so
the clash is reported up front, while still allowing two distinct specific
addresses to share a port.

* fix(db): mark the IP-limit cleanup seeder done on a fresh install

ResetIpLimitNoFail2ban is a one-time migration that, on a host without fail2ban,
zeroes every existing client's limitIp because the limit can't be enforced. It
was missing from the fresh-install fast-path seeder list, so on a brand-new DB it
did not run on the first boot but fired on the second — wiping any IP limits the
admin had set in between. Add it to the fast-path so a truly fresh install marks
it done up front (there is nothing to clean), leaving later admin-set limits
intact.

* fix(security): dial outbound subscriptions through the SSRF guard

The outbound-subscription fetch validated the URL host once (resolving DNS and
rejecting private targets) but then fetched with a plain HTTP client that
re-resolves the host at dial time, so a subscription domain the attacker controls
could pass validation as a public IP and rebind to 127.0.0.1 / a cloud metadata
endpoint / an internal host for the actual dial — a blind SSRF into the panel's
network. Route the direct fetch (and its redirects) through
netsafe.SSRFGuardedDialContext, which resolves, checks and dials the same IP
atomically, carrying the subscription's AllowPrivate flag on the request context;
a configured egress proxy still dials its loopback bridge unguarded.

* fix(security): bound the login-limiter attempts map

The login rate limiter keys its records on the caller-supplied username and only
evicted a record when that exact key was revisited or the login succeeded. An
unauthenticated attacker replaying one CSRF token while rotating a fresh username
per request seeded a record that was never revisited, growing the map without
bound until the panel OOMs. Cap the map: before inserting a new record, reclaim
records whose block has lapsed and whose failures aged out, and if the map is
still at the ceiling under a broad flood, drop one so memory can never grow past
the cap.

* fix(tgbot): require admin for privileged callbacks, not just the first switch

answerCallback wraps only its first callback switch in an isAdmin guard; the
second switch (server usage, inbound/online enumeration, database backup export,
ban logs, mass traffic reset, client creation) ran for every caller. Telegram
delivers a callback with the tapping user's id, so a non-admin who can see an
admin's inline keyboard — as when the bot runs in a group — could tap Backup and
receive the full database and config, or reset all traffic. Default-deny before
the second switch: a non-admin may only run the per-user client_* callbacks that
resolve their own data from their Telegram id.

* fix(eventbus): dispatch each subscriber in its own goroutine

The fan-out loop called every subscriber's handler sequentially on the
single dispatch goroutine. The email and Telegram notifiers block on
network I/O for tens of seconds (or minutes when the remote is slow), so
one slow subscriber stalled the whole loop: the 256-slot channel then
filled and Publish silently dropped later events — including high-value
xray.crash and node.down notifications unrelated to the slow handler.

Hand each delivered event to every handler in its own goroutine so a
blocking subscriber can no longer stall delivery to the others. safeCall
already recovers panics, so a detached handler cannot take down the bus.

* fix(integration): cap WARP API response body size

doWarpRequest read the response with an unbounded io.ReadAll, unlike the
sibling NordVPN client which already caps every read at maxResponseSize.
A hostile panel egress proxy or a MITM on the Cloudflare WARP endpoint
could stream an arbitrarily large body and force the panel into an
unbounded allocation. Wrap the body in an io.LimitReader(maxResponseSize)
to match the NordVPN client.

* fix(email): bound every SMTP step with a connection deadline

The "starttls"/"none" transport delivered through net/smtp.SendMail, which
dials with an untimed net.Dial and never sets a socket deadline. When an
SMTP server accepted the TCP connection but then stalled (or was a
blackhole), the caller was released by Send's 30s select, but the sender
goroutine and its socket stayed blocked until the OS TCP timeout — minutes
per notification, leaking a goroutine and a connection each time.
sendWithTLS dialed with a timeout but likewise armed no deadline on the
protocol phase, and TestConnection (called synchronously from the settings
handler, with no select guard) could hang the request indefinitely.

Replace SendMail with sendPlain, which dials with smtpConnectTimeout and
arms conn.SetDeadline(smtpDeadline) before the greeting read, preserving
SendMail's opportunistic STARTTLS upgrade. Arm the same deadline in
sendWithTLS and TestConnection so every SMTP step is bounded.

* fix(server): guard access-log parser against malformed lines

GetXrayLogs split each Xray access-log line on whitespace and then read
fixed offsets — parts[1] for the timestamp and parts[i+1] after the "from",
"accepted" and "email:" markers — without checking the line had that many
fields. A truncated or malformed line (the logged destination is
attacker-influenced) indexed past the slice and panicked; the panel handler
returned a 500 via Gin's recovery.

Extract the per-line field parsing into parseAccessLogFields and length
guard every positional lookup so a short line yields a partial entry
instead of panicking.

* fix(server): guard xray key-generator output parsing

GetNewX25519Cert, GetNewmldsa65 and GetNewmlkem768 parsed xray's stdout by
reading lines[0], lines[1] and each line's second colon-separated field
without any length check — unlike GetNewEchCert, which already guards its
line count. If the xray binary printed fewer than two lines or reformatted
its labels (a version change, or a silent failure that emitted nothing),
the fixed slice index panicked and the handler 500'd.

Extract the shared parsing into parseXrayKeyPairOutput, which length guards
the line count and each label split and returns an error instead of
panicking, then route all three generators through it.

* fix(tgbot): stop auto-deleted messages from resetting wizard state

SendMsgToTgbotDeleteAfter spawns a goroutine that, after the display delay,
deleted the transient message and then unconditionally cleared the chat's
conversation state. Every caller that ends a wizard step already clears the
state synchronously, so that call was redundant — and harmful: if within
the delay the user advanced to the next step (a callback sets a fresh
awaiting_* state), the late goroutine wiped it, and the user's next message
fell through unrecognized, silently dropping their input.

Move the delayed deletion into deleteMessageAfterDelay, which only removes
the message and no longer touches the conversation state. Guard
deleteMessageTgBot against a nil bot so the deletion path is unit-testable.

* fix(frontend): refetch a fresh CSRF token on 403 instead of reusing the stale meta tag

On a 403 to an unsafe method the client cleared its cached CSRF token and
called ensureCsrfToken to retry. But ensureCsrfToken prefers the
<meta name="csrf-token"> tag baked into the page, which the production
panel always injects, so the "refresh" re-read the same stale token and the
/csrf-token refetch was never reached — the retry re-sent the token that had
just been rejected and the save failed with an error toast.

The token lives in the session and rotates when the session is regenerated
(for example re-login in another tab), leaving the tab's baked-in meta token
stale. Fetch the current token straight from /csrf-token in the 403 branch so
the retry uses the authoritative server value. The existing tests only passed
because they strip the meta tag; the new test keeps a stale tag present.

* fix(frontend): surface backend error text from failed requests

HttpUtil.get/post read the thrown HttpError body as response.data.message,
but the backend error envelope (entity.Msg) serializes its text as msg. On
any non-2xx JSON response the real reason was therefore dropped and the
operator saw only the generic "Request failed with status N" toast.

Read response.data.msg first (keeping message and the native error text as
fallbacks). The sibling test had pinned the wrong body shape ({ message });
correct it to the real backend shape ({ success:false, msg }) so it exercises
the actual envelope.

* fix(frontend): share one WebSocket connection across bridge and hooks

websocketBridge.ts and useWebSocket.ts each declared their own
module-scoped sharedClient plus an identical getSharedClient, so the
"shared" client was not shared between them: whenever a page using
useWebSocket (Clients/Inbounds) mounted alongside the always-mounted
bridge, the panel opened two sockets to /ws. The server then pushed every
traffic/stats/nodes/inbounds snapshot to both, doubling WebSocket bandwidth
and running two independent reconnect loops, and the hook's socket was never
disconnected on unmount.

Hoist a single getSharedWebSocketClient into api/websocket.ts and route both
the bridge and the hook through it, so exactly one connection is opened.

* fix(frontend): guard the outbounds WebSocket handler against non-array payloads

onOutbounds wrote the raw WebSocket payload straight into the
outboundsTraffic cache, unlike the sibling onNodes/onInbounds handlers which
first check Array.isArray. A malformed non-array push (for example an object)
would land in the cache with staleTime Infinity; consumers that call
.find()/.map() on the outbounds list would then throw and crash the Outbounds
tab. Add the same Array.isArray guard so a bad push is ignored.

* fix(frontend): key the node table by the computed row key, not id

The desktop node table used rowKey="id", but transitive sub-nodes (the
read-only rows surfaced from downstream nodes) all carry id 0, so a topology
with two or more transitive rows gave React duplicate keys. antd's rowKey
prop overrides the row object's own computed `key` (`t-${guid}` for
transitive rows, the numeric id otherwise), so the unique key the code
already builds was ignored — causing row-state/DOM mis-association on any
re-render (heartbeat refetch, address-eye toggle). The mobile card path
already keyed by record.key.

Key the table by "key" so transitive rows get their distinct t-${guid}
identity; direct nodes keep key === id, so row selection (filtered to numeric
keys) is unchanged.

* fix(frontend): map routing row actions through the rule's real index

The routing table hides balancer-loopback rules (`_bl_*`) but keeps each
visible row's original index in `key`, then handed antd's positional row
index straight to edit/delete/toggle/move/drag — all of which mutate the
full, unfiltered routing.rules array. Once a hidden loopback rule precedes a
visible one (e.g. a balancer whose fallback is another balancer, plus any
rule added afterwards), the positional index no longer matches the array
index, so deleting or editing a rule silently hit the wrong one — including
destroying the loopback rule that keeps the balancer alive.

Add originalRuleIndex to translate a positional row index back through the
row's `key`, and route every mutating handler (openEdit, confirmDelete,
toggleRule, moveUp/moveDown, drag) through it. When no loopback rows are
hidden the mapping is the identity, so ordinary configs are unaffected.

* fix(frontend): map outbound row actions through the outbound's real index

The outbounds table hides balancer-loopback outbounds (`_bl_*`) but keeps
each visible row's original index in `key`, then passed antd's positional
row index to edit/delete/move and to the per-row probe (onTest) and its
result lookup — all of which address the full, unfiltered outbounds array.
Once a hidden loopback outbound precedes a visible one, the positional index
diverges from the array index, so deleting or editing an outbound hit the
wrong one (its deletion-impact plan and removal targeting the wrong entry),
and the test button probed / showed results against the wrong outbound.

Add originalOutboundIndex and route the mutating handlers through it; key
the probe trigger and test-result columns by record.key. With no loopback
rows hidden the mapping is the identity, so ordinary configs are unaffected.

* fix(frontend): tolerate a malformed happyEyeballs value in the Xray Basics tab

BasicsTab derived directHappyEyeballs by calling HappyEyeballsSchema.parse
during render, guarding only against null/non-object. A wrong-typed field
(e.g. happyEyeballs.tryDelayMs as a string) or any other shape mismatch —
reachable via the Complete Template JSON editor or an imported config — threw
straight out of render, white-screening the default Xray landing tab.

Use safeParse and fall back to null so a bad value degrades to "no override"
instead of crashing the page.

* fix(frontend): preserve routing-rule fields the form does not surface

The rule form rebuilt the rule from a fixed literal of only the fields it
edits, and RoutingTab replaces the rule wholesale on confirm. Fields the
form never exposes — localPort, localIP, process, ruleTag, webhook — are in
the rule schema and can arrive via the advanced JSON editor or Import Rules;
opening such a rule in the form and saving silently dropped them.

Carry over every key of the original rule the form does not manage before
applying the form-derived fields, so an edit only touches what it surfaces.

* fix(frontend): re-sync the sniffing island when its value changes externally

The sniffing config editor froze its seed value at mount and only watched
its own inner AntD form, never reflecting a later change to the shared RHF
`sniffing` path. Because the inbound form mounts every tab with
forceRender, the friendly Sniffing tab and the Advanced JSON editor are live
at once: editing sniffing in the JSON editor updated the RHF value but not
the frozen island, so the next interaction with the friendly tab emitted the
stale value and silently discarded the JSON edit.

Add an effect that pushes an external value change into the inner form,
guarded by the same lastEmitted marker the emit path uses so the island
never re-seeds from its own echo and no update loop forms.

* fix(frontend): don't drift a client's byte quota on a no-op save

The quota field shows the total in GB rounded to two decimals; editing a
client and saving converted that display value straight back to bytes. A
byte total not aligned to 0.01 GB — one set via the API or an import — was
therefore rewritten to the rounded value on any save that never touched the
field, losing a few MB each time.

Add resolveTotalBytes: keep the original byte total when the displayed GB
still matches it, and only re-derive from GB when the user actually changed
the field.

* fix(eventbus): deliver events on a bounded per-subscriber worker

The previous fix dispatched each event to every subscriber with a bare
`go safeCall`. That unblocked the dispatch loop, but removed the bus's
backpressure: under a login-attempt flood (which both notifier subscribers
process without rate-limiting) with email/Telegram enabled, every attempt
spawned handler goroutines that each block on network I/O for up to ~30s,
with no bound — a goroutine and outbound-connection storm. It also let a
subscriber's handler run concurrently with itself, racing the Telegram
notifier's lazily-cached hostname.

Give each subscriber its own bounded queue drained by a single worker
goroutine. Dispatch does a non-blocking send per subscriber (dropping only
that subscriber's event when its queue is full), so a slow subscriber still
can't stall the others, concurrency is bounded to one in-flight handler per
subscriber, per-subscriber event order is preserved, and Stop again waits
for in-flight handlers to finish.

* fix(frontend): map outbound mobile-card actions through the real index too

The desktop outbounds table was keyed by the outbound's real index, but the
mobile card list was left keying the probe trigger and every test-state
lookup by the positional row index. With a hidden balancer-loopback outbound
present, tapping Check on a mobile card probed the wrong outbound and the
Test-All results landed on the wrong card. Key onTest and the
testResult/isTesting reads by record.key, matching the desktop columns.

* fix(frontend): meet WCAG AA contrast on the config-block link text

The Storybook accessibility test flagged the share-link <code> block: with no
explicit color it inherited a muted grey that renders as #888888 on the
#f8f8f8 tertiary-fill background in CI's Chromium — a 3.33:1 contrast, below
the 4.5:1 AA threshold. Set the text to the theme's primary text token so the
colour is explicit and high-contrast in both light and dark themes instead of
depending on an inherited value that varies by browser.

* style(sub): simplify a negated conjunction to satisfy staticcheck QF1001

golangci-lint (staticcheck QF1001) flagged the `!(a && b)` guard in
expandSegment. Rewrite it via De Morgan's law to the equivalent
`!a || !b` form so the linter passes; behavior is unchanged.

* fix: close panics and races the audit's own fixes left nearby

Second-pass review of the 54-commit self-correcting audit. Each item below
was confirmed by reading the surrounding source (and, where practical, the
pre-fix code) before being changed; regression tests are included for every
behavioral fix.

Concurrency:
- eventbus: Bus.Subscribe called wg.Add with no synchronization against a
  concurrent Bus.Stop's wg.Wait, a real "WaitGroup misuse" panic risk (e.g. a
  Telegram-bot settings save racing panel shutdown/restart). Stop now flips a
  mu-guarded `stopped` flag before waiting, and Subscribe checks it under the
  same lock, so Add and Wait can no longer race.

Security:
- login_limiter: evictForRoom's fallback eviction picked an arbitrary map
  key, including ones still under an active cooldown - an attacker flooding
  /login with fresh usernames could evict their own (or anyone's) blocked
  record and reset the lockout. The fallback now skips actively-blocked
  records, only falling back to an unconditional evict if the map is
  somehow entirely full of active blocks (preserves the hard memory cap).

Subscription-endpoint panics (reachable by any client hitting /sub):
- internal/sub/service.go: applyPathAndHostParams/Obj (ws/httpupgrade/xhttp
  with no path settings object) and the TLS alpn readers in three places
  used unchecked type assertions - exactly the bug class abab7cd0 patched
  elsewhere in the same switch statements, just not these call sites.
- internal/sub/json_service.go, clash_service.go: the externalProxy loops
  in the JSON and Clash generators used unchecked assertions on a
  legacy/admin-supplied field (missing "port", non-object entry, etc.).
- internal/sub/json_service.go: realityData's shortId/serverName selection
  could assert a non-string array element.

Other correctness:
- client_traffic.go: ResetAllTraffics (touched by 3eb214d0) still skipped
  clearing NodeClientTraffic node-sync baselines, unlike its sibling reset
  paths in the same file - a node's next sync would re-add pre-reset delta
  on top of the freshly-zeroed counter.
- inbound_traffic.go: the traffic-tick tx's Commit/Rollback errors were
  silently discarded; now logged so a backend-level commit failure (e.g. an
  aborted Postgres tx from a best-effort helper) doesn't masquerade as a
  successful tick.
- outbound_subscription.go: the new subscriptionFetchClient doc comment was
  wedged between fetchAndStore's existing comment and fetchAndStore itself,
  leaving fetchAndStore undocumented and the comment describing the wrong
  function.

Convention cleanup:
- Removed narrative // comments added by the audit that violate this repo's
  no-inline-comment rule (mostly narrating the specific bug/fix rather than
  a lasting contract, and mostly on new Test functions, which this repo's
  existing tests never comment) - calibrated against this exact codebase's
  own pre-existing comment style so legitimate godoc-style doc comments
  were left alone.

---------

Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com>
2026-07-17 00:33:06 +02:00

2241 lines
134 KiB
JSON
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{
"username": "Nombre de Usuario",
"password": "Contraseña",
"login": "Acceder",
"confirm": "Confirmar",
"cancel": "Cancelar",
"close": "Cerrar",
"save": "Guardar",
"logout": "Cerrar Sesión",
"create": "Crear",
"add": "Añadir",
"remove": "Quitar",
"update": "Actualizar",
"copy": "Copiar",
"copied": "Copiado",
"more": "más",
"download": "Descargar",
"regenerate": "Regenerar",
"jsonEditor": "Editor JSON",
"downloadImage": "Descargar imagen",
"sort": "Ordenar",
"remark": "Notas",
"enable": "Habilitar",
"protocol": "Protocolo",
"search": "Buscar",
"filter": "Filtrar",
"all": "Todos",
"from": "Desde",
"to": "Hasta",
"done": "Hecho",
"loading": "Cargando...",
"refresh": "Actualizar",
"clear": "Borrar",
"second": "Segundo",
"minute": "Minuto",
"hour": "Hora",
"day": "Día",
"check": "Verificar",
"indefinite": "Indefinido",
"unlimited": "Ilimitado",
"none": "Ninguno",
"qrCode": "Código QR",
"info": "Más Información",
"edit": "Editar",
"delete": "Eliminar",
"reset": "Restablecer",
"noData": "Sin datos",
"copySuccess": "Copiado exitosamente",
"sure": "Seguro",
"encryption": "Encriptación",
"useIPv4ForHost": "Usar IPv4 para el host",
"transmission": "Transmisión",
"host": "Host",
"path": "Ruta",
"camouflage": "Ofuscación",
"status": "Estado",
"enabled": "Habilitado",
"disabled": "Deshabilitado",
"depleted": "Agotado",
"depletingSoon": "Agotándose",
"offline": "Sin conexión",
"online": "En línea",
"domainName": "Nombre de dominio",
"monitor": "Listening IP",
"certificate": "Certificado Digital",
"fail": "Falló",
"comment": "Comentario",
"success": "Éxito",
"lastOnline": "Última conexión",
"getVersion": "Obtener versión",
"install": "Instalar",
"clients": "Clientes",
"usage": "Uso",
"twoFactorCode": "Código",
"remained": "Restante",
"security": "Seguridad",
"secAlertTitle": "Alerta de Seguridad",
"secAlertSsl": "Esta conexión no es segura. Por favor, evite ingresar información sensible hasta que se active TLS para la protección de datos.",
"secAlertConf": "Ciertas configuraciones son vulnerables a ataques. Se recomienda reforzar los protocolos de seguridad para prevenir posibles violaciones.",
"secAlertSSL": "El panel carece de una conexión segura. Por favor, instale un certificado TLS para la protección de datos.",
"secAlertPanelPort": "El puerto predeterminado del panel es vulnerable. Por favor, configure un puerto aleatorio o específico.",
"secAlertPanelURI": "La ruta URI predeterminada del panel no es segura. Por favor, configure una ruta URI compleja.",
"secAlertSubURI": "La ruta URI predeterminada de la suscripción no es segura. Por favor, configure una ruta URI compleja.",
"secAlertSubJsonURI": "La ruta URI JSON predeterminada de la suscripción no es segura. Por favor, configure una ruta URI compleja.",
"emptyDnsDesc": "No hay servidores DNS añadidos.",
"emptyFakeDnsDesc": "No hay servidores Fake DNS añadidos.",
"emptyBalancersDesc": "No hay balanceadores añadidos.",
"emptyReverseDesc": "No hay proxies inversos añadidos.",
"somethingWentWrong": "Algo salió mal",
"subscription": {
"title": "Información de suscripción",
"subId": "ID de suscripción",
"status": "Estado",
"downloaded": "Descargado",
"uploaded": "Subido",
"expiry": "Caducidad",
"totalQuota": "Cuota total",
"individualLinks": "Enlaces individuales",
"active": "Activo",
"inactive": "Inactivo",
"unlimited": "Ilimitado",
"noExpiry": "Sin caducidad",
"copyAllConfigs": "Copiar Todas las Configuraciones",
"copyAllConfigsCopied": "Todas las configuraciones copiadas",
"email": "Email"
},
"menu": {
"theme": "Tema",
"dark": "Oscuro",
"ultraDark": "Ultra Oscuro",
"dashboard": "Estado del Sistema",
"inbounds": "Entradas",
"clients": "Clientes",
"groups": "Grupos",
"nodes": "Nodos",
"settings": "Ajustes del panel",
"xray": "Configuración Xray",
"routing": "Enrutamiento",
"outbounds": "Salidas",
"apiDocs": "Documentación de la API",
"logout": "Cerrar Sesión",
"link": "Gestionar",
"donate": "Donar",
"hosts": "Hosts",
"docs": "Documentación",
"openMenu": "Abrir menú"
},
"pages": {
"login": {
"hello": "Hola",
"title": "Bienvenido",
"loginAgain": "El límite de tiempo de inicio de sesión ha expirado. Por favor, inicia sesión nuevamente.",
"toasts": {
"invalidFormData": "El formato de los datos de entrada es inválido.",
"emptyUsername": "Por favor ingresa el nombre de usuario.",
"emptyPassword": "Por favor ingresa la contraseña.",
"wrongUsernameOrPassword": "Nombre de usuario, contraseña o código de dos factores incorrecto.",
"successLogin": "Has iniciado sesión en tu cuenta correctamente."
}
},
"index": {
"title": "Estado del Sistema",
"cpu": "CPU",
"logicalProcessors": "Procesadores lógicos",
"frequency": "Frecuencia",
"swap": "Swap",
"storage": "Almacenamiento",
"memory": "Memoria",
"threads": "Hilos",
"xrayStatus": "Xray",
"stopXray": "Detener",
"restartXray": "Reiniciar",
"xraySwitch": "Versión",
"xrayUpdates": "Actualizaciones de Xray",
"xraySwitchClick": "Elige la versión a la que deseas cambiar.",
"xraySwitchClickDesk": "Elige sabiamente, ya que las versiones anteriores pueden no ser compatibles con las configuraciones actuales.",
"updatePanel": "Actualizar panel",
"panelUpdateDesc": "Esto actualizará 3X-UI a la última versión y reiniciará el servicio del panel.",
"currentPanelVersion": "Versión actual del panel",
"latestPanelVersion": "Última versión del panel",
"panelUpToDate": "El panel está actualizado",
"devChannel": "Canal de desarrollo",
"devChannelWarning": "Las compilaciones de desarrollo siguen cada commit en main y no son versiones estables; no hay reversión automática.",
"currentCommit": "Commit actual",
"latestCommit": "Último commit",
"updateChannelChanged": "Canal de actualización cambiado",
"upToDate": "Actualizado",
"xrayStatusUnknown": "Desconocido",
"xrayStatusRunning": "En ejecución",
"xrayStatusStop": "Detenido",
"xrayStatusError": "Error",
"xrayErrorPopoverTitle": "Se produjo un error al ejecutar Xray",
"operationHours": "Tiempo de Funcionamiento",
"systemHistoryTitle": "Historial del Sistema",
"historyTitleCpu": "Uso de CPU",
"historyTitleMem": "Uso de Memoria",
"historyTitleNetwork": "Ancho de Banda de Red",
"historyTitlePackets": "Paquetes de Red",
"historyTitleDisk": "E/S de Disco",
"historyTitleOnline": "Clientes en Línea",
"historyTitleLoad": "Carga Media del Sistema (1 / 5 / 15 min)",
"historyTitleConnections": "Conexiones Activas (TCP / UDP)",
"historyTitleDiskUsage": "Uso del Espacio en Disco",
"historyTabBandwidth": "Ancho de Banda",
"historyTabPackets": "Paquetes",
"historyTabDisk": "Disco I/O",
"historyTabOnline": "En línea",
"historyTabLoad": "Carga",
"historyTabConnections": "Conexiones",
"historyTabDiskUsage": "Uso de Disco",
"charts": "Gráficos",
"xrayMetricsTitle": "Métricas de Xray",
"xrayTitleHeap": "Memoria Heap Asignada",
"xrayTitleSys": "Memoria Reservada del SO",
"xrayTitleObjects": "Objetos Heap Activos",
"xrayTitleGcCount": "Ciclos de GC Completados",
"xrayTitleGcPause": "Duración de Pausa de GC",
"xrayTitleObservatory": "Estado de Conexiones Salientes",
"xrayTabHeap": "Heap",
"xrayTabSys": "Sys",
"xrayTabObjects": "Objetos",
"xrayTabGcCount": "Recuento GC",
"xrayTabGcPause": "Pausa GC",
"xrayTabObservatory": "Observatorio",
"xrayMetricsDisabled": "Endpoint de métricas de Xray no configurado",
"xrayMetricsHint": "Añade un bloque metrics de nivel superior a la configuración de xray con tag metrics_out y listen 127.0.0.1:11111, luego reinicia xray.",
"xrayObservatoryEmpty": "Aún no hay datos de Observatory",
"xrayObservatoryHint": "Añade un bloque observatory a la configuración de xray listando los tags de outbound a sondear, luego reinicia xray.",
"xrayObservatoryTagPlaceholder": "Seleccionar outbound",
"xrayObservatoryAlive": "Activo",
"xrayObservatoryDead": "Caído",
"xrayObservatoryLastSeen": "Visto por última vez",
"xrayObservatoryLastTry": "Último intento",
"trendLast2Min": "Últimos 2 minutos",
"systemLoad": "Carga del Sistema",
"systemLoadDesc": "promedio de carga del sistema en los últimos 1, 5 y 15 minutos",
"connectionCount": "Número de Conexiones",
"ipAddresses": "Direcciones IP",
"toggleIpVisibility": "Alternar visibilidad de la IP",
"overallSpeed": "Velocidad general",
"upload": "Subida",
"download": "Descargar",
"totalData": "Datos totales",
"sent": "Enviado",
"received": "Recibido",
"documentation": "Documentación",
"xraySwitchVersionDialog": "¿Realmente deseas cambiar la versión de Xray?",
"xraySwitchVersionDialogDesc": "Esto cambiará la versión de Xray a #version#.",
"xraySwitchVersionPopover": "Xray se actualizó correctamente",
"panelUpdateDialog": "¿Deseas actualizar el panel?",
"panelUpdateDialogDesc": "Esto actualizará 3X-UI a la versión #version# y reiniciará el servicio del panel.",
"panelUpdateCheckPopover": "Fallo al comprobar actualización del panel",
"panelUpdateStartedPopover": "Actualización del panel iniciada",
"panelUpdateFailedTitle": "Error al actualizar el panel",
"panelUpdateFailedDesc": "La actualización no se completó correctamente. Revisa los registros del servidor o ejecuta 'x-ui update' desde la línea de comandos.",
"panelUpdateUnknownTitle": "No se pudo confirmar si la actualización terminó",
"panelUpdateUnknownDesc": "El panel no informó un resultado a tiempo. Recarga la página para comprobar la versión actual, o revisa los registros del servidor.",
"geofileUpdateDialog": "¿Realmente deseas actualizar el geofichero?",
"geofileUpdateDialogDesc": "Esto actualizará el archivo #filename#.",
"geofilesUpdateDialogDesc": "Esto actualizará todos los archivos.",
"geofilesUpdateAll": "Actualizar todo",
"geofileUpdatePopover": "Geofichero actualizado correctamente",
"geodataTitle": "Actualización automática de Geodata",
"geodataHint": "Xray descarga estos archivos según la programación y los recarga en caliente sin reiniciar. Las URL deben ser HTTPS. Cada archivo debe existir previamente en la carpeta bin para que Xray pueda actualizarlo.",
"geodataCron": "Programación (cron)",
"geodataOutbound": "Descargar a través de outbound (opcional)",
"geodataFile": "Nombre de archivo",
"geodataAddFile": "Añadir archivo",
"geodataSaveRestart": "Guardar y reiniciar Xray",
"geodataConfirmTitle": "¿Guardar la configuración de geodata?",
"geodataConfirmContent": "Se actualizará la plantilla de configuración de Xray y se reiniciará Xray.",
"geodataInvalidUrl": "Cada archivo necesita una URL HTTPS.",
"geodataInvalidFile": "El nombre de archivo debe ser simple, p. ej. geosite_custom.dat (sin rutas).",
"geodataInvalidCron": "Cron debe tener 5 campos, p. ej. 0 4 * * *",
"geodataEmpty": "No hay archivos configurados. En las reglas de enrutamiento se referencian como ext:geosite_custom.dat:category.",
"dontRefresh": "La instalación está en progreso, por favor no actualices esta página.",
"logs": "Registros",
"accessLogs": "Registros de acceso",
"autoUpdate": "Actualización automática",
"config": "Configuración",
"backup": "Copia de seguridad",
"backupTitle": "Copia & Restauración",
"exportDatabase": "Copia de seguridad",
"exportDatabaseDesc": "Haz clic para descargar un archivo .db que contiene una copia de seguridad de tu base de datos actual en tu dispositivo. El mismo archivo también puede restaurarse en un panel que funcione con PostgreSQL.",
"importDatabase": "Restaurar",
"importDatabaseDesc": "Haz clic para seleccionar y cargar una copia de seguridad .db o un volcado de migración (.dump) desde tu dispositivo para restaurar tu base de datos.",
"importDatabaseSuccess": "La base de datos se ha importado correctamente",
"importDatabaseError": "Ocurrió un error al importar la base de datos",
"readDatabaseError": "Ocurrió un error al leer la base de datos",
"getDatabaseError": "Ocurrió un error al obtener la base de datos",
"getConfigError": "Ocurrió un error al obtener el archivo de configuración",
"backupPostgresNote": "Este panel funciona con PostgreSQL. «Copia de seguridad» descarga un archivo pg_dump (.dump) y «Restaurar» lo vuelve a cargar con pg_restore. «Restaurar» también acepta una base de datos SQLite (.db) o un volcado de migración de SQLite e importa sus datos a PostgreSQL. El servidor necesita tener instaladas las herramientas cliente de PostgreSQL (pg_dump y pg_restore).",
"exportDatabasePgDesc": "Haz clic para descargar un volcado de PostgreSQL (.dump) de tu base de datos actual en tu dispositivo.",
"importDatabasePgDesc": "Haz clic para seleccionar y subir una copia de seguridad de PostgreSQL (.dump), una base de datos SQLite (.db) o un volcado de migración de SQLite para restaurar tu base de datos. Esto reemplaza todos los datos actuales.",
"migrationDownload": "Descargar migración",
"migrationDownloadPgDesc": "Haz clic para descargar una base de datos SQLite .db creada a partir de tus datos de PostgreSQL, lista para ejecutar este panel en SQLite."
},
"inbounds": {
"title": "Entradas",
"totalDownUp": "Subidas/Descargas Totales",
"totalUsage": "Uso Total",
"inboundCount": "Número de Entradas",
"operate": "Menú",
"enable": "Habilitar",
"remark": "Notas",
"node": "Nodo",
"deployTo": "Desplegar en",
"localPanel": "Panel local",
"fallbacks": {
"title": "Fallbacks",
"help": "Cuando una conexión en este inbound no coincide con ningún cliente, redirígela a otro lugar. Elige un inbound hijo abajo para rellenar automáticamente los campos de enrutamiento (SNI / ALPN / Path / xver) desde su transporte, o deja el selector vacío y define Dest directamente (p. ej. 8080 o 127.0.0.1:8080) para redirigir a un servidor externo como Nginx. Cada inbound hijo debe escuchar en 127.0.0.1 con security=none.",
"empty": "Aún no hay fallbacks",
"add": "Añadir fallback",
"pickInbound": "Selecciona un inbound",
"matchAny": "cualquiera",
"destPlaceholder": "automático (listen:puerto del hijo)",
"rederive": "Rellenar desde el hijo",
"rederived": "Rellenado desde el hijo",
"editAdvanced": "Editar campos de enrutamiento",
"hideAdvanced": "Ocultar avanzado",
"quickAddAll": "Añadir todos los elegibles",
"quickAdded": "Se añadieron {n} fallback(s)",
"quickAddedNone": "No hay nuevos inbounds elegibles",
"routesWhen": "Enruta cuando",
"defaultCatchAll": "Por defecto — captura cualquier otra cosa",
"needsTls": "Los fallbacks estarán disponibles al seleccionar TLS o Reality en la pestaña de Seguridad (solo VLESS/Trojan sobre RAW)."
},
"protocol": "Protocolo",
"port": "Puerto",
"portMap": "Asignación de puertos",
"traffic": "Tráfico",
"speed": "Velocidad",
"details": "Detalles",
"transportConfig": "Transporte",
"expireDate": "Fecha de Expiración",
"createdAt": "Creado",
"updatedAt": "Actualizado",
"resetTraffic": "Restablecer tráfico",
"addInbound": "Agregar Entrada",
"generalActions": "Acciones Generales",
"modifyInbound": "Modificar Entrada",
"deleteInbound": "Eliminar Entrada",
"deleteInboundContent": "¿Confirmar eliminación de entrada?",
"deleteConfirmTitle": "¿Eliminar el inbound \"{remark}\"?",
"deleteConfirmContent": "Esto elimina el inbound y todos sus clientes. No se puede deshacer.",
"resetConfirmTitle": "¿Restablecer el tráfico de \"{remark}\"?",
"resetConfirmContent": "Restablece los contadores de subida/bajada a 0 para este inbound.",
"selectedCount": "{count} seleccionado(s)",
"selectAll": "Seleccionar todo",
"bulkDeleteConfirmTitle": "¿Eliminar {count} inbounds?",
"bulkDeleteConfirmContent": "Esto elimina los inbounds seleccionados y todos sus clientes. No se puede deshacer.",
"cloneConfirmTitle": "¿Clonar el inbound \"{remark}\"?",
"cloneConfirmContent": "Crea una copia con un puerto nuevo y una lista de clientes vacía.",
"delAllClients": "Eliminar todos los clientes",
"delAllClientsConfirmTitle": "¿Eliminar los {count} clientes de \"{remark}\"?",
"delAllClientsConfirmContent": "Elimina todos los clientes de este inbound y sus registros de tráfico. El inbound se mantiene. Esto no se puede deshacer.",
"attachClients": "Asociar clientes a…",
"addClientsToGroup": "Añadir clientes al grupo…",
"attachClientsTitle": "Asociar clientes desde «{remark}»",
"attachClientsDesc": "Asocia los mismos {count} cliente(s) (mismo UUID/contraseña y tráfico compartido) a las entradas seleccionadas. Permanecen también en esta entrada.",
"attachClientsTargets": "Entradas objetivo",
"attachClientsNoTargets": "No hay otras entradas compatibles disponibles para asociar.",
"attachClientsResult": "Asociados {attached}, omitidos {skipped}.",
"attachClientsResultMixed": "Asociados {attached}, omitidos {skipped}, errores {errors}.",
"attachClientsSelectLabel": "Clientes para asociar",
"attachClientsSearchPlaceholder": "Buscar email o comentario",
"attachClientsStatusDisabled": "Deshabilitado",
"attachClientsSelectedCount": "{selected} de {total} seleccionado(s)",
"attachExistingClients": "Asociar clientes existentes…",
"attachExistingTitle": "Asociar clientes existentes a «{remark}»",
"attachExistingDesc": "Asocia los clientes existentes ({count} disponibles) a esta entrada: mismo UUID/contraseña y tráfico compartido. Los clientes que ya están en ella se omiten.",
"attachExistingNoClients": "Aún no hay clientes. Cree clientes primero y luego asócielos aquí.",
"attachExistingStatusAttached": "Ya asociado",
"detachClients": "Desasociar clientes",
"detachClientsTitle": "Desasociar clientes de «{remark}»",
"detachClientsDesc": "Quita el cliente o clientes seleccionados solo de esta entrada. Los registros se conservan (usa Delete para eliminar por completo). El origen tiene {count} cliente(s) en total.",
"detachClientsResult": "Desasociados {detached}, omitidos {skipped}.",
"detachClientsResultMixed": "Desasociados {detached}, omitidos {skipped}, errores {errors}.",
"detachClientsSelectLabel": "Clientes para desasociar",
"exportLinksTitle": "Exportar enlaces del inbound",
"exportSubsTitle": "Exportar enlaces de suscripción",
"exportAllLinksTitle": "Exportar todos los enlaces de inbound",
"exportAllSubsTitle": "Exportar todos los enlaces de suscripción",
"exportAllLinksFileName": "Todas-las-entradas",
"exportAllSubsFileName": "Todas-las-entradas-Subs",
"inboundJsonTitle": "JSON de entrada",
"deleteClient": "Eliminar cliente",
"deleteClientContent": "¿Está seguro de que desea eliminar el cliente?",
"resetTrafficContent": "¿Confirmar restablecimiento de tráfico?",
"copyLink": "Copiar Enlace",
"address": "Dirección",
"network": "Red",
"destinationPort": "Puerto de Destino",
"targetAddress": "Dirección de Destino",
"monitorDesc": "Dejar en blanco por defecto",
"meansNoLimit": "= Ilimitado. (unidad: GB)",
"totalFlow": "Flujo Total",
"leaveBlankToNeverExpire": "Dejar en Blanco para Nunca Expirar",
"noRecommendKeepDefault": "No hay requisitos especiales para mantener la configuración predeterminada",
"certificatePath": "Ruta Cert",
"certificateContent": "Datos Cert",
"publicKey": "Clave Pública",
"privatekey": "Clave Privada",
"clickOnQRcode": "Haz clic en el Código QR para Copiar",
"client": "Cliente",
"export": "Exportar Enlaces",
"clone": "Clonar",
"cloneInbound": "Clonar Entradas",
"cloneInboundContent": "Se aplicarán todas las configuraciones de esta entrada, excepto el Puerto, la IP de Escucha y los Clientes, al clon.",
"cloneInboundOk": "Clonar",
"resetAllTraffic": "Restablecer Tráfico de Todas las Entradas",
"resetAllTrafficTitle": "Restablecer tráfico de todas las entradas",
"resetAllTrafficContent": "¿Estás seguro de que deseas restablecer el tráfico de todas las entradas?",
"resetInboundClientTraffics": "Restablecer Tráfico de Clientes",
"resetInboundClientTrafficTitle": "Restablecer todo el tráfico de clientes",
"resetInboundClientTrafficContent": "¿Estás seguro de que deseas restablecer todo el tráfico para los clientes de esta entrada?",
"resetAllClientTraffics": "Restablecer Tráfico de Todos los Clientes",
"resetAllClientTrafficTitle": "Restablecer todo el tráfico de clientes",
"resetAllClientTrafficContent": "¿Estás seguro de que deseas restablecer todo el tráfico para todos los clientes?",
"delDepletedClients": "Eliminar Clientes Agotados",
"delDepletedClientsTitle": "Eliminar clientes agotados",
"delDepletedClientsContent": "¿Estás seguro de que deseas eliminar todos los clientes agotados?",
"email": "Email",
"emailDesc": "Por favor proporciona una dirección de correo electrónico única.",
"IPLimit": "Límite de IP",
"IPLimitDesc": "Desactiva la entrada si la cantidad supera el valor ingresado (ingresa 0 para desactivar el límite de IP).",
"IPLimitlog": "Registro de IP",
"IPLimitlogDesc": "Registro de historial de IPs (antes de habilitar la entrada después de que haya sido desactivada por el límite de IP, debes borrar el registro).",
"IPLimitlogclear": "Limpiar el Registro",
"setDefaultCert": "Establecer certificado desde el panel",
"setDefaultCertEmpty": "No hay certificado configurado para el panel. Configura uno en Ajustes primero.",
"streamTab": "Transmisión",
"securityTab": "Seguridad",
"sniffingTab": "Inspección",
"sniffingMetadataOnly": "Solo metadatos",
"sniffingRouteOnly": "Solo enrutamiento",
"sniffingIpsExcluded": "IPs excluidas",
"sniffingDomainsExcluded": "Dominios excluidos",
"decryption": "Descifrado",
"encryption": "Cifrado",
"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": "Personalizado",
"vlessAuthSelected": "Seleccionado: {auth}",
"vlessAuthGenerate": "Generar claves",
"vlessAuthGenerateButton": "Generar",
"advanced": {
"title": "Secciones JSON del inbound",
"subtitle": "JSON completo del inbound y editores específicos para settings, sniffing y streamSettings.",
"all": "Todo",
"allHelp": "Objeto inbound completo con todos los campos en un solo editor.",
"settings": "Ajustes",
"settingsHelp": "Envoltorio del bloque settings de Xray:",
"sniffing": "Sniffing",
"sniffingHelp": "Envoltorio del bloque sniffing de Xray:",
"stream": "Stream",
"streamHelp": "Envoltorio del bloque stream de Xray:",
"jsonErrorPrefix": "JSON avanzado"
},
"telegramDesc": "Por favor, proporciona el ID de Chat de Telegram. (usa el comando '/id' en el bot) o ({'@'}userinfobot)",
"subscriptionDesc": "Puedes encontrar tu enlace de suscripción en Detalles, también puedes usar el mismo nombre para varias configuraciones.",
"subSortIndex": "Orden sub",
"same": "misma",
"inboundInfo": "Información de entrada",
"exportInbound": "Exportación entrante",
"import": "Importar",
"importInbound": "Importar un entrante",
"periodicTrafficResetTitle": "Reset de Tráfico",
"periodicTrafficResetDesc": "Reiniciar automáticamente el contador de tráfico en intervalos especificados",
"lastReset": "Último reinicio",
"periodicTrafficReset": {
"never": "Nunca",
"daily": "Diariamente",
"weekly": "Semanalmente",
"monthly": "Mensualmente",
"hourly": "Cada hora"
},
"toasts": {
"obtain": "Recibir",
"updateSuccess": "La actualización fue exitosa",
"logCleanSuccess": "El registro ha sido limpiado",
"inboundsUpdateSuccess": "Entradas actualizadas correctamente",
"inboundUpdateSuccess": "Entrada actualizada correctamente",
"inboundCreateSuccess": "Entrada creada correctamente",
"bulkDeleted": "{count} inbounds eliminados",
"bulkDeletedMixed": "{ok} eliminados, {failed} fallidos",
"inboundDeleteSuccess": "Entrada eliminada correctamente",
"inboundClientAddSuccess": "Cliente(s) de entrada añadido(s)",
"inboundClientDeleteSuccess": "Cliente de entrada eliminado",
"inboundClientUpdateSuccess": "Cliente de entrada actualizado",
"savedNodeOfflineWillSync": "Guardado localmente. Un nodo de respaldo está desconectado o deshabilitado: el cambio se sincronizará cuando vuelva a conectarse.",
"delDepletedClientsSuccess": "Todos los clientes con tráfico agotado fueron eliminados",
"resetAllClientTrafficSuccess": "Todo el tráfico del cliente ha sido reiniciado",
"resetAllTrafficSuccess": "Todo el tráfico ha sido reiniciado",
"resetInboundClientTrafficSuccess": "El tráfico ha sido reiniciado",
"resetInboundTrafficSuccess": "El tráfico de entrada ha sido reiniciado",
"trafficGetError": "Error al obtener los tráficos",
"getNewX25519CertError": "Error al obtener el certificado X25519.",
"getNewmldsa65Error": "Error al obtener el certificado mldsa65.",
"getNewVlessEncError": "Error al obtener el certificado VlessEnc.",
"scanRealityTargetError": "No se pudo escanear el objetivo REALITY.",
"scanRealityTargetFeasible": "El objetivo es apto: se rellenaron el objetivo y el SNI.",
"scanRealityTargetNotFeasible": "El objetivo es accesible pero no apto para REALITY.",
"invalidClientField": "Cliente {client}: campo {field} — {reason}",
"invalidField": "{field} — {reason}",
"moreIssues": "{message} (+{count} más)"
},
"form": {
"moveUp": "Subir",
"moveDown": "Bajar",
"addAll": "Añadir todo",
"addAllFallbackTooltip": "Añade una fila de fallback para cada entrada elegible aún no conectada",
"peers": "Peers",
"addPeer": "Añadir peer",
"keepAlive": "Keep-alive",
"autoSystemRoutesTooltip": "Solo Windows. Los CIDR se añaden automáticamente a la tabla de enrutamiento del sistema para que el tráfico coincidente pase por TUN.",
"autoOutboundsInterface": "Interfaz de salidas automática",
"autoOutboundsInterfaceTooltip": "Interfaz física para tráfico de salida. Usa 'auto' para detectar; se habilita automáticamente cuando se establece Auto system routes.",
"rewriteAddress": "Reescribir dirección",
"rewritePort": "Reescribir puerto",
"allowedNetwork": "Red permitida",
"followRedirect": "Seguir redirección",
"accounts": "Cuentas",
"allowTransparent": "Permitir transparente",
"encryptionMethod": "Método de cifrado",
"fakeTlsDomain": "Dominio FakeTLS (SNI)",
"mtprotoSecret": "Secreto",
"mtgDomainFrontingIp": "IP de domain fronting",
"mtgDomainFrontingPort": "Puerto de domain fronting",
"mtgDomainFrontingProxyProtocol": "Protocolo PROXY de domain fronting",
"mtgDomainFrontingHint": "Adónde envía mtg el tráfico que no es de Telegram, p. ej. tu sitio web falso de NGINX. Deja la IP vacía para usar el dominio FakeTLS mediante DNS; el puerto predeterminado es 443.",
"mtgProxyProtocolListener": "Aceptar protocolo PROXY (escucha)",
"mtgPreferIp": "Preferencia de IP",
"mtgDebug": "Registro de depuración",
"mtgRouteThroughXray": "Enrutar a través de Xray",
"mtgRouteThroughXrayHint": "Envía el tráfico de Telegram de este proxy a través de Xray para que siga tus reglas de enrutamiento. El sidecar mtg sale por un puente SOCKS local con la etiqueta de esta entrada; usa esa etiqueta en la pestaña Enrutamiento para reglas avanzadas.",
"mtgRouteOutbound": "Salida",
"mtgRouteOutboundHint": "Opcional. Fuerza el tráfico de Telegram a salir por esta salida (o balanceador). Déjalo vacío para que decidan tus reglas de enrutamiento.",
"mtgRouteOutboundPlaceholder": "Usar reglas de enrutamiento",
"mtprotoFakeTlsDomainHint": "Dominio FakeTLS predeterminado para generar el secreto de un nuevo cliente. Cada cliente puede usar su propio dominio.",
"mtgThrottleMaxConnections": "Conexiones máximas",
"mtgThrottleMaxConnectionsHint": "Limita las conexiones simultáneas de todos los usuarios con reparto equitativo. 0 desactiva el límite.",
"mtgAdTagInvalid": "El ad-tag debe tener exactamente 32 caracteres hexadecimales.",
"mtgPublicIpv4": "IPv4 pública",
"mtgPublicIpv6": "IPv6 pública",
"mtgPublicIpHint": "La dirección pública accesible de este servidor, usada por el proxy intermedio del ad-tag. Déjalo en blanco para que mtg la detecte automáticamente.",
"visionTestseed": "Vision testseed",
"version": "Versión",
"udpIdleTimeout": "UDP idle timeout (s)",
"masquerade": "Masquerade",
"type": "Tipo",
"upstreamUrl": "URL Upstream",
"rewriteHost": "Reescribir Host",
"skipTlsVerify": "Saltar verificación TLS",
"directory": "Directorio",
"statusCode": "Código de estado",
"body": "Body",
"headers": "Cabeceras",
"proxyProtocol": "Proxy Protocol",
"requestVersion": "Versión de petición",
"requestMethod": "Método de petición",
"requestPath": "Ruta de petición",
"requestHeaders": "Cabeceras de petición",
"responseVersion": "Versión de respuesta",
"responseStatus": "Estado de respuesta",
"responseReason": "Razón de respuesta",
"responseHeaders": "Cabeceras de respuesta",
"heartbeatPeriod": "Periodo de heartbeat",
"serviceName": "Nombre de servicio",
"authority": "Authority",
"multiMode": "Multi Mode",
"maxBufferedUpload": "Máx. subida en búfer",
"maxUploadSize": "Tamaño máx. de subida (Byte)",
"streamUpServer": "Stream-Up Server",
"serverMaxHeaderBytes": "Máx. bytes cabecera servidor",
"paddingBytes": "Bytes de Padding",
"uplinkHttpMethod": "Método HTTP Uplink",
"paddingObfsMode": "Modo obfs de Padding",
"paddingKey": "Padding Key",
"paddingHeader": "Padding Header",
"paddingPlacement": "Ubicación de Padding",
"paddingMethod": "Método de Padding",
"sessionPlacement": "Session Placement",
"sessionKey": "Session Key",
"sessionIDTable": "Tabla de Session ID",
"sessionIDTableHint": "Conjunto de caracteres para generar los session ID: un nombre predefinido (ALPHABET, Base62, hex, number, …) o una cadena ASCII literal. Déjalo vacío para el valor por defecto de xray-core.",
"sessionIDLength": "Longitud de Session ID",
"sessionIDLengthHint": "Longitud o rango (p. ej. 8-16) del session ID generado. Solo se usa cuando hay una Tabla de Session ID definida; el mínimo debe ser mayor que 0.",
"sequencePlacement": "Sequence Placement",
"sequenceKey": "Sequence Key",
"uplinkDataPlacement": "Uplink Data Placement",
"uplinkDataKey": "Uplink Data Key",
"noSseHeader": "Sin cabecera SSE",
"ttiMs": "TTI (ms)",
"uplinkMbps": "Subida (MB/s)",
"downlinkMbps": "Bajada (MB/s)",
"cwndMultiplier": "Multiplicador CWND",
"maxSendingWindow": "Máx. ventana de envío",
"externalProxy": "Proxy externo",
"forceTls": "Forzar TLS",
"fingerprint": "Fingerprint",
"defaultOption": "Por defecto",
"routeMark": "Route Mark",
"tcpKeepAliveInterval": "TCP Keep Alive Interval",
"tcpKeepAliveIdle": "TCP Keep Alive Idle",
"tcpMaxSeg": "TCP Max Seg",
"tcpUserTimeout": "TCP User Timeout",
"tcpWindowClamp": "TCP Window Clamp",
"tcpWindowClampHint": "Deja 0 para usar el valor predeterminado del sistema. Los valores distintos de cero limitan la ventana de recepción TCP anunciada; valores como 600 (del ejemplo de la documentación de Xray) pueden hundir el rendimiento en enlaces de alta latencia.",
"tcpFastOpen": "TCP Fast Open",
"multipathTcp": "Multipath TCP",
"penetrate": "Penetrate",
"v6Only": "Solo V6",
"tcpCongestion": "TCP Congestion",
"dialerProxy": "Dialer Proxy",
"trustedXForwardedFor": "X-Forwarded-For de confianza",
"trustedXForwardedForHint": "Confía en esta cabecera de solicitud para obtener la IP real del cliente (p. ej. CF-Connecting-IP detrás del CDN de Cloudflare). Solo válido en los transportes WebSocket, HTTPUpgrade, XHTTP y gRPC. Déjalo vacío para ignorar las cabeceras reenviadas.",
"proxyProtocolHint": "Acepta la cabecera PROXY protocol para obtener la IP real del cliente desde un túnel/relé L4 superior (HAProxy, gost, nginx-stream, Xray dokodemo-door) o Cloudflare Spectrum. El nodo superior DEBE enviar PROXY protocol. Funciona en TCP, WebSocket, HTTPUpgrade y gRPC; no en mKCP.",
"realClientIp": "IP real del cliente",
"realClientIpHint": "Captura la IP real del visitante cuando el tráfico llega a este inbound a través de un CDN o relé, en lugar de registrar la dirección del intermediario. Elige un preajuste para rellenar los campos sockopt correspondientes más abajo. Estos campos nunca se envían a los clientes en las suscripciones.",
"realClientIpPresetOff": "Desactivado / directo",
"realClientIpPresetCloudflare": "Cloudflare CDN",
"realClientIpPresetProxyProtocol": "Relé L4 / Spectrum (PROXY)",
"realClientIpTrustedHeaderTransportWarn": "Trusted X-Forwarded-For solo es válido en WebSocket, HTTPUpgrade y XHTTP. En el transporte actual esta cabecera se ignora.",
"realClientIpProxyProtocolTransportWarn": "PROXY protocol no es compatible con este transporte (mKCP). Usa TCP/RAW, WebSocket, HTTPUpgrade, gRPC o XHTTP.",
"addressPortStrategy": "Estrategia dirección+puerto",
"tryDelayMs": "Retraso de intento (ms)",
"prioritizeIPv6": "Priorizar IPv6",
"interleave": "Interleave",
"maxConcurrentTry": "Máx. intentos simultáneos",
"customSockopt": "Sockopt personalizado",
"addCustomOption": "Añadir opción personalizada",
"serverNameIndication": "SNI",
"cipherSuites": "Cipher Suites",
"autoOption": "Auto",
"minMaxVersion": "Versión mín/máx",
"rejectUnknownSni": "Rechazar SNI desconocido",
"disableSystemRoot": "Deshabilitar System Root",
"sessionResumption": "Reanudación de sesión",
"oneTimeLoading": "Carga única",
"usageOption": "Opción de uso",
"buildChain": "Construir cadena",
"echKey": "ECH key",
"echConfig": "Config ECH",
"pinnedPeerCertSha256": "SHA-256 del cert. del par fijado",
"pinnedPeerCertSha256Tip": "Hashes SHA-256 del certificado del par como cadena hexadecimal (p. ej. e8e2d3…), separados por comas. Solo en el panel — no se escribe en la config xray del servidor, pero se incluye en los enlaces para que los clientes puedan fijar el certificado.",
"pinnedPeerCertSha256Placeholder": "hash(es) hexadecimal, separados por comas",
"getNewEchCert": "Obtener nuevo cert ECH",
"echSockopt": "ECH Sockopt",
"echSockoptTip": "Opciones de socket para la conexión que Xray usa al obtener la lista de configuración ECH (por ejemplo, enrutar la consulta a través de una salida dialerProxy). Déjalo deshabilitado para usar los valores por defecto.",
"curvePreferences": "Preferencias de curvas",
"curvePreferencesTip": "Restringe las curvas de intercambio de claves TLS que ofrece el servidor, por orden de preferencia (por ejemplo, X25519MLKEM768, X25519). Déjalo vacío para usar los valores por defecto de Xray-core.",
"masterKeyLog": "Registro de clave maestra",
"masterKeyLogTip": "Ruta donde escribir las claves maestras TLS (formato SSLKEYLOGFILE) para depurar con Wireshark. Déjalo vacío en producción — permite que cualquiera con el archivo descifre el tráfico.",
"verifyPeerCertByName": "Verificar cert. del par por nombre",
"verifyPeerCertByNameTip": "Indica a los clientes que verifiquen el certificado del servidor con este nombre en lugar del SNI. Nombres separados por comas. Solo en el panel — se incluye en los enlaces (vcn). El reemplazo moderno de allowInsecure, que Xray eliminó después del 2026-06-01.",
"pinFromCert": "Rellenar desde el certificado de este inbound",
"pinFromRemote": "Obtener el hash haciendo ping al SNI (xray tls ping)",
"pinFromRemoteNoSni": "Establece primero el SNI (serverName) para hacer ping al certificado remoto.",
"pinFromRemoteFailed": "No se pudo obtener el hash del certificado remoto.",
"limitFallback": "Limitar fallback",
"limitFallbackUpload": "Limitar subida del fallback",
"limitFallbackDownload": "Limitar bajada del fallback",
"afterBytes": "Tras bytes",
"afterBytesTip": "Deja que el fallback funcione a máxima velocidad durante esta cantidad de bytes y luego empieza a limitar. 0 = limitar desde el primer byte.",
"bytesPerSec": "Bytes por seg.",
"bytesPerSecTip": "Límite de velocidad (bytes/seg.) aplicado al tráfico del fallback tras el umbral, para que las sondas no puedan usar tu servidor como ancho de banda gratuito hacia el destino. 0 = sin límite (deshabilita esta dirección).",
"burstBytesPerSec": "Ráfaga de bytes por seg.",
"burstBytesPerSecTip": "Margen para ráfagas breves por encima de la tasa constante (tamaño del token-bucket). Si es menor que Bytes por seg., se eleva para igualarlo.",
"show": "Mostrar",
"xver": "Xver",
"target": "Objetivo",
"maxTimeDiff": "Máx. diferencia de tiempo (ms)",
"minClientVer": "Mín. versión cliente",
"maxClientVer": "Máx. versión cliente",
"shortIds": "Short IDs",
"realityTargetHint": "Obligatorio. Debe incluir un puerto (p. ej. example.com:443). Sin puerto, Xray-core no arranca.",
"realityTargetRequired": "El destino REALITY es obligatorio",
"realityTargetNeedsPort": "El destino REALITY debe incluir un puerto (p. ej. example.com:443)",
"realityTargetInvalidPort": "El destino REALITY tiene un puerto no válido",
"scan": "Escanear",
"findTargets": "Buscar objetivos",
"scanModalTitle": "Escáner de objetivos REALITY",
"scanModalDesc": "Valida un dominio o escanea un rango IP / CIDR para descubrir nuevos objetivos REALITY a partir de sus certificados. Deja el campo vacío para probar los candidatos comunes.",
"scanDiscoverPlaceholder": "IP, CIDR o dominio — déjalo vacío para candidatos comunes",
"scanStatus": "Estado",
"scanFeasible": "Apto",
"scanNotFeasible": "No apto",
"scanCurve": "Intercambio de claves",
"scanCert": "Certificado",
"scanCertInvalid": "No confiable",
"scanLatency": "Latencia",
"scanUse": "Usar",
"scanRescan": "Reescanear",
"spiderX": "SpiderX",
"spiderXHint": "Semilla por cliente: el panel deriva de ella una ruta spx única para cada cliente; regenera para rotar las rutas de todos",
"getNewCert": "Obtener nuevo cert",
"mldsa65Seed": "mldsa65 Seed",
"mldsa65Verify": "mldsa65 Verify",
"getNewSeed": "Obtener nuevo Seed",
"listenHelp": "También puedes introducir una ruta de socket Unix (p. ej. /run/xray/in.sock), o un nombre de socket abstracto con el prefijo @ (p. ej. @xray/in.sock), para escuchar en un socket en lugar de un puerto TCP; en ese caso, establece el Puerto en 0.",
"shareAddrStrategy": "Estrategia de dirección para compartir",
"shareAddrStrategyHelp": "Controla qué dirección se escribe en los enlaces compartidos exportados, códigos QR y la salida de suscripción.",
"shareAddr": "Dirección compartida personalizada",
"shareAddrHelp": "Solo se usa cuando la estrategia de dirección para compartir es Personalizada. Introduce un host o IP sin esquema ni puerto.",
"subSortIndex": "Orden en la suscripción",
"subSortIndexHelp": "Posición de los enlaces de esta entrada en la salida de la suscripción (página de suscripción y apps cliente). Los valores más bajos van primero; con valores iguales se mantiene el orden de creación. No afecta a la lista de entradas del panel.",
"shareAddrStrategyOptions": {
"node": "Dirección del nodo",
"listen": "Dirección de escucha del inbound",
"custom": "Personalizada"
}
},
"info": {
"mode": "Modo",
"grpcServiceName": "grpc serviceName",
"grpcMultiMode": "grpc multiMode",
"interfaceName": "Nombre de interfaz",
"mtu": "MTU",
"gateway": "Gateway",
"dns": "DNS",
"outboundsInterface": "Interfaz de salidas",
"autoSystemRoutes": "Rutas del sistema automáticas",
"followRedirect": "FollowRedirect",
"auth": "Auth",
"noKernelTun": "TUN sin kernel",
"keepAlive": "Keep alive",
"peerNumber": "Peer {n}",
"peerNumberConfig": "Config Peer {n}"
},
"stream": {
"general": {
"request": "Pedido",
"response": "Respuesta",
"name": "Nombre",
"value": "Valor"
},
"tcp": {
"version": "Versión",
"method": "Método",
"path": "Ruta",
"status": "Estado",
"statusDescription": "Descripción de la Situación",
"requestHeader": "Encabezado de solicitud",
"responseHeader": "Encabezado de respuesta"
}
},
"sniffingDestOverride": "Anulación de destino"
},
"clients": {
"tabBasics": "Básico",
"tabCredentials": "Credenciales",
"tabLinks": "Enlaces",
"wireguardConfig": "Configuración de WireGuard",
"config": "Configuración",
"linksHint": "Añade enlaces de terceros y URLs de suscripción remotas para incluirlos en la suscripción de este cliente.",
"addExternalLink": "Añadir enlace externo",
"addExternalSubscription": "Añadir suscripción externa",
"noExternalLinks": "Aún no hay enlaces externos.",
"noExternalSubscriptions": "Aún no hay suscripciones externas.",
"add": "Añadir cliente",
"edit": "Editar cliente",
"submitAdd": "Añadir cliente",
"submitEdit": "Guardar cambios",
"clientCount": "Número de clientes",
"bulk": "Añadir en lote",
"copyFromInbound": "Copiar clientes desde inbound",
"copyToInbound": "Copiar clientes a",
"copySelected": "Copiar selección",
"copySource": "Origen",
"copyEmailPreview": "Vista previa del correo resultante",
"copySelectSourceFirst": "Selecciona primero un inbound de origen.",
"copyResult": "Resultado de la copia",
"copyResultSuccess": "Copiado correctamente",
"copyResultNone": "Nada que copiar: no hay clientes seleccionados o el origen está vacío",
"copyResultErrors": "Errores de copia",
"copyFlowLabel": "Flow para clientes nuevos (VLESS)",
"copyFlowHint": "Se aplica a todos los clientes copiados. Déjalo vacío para omitir.",
"selectAll": "Seleccionar todo",
"clearAll": "Limpiar todo",
"method": "Método",
"first": "Primero",
"last": "Último",
"ipLog": "Registro de IP",
"prefix": "Prefijo",
"postfix": "Sufijo",
"delayedStart": "Iniciar tras el primer uso",
"expireDays": "Duración (días)",
"days": "Día(s)",
"renew": "Renovación automática",
"renewDesc": "Renovación automática tras la expiración. (0 = desactivado) (unidad: día)",
"renewDays": "Renovación automática (días)",
"searchPlaceholder": "Buscar email, comentario, sub ID, UUID, contraseña, auth, Telegram ID…",
"filterTitle": "Filtrar clientes",
"clearAllFilters": "Limpiar todo",
"filters": {
"nodes": "Nodos",
"localPanel": "Local (este panel)"
},
"showingCount": "Mostrando {shown} de {total}",
"sortOldest": "Más antiguos",
"sortNewest": "Más recientes",
"sortRecentlyUpdated": "Recientemente actualizados",
"sortRecentlyOnline": "Recientemente en línea",
"sortEmailAZ": "Email A→Z",
"sortEmailZA": "Email Z→A",
"sortMostTraffic": "Mayor tráfico",
"sortHighestRemaining": "Mayor restante",
"sortExpiringSoonest": "Caducidad más próxima",
"has": "Tiene",
"hasNot": "No tiene",
"title": "Clientes",
"actions": "Acciones",
"totalGB": "Límite de tráfico (GB)",
"totalGBDesc": "Cuota de datos para este cliente. 0 = ilimitado.",
"expiryTime": "Expiración",
"addClients": "Añadir clientes",
"limitIp": "Límite de IP",
"limitIpDesc": "Máximo de IP simultáneas. 0 = ilimitado.",
"limitIpFail2banMissing": "Fail2ban no está instalado, por lo que no se puede aplicar el límite de IP. Instala Fail2ban desde el menú bash de x-ui para habilitar esta opción.",
"limitIpFail2banWindows": "Fail2ban no está disponible en Windows, por lo que no se puede aplicar el límite de IP.",
"limitIpDisabled": "La función de límite de IP está deshabilitada en este servidor.",
"password": "Contraseña",
"passwordDesc": "Solo la usan los clientes Trojan y Shadowsocks; se ignora para VLESS, VMess, Hysteria y WireGuard.",
"subId": "ID de suscripción",
"online": "En línea",
"email": "Email",
"emailInvalidChars": "El correo no puede contener espacios, '/', '\\' ni caracteres de control",
"subIdInvalidChars": "El ID de suscripción no puede contener espacios, '/', '\\' ni caracteres de control",
"group": "Grupo",
"groupDesc": "Etiqueta lógica para agrupar clientes relacionados (p. ej. equipo, cliente, región). Filtrable desde la barra de herramientas.",
"groupPlaceholder": "p. ej. customer-a",
"comment": "Comentario",
"traffic": "Tráfico",
"speed": "Velocidad",
"offline": "Sin conexión",
"addClient": "Añadir cliente",
"qrCode": "Código QR",
"clientInfo": "Información del cliente",
"delete": "Eliminar",
"reset": "Restablecer tráfico",
"editClient": "Editar cliente",
"client": "Cliente",
"enabled": "Habilitado",
"remaining": "Restante",
"duration": "Duración",
"attachedInbounds": "Inbounds asociados",
"selectInbound": "Selecciona uno o más inbounds",
"selectAllInbounds": "Seleccionar todo",
"clearAllInbounds": "Limpiar todo",
"noSubId": "Este cliente no tiene subId, no hay enlace compartible.",
"noLinks": "No hay enlaces compartibles — asocia primero este cliente a un inbound con protocolo válido.",
"link": "Enlace",
"resetNotPossible": "Asocia primero este cliente a un inbound.",
"general": "General",
"resetAllTraffics": "Restablecer tráfico de todos los clientes",
"resetAllTrafficsTitle": "¿Restablecer tráfico de todos los clientes?",
"resetAllTrafficsContent": "El contador de subida/bajada de cada cliente vuelve a cero. Las cuotas y la expiración no se modifican. Esta acción no se puede deshacer.",
"deleteConfirmTitle": "¿Eliminar al cliente {email}?",
"deleteConfirmContent": "Esto elimina al cliente de cada inbound asociado y descarta su registro de tráfico. No se puede deshacer.",
"deleteSelected": "Eliminar ({count})",
"adjustSelected": "Ajustar ({count})",
"subLinksSelected": "Enlaces sub ({count})",
"addToGroupTitle": "Añadir {count} cliente(s) a un grupo",
"addToGroupTooltip": "Selecciona un grupo existente o escribe un nombre nuevo. Usa Ungroup para quitar clientes de su grupo actual.",
"groupName": "Nombre del grupo",
"addToGroupSuccessToast": "Se añadieron {count} cliente(s) a {group}",
"ungroupSuccessToast": "Grupo limpiado de {count} cliente(s)",
"ungroup": "Desagrupar",
"ungroupConfirmTitle": "¿Quitar {count} cliente(s) de su grupo?",
"ungroupConfirmContent": "Limpia la etiqueta de grupo en cada cliente seleccionado. Los clientes se conservan (usa Delete para eliminarlos por completo).",
"addToGroup": "Añadir al grupo",
"attach": "Asociar",
"adjust": "Ajustar",
"subLinks": "Enlaces sub",
"enable": "Habilitar",
"disable": "Deshabilitar",
"bulkEnableConfirmTitle": "¿Habilitar {count} clientes?",
"bulkEnableConfirmContent": "Habilita cada cliente seleccionado en todos los inbounds asociados. Los clientes cuya cuota se haya agotado o cuya caducidad haya pasado se deshabilitarán de nuevo automáticamente.",
"bulkDisableConfirmTitle": "¿Deshabilitar {count} clientes?",
"bulkDisableConfirmContent": "Deshabilita cada cliente seleccionado en todos los inbounds asociados. Pierden el acceso de inmediato, pero se conservan sus registros y su tráfico.",
"selectedCount": "{count} seleccionado(s)",
"attachSelected": "Asociar ({count})",
"attachToInboundsTitle": "Asociar {count} cliente(s) a entrada(s)",
"attachToInboundsDesc": "Asocia los {count} cliente(s) seleccionados (mismo UUID/contraseña y tráfico compartido) a las entradas elegidas. Mantienen sus asociaciones existentes.",
"attachToInboundsTargets": "Entradas objetivo",
"attachToInboundsNoTargets": "No hay entradas multiusuario disponibles para asociar.",
"detachSelected": "Desasociar ({count})",
"detach": "Desasociar",
"detachFromInboundsTitle": "Desasociar {count} cliente(s) de entrada(s)",
"detachFromInboundsDesc": "Quita los {count} cliente(s) seleccionados de las entradas elegidas. Las parejas donde el cliente no estaba asociado se omiten silenciosamente. Los registros de los clientes se conservan (usa Delete para eliminar por completo).",
"detachFromInboundsTargets": "Entradas para desasociar",
"detachFromInboundsNoTargets": "No hay entradas multiusuario disponibles.",
"detachFromInboundsResult": "Desasociados {detached}, omitidos {skipped}.",
"detachFromInboundsResultMixed": "Desasociados {detached}, omitidos {skipped}, errores {errors}.",
"subLinksTitle": "Enlaces sub ({count})",
"subLinkColumn": "URL de suscripción",
"subJsonLinkColumn": "URL JSON de suscripción",
"subLinksCopyAll": "Copiar todo",
"subLinksCopiedAll": "Copiados {count} enlace(s)",
"subLinksEmpty": "Ninguno de los clientes seleccionados tiene ID de suscripción.",
"subLinksDisabled": "El servicio de suscripción está deshabilitado.",
"subLinksDisabledHint": "Habilita la suscripción en Ajustes del panel → Suscripción para generar enlaces.",
"bulkDeleteConfirmTitle": "¿Eliminar {count} clientes?",
"bulkDeleteConfirmContent": "Cada cliente seleccionado se elimina de los inbounds asociados y se descarta su registro de tráfico. No se puede deshacer.",
"bulkAdjustTitle": "Ajustar {count} clientes",
"bulkAdjustHint": "Los valores positivos extienden, los negativos reducen. Los clientes con expiración o tráfico ilimitado se omiten para ese campo.",
"bulkAdjustNothing": "Establece días o tráfico antes de aplicar.",
"addDays": "Añadir días",
"addTrafficGB": "Añadir tráfico (GB)",
"bulkFlow": "Establecer flow",
"bulkFlowNoChange": "Sin cambios",
"bulkFlowDisable": "Desactivar (borrar flow)",
"delDepleted": "Eliminar agotados",
"delDepletedConfirmTitle": "¿Eliminar clientes agotados?",
"delDepletedConfirmContent": "Elimina todos los clientes con cuota agotada o expirados. No se puede deshacer.",
"exportClients": "Exportar clientes",
"importClients": "Importar clientes",
"import": "Importar",
"delOrphans": "Eliminar clientes sin entrante",
"delOrphansConfirmTitle": "¿Eliminar clientes sin entrante?",
"delOrphansConfirmContent": "Elimina todos los clientes que no están vinculados a ningún entrante, junto con su registro de tráfico. No se puede deshacer.",
"auth": "Auth",
"hysteriaAuth": "Hysteria Auth",
"hysteriaAuthDesc": "Credencial usada únicamente por los clientes Hysteria. Trojan y Shadowsocks usan el campo «Contraseña» en su lugar.",
"uuid": "UUID",
"flow": "Flow",
"vmessSecurity": "Seguridad VMess",
"wireguardPrivateKey": "Clave privada de WireGuard",
"wireguardPublicKey": "Clave pública de WireGuard",
"wireguardPreSharedKey": "Clave precompartida de WireGuard",
"wireguardAllowedIPs": "IP permitidas de WireGuard",
"wireguardAllowedIPsHint": "Déjalo vacío para asignar automáticamente; separa las entradas con comas",
"mtprotoSecret": "Secreto MTProto",
"mtprotoSecretHint": "El secreto FakeTLS del cliente. Vuelve a generarlo para cambiarlo.",
"mtprotoAdTag": "Ad-tag (canal patrocinado)",
"mtprotoAdTagHint": "Etiqueta hexadecimal opcional de 32 caracteres del registro de proxy de Telegram. Si se establece, este cliente se enruta a través de los proxies intermedios de Telegram y aparece un canal patrocinado en la parte superior de su lista de chats.",
"reverseTag": "Etiqueta inversa",
"reverseTagPlaceholder": "Reverse tag opcional",
"telegramId": "ID de usuario de Telegram",
"telegramIdPlaceholder": "ID numérico de usuario de Telegram (0 = ninguno)",
"created": "Creado",
"updated": "Actualizado",
"ipLimit": "Límite de IP",
"toasts": {
"deleted": "Cliente eliminado",
"trafficReset": "Tráfico restablecido",
"allTrafficsReset": "Tráfico de todos los clientes restablecido",
"bulkDeleted": "{count} clientes eliminados",
"bulkDeletedMixed": "{ok} eliminados, {failed} fallidos",
"bulkEnabled": "{count} clientes habilitados",
"bulkEnabledMixed": "{ok} habilitados, {failed} fallidos",
"bulkDisabled": "{count} clientes deshabilitados",
"bulkDisabledMixed": "{ok} deshabilitados, {failed} fallidos",
"bulkCreated": "{count} clientes creados",
"bulkCreatedMixed": "{ok} creados, {failed} fallidos",
"bulkAdjusted": "{count} clientes ajustados",
"bulkAdjustedMixed": "{ok} ajustados, {skipped} omitidos",
"delDepleted": "{count} clientes agotados eliminados",
"delOrphans": "{count} clientes sin entrante eliminados",
"imported": "{count} clientes importados",
"importedMixed": "{ok} importados, {failed} omitidos"
}
},
"groups": {
"title": "Grupos",
"name": "Nombre",
"clientCount": "Clientes",
"totalGroups": "Total de grupos",
"totalGroupedClients": "Clientes con grupo",
"trafficUsed": "Tráfico usado",
"upload": "Subida",
"download": "Bajada",
"totalTraffic": "Tráfico total",
"totalUpDown": "Subida / bajada total",
"addGroup": "Añadir grupo",
"createSuccess": "Grupo «{name}» creado.",
"rename": "Renombrar",
"renameTitle": "Renombrar {name}",
"renameCollision": "Ya existe un grupo llamado «{name}».",
"renameSuccess": "Grupo renombrado en {count} cliente(s).",
"deleteConfirmTitle": "¿Eliminar el grupo {name}?",
"deleteConfirmContent": "Esto elimina el grupo y limpia su etiqueta de {count} cliente(s). Los clientes en sí no se eliminan.",
"deleteSuccess": "Grupo limpiado de {count} cliente(s).",
"resetTraffic": "Restablecer tráfico",
"resetConfirmTitle": "¿Restablecer tráfico del grupo {name}?",
"resetConfirmContent": "Esto restablece solo el contador de tráfico del grupo. Los contadores de cada cliente no se ven afectados.",
"resetSuccess": "Tráfico del grupo {name} restablecido.",
"adjustSuccess": "Ajustados {count} cliente(s) en {name}.",
"emptyForAction": "Este grupo aún no tiene clientes.",
"deleteGroupOnly": "Eliminar grupo (conservar clientes)",
"deleteClients": "Eliminar clientes del grupo",
"deleteClientsConfirmTitle": "¿Eliminar todos los clientes en {name}?",
"deleteClientsConfirmContent": "Esto elimina permanentemente {count} cliente(s) junto con sus registros de tráfico. La etiqueta de grupo también se limpia. Esto no se puede deshacer.",
"deleteClientsSuccess": "Eliminados {count} cliente(s).",
"deleteClientsMixed": "{ok} eliminados, {failed} omitidos",
"addToGroup": "Añadir clientes…",
"addToGroupTitle": "Añadir clientes al grupo «{name}»",
"addToGroupDesc": "Selecciona clientes para añadir a este grupo. Mantienen sus asociaciones de entrada existentes; solo cambia la etiqueta de grupo. Los clientes que ya están en este grupo no se muestran.",
"addToGroupEmpty": "No hay otros clientes disponibles para añadir.",
"addToGroupResult": "Añadidos {count} cliente(s) a {name}.",
"removeFromGroup": "Quitar clientes…",
"removeFromGroupTitle": "Quitar clientes del grupo «{name}»",
"removeFromGroupDesc": "Selecciona miembros para quitar de este grupo. Los clientes se conservan (usa «Eliminar clientes del grupo» para eliminarlos por completo).",
"removeFromGroupResult": "Quitados {count} cliente(s) de {name}."
},
"nodes": {
"title": "Nodos",
"addNode": "Agregar nodo",
"editNode": "Editar nodo",
"totalNodes": "Total de nodos",
"onlineNodes": "En línea",
"offlineNodes": "Sin conexión",
"avgLatency": "Latencia media",
"name": "Nombre",
"namePlaceholder": "p. ej. de-frankfurt-1",
"addressPlaceholder": "panel.example.com o 1.2.3.4",
"remark": "Notas",
"scheme": "Esquema",
"address": "Dirección",
"port": "Puerto",
"basePath": "Ruta base",
"apiToken": "Token API",
"apiTokenPlaceholder": "Token desde la página de Configuración del panel remoto",
"apiTokenHint": "El panel remoto expone su token de API en Configuraciones de Seguridad → Token de API.",
"regenerate": "Regenerar token",
"regenerateConfirm": "Regenerar invalida el token actual. Cualquier panel central que lo use perderá el acceso hasta que se actualice. ¿Continuar?",
"allowPrivateAddress": "Permitir dirección privada",
"allowPrivateAddressHint": "Habilitar solo para nodos en una red privada o VPN.",
"outboundTag": "Outbound de conexión",
"outboundTagHint": "Enruta el tráfico de la API del panel de este nodo a través del outbound Xray seleccionado. Un inbound de puente loopback se agrega automáticamente a la configuración en ejecución y se aplica en vivo. Déjelo vacío para una conexión directa.",
"outboundTagPlaceholder": "Conexión directa",
"inboundSyncMode": "Importación de inbounds",
"inboundSyncModeHint": "Elige qué inbounds importar desde este nodo. Los nodos existentes importan todos de forma predeterminada.",
"allInbounds": "Todos los inbounds",
"selectedInbounds": "Inbounds seleccionados",
"inboundTags": "Inbounds",
"inboundTagsHint": "La selección se compara por la etiqueta del inbound. Una selección vacía no importa ninguno.",
"inboundTagsPlaceholder": "Carga y selecciona inbounds",
"loadInbounds": "Cargar inbounds desde el nodo",
"inboundsLoaded": "Se cargaron {{count}} inbounds",
"inboundsLoadFailed": "No se pudieron cargar los inbounds",
"enable": "Habilitado",
"status": "Estado",
"cpu": "CPU",
"mem": "Memoria",
"netUp": "Subida de red (KB/s)",
"netDown": "Bajada de red (KB/s)",
"uptime": "Tiempo activo",
"latency": "Latencia",
"lastHeartbeat": "Último latido",
"xrayVersion": "Versión de Xray",
"panelVersion": "Versión del panel",
"actions": "Acciones",
"probe": "Sondear ahora",
"updatePanel": "Actualizar panel",
"updateSelected": "Actualizar seleccionados ({count})",
"updateAvailable": "Actualización disponible",
"upToDate": "Actualizado",
"updateConfirmTitle": "¿Actualizar {count} nodo(s) a la última versión?",
"updateConfirmContent": "Cada nodo seleccionado descarga la última versión y se reinicia con ella. Solo se actualizan los nodos habilitados y en línea.",
"updateDevChannel": "Actualizar al canal de desarrollo (último commit)",
"testConnection": "Probar conexión",
"connectionOk": "Conexión correcta ({ms} ms)",
"connectionFailed": "Conexión fallida",
"never": "nunca",
"justNow": "ahora mismo",
"subNode": "Subnodo",
"subNodeTip": "Solo lectura: un nodo descendente al que se llega a través de {parent}. Gestiónalo desde el propio panel de {parent}.",
"deleteConfirmTitle": "¿Eliminar el nodo \"{name}\"?",
"deleteConfirmContent": "Esto detiene la monitorización del nodo. El panel remoto en sí no se ve afectado.",
"statusValues": {
"online": "En línea",
"offline": "Sin conexión",
"unknown": "Desconocido",
"xrayError": "Error de Xray",
"xrayStopped": "Detenido"
},
"toasts": {
"list": "Error al cargar los nodos",
"obtain": "Error al cargar el nodo",
"add": "Agregar nodo",
"update": "Actualizar nodo",
"delete": "Eliminar nodo",
"deleted": "Nodo eliminado",
"test": "Probar conexión",
"fillRequired": "El nombre, la dirección, el puerto y el token de API son obligatorios",
"probeFailed": "Sondeo fallido",
"updateStarted": "Actualización del panel iniciada",
"updateResult": "Actualización iniciada en {ok} nodo(s), {failed} fallaron",
"updateNoneEligible": "Selecciona al menos un nodo en línea y habilitado",
"saveMtls": "Guardar mTLS del nodo"
},
"tlsVerifyMode": "Verificación TLS",
"tlsVerifyModeHint": "Cómo valida el panel el certificado HTTPS del nodo. Fijar u Omitir son para certificados autofirmados (solo nodos https).",
"tlsVerify": "Verificar (CA predeterminada)",
"tlsPin": "Fijar certificado (SHA-256)",
"tlsSkip": "Omitir verificación",
"tlsMtls": "TLS mutuo (certificado de cliente)",
"mtlsFormHint": "Este nodo autentica al panel con un certificado de cliente. Copia el CA de este panel desde la sección mTLS del nodo al nodo, configura su CA de confianza y luego reinícialo.",
"mtls": {
"title": "mTLS del nodo",
"intro": "TLS mutuo añade un factor de certificado de cliente además del token de API para las llamadas entre nodos. Es opcional: déjalo vacío para mantener solo la autenticación por token.",
"copyCa": "Copiar el CA de este panel",
"copyCaHint": "Entrega este CA a los nodos que gestiona este panel y luego configura su verificación TLS como TLS mutuo.",
"caCopied": "Certificado CA copiado al portapapeles",
"caFailed": "No se pudo obtener el certificado CA",
"trustLabel": "CA de confianza (panel superior)",
"trustHint": "Cuando este panel es a su vez un nodo, pega aquí el CA del panel que lo gestiona para exigir su certificado de cliente. Reinicia el panel para aplicar.",
"trustPlaceholder": "-----BEGIN CERTIFICATE-----",
"save": "Guardar CA de confianza",
"saved": "CA de confianza guardado — reinicia el panel para aplicar"
},
"tlsSkipWarning": "Omitir la verificación elimina la protección contra ataques de intermediario; el token de API podría ser interceptado. Es preferible fijar el certificado.",
"pinnedCert": "SHA-256 del certificado fijado",
"pinnedCertHint": "SHA-256 del certificado del nodo en base64 o hex. Usa Obtener para leerlo del nodo ahora.",
"pinnedCertPlaceholder": "SHA-256 en base64 o hex",
"fetchPin": "Obtener",
"pinFetched": "Se obtuvo el certificado actual del nodo",
"pinFetchFailed": "No se pudo obtener el certificado"
},
"settings": {
"title": "Configuraciones",
"save": "Guardar",
"infoDesc": "Cada cambio realizado aquí debe ser guardado. Por favor, reinicie el panel para aplicar los cambios.",
"restartPanel": "Reiniciar panel",
"restartPanelDesc": "¿Está seguro de que desea reiniciar el panel? Haga clic en Aceptar para reiniciar después de 3 segundos. Si no puede acceder al panel después de reiniciar, por favor, consulte la información de registro del panel en el servidor.",
"restartPanelSuccess": "El panel se reinició correctamente",
"actions": "Acciones",
"resetDefaultConfig": "Restablecer a Configuración Predeterminada",
"panelSettings": "Configuraciones del Panel",
"securitySettings": "Configuraciones de Seguridad",
"securityWarnings": "Advertencias de seguridad",
"panelExposed": "Es posible que su panel esté expuesto:",
"warnHttp": "El panel se sirve por HTTP sin cifrar — configure TLS para producción.",
"warnDefaultPort": "El puerto por defecto 2053 es conocido — cámbielo a uno aleatorio.",
"warnDefaultBasePath": "La ruta base por defecto \"/\" es conocida — cámbiela a una ruta aleatoria.",
"warnDefaultSubPath": "La ruta de suscripción por defecto \"/sub/\" es conocida — cámbiela.",
"warnDefaultJsonPath": "La ruta de suscripción JSON por defecto \"/json/\" es conocida — cámbiela.",
"TGBotSettings": "Bot de Telegram",
"panelListeningIP": "IP de Escucha del Panel",
"panelListeningIPDesc": "Dejar en blanco por defecto para monitorear todas las IPs.",
"panelListeningDomain": "Dominio de Escucha del Panel",
"panelListeningDomainDesc": "Dejar en blanco por defecto para monitorear todos los dominios e IPs.",
"panelPort": "Puerto del Panel",
"panelPortDesc": "El puerto utilizado para mostrar este panel.",
"publicKeyPath": "Ruta del Archivo de Clave Pública del Certificado del Panel",
"publicKeyPathDesc": "Complete con una ruta absoluta que comience con.",
"privateKeyPath": "Ruta del Archivo de Clave Privada del Certificado del Panel",
"privateKeyPathDesc": "Complete con una ruta absoluta que comience con.",
"panelUrlPath": "Ruta URI",
"panelUrlPathDesc": "Debe empezar con '/' y terminar con.",
"pageSize": "Tamaño de paginación",
"pageSizeDesc": "Defina el tamaño de página para la tabla de entradas. Establezca 0 para desactivar",
"panelOutbound": "Salida del tráfico del panel",
"panelOutboundDesc": "Enruta las peticiones del propio panel — comprobaciones de versión y descargas de panel/Xray, Telegram y la actualización normal de archivos geo — a través de esta salida de Xray para sortear el filtrado de GitHub/Telegram en el servidor. Una entrada puente local se añade automáticamente a la configuración en ejecución y se aplica en vivo. La Autoactualización de Geodata nativa de Xray no se ve afectada; tiene su propia salida de descarga. Deja vacío para conexión directa.",
"panelOutboundPh": "Conexión directa",
"datepicker": "selector de fechas",
"datepickerPlaceholder": "Seleccionar fecha",
"datepickerDescription": "El tipo de calendario selector especifica la fecha de vencimiento",
"oldUsername": "Nombre de Usuario Actual",
"currentPassword": "Contraseña Actual",
"newUsername": "Nuevo Nombre de Usuario",
"newPassword": "Nueva Contraseña",
"telegramBotEnable": "Habilitar bot de Telegram",
"telegramBotEnableDesc": "Conéctese a las funciones de este panel a través del bot de Telegram.",
"telegramToken": "Token de Telegram",
"telegramTokenDesc": "Debe obtener el token del administrador de bots de Telegram {'@'}botfather.",
"telegramProxy": "Proxy SOCKS",
"telegramProxyDesc": "Si necesita el proxy Socks5 para conectarse a Telegram. Ajuste su configuración según la guía.",
"telegramAPIServer": "Servidor API de Telegram",
"telegramAPIServerDesc": "El servidor API de Telegram a utilizar. Déjelo en blanco para utilizar el servidor predeterminado.",
"telegramChatId": "IDs de Chat de Telegram para Administradores",
"telegramChatIdDesc": "IDs de Chat múltiples separados por comas. Use {'@'}userinfobot o use el comando '/id' en el bot para obtener sus IDs de Chat.",
"telegramNotifyTime": "Hora de Notificación del Bot de Telegram",
"telegramNotifyTimeDesc": "Con qué frecuencia el bot de Telegram envía informes periódicos. Elige un intervalo predefinido o selecciona Personalizado para introducir una expresión crontab.",
"notifyTime": {
"every": "@every — repetir en un intervalo",
"hourly": "@hourly — cada hora",
"daily": "@daily — cada día a las 00:00",
"weekly": "@weekly — cada semana",
"monthly": "@monthly — cada mes",
"custom": "Personalizado (crontab)",
"seconds": "Segundos",
"minutes": "Minutos",
"hours": "Horas",
"interval": "Intervalo",
"unit": "Unidad"
},
"tgNotifyBackup": "Respaldo de Base de Datos",
"tgNotifyBackupDesc": "Incluir archivo de respaldo de base de datos con notificación de informe.",
"tgNotifyLogin": "Notificación de Inicio de Sesión",
"tgNotifyLoginDesc": "Muestra el nombre de usuario, dirección IP y hora cuando alguien intenta iniciar sesión en su panel.",
"sessionMaxAge": "Edad Máxima de Sesión",
"sessionMaxAgeDesc": "La duración de una sesión de inicio de sesión (unidad: minutos).",
"expireTimeDiff": "Umbral de Expiración para Notificación",
"expireTimeDiffDesc": "Reciba notificaciones sobre la expiración de la cuenta antes del umbral (unidad: días).",
"trafficDiff": "Umbral de Tráfico para Notificación",
"trafficDiffDesc": "Reciba notificaciones sobre el agotamiento del tráfico antes de alcanzar el umbral (unidad: GB).",
"tgNotifyCpu": "Umbral de Alerta de Porcentaje de CPU",
"tgNotifyCpuDesc": "Reciba notificaciones si el uso de la CPU supera este umbral (unidad: %).",
"timeZone": "Zona Horaria",
"timeZoneDesc": "Las tareas programadas se ejecutan de acuerdo con la hora en esta zona horaria.",
"subSettings": "Suscripción",
"subEnable": "Habilitar Servicio",
"subEnableDesc": "Función de suscripción con configuración separada.",
"subJsonEnable": "Habilitar/Deshabilitar el endpoint de suscripción JSON de forma independiente.",
"subJsonEnableTitle": "Suscripción JSON",
"subClashEnableTitle": "Suscripción Clash / Mihomo",
"subFormatsTipTitle": "Configuración de suscripción específica por formato",
"subFormatsTipDesc": "Configura por separado las rutas URL, las URL inversas y la detección automática de clientes para JSON y Clash / Mihomo.",
"subFormatsTipAction": "Abrir formatos de suscripción",
"subJsonAutoDetect": "Detectar automáticamente clientes Xray JSON",
"subJsonAutoDetectDesc": "Al activarlo, los clientes compatibles reconocidos que soliciten la URL de suscripción estándar recibirán automáticamente una matriz de configuraciones Xray JSON. Los demás clientes conservarán la respuesta sin procesar/Base64. Requiere activar la suscripción JSON y reiniciar el panel.",
"subJsonAlwaysArray": "Devolver siempre una matriz JSON",
"subJsonAlwaysArrayDesc": "Devuelve el endpoint JSON explícito como una matriz incluso con un solo perfil, según el estándar XTLS. Las respuestas JSON detectadas automáticamente siempre usan matrices. Desactívalo para conservar la respuesta heredada de un solo objeto.",
"subJsonUserAgentRegex": "Expresión User-Agent de Xray JSON",
"subJsonUserAgentRegexDesc": "Expresión regular Go RE2 que se compara con el User-Agent del cliente para seleccionar automáticamente el formato Xray JSON en la URL de suscripción estándar. Vacía de forma predeterminada, por lo que la detección automática permanece desactivada hasta que definas un patrón para los clientes que quieras atender. Los demás clientes conservan la respuesta sin procesar/Base64. Reinicia el panel después de cambiarla.",
"subClashAutoDetect": "Detectar automáticamente clientes Clash/Mihomo",
"subClashAutoDetectDesc": "Al activarlo, los clientes Clash/Mihomo reconocidos que soliciten la URL de suscripción estándar recibirán automáticamente YAML de Clash. Los navegadores seguirán mostrando la página de suscripción, los demás clientes conservarán la respuesta sin procesar/Base64 y las URL explícitas de JSON y Clash seguirán disponibles. Requiere activar la suscripción Clash/Mihomo y reiniciar el panel para aplicar el cambio.",
"subClashUserAgentRegex": "Expresión User-Agent de Clash/Mihomo",
"subClashUserAgentRegexDesc": "Expresión regular Go RE2 que se compara con el User-Agent del cliente para reconocer clientes Clash/Mihomo en la URL de suscripción estándar. Déjala vacía para usar el patrón predeterminado. Reinicia el panel después de cambiarla.",
"subTitle": "Título de la Suscripción",
"subTitleDesc": "Título mostrado en el cliente VPN",
"subSupportUrl": "URL de soporte",
"subSupportUrlDesc": "Enlace de soporte técnico mostrado en el cliente VPN",
"subProfileUrl": "URL del perfil",
"subProfileUrlDesc": "Un enlace a tu sitio web mostrado en el cliente VPN",
"subAnnounce": "Anuncio",
"subAnnounceDesc": "El texto del anuncio mostrado en el cliente VPN",
"subThemeDir": "Directorio del tema de suscripción",
"subThemeDirDesc": "Ruta absoluta a una carpeta que contiene una plantilla personalizada (index.html/sub.html) para la página de suscripción (p. ej. /etc/3x-ui/sub_templates/my-theme/). Déjalo vacío para usar la página predeterminada.",
"subThemeDirDocs": "Guía de plantillas ↗",
"subEnableRouting": "Habilitar enrutamiento",
"subEnableRoutingDesc": "Configuración global para habilitar el enrutamiento en el cliente VPN. (Solo para Happ)",
"subRoutingRules": "Reglas de enrutamiento",
"subRoutingRulesDesc": "Reglas de enrutamiento globales para el cliente VPN. (Solo para Happ)",
"subHideSettings": "Ocultar configuración del servidor",
"subHideSettingsDesc": "Ocultar la posibilidad de ver y editar las configuraciones del servidor en el cliente VPN. (Solo para Happ)",
"subIncyEnableRouting": "Habilitar enrutamiento",
"subIncyEnableRoutingDesc": "Inyectar un perfil de enrutamiento en el cuerpo de la suscripción para el cliente Incy. (Solo para Incy)",
"subIncyRoutingRules": "Reglas de enrutamiento",
"subIncyRoutingRulesDesc": "Enlace de enrutamiento de Incy añadido al cuerpo de la suscripción, p. ej. incy://routing/onadd/<base64>. (Solo para Incy)",
"subClashEnableRouting": "Habilitar enrutamiento",
"subClashEnableRoutingDesc": "Incluir reglas globales de enrutamiento Clash/Mihomo en las suscripciones YAML generadas.",
"subClashRoutingRules": "Reglas globales de enrutamiento",
"subClashRoutingRulesDesc": "Reglas Clash/Mihomo agregadas al inicio de cada suscripción YAML antes de MATCH,PROXY.",
"subListen": "Listening IP",
"subListenDesc": "Dejar en blanco por defecto para monitorear todas las IPs.",
"subPort": "Puerto de Suscripción",
"subPortDesc": "El número de puerto para el servicio de suscripción debe estar sin usar en el servidor. También se usa para construir el enlace/QR de suscripción mostrado en el panel cuando «URI de proxy inverso» está vacío — si la suscripción se accede a través de un proxy inverso en otro puerto, configure «URI de proxy inverso» en su lugar.",
"subCertPath": "Ruta del Archivo de Clave Pública del Certificado de Suscripción",
"subCertPathDesc": "Complete con una ruta absoluta que comience con '/'",
"subKeyPath": "Ruta del Archivo de Clave Privada del Certificado de Suscripción",
"subKeyPathDesc": "Complete con una ruta absoluta que comience con '/'",
"subPath": "Ruta URI",
"subPathDesc": "Debe empezar con '/' y terminar con '/'",
"subDomain": "Dominio de Escucha",
"subDomainDesc": "Dejar en blanco por defecto para monitorear todos los dominios e IPs. También se usa como dominio de reserva para el enlace de suscripción mostrado cuando «URI de proxy inverso» está vacío — configure «URI de proxy inverso» si el panel y la suscripción se acceden por dominios diferentes (por ejemplo, detrás de un proxy inverso).",
"subUpdates": "Intervalos de Actualización de Suscripción",
"subUpdatesDesc": "Horas de intervalo entre actualizaciones en la aplicación del cliente.",
"subEncrypt": "Codificar",
"subEncryptDesc": "Encriptar las configuraciones devueltas en la suscripción.",
"subURI": "URI de proxy inverso",
"subURIDesc": "La URL base completa (scheme://dominio[:puerto]/ruta/) para el enlace de suscripción y el código QR, usada en lugar de Dominio de Escucha/Puerto de Suscripción. Configúrela cuando la suscripción se acceda a través de un proxy inverso o un dominio/puerto distinto a los anteriores.",
"externalTrafficInformEnable": "Informe de tráfico externo",
"externalTrafficInformEnableDesc": "Informar a una API externa en cada actualización de tráfico.",
"externalTrafficInformURI": "URI de información de tráfico externo",
"externalTrafficInformURIDesc": "Las actualizaciones de tráfico se envían a este URI.",
"restartXrayOnClientDisable": "Reiniciar Xray tras desactivación automática",
"restartXrayOnClientDisableDesc": "Cuando un cliente se desactive automáticamente por vencimiento o límite de tráfico, reiniciar Xray.",
"fragment": "Fragmentación",
"fragmentDesc": "Habilitar la fragmentación para el paquete de saludo de TLS",
"fragmentSett": "Configuración de Fragmentación",
"noisesDesc": "Activar Sonidos",
"noisesSett": "Configuración de Sonidos",
"trustedProxyCidrs": "CIDR de proxy de confianza",
"trustedProxyCidrsDesc": "IP/CIDR separados por coma que pueden establecer las cabeceras de host, proto e IP del cliente reenviadas.",
"ldap": {
"enable": "Habilitar sincronización LDAP",
"host": "Host LDAP",
"port": "Puerto LDAP",
"useTls": "Usar TLS (LDAPS)",
"skipTlsVerify": "Omitir verificación de certificado TLS",
"skipTlsVerifyDesc": "Inseguro — desactiva la validación del certificado del servidor. Usar solo con CA internos/no confiables.",
"bindDn": "Bind DN",
"passwordConfigured": "Configurada; deja en blanco para mantener la contraseña actual.",
"passwordUnconfigured": "No configurada.",
"passwordPlaceholder": "Configurada — introduce un nuevo valor para reemplazar",
"baseDn": "Base DN",
"userFilter": "Filtro de usuario",
"userAttr": "Atributo de usuario (username/email)",
"vlessField": "Atributo flag VLESS",
"flagField": "Atributo flag genérico (opcional)",
"flagFieldDesc": "Si se establece, sobrescribe el flag VLESS — p. ej. shadowInactive.",
"truthyValues": "Valores truthy",
"truthyValuesDesc": "Separados por coma; por defecto: true,1,yes,on",
"invertFlag": "Invertir flag",
"invertFlagDesc": "Habilita cuando el atributo significa «deshabilitado» (p. ej. shadowInactive).",
"syncSchedule": "Programación de sincronización",
"syncScheduleDesc": "Cadena tipo cron, p. ej. @every 1m",
"inboundTags": "Etiquetas de entradas",
"inboundTagsDesc": "Entradas en las que la sincronización LDAP puede auto-crear o auto-eliminar clientes.",
"noInbounds": "No se encontraron entradas. Crea una en Entradas primero.",
"autoCreate": "Crear clientes automáticamente",
"autoDelete": "Eliminar clientes automáticamente",
"defaultTotalGb": "Total por defecto (GB)",
"defaultExpiryDays": "Caducidad por defecto (días)",
"defaultIpLimit": "Límite IP por defecto"
},
"subFormats": {
"finalMask": "Final Mask",
"finalMaskDesc": "Inyecta máscaras TCP/UDP de finalmask de Xray y parámetros QUIC en cada perfil Xray JSON generado. Requiere una aplicación compatible con suscripciones Xray JSON y un núcleo Xray reciente.",
"packets": "Paquetes",
"length": "Longitud",
"interval": "Intervalo",
"maxSplit": "Máx. división",
"noises": "Ruidos",
"noiseItem": "Ruido №{n}",
"type": "Tipo",
"packet": "Paquete",
"delayMs": "Retraso (ms)",
"applyTo": "Aplicar a",
"addNoise": "+ Ruido",
"concurrency": "Concurrencia",
"xudpConcurrency": "Concurrencia xudp",
"xudpUdp443": "xudp UDP 443"
},
"mux": "Mux",
"muxDesc": "Transmite múltiples flujos de datos independientes dentro de un flujo de datos establecido.",
"muxSett": "Configuración Mux",
"direct": "Conexión Directa",
"directDesc": "Establece conexiones directas con dominios o rangos de IP de un país específico.",
"notifications": "Notificaciones",
"certs": "Certificados",
"externalTraffic": "Tráfico Externo",
"dateAndTime": "Fecha y Hora",
"proxyAndServer": "Proxy y Servidor",
"intervals": "Intervalos",
"information": "Información",
"profile": "Perfil",
"language": "Idioma",
"telegramBotLanguage": "Idioma del Bot de Telegram",
"security": {
"admin": "Credenciales de administrador",
"twoFactor": "Autenticación de dos factores",
"twoFactorEnable": "Habilitar 2FA",
"twoFactorEnableDesc": "Añade una capa adicional de autenticación para mayor seguridad.",
"twoFactorModalSetTitle": "Activar autenticación de dos factores",
"twoFactorModalDeleteTitle": "Desactivar autenticación de dos factores",
"twoFactorModalSteps": "Para configurar la autenticación de dos factores, sigue estos pasos:",
"twoFactorModalFirstStep": "1. Escanea este código QR en la aplicación de autenticación o copia el token cerca del código QR y pégalo en la aplicación",
"twoFactorModalSecondStep": "2. Ingresa el código de la aplicación",
"twoFactorModalRemoveStep": "Ingresa el código de la aplicación para eliminar la autenticación de dos factores.",
"twoFactorModalChangeCredentialsTitle": "Cambiar credenciales",
"twoFactorModalChangeCredentialsStep": "Ingrese el código de la aplicación para cambiar las credenciales del administrador.",
"twoFactorModalSetSuccess": "La autenticación de dos factores se ha establecido con éxito",
"twoFactorModalDeleteSuccess": "La autenticación de dos factores se ha eliminado con éxito",
"twoFactorModalError": "Código incorrecto",
"show": "Mostrar",
"hide": "Ocultar",
"apiTokenNew": "Nuevo token",
"apiTokenName": "Nombre",
"apiTokenNamePlaceholder": "por ejemplo central-panel-a",
"apiTokenNameRequired": "El nombre es obligatorio",
"apiTokenEmpty": "Aún no hay tokens — crea uno para autenticar bots o paneles remotos.",
"apiTokenDeleteWarning": "Cualquier cliente que use este token dejará de autenticarse inmediatamente.",
"apiTokenCreatedTitle": "Token creado",
"apiTokenCreatedNotice": "Copia este token ahora. Por seguridad, no se almacena de forma legible y no se volverá a mostrar."
},
"toasts": {
"modifySettings": "Los parámetros han sido modificados.",
"getSettings": "Ocurrió un error al obtener los parámetros.",
"modifyUserError": "Ocurrió un error al cambiar las credenciales del administrador.",
"modifyUser": "Has cambiado exitosamente las credenciales del administrador.",
"originalUserPassIncorrect": "Nombre de usuario o contraseña original incorrectos",
"userPassMustBeNotEmpty": "El nuevo nombre de usuario y la nueva contraseña no pueden estar vacíos",
"getOutboundTrafficError": "Error al obtener el tráfico saliente",
"resetOutboundTrafficError": "Error al reiniciar el tráfico saliente"
},
"smtpSettings": "Configuración de SMTP",
"smtpEnable": "Activar notificaciones por correo",
"smtpEnableDesc": "Activar notificaciones por correo mediante SMTP",
"smtpHost": "Servidor SMTP",
"smtpHostDesc": "Nombre del servidor SMTP (p. ej. smtp.gmail.com)",
"smtpPort": "Puerto SMTP",
"smtpPortDesc": "Puerto del servidor SMTP (predeterminado: 587)",
"smtpUsername": "Usuario SMTP",
"smtpUsernameDesc": "Usuario de autenticación SMTP",
"smtpFrom": "Dirección de remitente (From)",
"smtpFromDesc": "Dirección usada en el encabezado From del correo. Déjelo vacío para usar el nombre de usuario.",
"smtpFromName": "Nombre del remitente (From)",
"smtpFromNameDesc": "Nombre para mostrar opcional antes de la dirección en el encabezado From.",
"smtpPassword": "Contraseña SMTP",
"smtpPasswordDesc": "Contraseña de autenticación SMTP",
"smtpTo": "Destinatarios",
"smtpToDesc": "Direcciones de correo de los destinatarios separadas por comas",
"emailSettings": "Correo",
"emailNotifications": "Notificaciones",
"smtpEventBusNotify": "Notificaciones por correo de eventos",
"smtpEventBusNotifyDesc": "Seleccione qué eventos generan notificaciones por correo",
"tgEventBusNotify": "Notificaciones de Telegram de eventos",
"tgEventBusNotifyDesc": "Seleccione qué eventos generan notificaciones de Telegram",
"testSmtp": "Enviar correo de prueba",
"testTgBot": "Enviar mensaje de prueba",
"eventGroupOutbound": "Saliente",
"eventGroupXray": "Núcleo de Xray",
"eventGroupSystem": "Sistema",
"eventGroupSecurity": "Seguridad",
"eventGroupNode": "Nodos",
"eventOutboundDown": "Caído",
"eventOutboundUp": "Activo",
"eventXrayCrash": "Caída",
"eventNodeDown": "Caído",
"eventNodeUp": "Activo",
"eventCPUHigh": "CPU alta (%)",
"requestFailed": "La solicitud falló",
"smtpEncryption": "Cifrado",
"smtpEncryptionDesc": "Método de cifrado de la conexión SMTP",
"smtpEncryptionNone": "Ninguno (texto sin cifrar)",
"smtpEncryptionStartTLS": "STARTTLS",
"smtpEncryptionTLS": "TLS (implícito)",
"smtpStageConnect": "Conexión",
"smtpStageAuth": "Autenticación",
"smtpStageSend": "Envío",
"smtpTestSuccess": "Correo de prueba enviado correctamente",
"smtpHostNotConfigured": "Servidor SMTP no configurado",
"smtpNoRecipients": "No hay destinatarios configurados",
"smtpFromNotConfigured": "La dirección del remitente SMTP no está configurada",
"eventLoginAttempt": "Intento de inicio de sesión",
"telegramTokenConfigured": "Configurado; deje en blanco para mantener el token actual.",
"telegramTokenPlaceholder": "Configurado: introduzca un nuevo token para reemplazarlo",
"smtpPasswordConfigured": "Configurada; deje en blanco para mantener la contraseña actual.",
"smtpPasswordPlaceholder": "Configurada: introduzca una nueva contraseña para reemplazarla",
"smtpNotInitialized": "SMTP no inicializado",
"tgBotNotEnabled": "El bot de Telegram no está activado",
"tgTestFailed": "La prueba de Telegram falló",
"tgTestSuccess": "Mensaje de prueba enviado a Telegram",
"tgBotNotRunning": "El bot de Telegram no está en ejecución",
"smtpErrorAuth": "Error de autenticación: compruebe el usuario y la contraseña",
"smtpErrorStarttls": "El servidor requiere STARTTLS: cambie el tipo de cifrado",
"smtpErrorTls": "El servidor requiere TLS: cambie el tipo de cifrado",
"smtpErrorRefused": "Conexión rechazada: compruebe el servidor y el puerto",
"smtpErrorTimeout": "Tiempo de conexión agotado: servidor inaccesible",
"smtpErrorRelay": "El servidor rechaza el envío desde esta dirección",
"smtpErrorEof": "Conexión cerrada por el servidor",
"smtpErrorUnknown": "Error de SMTP: {{ .Error }}",
"eventMemoryHigh": "Uso de memoria alto (%)",
"remarkTemplate": "Plantilla de notas",
"remarkTemplateDesc": "Cuando se define, esto reemplaza el modelo de notas para cada enlace de suscripción — escribe tu propio formato con los tokens de variable (usa el botón para insertarlos). Déjalo vacío para usar el modelo anterior.",
"validation": {
"pathLeadingSlash": "La ruta debe comenzar con /"
},
"secretClear": "Borrar",
"secretClearUndo": "Deshacer borrado"
},
"xray": {
"title": "Xray Configuración",
"save": "Guardar configuración",
"restart": "Reiniciar Xray",
"restartSuccess": "Xray se ha reiniciado correctamente",
"restartOutputTitle": "Salida del reinicio de Xray",
"restartConfirmTitle": "¿Reiniciar xray?",
"restartConfirmContent": "Recarga el servicio xray con la configuración guardada.",
"stopSuccess": "Xray se ha detenido correctamente",
"restartError": "Ocurrió un error al reiniciar Xray.",
"stopError": "Ocurrió un error al detener Xray.",
"basicTemplate": "Perfil Básico",
"advancedTemplate": "Perfil Avanzado",
"generalConfigs": "Configuraciones Generales",
"generalConfigsDesc": "Estas opciones proporcionarán ajustes generales.",
"logConfigs": "Registro",
"logConfigsDesc": "Los registros pueden afectar la eficiencia de su servidor. Se recomienda habilitarlos sabiamente solo en caso de sus necesidades.",
"blockConfigsDesc": "Estas opciones evitarán que los usuarios se conecten a protocolos y sitios web específicos.",
"basicRouting": "Enrutamiento Básico",
"blockConnectionsConfigsDesc": "Estas opciones bloquearán el tráfico según el país solicitado específico.",
"directConnectionsConfigsDesc": "Una conexión directa asegura que el tráfico específico no sea enrutado a través de otro servidor.",
"blockips": "Bloquear IPs",
"blockdomains": "Bloquear Dominios",
"directips": "IPs Directas",
"directdomains": "Dominios Directos",
"ipv4Routing": "Enrutamiento IPv4",
"ipv4RoutingDesc": "Estas opciones solo enrutarán a los dominios objetivo a través de IPv4.",
"warpRouting": "Enrutamiento WARP",
"warpRoutingDesc": "Precaución: Antes de usar estas opciones, instale WARP en modo de proxy socks5 en su servidor siguiendo los pasos en el GitHub del panel. WARP enrutará el tráfico a los sitios web a través de los servidores de Cloudflare.",
"nordRouting": "Enrutamiento NordVPN",
"nordRoutingDesc": "Estas opciones enrutarán el tráfico basado en un destino específico a través de NordVPN.",
"Template": "Plantilla de Configuración de Xray",
"TemplateDesc": "Genera el archivo de configuración final de Xray basado en esta plantilla.",
"FreedomStrategy": "Configurar Estrategia para el Protocolo Freedom",
"FreedomStrategyDesc": "Establece la estrategia de salida de la red en el Protocolo Freedom.",
"FreedomHappyEyeballs": "Freedom Happy Eyeballs (IPv4/IPv6)",
"FreedomHappyEyeballsDesc": "Marcado de doble pila para la salida directa (freedom): útil en servidores de salida con IPv4 e IPv6.",
"FreedomHappyEyeballsTryDelayDesc": "Milisegundos antes de probar la otra familia de direcciones. 150250 ms es un buen punto de partida.",
"RoutingStrategy": "Configurar Estrategia de Enrutamiento de Dominios",
"RoutingStrategyDesc": "Establece la estrategia general de enrutamiento para la resolución de DNS.",
"outboundTestUrl": "URL de prueba de outbound",
"outboundTestUrlDesc": "URL usada al probar la conectividad del outbound",
"Torrent": "Prohibir Uso de BitTorrent",
"Inbounds": "Entradas",
"InboundsDesc": "Cambia la plantilla de configuración para aceptar clientes específicos.",
"Outbounds": "Salidas",
"OutboundSubscriptions": "Suscripciones de salida",
"OutboundSubscriptionsDesc": "Importa salidas desde URLs de suscripción remotas (vmess/vless/trojan/ss/...). Las etiquetas se mantienen estables para usarlas en balanceadores y reglas de enrutamiento. Las actualizaciones son automáticas.",
"Balancers": "Equilibradores",
"balancerTagRequired": "La etiqueta es obligatoria",
"balancerSelectorRequired": "Elige al menos una salida",
"balancerLive": "Destino actual",
"balancerOverride": "Forzar destino",
"balancerOverridePh": "Automático (estrategia)",
"balancerLiveRefresh": "Actualizar estado del balanceador",
"balancerNotRunning": "Este balanceador no está activo en el Xray en ejecución — guarda los cambios o inicia Xray primero",
"routeTester": "Prueba de ruta",
"routeTesterDesc": "Pregunta al Xray en ejecución qué salida gestionaría una conexión. No se envía tráfico real — la decisión viene directamente del motor de enrutamiento en vivo.",
"routeTesterDest": "Dominio o IP",
"routeTesterPort": "Puerto",
"routeTesterInbound": "Entrante",
"routeTesterProtocol": "Protocolo detectado",
"routeTesterTest": "Probar ruta",
"routeTesterMatchedOutbound": "Salida coincidente",
"routeTesterViaBalancer": "vía balanceador",
"routeTesterDefaultOutbound": "Ninguna regla de enrutamiento coincidió — el tráfico va a la salida predeterminada (primera).",
"OutboundsDesc": "Cambia la plantilla de configuración para definir formas de salida para este servidor.",
"Routings": "Reglas de enrutamiento",
"RoutingsDesc": "¡La prioridad de cada regla es importante!",
"completeTemplate": "Todo",
"logLevel": "Nivel de registro",
"logLevelDesc": "El nivel de registro para registros de errores, que indica la información que debe registrarse.",
"accessLog": "Registro de acceso",
"accessLogDesc": "La ruta del archivo para el registro de acceso. El valor especial 'ninguno' deshabilita los registros de acceso",
"errorLog": "Registro de Errores",
"errorLogDesc": "La ruta del archivo para el registro de errores. El valor especial 'none' desactiva los registros de errores.",
"dnsLog": "Registro DNS",
"dnsLogDesc": "Si habilitar los registros de consulta DNS",
"maskAddress": "Enmascarar Dirección",
"maskAddressDesc": "Máscara de dirección IP, cuando se habilita, reemplazará automáticamente la dirección IP que aparece en el registro.",
"statistics": "Estadísticas",
"statsInboundUplink": "Estadísticas de Subida de Entrada",
"statsInboundUplinkDesc": "Habilita la recopilación de estadísticas para el tráfico ascendente de todos los proxies de entrada.",
"statsInboundDownlink": "Estadísticas de Bajada de Entrada",
"statsInboundDownlinkDesc": "Habilita la recopilación de estadísticas para el tráfico descendente de todos los proxies de entrada.",
"statsOutboundUplink": "Estadísticas de Subida de Salida",
"statsOutboundUplinkDesc": "Habilita la recopilación de estadísticas para el tráfico ascendente de todos los proxies de salida.",
"statsOutboundDownlink": "Estadísticas de Bajada de Salida",
"statsOutboundDownlinkDesc": "Habilita la recopilación de estadísticas para el tráfico descendente de todos los proxies de salida.",
"connectionLimits": "Límites de conexión",
"connectionLimitsDesc": "Políticas a nivel de conexión para el nivel de usuario 0. Deja un campo vacío para usar el valor predeterminado de Xray.",
"connIdle": "Tiempo de inactividad",
"connIdleDesc": "Cierra una conexión después de que permanezca inactiva durante esta cantidad de segundos. Reducirlo libera memoria y descriptores de archivo más rápido en servidores con mucha carga (predeterminado de Xray: 300).",
"bufferSize": "Tamaño del búfer",
"bufferSizeDesc": "Tamaño del búfer interno por conexión en KB. Ponlo en 0 para minimizar el uso de memoria en servidores con poca RAM (el valor predeterminado de Xray depende de la plataforma).",
"bufferSizePlaceholder": "automático",
"seconds": "segundos",
"importRules": "Importar reglas",
"exportRules": "Exportar reglas",
"importOutbounds": "Importar salidas",
"exportOutbounds": "Exportar salidas",
"importInvalidJson": "JSON no válido — se esperaba un array o un objeto con una clave coincidente.",
"metricsListen": "Punto de métricas",
"metricsListenDesc": "Expone las métricas estilo Prometheus de Xray en esta dirección:puerto (por ejemplo, 127.0.0.1:11111). Déjalo vacío para deshabilitarlo. Vincúlalo a localhost y ponlo tras un proxy inverso — no está autenticado.",
"metricsTag": "Etiqueta de métricas",
"rules": {
"first": "Primero",
"last": "Último",
"up": "Arriba",
"down": "Abajo",
"source": "Fuente",
"dest": "Destino",
"inbound": "Entrante",
"outbound": "Saliente",
"balancer": "Equilibrador",
"info": "Info",
"add": "Agregar Regla",
"edit": "Editar Regla",
"useComma": "Elementos separados por comas"
},
"routing": {
"dragToReorder": "Arrastra para reordenar"
},
"ruleForm": {
"sourceIps": "IPs de origen",
"sourcePort": "Puerto de origen",
"vlessRoute": "Ruta VLESS",
"attributes": "Atributos",
"value": "Valor",
"user": "Usuario",
"inboundTags": "Etiquetas de entradas",
"outboundTag": "Etiqueta de salida",
"balancerTag": "Etiqueta de balanceador",
"balancerTagTooltip": "Enruta el tráfico a través de uno de los balanceadores configurados"
},
"outboundForm": {
"tagDuplicate": "Etiqueta ya usada por otra salida",
"tagRequired": "La etiqueta es obligatoria",
"tagPlaceholder": "etiqueta-única",
"localIpPlaceholder": "IP local",
"dialerProxyPlaceholder": "Selecciona una salida para encadenar",
"dialerProxyHint": "Conecta esta salida a través de otra salida (por etiqueta) para crear una cadena de proxy. Déjalo vacío para conectar directamente.",
"targetStrategyHint": "Cómo se resuelve el dominio de destino antes de conectar: AsIs (predeterminado) lo envía sin resolver, UseIP… resuelve con respaldo, ForceIP… exige resolución.",
"addressRequired": "La dirección es obligatoria",
"portRequired": "El puerto es obligatorio",
"optional": "opcional",
"udpOverTcp": "UDP sobre TCP",
"uotVersion": "Versión UoT",
"inboundTag": "Etiqueta de entrada",
"inboundTagPlaceholder": "etiqueta de entrada usada en reglas de enrutamiento",
"responseType": "Tipo de respuesta",
"rewriteNetwork": "Reescribir red",
"unchanged": "(sin cambios)",
"unchangedAddress": "(sin cambios) p. ej. 1.1.1.1",
"rules": "Reglas",
"ruleN": "Regla {n}",
"action": "Acción",
"redirect": "Redirect",
"fragment": "Fragment",
"finalRules": "Reglas finales",
"overrideXrayPrivateIp": "Sobrescribir el bloqueo de IP privada por defecto de Xray",
"blockDelay": "Retraso de bloqueo (ms)",
"reverseSniffing": "Sniffing inverso",
"reserved": "Reservado",
"minUploadInterval": "Intervalo mín. de subida (ms)",
"maxUploadSizeBytes": "Tamaño máx. de subida (bytes)",
"uplinkChunkSize": "Tamaño de chunk Uplink",
"noGrpcHeader": "Sin cabecera gRPC",
"maxConcurrency": "Máx. concurrencia",
"maxConnections": "Máx. conexiones",
"maxReuseTimes": "Máx. reutilizaciones",
"maxRequestTimes": "Máx. peticiones",
"maxReusableSecs": "Máx. segundos reutilizables",
"keepAlivePeriod": "Periodo keep alive",
"authPassword": "Contraseña de auth",
"visionTestpre": "Vision testpre",
"serverNamePlaceholder": "nombre del servidor",
"verifyPeerName": "Verificar nombre del peer",
"pinnedSha256": "SHA256 pinned",
"shortId": "Short ID",
"sockopts": "Sockopts",
"keepAliveInterval": "Intervalo keep alive",
"markFwmark": "Mark (fwmark)",
"interface": "Interfaz",
"ipv6Only": "Solo IPv6",
"acceptProxyProtocol": "Aceptar proxy protocol",
"proxyProtocol": "Proxy protocol",
"tcpUserTimeoutMs": "TCP user timeout (ms)",
"tcpKeepAliveIdleS": "TCP keep-alive idle (s)"
},
"outbound": {
"addOutbound": "Agregar salida",
"addReverse": "Agregar reverso",
"editOutbound": "Editar salida",
"editReverse": "Editar reverso",
"reverseTag": "Etiqueta Reverso",
"reverseTagDesc": "Etiqueta de salida del proxy inverso simple VLESS. Dejar vacío para deshabilitar. Cuando se establece, las conexiones de este cliente pueden usarse como túnel de proxy inverso.",
"reverseTagPlaceholder": "etiqueta de salida (vacío para deshabilitar)",
"tag": "Etiqueta",
"tagDesc": "etiqueta única",
"address": "Dirección",
"egress": "Egress",
"egressHint": "Run an HTTP test to show egress IP and country.",
"reverse": "Reverso",
"domain": "Dominio",
"type": "Tipo",
"bridge": "Bridge",
"portal": "Portal",
"link": "Enlace",
"intercon": "Interconexión",
"settings": "Configuración",
"accountInfo": "Información de la Cuenta",
"outboundStatus": "Estado de Salida",
"sendThrough": "Enviar a través de",
"targetStrategy": "Estrategia de destino",
"test": "Probar",
"testResult": "Resultado de la prueba",
"testing": "Probando conexión...",
"testSuccess": "Prueba exitosa",
"testFailed": "Prueba fallida",
"testError": "Error al probar la salida",
"modeRealDelay": "Retardo real",
"testModeTooltip": "TCP: sonda rápida solo de dial. HTTP: petición completa a través de xray. Retardo real: tiempo total incluyendo el establecimiento de la conexión.",
"testAll": "Probar todo",
"httpStatus": "Estado HTTP",
"breakdownConnect": "Conexión al proxy",
"breakdownTls": "TLS vía salida",
"breakdownTtfb": "Primer byte",
"nordvpn": "NordVPN",
"accessToken": "Token de acceso",
"country": "País",
"server": "Servidor",
"city": "Ciudad",
"allCities": "Todas las ciudades",
"privateKey": "Clave privada",
"load": "Carga",
"moveToTop": "Mover al principio"
},
"outboundSub": {
"manage": "Suscripciones",
"title": "Suscripciones de salida",
"remark": "Notas (opcional)",
"remarkPlaceholder": "p. ej. nodos HK",
"url": "URL de suscripción",
"urlPlaceholder": "https://... (lista de enlaces en base64)",
"tagPrefix": "Prefijo de etiqueta",
"tagPrefixPlaceholder": "hk-",
"interval": "Intervalo de actualización",
"hours": "h",
"minutes": "min",
"intervalHint": "Por defecto 10 minutos. La tarea en segundo plano comprueba con frecuencia; cada suscripción solo vuelve a descargarse cuando ha transcurrido su propio intervalo.",
"enabled": "Habilitado",
"allowPrivate": "Permitir direcciones privadas",
"allowPrivateHint": "Permite localhost, la red local (LAN) y las IP privadas en la URL de esta suscripción. Desactivado por defecto por seguridad; actívalo solo para una fuente local de confianza.",
"prepend": "Antes de las salidas manuales",
"prependHint": "Coloca las salidas de esta suscripción antes de las configuradas manualmente, de modo que una de ellas pueda convertirse en la predeterminada.",
"preview": "Vista previa",
"previewEmpty": "No se encontraron salidas en esta URL.",
"refreshAll": "Actualizar todo",
"statusOk": "Correcto",
"toastUpdated": "Suscripción actualizada",
"addButton": "Añadir",
"active": "Suscripciones activas",
"empty": "Aún no hay suscripciones. Añade una arriba.",
"colRemark": "Notas",
"colPrefix": "Prefijo",
"colInterval": "Intervalo",
"colLastFetch": "Última descarga",
"colEnabled": "Habilitado",
"auto": "auto",
"never": "nunca",
"yes": "Sí",
"no": "No",
"refreshNow": "Actualizar ahora",
"lastError": "Último error",
"deleteConfirm": "¿Eliminar esta suscripción?",
"restartHint": "Después de añadir o actualizar, reinicia Xray (o espera a la próxima recarga automática) para activar las salidas.",
"fromSubsTitle": "Desde suscripciones de salida (solo lectura)",
"fromSubsDesc": "Importadas desde tus suscripciones activas. Gestiónalas en el panel de Suscripciones de arriba.",
"toastLoadFailed": "No se pudieron cargar las suscripciones",
"toastUrlRequired": "La URL de suscripción es obligatoria",
"toastAdded": "Suscripción añadida",
"toastAddFailed": "No se pudo añadir la suscripción",
"toastRefreshed": "Actualizada",
"toastRefreshFailed": "Error al actualizar",
"toastDeleted": "Eliminada",
"toastDeleteFailed": "Error al eliminar"
},
"tabBalancerSettings": "Ajustes del balanceador",
"tabObservatory": "Observatorio",
"observatory": {
"title": "Observatorio",
"burstTitle": "Observatorio Burst",
"autoManaged": "Los observadores se gestionan automáticamente a partir de tus balanceadores. Ajusta abajo cómo sondean; las salidas vigiladas siguen los selectores del balanceador.",
"emptyHint": "No hay ningún observador de conexión activo. Se añade uno automáticamente al crear un balanceador Least Ping o Least Load —o un balanceador Random / Round-robin con fallback— para que los balanceadores que usan observador puedan comprobar la salud de las salidas antes de elegir un destino.",
"mixedLegacy": "Esta configuración contiene Observatory y Burst Observatory a la vez. Xray usa un único observador global, por lo que este estado mixto heredado no está soportado; al guardar balanceadores se normalizará a un solo observador.",
"subjectSelector": "Salidas vigiladas",
"subjectSelectorDesc": "Etiquetas de salida que sondea este observador. Se gestionan automáticamente a partir de tus balanceadores.",
"probeURL": "URL de sondeo",
"probeURLDesc": "URL solicitada para medir cada salida. Debe devolver HTTP 204.",
"probeInterval": "Intervalo de sondeo",
"probeIntervalDesc": "Con qué frecuencia se sondea cada salida, p. ej. 30s, 1m, 2h45m.",
"enableConcurrency": "Sondeo concurrente",
"enableConcurrencyDesc": "Sondea todas las salidas vigiladas a la vez en lugar de una a una. Más rápido, pero más visible en la red.",
"destination": "Destino de sondeo",
"destinationDesc": "URL solicitada para medir cada salida. Debe devolver HTTP 204.",
"connectivity": "Comprobación de conectividad",
"connectivityDesc": "URL opcional de comprobación de red local, se prueba solo si falla el destino. Déjalo vacío para omitir.",
"interval": "Intervalo de sondeo",
"intervalDesc": "Tiempo medio entre sondeos por salida, p. ej. 1m. Mínimo 10s.",
"timeout": "Tiempo de espera del sondeo",
"timeoutDesc": "Cuánto esperar un sondeo antes de considerarlo fallido, p. ej. 5s.",
"sampling": "Número de muestras",
"samplingDesc": "Número de resultados de sondeo recientes que se conservan para puntuar cada salida.",
"httpMethod": "Método HTTP",
"httpMethodDesc": "Método HTTP usado para los sondeos.",
"deleteAlsoObservatory": "Este es el último balanceador que usa el Observatorio, por lo que también se eliminará.",
"deleteAlsoBurst": "Este es el último balanceador que usa el Observatorio Burst, por lo que también se eliminará."
},
"refCleanup": {
"header": "Al eliminar esto también se actualiza tu enrutamiento:",
"ruleRemoved": "Regla {label} — eliminada (sin destino restante)",
"ruleModified": "Regla {label} — conservada (ahora usa {keeps})",
"balancerRemoved": "Balanceador {tag} — eliminado (sin destinos restantes)"
},
"balancer": {
"addBalancer": "Agregar equilibrador",
"editBalancer": "Editar balanceador",
"balancerStrategy": "Estrategia",
"balancerSelectors": "Selectores",
"tag": "Etiqueta",
"tagDesc": "etiqueta única",
"tagDuplicate": "Etiqueta ya usada por otro balanceador",
"tagPlaceholder": "etiqueta única de balanceador",
"selector": "Selector",
"fallback": "Fallback",
"cycleTooltip": "Ciclo: {path} → (volver a {start})",
"expected": "Esperado",
"expectedPlaceholder": "número óptimo de nodos",
"maxRtt": "Máx. RTT",
"tolerance": "Tolerancia",
"baselines": "Baselines",
"costs": "Costs",
"balancerDesc": "No es posible utilizar balancerTag y outboundTag al mismo tiempo. Si se utilizan al mismo tiempo, sólo funcionará outboundTag.",
"costMatch": "Patrón de etiqueta",
"costValue": "Peso",
"costRegexp": "Coincidencia por expresión regular",
"balancerDeleteInUse": "No se puede eliminar este balanceador — se usa como respaldo para: {names}",
"balancerFallbackCycle": "No se puede establecer este balanceador como respaldo — crearía una dependencia circular.",
"balancerFallbackInfo": "El tráfico se enrutará a través de: Balanceador → Loopback → Servidor → Balanceador destino → Conexión saliente. Esto agrega un salto adicional a través del servidor, lo que puede introducir ligeros retrasos.",
"fallbackBalancerHint": "Seleccione otro balanceador como respaldo",
"reservedPrefix": "El prefijo _bl_ está reservado para objetos loopback internos del balanceador"
},
"wireguard": {
"secretKey": "Llave secreta",
"publicKey": "Llave pública",
"allowedIPs": "IP permitidas",
"endpoint": "Punto final",
"psk": "Clave precompartida",
"domainStrategy": "Estrategia de dominio"
},
"tun": {
"nameDesc": "El nombre de la interfaz TUN. El valor predeterminado es 'xray0'",
"mtuDesc": "Unidad Máxima de Transmisión. El tamaño máximo de los paquetes de datos. El valor predeterminado es 1500",
"userLevel": "Nivel de Usuario",
"userLevelDesc": "Todas las conexiones realizadas a través de este entrada utilizarán este nivel de usuario. El valor predeterminado es 0"
},
"nord": {
"accessToken": "Access token",
"privateKey": "Clave privada",
"noServers": "No se encontraron servidores para el país seleccionado",
"noPublicKey": "El servidor seleccionado no anuncia una clave pública NordLynx.",
"outboundAdded": "Salida NordVPN añadida",
"outboundUpdated": "Salida NordVPN actualizada"
},
"warp": {
"changeIp": "Cambiar IP",
"changeIpSuccess": "¡IP de WARP cambiada correctamente!",
"autoUpdateIp": "Actualizar IP automáticamente",
"intervalDays": "Intervalo (días)",
"intervalDesc": "0 para desactivar. Cambia la dirección IP automáticamente.",
"licenseError": "No se pudo establecer la licencia WARP.",
"fetchFirst": "Obtén primero la configuración WARP.",
"createAccount": "Crear cuenta WARP",
"accessToken": "Access token",
"deviceId": "ID del dispositivo",
"licenseKey": "Clave de licencia",
"privateKey": "Clave privada",
"deleteAccount": "Eliminar cuenta",
"settings": "Ajustes",
"licenseKeyLabel": "Clave de licencia WARP / WARP+",
"key": "Clave",
"keyPlaceholder": "clave WARP+ de 26 caracteres",
"accountInfo": "Información de cuenta",
"deviceName": "Nombre del dispositivo",
"deviceModel": "Modelo del dispositivo",
"deviceEnabled": "Dispositivo habilitado",
"accountType": "Tipo de cuenta",
"role": "Rol",
"warpPlusData": "Datos WARP+",
"quota": "Cuota",
"usage": "Uso",
"addOutbound": "Añadir salida"
},
"dns": {
"enable": "Habilitar DNS",
"enableDesc": "Habilitar servidor DNS incorporado",
"tag": "Etiqueta de Entrada DNS",
"tagDesc": "Esta etiqueta estará disponible como una etiqueta de entrada en las reglas de enrutamiento.",
"clientIp": "IP del cliente",
"clientIpDesc": "Se utiliza para notificar al servidor la ubicación IP especificada durante las consultas DNS",
"disableCache": "Desactivar caché",
"disableCacheDesc": "Desactiva el almacenamiento en caché de DNS",
"disableFallback": "Desactivar respaldo",
"disableFallbackDesc": "Desactiva las consultas DNS de respaldo",
"disableFallbackIfMatch": "Desactivar respaldo si coincide",
"disableFallbackIfMatchDesc": "Desactiva las consultas DNS de respaldo cuando se acierta en la lista de dominios coincidentes del servidor DNS",
"enableParallelQuery": "Habilitar consulta paralela",
"enableParallelQueryDesc": "Habilitar consultas DNS paralelas a múltiples servidores para una resolución más rápida",
"strategy": "Estrategia de Consulta",
"strategyDesc": "Estrategia general para resolver nombres de dominio",
"add": "Agregar Servidor",
"edit": "Editar Servidor",
"domains": "Dominios",
"expectIPs": "IPs esperadas",
"unexpectIPs": "IPs inesperadas",
"useSystemHosts": "Usar Hosts del sistema",
"useSystemHostsDesc": "Usar el archivo hosts de un sistema instalado",
"serveStale": "Servir caducados",
"serveStaleDesc": "Devolver resultados caducados de la caché mientras se actualiza en segundo plano",
"serveExpiredTTL": "TTL de caducados",
"serveExpiredTTLDesc": "Validez (segundos) de las entradas caducadas en la caché; 0 = nunca caduca",
"timeoutMs": "Tiempo de espera (ms)",
"skipFallback": "Omitir respaldo",
"finalQuery": "Consulta final",
"hosts": "Hosts",
"hostsAdd": "Agregar Host",
"hostsEmpty": "No hay Hosts definidos",
"hostsDomain": "Dominio (ej. domain:example.com)",
"hostsValues": "IP o dominio — escribe y presiona Enter",
"usePreset": "Usar plantilla",
"dnsPresetTitle": "Plantillas DNS",
"dnsPresetFamily": "Familiar",
"clearAll": "Eliminar todos",
"clearAllTitle": "¿Eliminar todos los servidores DNS?",
"clearAllConfirm": "Esto eliminará todos los servidores DNS de la lista. No se puede deshacer.",
"dnsLeakWarning": "El DNS puede filtrarse por localhost, UDP/TCP sin cifrar, DoH/DoQ en modo local, consultas de respaldo o EDNS client IP. Usa DoH enrutado, fija los resolutores en hosts y desactiva el respaldo cuando la privacidad importe."
},
"fakedns": {
"add": "Agregar DNS Falso",
"edit": "Editar DNS Falso",
"ipPool": "Subred del grupo de IP",
"poolSize": "Tamaño del grupo"
},
"defaultOutbound": "Salida predeterminada",
"defaultOutboundDesc": "El tráfico que no coincide con ninguna regla de enrutamiento usa esta salida (la primera de la lista)."
},
"hosts": {
"addHost": "Agregar host",
"editHost": "Editar host",
"selectInbound": "Selecciona un inbound",
"selectedCount": "{count} seleccionado(s)",
"summary": {
"total": "Total",
"enabled": "Habilitados",
"disabled": "Deshabilitados"
},
"moveUp": "Subir",
"moveDown": "Bajar",
"bulkEnable": "Habilitar",
"bulkDisable": "Deshabilitar",
"bulkDelete": "Eliminar",
"bulkDeleteConfirm": "¿Eliminar {count} host(s) seleccionado(s)?",
"deleteConfirmTitle": "¿Eliminar el host \"{name}\"?",
"sections": {
"basic": "Básico",
"security": "Seguridad",
"advanced": "Avanzado",
"general": "General",
"clash": "Clash (mihomo)"
},
"fields": {
"remark": "Notas",
"serverDescription": "Descripción",
"inbound": "Inbounds",
"address": "Dirección",
"port": "Puerto",
"endpoint": "Punto final",
"enable": "Habilitar",
"actions": "Acciones",
"security": "Seguridad",
"sni": "SNI",
"overrideSniFromAddress": "Usar la dirección como SNI",
"keepSniBlank": "Dejar el SNI en blanco",
"hostHeader": "Cabecera Host",
"path": "Ruta",
"alpn": "ALPN",
"fingerprint": "Fingerprint",
"pins": "SHA-256 del cert. fijado",
"verifyPeerCertByName": "Verificar cert. del par por nombre",
"allowInsecure": "Permitir inseguro",
"echConfigList": "Lista de config. ECH",
"muxParams": "Mux",
"sockoptParams": "Sockopt",
"finalMask": "Máscara final",
"vlessRoute": "Ruta VLESS",
"mihomoIpVersion": "Versión de IP",
"mihomoX25519": "Mihomo X25519",
"shuffleHost": "Barajar host",
"tags": "Etiquetas",
"nodeGuids": "Nodos",
"excludeFromSubTypes": "Excluir de formatos",
"inheritAddress": "Hereda dirección"
},
"hints": {
"address": "Déjalo en blanco para heredar la dirección propia del inbound.",
"port": "0 hereda el puerto del inbound.",
"tags": "No visible para los usuarios finales; se envía solo con la suscripción RAW. Solo letras mayúsculas, dígitos, _ y :.",
"nodeGuids": "Elige los nodos que se resolvieron desde este host. Solo asignación visual.",
"serverDescription": "Nota opcional que se muestra bajo las notas.",
"allowInsecure": "Omitir la verificación del certificado TLS (allowInsecure / skip-cert-verify).",
"vlessRoute": "Un único valor de ruta VLESS (0-65535) incrustado en el UUID, p. ej. 443. Déjalo en blanco para ninguno.",
"remark": "Una etiqueta simple para este host. Se muestra como nombre de la configuración solo cuando el inbound no tiene notas propias."
},
"remarkVars": {
"title": "Variables de plantilla",
"intro": "Haz clic en una variable para añadirla. Se sustituye por cliente al generar la suscripción.",
"preview": "Vista previa",
"groups": {
"client": "Cliente",
"traffic": "Tráfico",
"time": "Tiempo y estado",
"connection": "Conexión"
},
"descEMAIL": "Email del cliente",
"descINBOUND": "Notas del propio inbound (nombre de la configuración)",
"descHOST": "Notas del host",
"descID": "UUID del cliente",
"descSHORT_ID": "Primeros 8 caracteres del UUID",
"descTELEGRAM_ID": "ID de Telegram del cliente (vacío si no está definido)",
"descSUB_ID": "ID de suscripción",
"descCOMMENT": "Comentario del cliente",
"descTRAFFIC_USED": "Tráfico usado (legible)",
"descTRAFFIC_LEFT": "Tráfico restante (oculto si es ilimitado)",
"descTRAFFIC_TOTAL": "Tráfico total (oculto si es ilimitado)",
"descTRAFFIC_USED_BYTES": "Tráfico usado en bytes",
"descTRAFFIC_LEFT_BYTES": "Tráfico restante en bytes",
"descTRAFFIC_TOTAL_BYTES": "Tráfico total en bytes",
"descUP": "Tráfico de subida",
"descDOWN": "Tráfico de bajada",
"descSTATUS": "activo / expirado / deshabilitado / agotado",
"descSTATUS_EMOJI": "Estado como emoji (✅ ⏳ 🚫)",
"descDAYS_LEFT": "Días hasta la expiración (oculto si es ilimitado)",
"descTIME_LEFT": "Tiempo restante (p. ej. 12d 4h 30m)",
"descUSAGE_PERCENTAGE": "Tráfico usado en porcentaje (oculto si es ilimitado)",
"descEXPIRE_DATE": "Fecha de expiración (AAAA-MM-DD)",
"descJALALI_EXPIRE_DATE": "Fecha de expiración en el calendario Jalali (AAAA/MM/DD)",
"descEXPIRE_UNIX": "Expiración como marca de tiempo Unix (segundos)",
"descCREATED_UNIX": "Hora de creación como marca de tiempo Unix (segundos)",
"descRESET_DAYS": "Periodo de reinicio de tráfico en días",
"descPROTOCOL": "Protocolo del inbound (VLESS, VMess, Trojan, …)",
"descTRANSPORT": "Red de transporte (tcp, ws, grpc, …)",
"descSECURITY": "Seguridad del transporte (TLS, REALITY, NONE)"
},
"toasts": {
"list": "Error al cargar los hosts",
"obtain": "Error al cargar el host",
"add": "Agregar host",
"update": "Actualizar host",
"delete": "Eliminar host",
"badTag": "Etiqueta no válida",
"badVlessRoute": "Introduce un único número entre 0 y 65535"
}
}
},
"tgbot": {
"keyboardClosed": "❌ Teclado cerrado!",
"noResult": "❗ ¡Sin resultados!",
"noQuery": "❌ ¡Consulta no encontrada! ¡Por favor, use el comando nuevamente!",
"wentWrong": "❌ ¡Algo salió mal!",
"noIpRecord": "❗ ¡No hay registro de IP!",
"noInbounds": "❗ ¡No se encontraron entradas!",
"unlimited": "♾ Ilimitado (Restablecer)",
"add": "Añadir",
"month": "Mes",
"months": "Meses",
"day": "Día",
"days": "Días",
"hours": "Horas",
"minutes": "Minutos",
"unknown": "Desconocido",
"inbounds": "Entradas",
"clients": "Clientes",
"offline": "🔴 Sin conexión",
"online": "🟢 En línea",
"commands": {
"unknown": "❗ Comando desconocido",
"pleaseChoose": "👇 Por favor elige:\r\n",
"help": "🤖 ¡Bienvenido a este bot! Está diseñado para ofrecerte datos específicos del servidor y te permite hacer modificaciones según sea necesario.\r\n\r\n",
"start": "👋 Hola <i>{{ .Firstname }}</i>.\r\n",
"welcome": "🤖 Bienvenido al bot de gestión de <b>{{ .Hostname }}</b>.\r\n",
"status": "✅ ¡El bot está bien!",
"usage": "❗ ¡Por favor proporciona un texto para buscar!",
"getID": "🆔 Tu ID: <code>{{ .ID }}</code>",
"helpAdminCommands": "Para reiniciar Xray Core:\r\n<code>/restart</code>\r\n\r\nPara buscar un correo electrónico de cliente:\r\n<code>/usage [Correo electrónico]</code>\r\n\r\nPara buscar entradas (con estadísticas de cliente):\r\n<code>/inbound [Observación]</code>\r\n\r\nID de Chat de Telegram:\r\n<code>/id</code>",
"helpClientCommands": "Para buscar estadísticas, utiliza el siguiente comando:\r\n<code>/usage [Correo electrónico]</code>\r\n\r\nID de Chat de Telegram:\r\n<code>/id</code>",
"restartUsage": "\r\n\r\n<code>/restart</code>",
"restartSuccess": "✅ ¡Operación exitosa!",
"restartFailed": "❗ Error en la operación.\r\n\r\n<code>Error: {{ .Error }}</code>.",
"xrayNotRunning": "❗ Xray Core no está en ejecución.",
"startDesc": "Mostrar el menú principal",
"helpDesc": "Ayuda del bot",
"statusDesc": "Comprobar el estado del bot",
"idDesc": "Mostrar tu ID de Telegram",
"usageDesc": "Ver el uso del cliente: /usage correo",
"inboundDesc": "Buscar entradas: /inbound nombre (admin)",
"restartDesc": "Reiniciar el núcleo de Xray (admin)",
"clearallDesc": "Restablecer el tráfico de todos los clientes (admin)"
},
"messages": {
"cpuThreshold": "El uso de CPU {{ .Percent }}% es mayor que el umbral {{ .Threshold }}%",
"selectUserFailed": "❌ ¡Error al seleccionar usuario!",
"userSaved": "✅ Usuario de Telegram guardado.",
"loginSuccess": "✅ Has iniciado sesión en el panel con éxito.\r\n",
"loginFailed": "❗️ Falló el inicio de sesión en el panel.\r\n",
"2faFailed": "Error de 2FA",
"report": "🕰 Informes programados: {{ .RunTime }}\r\n",
"datetime": "⏰ Fecha y Hora: {{ .DateTime }}\r\n",
"hostname": "💻 Host: {{ .Hostname }}\r\n",
"version": "🚀 Versión de X-UI: {{ .Version }}\r\n",
"xrayVersion": "📡 Versión de 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": "⏳ Tiempo de actividad del servidor: {{ .UpTime }} {{ .Unit }}\r\n",
"serverLoad": "📈 Carga del servidor: {{ .Load1 }}, {{ .Load2 }}, {{ .Load3 }}\r\n",
"serverMemory": "📋 RAM: {{ .Current }}/{{ .Total }}\r\n",
"tcpCount": "🔹 TCP: {{ .Count }}\r\n",
"udpCount": "🔸 UDP: {{ .Count }}\r\n",
"traffic": "🚦 Tráfico: {{ .Total }} (↑{{ .Upload }},↓{{ .Download }})\r\n",
"xrayStatus": "️ Estado: {{ .State }}\r\n",
"username": "👤 Nombre de usuario: {{ .Username }}\r\n",
"reason": "❗️ Motivo: {{ .Reason }}\r\n",
"time": "⏰ Hora: {{ .Time }}\r\n",
"inbound": "📍 Entrada: {{ .Remark }}\r\n",
"port": "🔌 Puerto: {{ .Port }}\r\n",
"expire": "📅 Fecha de Vencimiento: {{ .Time }}\r\n",
"expireIn": "📅 Vence en: {{ .Time }}\r\n",
"active": "💡 Activo: {{ .Enable }}\r\n",
"enabled": "🚨 Habilitado: {{ .Enable }}\r\n",
"online": "🌐 Estado de conexión: {{ .Status }}\r\n",
"lastOnline": "🔙 Última conexión: {{ .Time }}\r\n",
"email": "📧 Email: {{ .Email }}\r\n",
"upload": "🔼 Subida: ↑{{ .Upload }}\r\n",
"download": "🔽 Descarga: ↓{{ .Download }}\r\n",
"total": "📊 Total: ↑↓{{ .UpDown }} / {{ .Total }}\r\n",
"TGUser": "👤 Usuario de Telegram: {{ .TelegramID }}\r\n",
"exhaustedMsg": "🚨 Agotado {{ .Type }}:\r\n",
"exhaustedCount": "🚨 Cantidad de Agotados {{ .Type }}:\r\n",
"onlinesCount": "🌐 Clientes en línea: {{ .Count }}\r\n",
"disabled": "🛑 Desactivado: {{ .Disabled }}\r\n",
"depleteSoon": "🔜 Se agotará pronto: {{ .Deplete }}\r\n\r\n",
"backupTime": "🗄 Hora de la Copia de Seguridad: {{ .Time }}\r\n",
"refreshedOn": "\r\n📋🔄 Actualizado en: {{ .Time }}\r\n\r\n",
"yes": "✅ Sí",
"no": "❌ No",
"received_id": "🔑📥 ID actualizado.",
"received_password": "🔑📥 Contraseña actualizada.",
"received_email": "📧📥 Correo electrónico actualizado.",
"received_comment": "💬📥 Comentario actualizado.",
"id_prompt": "🔑 ID predeterminado: {{ .ClientId }}\n\nIntroduce tu ID.",
"pass_prompt": "🔑 Contraseña predeterminada: {{ .ClientPassword }}\n\nIntroduce tu contraseña.",
"email_prompt": "📧 Correo electrónico predeterminado: {{ .ClientEmail }}\n\nIntroduce tu correo electrónico.",
"comment_prompt": "💬 Comentario predeterminado: {{ .ClientComment }}\n\nIntroduce tu comentario.",
"inbound_client_data_id": "🔄 Entrada: {{ .InboundRemark }}\n\n🔑 ID: {{ .ClientId }}\n📧 Correo: {{ .ClientEmail }}\n📊 Tráfico: {{ .ClientTraffic }}\n📅 Fecha de expiración: {{ .ClientExp }}\n🌐 Límite de IP: {{ .IpLimit }}\n💬 Comentario: {{ .ClientComment }}\n\n¡Ahora puedes agregar al cliente a la entrada!",
"inbound_client_data_pass": "🔄 Entrada: {{ .InboundRemark }}\n\n🔑 Contraseña: {{ .ClientPass }}\n📧 Correo: {{ .ClientEmail }}\n📊 Tráfico: {{ .ClientTraffic }}\n📅 Fecha de expiración: {{ .ClientExp }}\n🌐 Límite de IP: {{ .IpLimit }}\n💬 Comentario: {{ .ClientComment }}\n\n¡Ahora puedes agregar al cliente a la entrada!",
"cancel": "❌ ¡Proceso cancelado! \n\nPuedes /start de nuevo en cualquier momento. 🔄",
"error_add_client": "⚠️ Error:\n\n {{ .error }}",
"using_default_value": "Está bien, me quedaré con el valor predeterminado. 😊",
"incorrect_input": "Tu entrada no es válida.\nLas frases deben ser continuas sin espacios.\nEjemplo correcto: aaaaaa\nEjemplo incorrecto: aaa aaa 🚫",
"AreYouSure": "¿Estás seguro? 🤔",
"SuccessResetTraffic": "📧 Correo: {{ .ClientEmail }}\n🏁 Resultado: ✅ Éxito",
"FailedResetTraffic": "📧 Correo: {{ .ClientEmail }}\n🏁 Resultado: ❌ Fallido \n\n🛠️ Error: [ {{ .ErrorMessage }} ]",
"FinishProcess": "🔚 Proceso de reinicio de tráfico finalizado para todos los clientes.",
"eventOutboundDown": "El saliente {{ .Tag }} está CAÍDO",
"eventOutboundUp": "El saliente {{ .Tag }} está ACTIVO",
"eventErrorDetail": "Error: {{ .Error }}",
"eventDelayDetail": "Retardo: {{ .Delay }} ms",
"eventXrayCrash": "Xray se ha BLOQUEADO",
"eventXrayCrashError": "Error: {{ .Error }}",
"eventNodeDown": "El nodo {{ .Name }} está CAÍDO",
"eventNodeUp": "El nodo {{ .Name }} está ACTIVO",
"eventCPUHigh": "CPU alta",
"eventCPUHighDetail": "CPU: {{ .Detail }}",
"eventLoginFallback": "Inicio de sesión fallido desde {{ .Source }}",
"memoryThreshold": "Uso de memoria {{ .Percent }}% supera el umbral de {{ .Threshold }}%"
},
"buttons": {
"closeKeyboard": "❌ Cerrar Teclado",
"cancel": "❌ Cancelar",
"cancelReset": "❌ Cancelar Reinicio",
"cancelIpLimit": "❌ Cancelar Límite de IP",
"confirmResetTraffic": "✅ ¿Confirmar Reinicio de Tráfico?",
"confirmClearIps": "✅ ¿Confirmar Limpiar IPs?",
"confirmRemoveTGUser": "✅ ¿Confirmar Eliminar Usuario de Telegram?",
"confirmToggle": "✅ ¿Confirmar habilitar/deshabilitar usuario?",
"dbBackup": "Obtener Copia de Seguridad de BD",
"serverUsage": "Uso del Servidor",
"getInbounds": "Obtener Entradas",
"depleteSoon": "Pronto se Agotará",
"clientUsage": "Obtener Uso",
"onlines": "Clientes en línea",
"commands": "Comandos",
"refresh": "🔄 Actualizar",
"clearIPs": "❌ Limpiar IPs",
"removeTGUser": "❌ Eliminar Usuario de Telegram",
"selectTGUser": "👤 Seleccionar Usuario de Telegram",
"selectOneTGUser": "👤 Selecciona un usuario de telegram:",
"resetTraffic": "📈 Reiniciar Tráfico",
"resetExpire": "📅 Cambiar fecha de Vencimiento",
"ipLog": "🔢 Registro de IP",
"ipLimit": "🔢 Límite de IP",
"setTGUser": "👤 Establecer Usuario de Telegram",
"toggle": "🔘 Habilitar / Deshabilitar",
"custom": "🔢 Personalizado",
"confirmNumber": "✅ Confirmar: {{ .Num }}",
"confirmNumberAdd": "✅ Confirmar agregando: {{ .Num }}",
"limitTraffic": "🚧 Límite de tráfico",
"getBanLogs": "Registros de prohibición",
"allClients": "Todos los Clientes",
"addClient": "Añadir cliente",
"submitDisable": "Enviar como deshabilitado ☑️",
"submitEnable": "Enviar como habilitado ✅",
"use_default": "🏷️ Usar por defecto",
"change_id": "⚙️🔑 ID",
"change_password": "⚙️🔑 Contraseña",
"change_email": "⚙️📧 Email",
"change_comment": "⚙️💬 Comentario",
"change_flow": "⚙️🚦 Flow",
"ResetAllTraffics": "Reiniciar todo el tráfico",
"SortedTrafficUsageReport": "Informe de uso de tráfico ordenado"
},
"answers": {
"successfulOperation": "✅ ¡Exitosa!",
"errorOperation": "❗ Error en la Operación.",
"getInboundsFailed": "❌ Error al obtener las entradas",
"getClientsFailed": "❌ No se pudo obtener los clientes.",
"canceled": "❌ {{ .Email }} : Operación cancelada.",
"clientRefreshSuccess": "✅ {{ .Email }} : Cliente actualizado exitosamente.",
"IpRefreshSuccess": "✅ {{ .Email }} : IPs actualizadas exitosamente.",
"TGIdRefreshSuccess": "✅ {{ .Email }} : Usuario de Telegram del cliente actualizado exitosamente.",
"resetTrafficSuccess": "✅ {{ .Email }} : Tráfico reiniciado exitosamente.",
"setTrafficLimitSuccess": "✅ {{ .Email }} : Límite de Tráfico guardado exitosamente.",
"expireResetSuccess": "✅ {{ .Email }} : Días de vencimiento reiniciados exitosamente.",
"resetIpSuccess": "✅ {{ .Email }} : Límite de IP {{ .Count }} guardado exitosamente.",
"clearIpSuccess": "✅ {{ .Email }} : IPs limpiadas exitosamente.",
"getIpLog": "✅ {{ .Email }} : Obtener Registro de IP.",
"getUserInfo": "✅ {{ .Email }} : Obtener Información de Usuario de Telegram.",
"removedTGUserSuccess": "✅ {{ .Email }} : Usuario de Telegram eliminado exitosamente.",
"enableSuccess": "✅ {{ .Email }} : Habilitado exitosamente.",
"disableSuccess": "✅ {{ .Email }} : Deshabilitado exitosamente.",
"askToAddUserId": "¡No se encuentra su configuración!\r\nPor favor, pídale a su administrador que use su ChatID de usuario de Telegram en su(s) configuración(es).\r\n\r\nSu ChatID de usuario: <code>{{ .TgUserID }}</code>",
"chooseClient": "Elige un Cliente para Inbound {{ .Inbound }}",
"chooseInbound": "Elige un Inbound"
}
},
"email": {
"subjectOutboundDown": "El saliente {{ .Tag }} está CAÍDO",
"subjectOutboundUp": "El saliente {{ .Tag }} está ACTIVO",
"subjectXrayCrash": "Xray se ha BLOQUEADO",
"subjectCPUHigh": "CPU alta",
"subjectLoginSuccess": "Inicio de sesión correcto",
"subjectLoginFailed": "Inicio de sesión fallido",
"titleOutboundDown": "Saliente CAÍDO",
"titleOutboundUp": "Saliente ACTIVO",
"titleXrayCrash": "Xray se ha BLOQUEADO",
"titleCPUHigh": "CPU alta",
"titleLoginSuccess": "Inicio de sesión correcto",
"titleLoginFailed": "Inicio de sesión fallido",
"labelStatus": "Estado",
"labelOutbound": "Saliente",
"labelNode": "Nodo",
"labelError": "Error",
"labelDelay": "Retardo",
"labelDetail": "Detalle",
"labelUsername": "Usuario",
"labelIP": "IP",
"labelReason": "Motivo",
"labelSource": "Origen",
"labelTime": "Hora",
"statusCrashed": "BLOQUEADO",
"statusRunning": "En ejecución",
"statusHigh": "ALTA",
"statusSuccess": "CORRECTO",
"statusFailed": "FALLIDO",
"statusDown": "CAÍDO",
"statusUp": "ACTIVO"
}
}