3x-ui/web/translation/zh-TW.json
Sanaei 85e2ded0e1
Feat/multi inbound clients (#4469)
* feat(clients): add shadow tables for first-class client promotion

Introduces three new GORM-backed tables (clients, client_inbounds,
inbound_fallback_children) and a populate-only seeder that backfills
them from each inbound's existing settings.clients JSON. Duplicate
emails across inbounds auto-merge under one client row, with each
field conflict logged. Existing services are unchanged and continue
reading from settings.clients — this commit is groundwork only.

* feat(clients): make clients+client_inbounds the runtime source of truth

Adds ClientService.SyncInbound that reconciles the new tables from
each inbound's clients list whenever existing service paths mutate
settings.clients. Wires it into AddInbound, UpdateInbound,
AddInboundClient, UpdateInboundClient, DelInboundClient,
DelInboundClientByEmail, DelDepletedClients, autoRenewClients, and
the timestamp-backfill path in adjustTraffics, plus DetachInbound
on DelInbound.

GetXrayConfig now builds settings.clients from the new tables before
writing config.json, and getInboundsBySubId joins through them
instead of JSON_EACH on settings JSON. Live Xray config and
subscription endpoints are now driven by the relational view;
settings.clients JSON stays in step as a side effect of every write.

* feat(clients): add top-level Clients tab and CRUD API

Adds /panel/api/clients endpoints (list, get, add, update, del,
attach, detach) backed by ClientService methods that orchestrate
the per-inbound Add/Update/Del flows so a single client row is
created once and attached to many inbounds in one operation.

The frontend gains a dedicated Clients page (frontend/clients.html
+ src/pages/clients/) with an AntD table, multi-inbound attach
modal, and full CRUD. Axios interceptor learns to honour
Content-Type: application/json so the JSON endpoints work
alongside the legacy form-encoded ones.

The legacy per-inbound client modal stays untouched in this PR —
both flows now write to the same source of truth.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* feat(inbounds): add Port-with-Fallback inbound type

Adds a new "portfallback" protocol that emits as a VLESS-TLS inbound
under the hood but is paired with a sidecar table of child inbounds.
Panel auto-builds settings.fallbacks at Xray-config-gen time from the
sidecar — each child's listen+port becomes the fallback dest, with
SNI/ALPN/path/xver match criteria pulled from the row. No more typing
loopback ports by hand or keeping settings.fallbacks in sync.

Backend: new FallbackService (Get/SetChildren, BuildFallbacksJSON);
two new routes (GET/POST /panel/api/inbounds/:id/fallbackChildren);
xray.GetXrayConfig injects fallbacks for PortFallback inbounds; the
inbound model emits protocol="vless" so Xray accepts the config.

Frontend: PORTFALLBACK joins the protocol dropdown; selecting it
shows the standard VLESS controls plus a Fallback Children table
(inbound picker + per-row SNI/ALPN/path/xver). Children are loaded
on edit and replaced atomically on save.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* feat(clients): add Reset Traffic, QR Code, Info actions + Online/Remaining columns

The Clients page table gains:
- Online column — green/grey tag driven by /panel/api/inbounds/onlines,
  polled every 10s.
- Remaining column — bytes-remaining tag, coloured green/orange/red
  against quota, purple infinity when unlimited.
- Action icons per row: QR, Info, Reset traffic, Edit, Delete.

ClientInfoModal shows the full client detail (uuid/password/auth,
traffic ↑/↓ + remaining + all-time, expiry absolute + relative,
attached inbounds chip list, online + last-online).

ClientQrModal fetches links for the client's subId via
/panel/api/inbounds/getSubLinks/:subId and renders each one through
the existing QrPanel component.

Reset Traffic confirms then calls the existing per-inbound endpoint
on the client's first attached inbound (the traffic row is keyed on
email globally, so any attached inbound resets the shared counter).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* fix(clients): expose Attached inbounds in edit mode

The multi-select was gated on add-only, so editing a client had no way
to change which inbounds it belonged to. The picker now shows in both
modes, and on submit the modal diffs the picked set against the
original attachedIds — additions go through the /attach endpoint,
removals through /detach, both after the field update lands so the
new attachments get the latest values.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* fix(clients): unbreak template parsing + stale i18n keys

- InboundFormModal: split the multi-line help string in the
  PortFallback section onto one line — Vue's template parser was
  bailing on Unterminated string constant because a single-quoted
  literal spanned two lines inside a {{ }} interpolation.
- ClientInfoModal: t('disable') was missing at the root level, so
  vue-i18n returned the key path literally. Use t('disabled') which
  exists.
- Linter cleanup elsewhere: pages.client.* references renamed to
  pages.clients.* to match the merged i18n block; whitespace
  normalisation in a few unrelated Vue templates.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* 1

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* refactor(traffic): drop all-time traffic tracking

Removes the AllTime field from Inbound and ClientTraffic and migrates
existing DBs by dropping the all_time columns on startup. The counter
duplicated up+down without adding signal, and the per-event accumulator
ran on every traffic write.

Frontend: drop the All-time column from the inbound list and the
client-row table, the All-time row from the client info modal, and the
All-Time Total Usage tile from the inbounds summary card. The
allTimeTraffic/allTimeTrafficUsage i18n keys are removed across every
locale.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* feat(clients): mobile cards, multi-select, bulk add

Adds the same row-card layout the inbounds page uses on mobile: the
table is suppressed under the mobile breakpoint and each client renders
as a compact card with a status dot, email, Info button, Enable switch,
and overflow menu. All the per-client detail (traffic, remaining,
expiry, attached inbounds, flow, created/updated, URL, subscription)
opens through the existing info modal.

Multi-select with bulk delete wires AntD row-selection on desktop and
a per-card checkbox on mobile; a Delete (N) button appears in the
toolbar when anything is selected.

Bulk add reuses the five email-generation modes from the inbound bulk
modal but takes a multi-inbound picker so one bulk run can attach to
several inbounds at once. Submits client-by-client through the
existing /panel/api/clients/add endpoint.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* refactor(inbounds): remove legacy per-inbound client UI

Now that clients live as first-class rows attached to one or many
inbounds, the per-inbound client UI on the inbounds page is dead
weight — every client action either has a global equivalent on the
Clients page or makes no sense in a many-to-many world.

Deletes ClientFormModal, ClientBulkModal, CopyClientsModal, and
ClientRowTable from inbounds/. Strips the matching emits, refs,
handlers, and dropdown menu items from InboundList and InboundsPage,
and removes the dead mobile expand-chevron state and the desktop
expanded-row plumbing that drove the inline client table.

The InboundFormModal Clients tab still works in add-mode (one inline
client at inbound creation) — that flow goes through ClientService.
SyncInbound on save and remains useful.

Fixes a stray "</a-dropdown>" left over by an earlier toolbar edit
in ClientsPage that broke the template parser.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* feat(clients): add Delete depleted action

Mirrors the legacy delDepletedClients action that lived under the
inbounds page, but as a first-class /panel/api/clients/delDepleted
endpoint backed by ClientService. The new path goes through
ClientService.Delete for each depleted email, so the new clients +
client_inbounds + xray_client_traffic tables stay consistent.

Adds a danger-styled toolbar button on the Clients page (next to
Reset all client traffic) with a confirm dialog and a toast
reporting the deleted count.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* refactor(api): move every client-shaped endpoint off /inbounds onto /clients

After the multi-inbound client migration, client state belongs to the
client API surface, not the inbound one. Twelve routes that were
crammed under /panel/api/inbounds/* now live where they belong, under
/panel/api/clients/*.

Moved (route, handler, doc):
  POST  /clientIps/:email
  POST  /clearClientIps/:email
  POST  /onlines
  POST  /lastOnline
  POST  /updateClientTraffic/:email
  POST  /resetAllClientTraffics/:id
  POST  /delDepletedClients/:id
  POST  /:id/resetClientTraffic/:email
  GET   /getClientTraffics/:email
  GET   /getClientTrafficsById/:id
  GET   /getSubLinks/:subId
  GET   /getClientLinks/:id/:email

Their /clients/* counterparts are:
  POST  /clients/clientIps/:email
  POST  /clients/clearClientIps/:email
  POST  /clients/onlines
  POST  /clients/lastOnline
  POST  /clients/updateTraffic/:email
  POST  /clients/resetTraffic/:email          (email-only, fans out)
  GET   /clients/traffic/:email
  GET   /clients/traffic/byId/:id
  GET   /clients/subLinks/:subId
  GET   /clients/links/:id/:email

per-inbound resetAllClientTraffics and delDepletedClients are dropped
entirely — the Clients page already exposes global Reset All Traffic
and Delete depleted actions, and per-inbound resets are meaningless
once a client can be attached to many inbounds.

ClientService.ResetTrafficByEmail is the new email-only reset path:
it looks up every inbound the client is attached to and pushes the
counter reset + Xray re-add through inboundService.ResetClientTraffic
for each one, so depleted users come back online instantly.

Frontend callers (ClientsPage, useClients, ClientQrModal,
ClientInfoModal, InboundInfoModal, InboundsPage, useInbounds) all
switched to the new paths. The Inbounds page drops its per-inbound
"Reset client traffic" and "Delete depleted clients" dropdown items —
users do those at the client level now. api-docs is rebuilt to match.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* refactor(service): switch tgbot + ldap callers to ClientService

Adds two thin helpers to ClientService (CreateOne, DetachByEmail) and
rewrites tgbot.SubmitAddClient and ldap_sync_job to call ClientService
directly. Removes the JSON-blob payloads (BuildJSONForProtocol output for
add, clientsToJSON/clientToJSON helpers) that callers previously fed to
InboundService.AddInboundClient/DelInboundClient.

ldap_sync_job.batchSetEnable now loops InboundService.SetClientEnableByEmail
per email instead of trying to coerce AddInboundClient into doing the
update — the old path would have failed duplicate-email validation for
existing clients anyway.

The legacy InboundService.AddInboundClient/UpdateInboundClient/
DelInboundClient methods stay in place; they are now only used internally
by ClientService Create/Update/Delete/Attach. Inlining + deleting them
follows in a separate commit.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* refactor(service): move all client mutation methods to ClientService

Moves the client mutation surface out of InboundService and into
ClientService. These methods all operate on a single client (identity
fields, traffic limits, expiry, ip limit, enable state, telegram tg id)
and didn't belong on the inbound aggregate.

Moved (12 methods): AddInboundClient, UpdateInboundClient, DelInboundClient,
DelInboundClientByEmail, checkEmailsExistForClients, SetClientTelegramUserID,
checkIsEnabledByEmail, ToggleClientEnableByEmail, SetClientEnableByEmail,
ResetClientIpLimitByEmail, ResetClientExpiryTimeByEmail,
ResetClientTrafficLimitByEmail.

Each method now takes an explicit *InboundService for the helpers that
legitimately stay on InboundService (GetInbound, GetClients, runtimeFor,
AddClientStat / UpdateClientStat / DelClientStat, DelClientIPs /
UpdateClientIPs, emailUsedByOtherInbounds, getAllEmailSubIDs,
GetClientInboundByEmail / GetClientInboundByTrafficID,
GetClientTrafficByEmail).

Stays on InboundService: ResetClientTrafficByEmail and
ResetClientTraffic(id, email) — these mutate xray_client_traffic rows,
not client identity, so they're inbound-side bookkeeping.

Callers updated: tgbot (6 calls), ldap_sync_job (1 call),
InboundService internal (writeBackClientSubID, CopyInboundClients,
AddInbound's email-uniqueness check), ClientService Create/Update/
Delete/Attach/Detach.

Also removes a dead resetAllClientTraffics controller handler whose
route was already gone after the previous /clients API migration.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* refactor(clients): finish migrating to ClientService + tidy IP routes

Two related cleanups in the new /clients surface:

1. Move ResetAllClientTraffics (bulk-reset of xray_client_traffic +
   last_traffic_reset_time, with node-runtime propagation) from
   InboundService to ClientService. PeriodicTrafficResetJob now holds
   a clientService and calls
   j.clientService.ResetAllClientTraffics(&j.inboundService, id).
   The last client-mutation method on InboundService is gone.

2. Shorten redundantly-named routes/handlers under /panel/api/clients:
   - /clientIps/:email      -> /ips/:email      (handler getIps)
   - /clearClientIps/:email -> /clearIps/:email (handler clearIps)
   The "client" prefix was redundant inside the clients namespace.

Frontend (InboundInfoModal) and api-docs updated to match.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* feat(inbounds,clients): clean up inbound modal + enrich client modal

Inbound modal rework (InboundFormModal.vue + inbound.js):
- Drop the embedded Client subform in the Protocol tab. Multi-inbound
  clients are managed exclusively from the Clients page now; a fresh
  inbound is created with zero clients (settings constructors default
  to []) and the user attaches clients afterwards.
- Hide the Protocol tab entirely when it has nothing to render
  (VMESS, Trojan without fallbacks, Hysteria). Auto-switches active
  tab to Basic when the tab disappears while focused.
- Move the Security section (Security selector + TLS block with certs
  and ECH + Reality block) out of the Stream tab into its own
  Security tab, sharing the canEnableStream gate.

Client modal additions (ClientFormModal.vue + ClientBulkAddModal.vue):
- Flow select (xtls-rprx-vision / -udp443) appears only when the
  panel actually has a Vision-capable inbound (VLESS or PortFallback
  on TCP with TLS or Reality). Hidden otherwise, and cleared when
  it disappears.
- IP Limit input is disabled when the panel-level ipLimitEnable
  setting is off, fetched into useClients alongside subSettings and
  threaded through ClientsPage to both modals.
- Edit modal now shows an "IP Log" section listing IPs that have
  connected with the client's credentials, with refresh and clear
  buttons (calls the renamed /panel/api/clients/ips and /clearIps
  endpoints).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* refactor(inbounds): drop manual Fallbacks UI from inbound modal

The PortFallback protocol type now covers the common
VLESS-master-plus-children case with auto-wired dests, so the manual
Fallbacks editor (showFallbacks block in the Protocol tab) is mostly
redundant. Removed:

- the v-if="showFallbacks" template block (SNI/ALPN/Path/dest/PROXY rows)
- the showFallbacks computed
- the addFallback / delFallback helpers
- the .fallbacks-header / .fallbacks-title styles
- the showFallbacks gate from hasProtocolTabContent (so Trojan-over-TCP
  no longer shows an empty Protocol tab)

Power users who need a non-inbound fallback dest (nginx, static site)
can still author settings.fallbacks via the Advanced JSON tab.

* feat(clients,inbounds): move search/filter to Clients page + small fixes

Search/filter relocation:
- Remove the search/filter toolbar (search switch + filter radio +
  protocol/node selects + the visibleInbounds projection +
  inboundsFilterState localStorage + filter CSS + the SearchOutlined/
  FilterOutlined/ObjectUtil/Inbound imports it required) from
  InboundList. The filters were all client-oriented buckets bolted
  onto the inbound row.
- Add a search/filter toolbar to ClientsPage with the same shape:
  switch between deep-text search and bucket filter (active /
  deactive / depleted / expiring / online) + protocol filter that
  matches clients attached to at least one inbound with the chosen
  protocol. State persists in clientsFilterState localStorage.
  filteredClients drives both the desktop table and the mobile card
  list, and select-all / allSelected / someSelected only span the
  visible subset.
- useClients now also fetches expireDiff and trafficDiff from
  /panel/setting/defaultSettings (used to detect the expiring
  bucket); ClientsPage threads them into the client-bucket helper.

Loose fixes folded in:
- Add Client: email field is auto-filled with a random handle on
  open, matching uuid/subId/password/auth.
- Inbound clone: parse and reuse the source settings JSON (with
  clients reset to []) instead of building a fresh defaulted
  Settings, so VLESS Encryption/Decryption and other non-client
  fields survive the clone.
- en-US.json: add the ipLog string used by the edit-client modal.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* feat(clients): add Reverse tag field for VLESS-attached clients

Mirrors the Flow field's pattern: a Reverse tag input appears in the
Add/Edit Client modal whenever at least one selected inbound is VLESS
or PortFallback. The value rides over the wire as
client.reverse = { tag: '...' } so it lands directly in model.Client's
*ClientReverse field; an empty value omits the reverse key entirely.

On edit the field is hydrated from props.client.reverse?.tag, and the
showReverseTag watcher clears the field if the user drops the last
VLESS-like inbound from the selection.

* fix(xray): emit only protocol-relevant fields per client entry

The Xray config synthesizer was writing every identifier field (id,
password, flow, auth, security/method, reverse) on every client entry
regardless of the inbound's protocol. Xray ignores unknown fields, so
the config worked, but it diverged from the spec and leaked secrets
across protocols when one client was attached to multiple inbounds —
a VLESS inbound's generated config carried the same client's Trojan
password and Hysteria auth alongside its uuid.

Switch on inbound.Protocol when building each entry:
- VLESS / PortFallback: id, flow, reverse
- VMess: id, security
- Trojan: password, flow
- Shadowsocks: password, method
- Hysteria / Hysteria2: auth
email is emitted for every protocol.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* fix(clients): restore auto-disable kick under new schema

disableInvalidClients still resolved (inbound_tag, email) pairs via
JSON_EACH(inbounds.settings.clients), which is empty after migrating
to the clients + client_inbounds tables. Result: xrayApi.RemoveUser
never ran for depleted clients, clients.enable stayed true so the UI
showed them as active, and only xray_client_traffic.enable got flipped
- making "Restart Xray After Auto Disable" only half-work.

Resolve the targets via a JOIN through the new schema, flip clients.enable
so the Clients page reflects the state, and drop the legacy JSON
write-back plus the subId cascade workaround (email is unique now).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* feat(clients): live WebSocket updates + Ended status surfacing

ClientsPage now subscribes to traffic / client_stats / invalidate
WebSocket events instead of polling /onlines every 10s. Per-row
traffic counters refresh in place, online state stays current, and
list-level mutations elsewhere trigger a refresh.

The client roll-up summary moves from InboundsPage to ClientsPage
where it belongs, restructured into six labeled stat tiles
(Total / Online / Ended / Expiring / Disabled / Active) with email
popovers on the ones with issues.

Auto-disabled clients (traffic exhausted or expiry passed) now
classify as 'depleted' even though clients.enable=false, so they
show up under the Ended filter and render a red Ended tag instead
of looking indistinguishable from an operator-disabled row.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* feat(nodes): per-node client roll-up and panel version

Added transient inboundCount / clientCount / onlineCount /
depletedCount fields to model.Node, populated by NodeService.GetAll
via aggregated queries (one join across inbounds + client_inbounds,
one over client_traffics intersected with the in-memory online
emails). The Nodes list renders these as colored chips on a new
"Clients" column so an operator can see at a glance how many users
each node carries and how many are currently online or depleted.

Also exposes the remote panel's version. The central panel adds
panelVersion to its /api/server/status payload (sourced from
config.GetVersion). Probe reads that field and persists it on the
node row, mirroring how xrayVersion already flows. NodesPage gets
a new column next to Xray Version, in both desktop and mobile
views, with English and Persian strings.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* fix(clients): stop node sync from resurrecting deleted clients

Several related issues around node-managed clients:

- Remote runtime: drop the per-inbound resetAllClientTraffics path
  and point traffic/onlines/lastOnline fetches at the new
  /panel/api/clients/* routes.
- Delete from master: always push the updated inbound to the node
  even when the client was already disabled or depleted, so the
  node actually loses the user instead of silently keeping it.
- setRemoteTraffic: mirror remote clients into the central tables
  only on first discovery of a node inbound. Matched inbounds let
  the master own the join table, so a stale snap can no longer
  re-create a ClientRecord (and join row) for a client that was
  just deleted on the master.
- ClientService.Delete: route through submitTrafficWrite so deletes
  serialize with node traffic merges, and switch the final
  ClientRecord delete to an explicit Where("id = ?") clause.
- setRemoteTraffic UNIQUE-constraint fix: use clause.OnConflict on
  inserts and email-keyed UPDATEs for client_traffics, so mirroring
  a snap doesn't trip the unique email index.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* refactor(clients): switch client API endpoints from id to email

All client-scoped routes now use the unique email as the path key
(get, update, del, attach, detach, links). Email is the stable,
protocol-independent identifier — UUIDs don't exist for trojan or
shadowsocks, and internal numeric ids leaked panel implementation
detail into the public API.

Removed the redundant /traffic/byId/:id endpoint (covered by
/traffic/:email) and collapsed /links/:id/:email into /links/:email,
which now returns links across every attached inbound for the client.

Frontend selection, bulk delete, and toggle state are now keyed by
email as well, dropping the id→email lookup workaround.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* refactor(server): move cached state and helpers into ServerService

ServerController had grown to hold its own status cache, version-list
TTL cache, history-bucket whitelist, and the loop that drove all three
— concerns that belong in the service layer. Pull them out:

- lastStatus + the @2s refresh become ServerService.RefreshStatus and
  ServerService.LastStatus; the controller's cron now just orchestrates
  the cross-service side effects (xrayMetrics sample, websocket broadcast).
- The 15-minute Xray-versions cache (with stale-on-error fallback) moves
  into ServerService.GetXrayVersionsCached, collapsing the controller
  handler to a single call.
- The freedom/blackhole outbound-tag walk used by /xraylogs becomes
  ServerService.GetDefaultLogOutboundTags.
- The allowed-history-bucket whitelist moves to package-level
  service.IsAllowedHistoryBucket, so both NodeController and
  ServerController validate against the same list.

Net result: web/controller/server.go drops from 458 to 365 lines and
contains only HTTP wiring + presentation-y side effects.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* refactor(api): emit JSON-text columns as nested objects

Inbound, ClientRecord, and InboundClientIps store settings /
streamSettings / sniffing / reverse / ips as JSON-text in the DB. The
API was passing that text through verbatim, so every consumer had to
JSON.parse a string inside a string. Add MarshalJSON / UnmarshalJSON so
the wire format is a real nested object, while still accepting the
legacy escaped-string shape on write. Frontend dbinbound.js gets a
matching coerceInboundJsonField helper for the same dual-shape read
path, and inbound.js toJson stops emitting empty/placeholder fields
(externalProxy [], sniffing destOverride when disabled, etc.) so the
new normalised JSON stays terse. api-docs and the inbound-clone path
are updated to the new shape. Controller route lists are regrouped so
all GETs sit above POSTs.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* fix(clients): include inboundIds and traffic in /clients/list

ClientRecord got its own MarshalJSON in the previous commit, and
ClientWithAttachments embeds it to add inboundIds and traffic. Go
promotes the embedded MarshalJSON to the outer struct, so the encoder
was calling ClientRecord.MarshalJSON for the whole value and silently
dropping the extras. The frontend reads row.inboundIds / row.traffic
from /clients/list, so attached inbounds didn't render and newly added
clients looked like they hadn't saved. Add an explicit MarshalJSON on
ClientWithAttachments that splices the extras in.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* fix(clients): gate IP Log on ipLimitEnable + clean access-log dropdown

Legacy panel hid the IP Log section when access logging was off; the
Vue 3 migration left it gated on isEdit only, so the section showed
even when xray's access log was 'none' and nothing was being recorded.
Restore the ipLimitEnable gate on the edit modal's IP Log form-item.

While here, clean up the Xray Settings access-log dropdown: previously
two 'none' entries appeared (an empty value labelled with t('none') and
the literal 'none' from the options array). Drop the empty option for
access log (the literal 'none' covers it) and relabel the empty option
for error log / mask address to t('empty') so they're distinguishable.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* fix(nodes): route per-client ops through node clients API + orphan sweep

Adds Runtime methods AddClient, UpdateUser, and DeleteUser so master
mutates clients on a node via /panel/api/clients/{add,update,del} rather
than pushing the whole inbound. The previous rt.UpdateInbound path made
the node DelInbound+AddInbound on every single-client change, briefly
cycling every other user on the same inbound.

DelInbound no longer filters by enable=true, so a disabled node inbound
actually gets removed from the node instead of being resurrected by the
next snap.

setRemoteTrafficLocked now sweeps any ClientRecord with zero
ClientInbound rows after SyncInbound rebuilds the attachments, which is
how a node-side delete propagates back to master instead of leaving a
detached ghost. ClientService.Delete tombstones the email first so a
snap arriving mid-delete can't re-create the record.

WebSocket broadcasts an "invalidate(clients)" message on every client
mutation so the Clients page refreshes without manual reload.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* feat(balancers): allow fallback on all strategies + feed burstObservatory from random/roundRobin

Drops the random/roundRobin gate on the Fallback field in
BalancerFormModal so every strategy can pick a fallback outbound.

syncObservatories now feeds burstObservatory from leastLoad +
random + roundRobin balancers (was leastLoad only), matching how
leastPing feeds observatory.

Fix the JsonEditor "Unexpected end of JSON input" that appeared
when switching a balancer between leastPing and another strategy:
the obsView watcher was gated on showObsEditor (a boolean OR of
the two flags) and missed the case where one observatory
swapped for the other in the same tick. Watch the individual
flags instead so obsView flips to the surviving editor and the
getter stops pointing at a deleted key.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* fix(inbounds): use sortedInbounds for mobile empty-state check

InboundList referenced an undefined visibleInbounds in the mobile
card list's empty-state guard, throwing "Cannot read properties of
undefined (reading 'length')" and breaking the entire mobile render.

* feat(clients): sortable table columns

Adds the same sortState / sortableCol / sortFns pattern InboundList
uses, wrapping filteredClients in sortedClients so sort composes with
the existing search/filter pipeline. Sortable: enable, email,
inboundIds (attachment count), traffic, remaining, expiryTime;
actions and online stay unsorted.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* fix(shadowsocks): generate valid ss2022 keys and per-client method for legacy ciphers

The Add Client flow on shadowsocks inbounds was producing xray configs
that failed to start:

- 2022-blake3-* ciphers need a base64-encoded key of an exact byte
  length per cipher. fillProtocolDefaults was assigning a uuid-style
  string, which xray rejects as "bad key". Now the password is
  generated (or replaced if invalid) via random.Base64Bytes(n) sized
  to the chosen cipher.
- Legacy ciphers (aes-256-gcm, chacha20-*, xchacha20-*) require a
  per-client method field in multi-user mode; model.Client has no
  Method, so settings.clients was stored without one and xray failed
  with "unsupported cipher method:". applyShadowsocksClientMethod
  now injects the top-level method into each client on add/update,
  and healShadowsocksClientMethods backfills it at xray-config-build
  time so existing inbounds heal on the next start.
- xray/api.go ssCipherType switch was missing aes-256-gcm, which
  fell through to ss2022 path.
- SSMethods dropdown now offers aes-256-gcm.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* fix(clients): preserve ClientRecord on inbound delete + filter Attached inbounds to multi-client protocols

Replace the global orphan sweep in setRemoteTrafficLocked with a
per-inbound diff cleanup: only delete a ClientRecord whose email
disappeared from a snap-tracked inbound (i.e. a node-side delete).
Inbounds that vanished entirely from the snap (e.g. admin deleted
the inbound on master) aren't iterated, so a client whose last
attachment came from that inbound is now left alone instead of
being deleted alongside the inbound.

ClientFormModal and ClientBulkAddModal now filter the Attached
inbounds dropdown to protocols that actually support multiple
clients: shadowsocks, vless, vmess, trojan, hysteria, hysteria2,
and portfallback (which routes through VLESS settings).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* fix(clients): make empty-state text readable on dark/ultra themes

The "No clients yet" empty state had a hardcoded black color
(rgba(0,0,0,0.45)) that vanished against the dark backgrounds.
Drop the inline color, let it inherit from the AntD theme, and
fade with opacity like the mobile card empty state already does.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* feat(clients): client-first tgbot add flow, tgId field, lightweight inbound options

- tgbot: drop legacy per-protocol Add Client UI in favour of a client-first
  multi-inbound flow. New BuildClientDraftMessage / getInboundsAttachPicker
  let an admin pick one or more inbounds and submit a single client; per-
  protocol secrets are now generated server-side via fillProtocolDefaults.
  Drops awaiting_id/awaiting_password_tr/awaiting_password_sh state cases
  and add_client_ch_default_id/pass_tr/pass_sh/flow callbacks. Adds a
  setTGUser button + awaiting_tg_id state so the bot can set Client.TgID
  during Add.
- clients UI: add Telegram user ID input to ClientFormModal (0 = none).
  Hide IP Limit field entirely when ipLimitEnable is off — disabled fields
  still take layout space, this collapses Auth(Hysteria) to full width.
- inbounds API: new GET /panel/api/inbounds/options that returns just
  {id, remark, protocol, port, tlsFlowCapable}. Used by the clients page
  pickers so the dropdown payload stays small on panels with thousands of
  clients (drops settings JSON, clientStats, streamSettings). Server-side
  TlsFlowCapable mirrors Inbound.canEnableTlsFlow so the modal no longer
  needs to parse streamSettings client-side.
- clientInfoMsg now shows attached inbound remarks, and getInboundUsages
  reports the attached client count per inbound.
- api-docs: document the new /options endpoint and add tgId / flow to the
  clients add/update bodies.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* fix(inbounds): keep Node column visible for node-attached inbounds

The Node column was bound to hasActiveNode, so disabling every node hid
the column even when inbounds were still attached to those nodes — the
admin lost the visual cue that those inbounds belonged to a node and
would come back when it was re-enabled. Combine hasActiveNode with a
new hasNodeAttachedInbound check (any dbInbound with nodeId != null) so
the column survives node-disable.

* fix(api-docs): accept functional-component icons in EndpointSection

AntD-Vue icons (SafetyCertificateOutlined, etc.) are functional
components, so the icon prop's type: Object validator was rejecting
them with a "Expected Object, got Function" warning at runtime.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* test: cover crypto, random, netsafe, sub helpers, xray equals, websocket hub, node service

Adds ~110 unit tests across previously untested packages. Focus on
pure-logic and concurrency surfaces where regressions would silently
affect users:

- util/crypto, util/random: password hashing round-trip, ss2022 key
  generation, alphabet/length invariants.
- util/netsafe: IsBlockedIP edge cases, NormalizeHost validation,
  SSRF guard with AllowPrivate context bypass.
- util/common, util/json_util: traffic formatter, Combine nil-skip,
  RawMessage empty-as-null and copy-on-unmarshal.
- sub: splitLinkLines, searchKey/searchHost, kcp share fields,
  finalmask normalization, buildVmessLink round-trip.
- xray: Config.Equals and InboundConfig.Equals field-by-field,
  getRequiredUserString/getOptionalUserString type checks.
- web/websocket: hub registration, throttling, slow-client eviction,
  nil-receiver safety, concurrent register/unregister.
- web/service: NodeService.normalize validation, normalizeBasePath,
  HeartbeatPatch.ToUI mapping.
- web/job: atomicBool concurrent set/takeAndReset semantics.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* i18n(clients): replace English fallbacks with proper translation keys

Pulls every hard-coded English label/title in the Clients page and its
four modals through the i18n layer so localized panels stop leaking
English. New keys live under pages.clients (auth, hysteriaAuth, uuid,
flow, flowNone, reverseTag, reverseTagPlaceholder, telegramId,
telegramIdPlaceholder, created, updated, ipLimit) plus refresh at the
root and toasts.bulkDeletedMixed / bulkCreatedMixed for partial-failure
toasts. Also switches the add-client modal's primary button from "Add"
to "Create" for consistency with other create flows.

The bulk-add Random/Random+Prefix/... email-method options stay
hard-coded by request - they're identifier-shaped strings.

* i18n: backfill 99 missing keys across all 12 non-English locales

Brings every translation file up to parity with en-US.json so the
Clients page, the fallback-children inbound section, the new refresh
verb, the Nodes panel-version label and a handful of older holes stop
falling through to the English fallback. New strings span:

- pages.clients.* (labels, confirmations, toasts, emailMethods)
- pages.inbounds.portFallback.* (Reality fallback inbound section)
- pages.nodes.panelVersion, menu.clients, refresh

Technical identifiers (Auth, UUID, Flow, Reverse tag) are intentionally
left untranslated since they correspond to xray-core field names.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* i18n: drop stale pages.client block duplicated in every non-English locale

Every non-English locale carried a pages.client (singular) section with
30 entries that duplicated pages.clients (plural). The plural namespace
is what the Vue code actually consumes; the singular one was dead
weight from an older rename that never got cleaned up in the
non-English files. Removing it brings every locale to exactly 984
keys, matching en-US.json.

* chore: apply modernize analyzer fixes across codebase

Mechanical replacements suggested by golang.org/x/tools/.../modernize:
strings.Cut/CutPrefix/SplitSeq, slices.Contains, maps.Copy, min(),
range-over-int, new(expr), strings.Builder for hot += loops,
reflect.TypeFor[T](), sync.WaitGroup.Go(), drop legacy //+build lines.

* feat(database): add PostgreSQL as an optional backend alongside SQLite

Lets operators with large client counts or multi-node setups pick PostgreSQL
at install time without breaking the existing SQLite default. Backend is
selected at runtime via XUI_DB_TYPE/XUI_DB_DSN, a small dialect layer keeps
the five JSON_EXTRACT/JSON_EACH queries portable, and a new `x-ui migrate-db`
subcommand copies SQLite data into PostgreSQL in FK-aware order.

* fix(inbounds): gate node selector to multi-node-capable protocols

Hide the Deploy-To selector and clear nodeId when switching to a
protocol that can't run on a remote node. Also:

- subs: return 404 (not 400) when subId matches no inbounds, so VPN
  clients distinguish "deleted/unknown" from a server error
- hysteria link gen: use the inbound's resolved address so node-managed
  inbounds advertise the node host instead of the central panel
- shadowsocks: default network to 'tcp' (udp was causing issues for some
  clients on first-create)
- vite dev proxy: rewrite migrated-route bypass against the live base
  path instead of a hardcoded single-segment regex

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* fix(clients): bulk add/delete correctness + perf, working pagination, delayed-start in form

Bulk add/delete were serial on the frontend (one toast per call, N round-trips)
and the backend race exposed by parallelizing them lost client attachments and
hit UNIQUE constraint failed on client_inbounds. The single add/edit modal also
had no Start-After-First-Use option, and the table never showed the delayed
duration.

Backend (web/service/client.go):
- Per-inbound mutex on Add/Update/Del InboundClient so concurrent writers on
  the same inbound don't lose the read-modify-write of settings JSON.
- SyncInbound skips create+join when the email is tombstoned so a concurrent
  maintenance pass (adjustTraffics, autoRenewClients, markClientsDisabledIn-
  Settings) that did a stale RMW can't resurrect a just-deleted client with a
  fresh id.
- compactOrphans sweeps settings.clients entries whose ClientRecord no longer
  exists, applied in Add/DelInboundClient + DelInboundClientByEmail so each
  user-initiated mutation self-heals the inbound's settings.
- DelInboundClient uses Pluck instead of First for the stats lookup so a
  missing row doesn't abort the delete with a noisy ErrRecordNotFound log.

Frontend:
- HttpUtil.{get,post} accept a silent option that suppresses the auto-toast.
- ClientBulkAddModal fires creates in parallel + silent + one summary toast.
- useClients.removeMany runs deletes in parallel + silent and refreshes once;
  ClientsPage bulk delete uses it and shows one aggregate toast.
- useClients.applyInvalidate debounces 200 ms so the burst of N WebSocket
  invalidate events from the backend collapses into a single refresh.
- ClientsPage pagination is reactive (paginationState ref + tablePagination
  computed); onTableChange persists page-size and page changes.
- ClientFormModal gains a Start-After-First-Use switch + Duration days input
  alongside the existing Expiry Date picker; on edit-mode open a negative
  expiryTime is decoded back to delayed mode + days; on submit the payload
  sends -86400000 * days or the absolute timestamp.
- ClientsPage table shows the delayed-start duration (blue tag Nd, tooltip
  Start After First Use: Nd) instead of infinity.
- Telegram ID field in the form is hidden when /panel/setting/defaultSettings
  reports tgBotEnable=false; Comment then fills the row.
- Form row 3 collapses UUID (span 12) + Total GB (span 8) + Limit IP (span 4)
  when ipLimitEnable is on, else UUID + Total GB at 12/12.
- useInbounds.rollupClients counts only clients with a matching clientStats
  row, so orphans in settings.clients no longer inflate the inbound's count.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* fix(windows): clean shutdown, working panel restart, harden kernel32 load

Three Windows-specific issues addressed:

1. Orphaned xray-windows-amd64 after VS Code debugger stop. Delve's
   "Stop" sends TerminateProcess to the Go binary, which is uncatchable
   — our signal handlers never run, so xrayService.StopXray() is skipped
   and xray is left dangling. Spawn xray as a child of a Job Object with
   JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE so the OS kills xray when our
   handle to the job is closed (which happens even on TerminateProcess).
   Also trap os.Interrupt in main so Ctrl+C in the terminal runs the
   graceful path.

2. /panel/setting/restartPanel logged "failed to send SIGHUP signal: not
   supported by windows" because Windows can't deliver arbitrary signals.
   Add a restart hook in web/global; main registers it to push SIGHUP
   into its own signal channel, and RestartPanel calls the hook before
   falling back to the (Unix-only) signal path. Same restart-loop code
   runs in both cases.

3. util/sys/sys_windows.go now uses windows.NewLazySystemDLL so the
   kernel32.dll resolve is pinned to %SystemRoot%\System32 (prevents
   DLL hijacking by a planted DLL next to the binary). Local filetime
   type replaced with windows.Filetime, and the unreliable
   syscall.GetLastError() fallback replaced with a type assertion on the
   errno captured at call time.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* fix(sys): correct CPU/connection accounting on linux + darwin

util/sys/sys_linux.go:
- GetTCPCount/GetUDPCount were counting the column header row in
  /proc/net/{tcp,udp}[6] as a connection, inflating the reported total
  by 1 per non-empty file (so the panel status line always showed 2
  more connections than actually existed). Replace getLinesNum +
  safeGetLinesNum with a single bufio.Scanner-based countConnections
  that skips the header.
- CPUPercentRaw now opens HostProc("stat") instead of a hardcoded
  /proc/stat so HOST_PROC overrides apply, matching the connection
  counters in the same file.
- Simplify CPU field unpacking: pad nums to 8 once instead of guarding
  every assignment with a len check.

util/sys/sys_darwin.go:
- Fix swapped idle/intr indices on kern.cp_time. BSD CPUSTATES order
  is user, nice, sys, intr, idle (CP_INTR=3, CP_IDLE=4) — gopsutil's
  cpu_darwin_nocgo.go reads the same layout. The previous code used
  out[3] as idle and out[4] as intr, so busy = total - dIdle was
  actually subtracting interrupt time, making the panel report CPU
  usage close to 100% on macOS regardless of actual load.
- Collapse the per-field delta math into a single loop.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* fix(xray): rotate crash reports into log folder, prevent overwrites

writeCrashReport had two flaws: it wrote to the bin folder (alongside the
xray binary) which conflates artifacts, and the second-precision timestamp
meant a tight restart-loop crash burst overwrote prior reports. Write to
the log folder with nanosecond precision and keep the last 10 reports.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* revert(inbounds): drop unreleased portfallback protocol

The Port-with-Fallback inbound (commit 62fd9f9d) was confusing as a
standalone protocol — fallbacks belong on a regular VLESS/Trojan TCP-TLS
inbound, the way Xray models them natively. Rip out the entire feature
cleanly (no migration needed since it was never released): protocol
constant, fallback children DB table, FallbackService, 2 API endpoints,
all UI rows, related translations and api-docs. A native fallback flow
attached to VLESS/Trojan TCP-TLS/Reality will land in a follow-up commit.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* feat(inbounds): native fallbacks on VLESS/Trojan TCP-TLS, with working child links

A VLESS or Trojan inbound on TCP with TLS or Reality can now act as a
fallback master: pick existing inbounds as children and the panel auto-
fills the SNI / ALPN / path / xver routing fields from each child's
transport, auto-builds settings.fallbacks at config-gen time, and
rewrites the child's client-share link so it advertises the master's
reachable endpoint and TLS state instead of the child's loopback listen.

Layout matches the Xray All-in-One Nginx example: master at :443 with
clients + TLS, each child on 127.0.0.1 with its own transport+clients.
Order matters (Xray walks fallbacks top-to-bottom) — reorder via the
per-row up/down arrows. Path / SNI / ALPN are exposed under a per-row
Edit toggle for the rare cases where the auto-derivation needs
overriding; otherwise just pick a child and you're done.

Backend: new InboundFallback table + FallbackService (GetByMaster /
SetByMaster / GetParentForChild / BuildFallbacksJSON); two routes
(GET / POST /panel/api/inbounds/:id/fallbacks); xray.GetXrayConfig
injects settings.fallbacks for any VLESS/Trojan TCP-TLS/Reality
inbound; GetInbounds annotates each child with FallbackParent so the
frontend can rewrite links without an extra round-trip.

Link projection covers every emission path — clients-page QR/links,
per-inbound Get URL, raw subscription, sub-JSON, sub-Clash, and the
inbounds-page link/info/QR — via a shared projectThroughFallbackMaster
on the backend and a shared projectChildThroughMaster on the frontend
that both handle the panel-tracked relationship and the legacy
unix-socket (@vless-ws) convention.

Strings translated into all 12 non-English locales.

* docs: rewrite CONTRIBUTING with full local-dev setup

The prior three-line CONTRIBUTING left newcomers guessing at every
non-trivial step: which Go / Node versions, where xray comes from, why
the panel goes blank when XUI_DEBUG=true is flipped on, how the Vue
multi-page setup is wired, what to do on Windows when go build trips
on the CGo SQLite driver.

Now covers prerequisites, MinGW-w64 install on Windows (niXman builds
or MSYS2), one-shot first-time setup, two frontend dev workflows with
the XUI_DEBUG asset-cache gotcha called out, the architecture and
conventions of the Vue side, a project-layout map, useful env vars,
and the PR checklist.

---------
2026-05-19 12:20:24 +02:00

1092 lines
No EOL
51 KiB
JSON
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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": "使用者名稱",
"password": "密碼",
"login": "登入",
"confirm": "確定",
"cancel": "取消",
"close": "關閉",
"save": "儲存",
"logout": "登出",
"create": "建立",
"update": "更新",
"copy": "複製",
"copied": "已複製",
"download": "下載",
"remark": "備註",
"enable": "啟用",
"protocol": "協議",
"search": "搜尋",
"filter": "篩選",
"loading": "載入中...",
"refresh": "重新整理",
"clear": "清除",
"second": "秒",
"minute": "分鐘",
"hour": "小時",
"day": "天",
"check": "檢視",
"indefinite": "無限期",
"unlimited": "無限制",
"none": "無",
"qrCode": "二維碼",
"info": "更多資訊",
"edit": "編輯",
"delete": "刪除",
"reset": "重置",
"noData": "無數據。",
"copySuccess": "複製成功",
"sure": "確定",
"encryption": "加密",
"useIPv4ForHost": "使用 IPv4 連接主機",
"transmission": "傳輸",
"host": "主機",
"path": "路徑",
"camouflage": "偽裝",
"status": "狀態",
"enabled": "開啟",
"disabled": "關閉",
"depleted": "耗盡",
"depletingSoon": "即將耗盡",
"offline": "離線",
"online": "線上",
"domainName": "域名",
"monitor": "監聽",
"certificate": "憑證",
"fail": "失敗",
"comment": "評論",
"success": "成功",
"lastOnline": "上次上線",
"getVersion": "獲取版本",
"install": "安裝",
"clients": "客戶端",
"usage": "使用情況",
"twoFactorCode": "代碼",
"remained": "剩餘",
"security": "安全",
"secAlertTitle": "安全警報",
"secAlertSsl": "此連線不安全。在啟用 TLS 進行資料保護之前,請勿輸入敏感資訊。",
"secAlertConf": "某些設定易受攻擊。建議加強安全協議以防止潛在漏洞。",
"secAlertSSL": "面板缺少安全連線。請安裝 TLS 證書以保護資料安全。",
"secAlertPanelPort": "面板預設埠存在安全風險。請配置隨機埠或特定埠。",
"secAlertPanelURI": "面板預設 URI 路徑不安全。請配置複雜的 URI 路徑。",
"secAlertSubURI": "訂閱預設 URI 路徑不安全。請配置複雜的 URI 路徑。",
"secAlertSubJsonURI": "訂閱 JSON 預設 URI 路徑不安全。請配置複雜的 URI 路徑。",
"emptyDnsDesc": "未添加DNS伺服器。",
"emptyFakeDnsDesc": "未添加Fake DNS伺服器。",
"emptyBalancersDesc": "未添加負載平衡器。",
"emptyReverseDesc": "未添加反向代理。",
"somethingWentWrong": "發生錯誤",
"subscription": {
"title": "訂閱資訊",
"subId": "訂閱 ID",
"status": "狀態",
"downloaded": "已下載",
"uploaded": "已上傳",
"expiry": "到期",
"totalQuota": "總配額",
"individualLinks": "個別連結",
"active": "啟用",
"inactive": "停用",
"unlimited": "無限制",
"noExpiry": "無到期"
},
"menu": {
"theme": "主題",
"dark": "深色",
"ultraDark": "超深色",
"dashboard": "系統狀態",
"inbounds": "入站列表",
"clients": "客戶端",
"nodes": "節點",
"settings": "面板設定",
"xray": "Xray 設定",
"apiDocs": "API 文件",
"logout": "退出登入",
"link": "管理"
},
"pages": {
"login": {
"hello": "你好",
"title": "歡迎",
"loginAgain": "登入時效已過,請重新登入",
"toasts": {
"invalidFormData": "資料格式錯誤",
"emptyUsername": "請輸入使用者名稱",
"emptyPassword": "請輸入密碼",
"wrongUsernameOrPassword": "用戶名、密碼或雙重驗證碼無效。",
"successLogin": "您已成功登入您的帳戶。"
}
},
"index": {
"title": "系統狀態",
"cpu": "CPU",
"logicalProcessors": "邏輯處理器",
"frequency": "頻率",
"swap": "交換空間",
"storage": "儲存",
"memory": "記憶體",
"threads": "執行緒",
"xrayStatus": "Xray",
"stopXray": "停止",
"restartXray": "重啟",
"xraySwitch": "版本",
"xrayUpdates": "Xray 更新",
"xraySwitchClick": "選擇你要切換到的版本",
"xraySwitchClickDesk": "請謹慎選擇,因為較舊版本可能與當前配置不相容",
"updatePanel": "更新面板",
"panelUpdateDesc": "這將把 3X-UI 更新到最新版本並重新啟動面板服務。",
"currentPanelVersion": "目前面板版本",
"latestPanelVersion": "最新面板版本",
"panelUpToDate": "面板已是最新",
"upToDate": "已是最新",
"xrayStatusUnknown": "未知",
"xrayStatusRunning": "運行中",
"xrayStatusStop": "停止",
"xrayStatusError": "錯誤",
"xrayErrorPopoverTitle": "執行Xray時發生錯誤",
"operationHours": "系統正常執行時間",
"systemHistoryTitle": "系統歷史",
"charts": "圖表",
"xrayMetricsTitle": "Xray 指標",
"xrayMetricsDisabled": "未設定 Xray 指標端點",
"xrayMetricsHint": "在 xray 設定中加入頂層 metrics 區塊,tag 為 metrics_out,listen 為 127.0.0.1:11111,然後重啟 xray。",
"xrayObservatoryEmpty": "尚無 Observatory 資料",
"xrayObservatoryHint": "在 xray 設定中加入 observatory 區塊,列出要探測的出站 tag,然後重啟 xray。",
"xrayObservatoryTagPlaceholder": "選擇出站",
"xrayObservatoryAlive": "在線",
"xrayObservatoryDead": "離線",
"xrayObservatoryLastSeen": "最後在線",
"xrayObservatoryLastTry": "最後嘗試",
"trendLast2Min": "最近 2 分鐘",
"systemLoad": "系統負載",
"systemLoadDesc": "過去 1、5 和 15 分鐘的系統平均負載",
"connectionCount": "連線數",
"ipAddresses": "IP地址",
"toggleIpVisibility": "切換IP可見性",
"overallSpeed": "整體速度",
"upload": "上傳",
"download": "下載",
"totalData": "總數據",
"sent": "已發送",
"received": "已接收",
"documentation": "文件",
"xraySwitchVersionDialog": "您確定要變更Xray版本嗎",
"xraySwitchVersionDialogDesc": "這將會把Xray版本變更為#version#。",
"xraySwitchVersionPopover": "Xray 更新成功",
"panelUpdateDialog": "您確定要更新面板嗎?",
"panelUpdateDialogDesc": "這將把 3X-UI 更新到 #version# 並重新啟動面板服務。",
"panelUpdateCheckPopover": "面板更新檢查失敗",
"panelUpdateStartedPopover": "面板更新已開始",
"geofileUpdateDialog": "您確定要更新地理檔案嗎?",
"geofileUpdateDialogDesc": "這將更新 #filename# 檔案。",
"geofilesUpdateDialogDesc": "這將更新所有文件。",
"geofilesUpdateAll": "全部更新",
"geofileUpdatePopover": "地理檔案更新成功",
"customGeoTitle": "自訂 GeoSite / GeoIP",
"customGeoAdd": "新增",
"customGeoType": "類型",
"customGeoAlias": "別名",
"customGeoUrl": "URL",
"customGeoEnabled": "啟用",
"customGeoLastUpdated": "上次更新",
"customGeoExtColumn": "路由 (ext:…)",
"customGeoToastUpdateAll": "所有自訂來源已更新",
"customGeoActions": "操作",
"customGeoEdit": "編輯",
"customGeoDelete": "刪除",
"customGeoDownload": "立即更新",
"customGeoModalAdd": "新增自訂 geo",
"customGeoModalEdit": "編輯自訂 geo",
"customGeoModalSave": "儲存",
"customGeoDeleteConfirm": "刪除此自訂 geo 來源?",
"customGeoRoutingHint": "在路由規則中將值欄寫為 ext:檔案.dat:標籤(替換標籤)。",
"customGeoInvalidId": "無效的資源 ID",
"customGeoAliasesError": "載入自訂 geo 別名失敗",
"customGeoValidationAlias": "別名只能包含小寫字母、數字、- 和 _",
"customGeoValidationUrl": "URL 必須以 http:// 或 https:// 開頭",
"customGeoAliasPlaceholder": "a-z 0-9 _ -",
"customGeoAliasLabelSuffix": "(自訂)",
"customGeoToastList": "自訂 geo 清單",
"customGeoToastAdd": "新增自訂 geo",
"customGeoToastUpdate": "更新自訂 geo",
"customGeoToastDelete": "自訂 geofile「{{ .fileName }}」已刪除",
"customGeoToastDownload": "geofile「{{ .fileName }}」已更新",
"customGeoErrInvalidType": "類型必須是 geosite 或 geoip",
"customGeoErrAliasRequired": "請填寫別名",
"customGeoErrAliasPattern": "別名包含不允許的字元",
"customGeoErrAliasReserved": "此別名已保留",
"customGeoErrUrlRequired": "請填寫 URL",
"customGeoErrInvalidUrl": "URL 無效",
"customGeoErrUrlScheme": "URL 必須使用 http 或 https",
"customGeoErrUrlHost": "URL 主機無效",
"customGeoErrDuplicateAlias": "此類型已使用該別名",
"customGeoErrNotFound": "找不到自訂 geo 來源",
"customGeoErrDownload": "下載失敗",
"customGeoErrUpdateAllIncomplete": "有一個或多個自訂 geo 來源更新失敗",
"customGeoEmpty": "尚無自訂 geo 來源 — 點擊「新增」以建立",
"dontRefresh": "安裝中,請勿重新整理此頁面",
"logs": "日誌",
"config": "配置",
"backup": "備份和恢復",
"backupTitle": "備份和恢復",
"exportDatabase": "備份",
"exportDatabaseDesc": "點擊下載包含當前資料庫備份的 .db 文件到您的設備。",
"importDatabase": "恢復",
"importDatabaseDesc": "點擊選擇並上傳設備中的 .db 文件以從備份恢復資料庫。",
"importDatabaseSuccess": "資料庫匯入成功",
"importDatabaseError": "匯入資料庫時發生錯誤",
"readDatabaseError": "讀取資料庫時發生錯誤",
"getDatabaseError": "檢索資料庫時發生錯誤",
"getConfigError": "檢索設定檔時發生錯誤"
},
"inbounds": {
"title": "入站列表",
"totalDownUp": "總上傳 / 下載",
"totalUsage": "總用量",
"inboundCount": "入站數量",
"operate": "選單",
"enable": "啟用",
"remark": "備註",
"node": "節點",
"deployTo": "部署到",
"localPanel": "本機面板",
"fallbacks": {
"title": "回落",
"help": "當此入站的連線未匹配任何用戶時將其路由到另一個入站。在下方選擇一個子入站路由欄位SNI / ALPN / Path / xver會自動從子入站的傳輸方式填入——大多數情境不需要再調整。每個子入站應監聽 127.0.0.1security=none。",
"empty": "尚未新增回落",
"add": "新增回落",
"pickInbound": "選擇一個入站",
"matchAny": "任何",
"rederive": "從子入站重新填入",
"rederived": "已從子入站重新填入",
"editAdvanced": "編輯路由欄位",
"hideAdvanced": "隱藏進階",
"quickAddAll": "一鍵新增所有符合的入站",
"quickAdded": "已新增 {n} 個回落",
"quickAddedNone": "沒有可新增的新入站",
"routesWhen": "當條件成立時路由",
"defaultCatchAll": "預設 — 兜底匹配其餘"
},
"protocol": "協議",
"port": "埠",
"portMap": "埠映射",
"traffic": "流量",
"details": "詳細資訊",
"transportConfig": "傳輸配置",
"expireDate": "到期時間",
"createdAt": "建立時間",
"updatedAt": "更新時間",
"resetTraffic": "重置流量",
"addInbound": "新增入站",
"generalActions": "通用操作",
"modifyInbound": "修改入站",
"deleteInbound": "刪除入站",
"deleteInboundContent": "確定要刪除入站嗎?",
"deleteClient": "刪除客戶端",
"deleteClientContent": "確定要刪除客戶端嗎?",
"resetTrafficContent": "確定要重置流量嗎?",
"copyLink": "複製連結",
"address": "地址",
"network": "網路",
"destinationPort": "目標埠",
"targetAddress": "目標地址",
"monitorDesc": "留空表示監聽所有 IP",
"meansNoLimit": "= 無限制單位GB)",
"totalFlow": "總流量",
"leaveBlankToNeverExpire": "留空表示永不過期",
"noRecommendKeepDefault": "建議保留預設值",
"certificatePath": "檔案路徑",
"certificateContent": "檔案內容",
"publicKey": "公鑰",
"privatekey": "私鑰",
"clickOnQRcode": "點選二維碼複製",
"client": "客戶",
"export": "匯出連結",
"clone": "複製",
"cloneInbound": "複製",
"cloneInboundContent": "此入站規則除埠Port、監聽 IPListening IP和客戶端Clients以外的所有配置都將應用於克隆",
"cloneInboundOk": "建立克隆",
"resetAllTraffic": "重置所有入站流量",
"resetAllTrafficTitle": "重置所有入站流量",
"resetAllTrafficContent": "確定要重置所有入站流量嗎?",
"resetInboundClientTraffics": "重置客戶端流量",
"resetInboundClientTrafficTitle": "重置所有客戶端流量",
"resetInboundClientTrafficContent": "確定要重置此入站客戶端的所有流量嗎?",
"resetAllClientTraffics": "重置所有客戶端流量",
"resetAllClientTrafficTitle": "重置所有客戶端流量",
"resetAllClientTrafficContent": "確定要重置所有客戶端的所有流量嗎?",
"delDepletedClients": "刪除流量耗盡的客戶端",
"delDepletedClientsTitle": "刪除流量耗盡的客戶端",
"delDepletedClientsContent": "確定要刪除所有流量耗盡的客戶端嗎?",
"email": "電子郵件",
"emailDesc": "電子郵件必須完全唯一",
"IPLimit": "IP 限制",
"IPLimitDesc": "如果數量超過設定值則禁用入站流量。0 = 禁用)",
"IPLimitlog": "IP 日誌",
"IPLimitlogDesc": "IP 歷史日誌(要啟用被禁用的入站流量,請清除日誌)",
"IPLimitlogclear": "清除日誌",
"setDefaultCert": "從面板設定證書",
"streamTab": "串流",
"securityTab": "安全",
"sniffingTab": "嗅探",
"sniffingMetadataOnly": "僅中繼資料",
"sniffingRouteOnly": "僅路由",
"sniffingIpsExcluded": "排除的 IP",
"sniffingDomainsExcluded": "排除的網域",
"decryption": "解密",
"encryption": "加密",
"vlessAuthX25519": "X25519 認證",
"vlessAuthMlkem768": "ML-KEM-768 認證",
"vlessAuthCustom": "自訂",
"vlessAuthSelected": "已選擇:{auth}",
"advanced": {
"title": "入站 JSON 部分",
"subtitle": "完整入站 JSON 以及針對 settings、sniffing 和 streamSettings 的專用編輯器。",
"all": "全部",
"allHelp": "在單一編輯器中編輯包含所有欄位的完整入站物件。",
"settings": "設定",
"settingsHelp": "Xray settings 區塊包裝:",
"sniffing": "嗅探",
"sniffingHelp": "Xray sniffing 區塊包裝:",
"stream": "串流",
"streamHelp": "Xray stream 區塊包裝:",
"jsonErrorPrefix": "進階 JSON"
},
"telegramDesc": "請提供Telegram聊天ID。在機器人中使用'/id'命令)或({'@'}userinfobot",
"subscriptionDesc": "要找到你的訂閱 URL請導航到“詳細資訊”。此外你可以為多個客戶端使用相同的名稱。",
"info": "資訊",
"same": "相同",
"inboundData": "入站資料",
"exportInbound": "匯出入站規則",
"import": "匯入",
"importInbound": "匯入入站規則",
"periodicTrafficResetTitle": "流量重置",
"periodicTrafficResetDesc": "按指定間隔自動重置流量計數器",
"lastReset": "上次重置",
"periodicTrafficReset": {
"never": "從不",
"daily": "每日",
"weekly": "每週",
"monthly": "每月",
"hourly": "每小時"
},
"toasts": {
"obtain": "獲取",
"updateSuccess": "更新成功",
"logCleanSuccess": "日誌已清除",
"inboundsUpdateSuccess": "入站連接已成功更新",
"inboundUpdateSuccess": "入站連接已成功更新",
"inboundCreateSuccess": "入站連接已成功建立",
"inboundDeleteSuccess": "入站連接已成功刪除",
"inboundClientAddSuccess": "已新增入站客戶端",
"inboundClientDeleteSuccess": "入站客戶端已刪除",
"inboundClientUpdateSuccess": "入站客戶端已更新",
"delDepletedClientsSuccess": "所有耗盡客戶端已刪除",
"resetAllClientTrafficSuccess": "客戶端所有流量已重置",
"resetAllTrafficSuccess": "所有流量已重置",
"resetInboundClientTrafficSuccess": "流量已重置",
"resetInboundTrafficSuccess": "入站流量已重置",
"trafficGetError": "取得流量資料時發生錯誤",
"getNewX25519CertError": "取得X25519憑證時發生錯誤。",
"getNewmldsa65Error": "取得mldsa65憑證時發生錯誤。",
"getNewVlessEncError": "取得VlessEnc憑證時發生錯誤。"
},
"stream": {
"general": {
"request": "請求",
"response": "響應",
"name": "名稱",
"value": "值"
},
"tcp": {
"version": "版本",
"method": "方法",
"path": "路徑",
"status": "狀態",
"statusDescription": "狀態說明",
"requestHeader": "請求頭",
"responseHeader": "響應頭"
}
}
},
"clients": {
"add": "新增客戶端",
"edit": "編輯客戶端",
"submitAdd": "新增客戶端",
"submitEdit": "儲存變更",
"clientCount": "客戶端數量",
"bulk": "批次新增",
"copyFromInbound": "從入站複製客戶端",
"copyToInbound": "複製客戶端至",
"copySelected": "複製所選",
"copySource": "來源",
"copyEmailPreview": "產生的信箱預覽",
"copySelectSourceFirst": "請先選擇一個來源入站。",
"copyResult": "複製結果",
"copyResultSuccess": "複製成功",
"copyResultNone": "沒有內容可複製:未選取客戶端或來源為空",
"copyResultErrors": "複製錯誤",
"copyFlowLabel": "新客戶端的 Flow (VLESS)",
"copyFlowHint": "套用至所有被複製的客戶端。留空則略過。",
"selectAll": "全選",
"clearAll": "全部清除",
"method": "方法",
"first": "首個",
"last": "末位",
"ipLog": "IP 日誌",
"prefix": "前綴",
"postfix": "後綴",
"delayedStart": "首次使用後開始",
"expireDays": "時長",
"days": "天",
"renew": "自動續期",
"renewDesc": "到期後自動續期。(0 = 停用) (單位: 天)",
"title": "客戶端",
"actions": "操作",
"totalGB": "總上傳/下載 (GB)",
"expiryTime": "到期時間",
"addClients": "新增客戶端",
"limitIp": "IP 限制",
"password": "密碼",
"subId": "訂閱 ID",
"online": "上線",
"email": "信箱",
"comment": "備註",
"traffic": "流量",
"offline": "離線",
"addTitle": "新增客戶端",
"qrCode": "QR 碼",
"moreInformation": "更多資訊",
"delete": "刪除",
"reset": "重設流量",
"editTitle": "編輯客戶端",
"client": "客戶端",
"enabled": "已啟用",
"remaining": "剩餘",
"duration": "時長",
"attachedInbounds": "關聯入站",
"selectInbound": "選擇一個或多個入站",
"noSubId": "此客戶端沒有 subId無法產生共享連結。",
"noLinks": "沒有可共享的連結 — 請先將此客戶端關聯至支援協定的入站。",
"link": "連結",
"resetNotPossible": "請先將此客戶端關聯至入站。",
"general": "通用",
"resetAllTraffics": "重設所有客戶端流量",
"resetAllTrafficsTitle": "重設所有客戶端流量?",
"resetAllTrafficsContent": "所有客戶端的上下行計數器將歸零。配額與到期時間不受影響。此操作無法復原。",
"empty": "尚無客戶端 — 新增一個開始使用。",
"deleteConfirmTitle": "刪除客戶端 {email}",
"deleteConfirmContent": "將從所有關聯入站中移除該客戶端並刪除其流量紀錄。此操作無法復原。",
"deleteSelected": "刪除 ({count})",
"bulkDeleteConfirmTitle": "刪除 {count} 個客戶端?",
"bulkDeleteConfirmContent": "每個所選客戶端都會從關聯的入站中被移除,其流量紀錄也會被刪除。此操作無法復原。",
"delDepleted": "刪除已耗盡",
"delDepletedConfirmTitle": "刪除已耗盡的客戶端?",
"delDepletedConfirmContent": "刪除所有流量配額已用盡或已過期的客戶端。此操作無法復原。",
"auth": "Auth",
"hysteriaAuth": "Hysteria Auth",
"uuid": "UUID",
"flow": "Flow",
"reverseTag": "Reverse tag",
"reverseTagPlaceholder": "選用 Reverse tag",
"telegramId": "Telegram 使用者 ID",
"telegramIdPlaceholder": "數字形式的 Telegram 使用者 ID (0 = 無)",
"created": "建立時間",
"updated": "更新時間",
"ipLimit": "IP 限制",
"toasts": {
"deleted": "客戶端已刪除",
"trafficReset": "流量已重設",
"allTrafficsReset": "所有客戶端流量已重設",
"bulkDeleted": "已刪除 {count} 個客戶端",
"bulkDeletedMixed": "已刪除 {ok} 個,失敗 {failed} 個",
"bulkCreated": "已建立 {count} 個客戶端",
"bulkCreatedMixed": "已建立 {ok} 個,失敗 {failed} 個",
"delDepleted": "已刪除 {count} 個已耗盡的客戶端"
}
},
"nodes": {
"title": "節點",
"addNode": "新增節點",
"editNode": "編輯節點",
"totalNodes": "節點總數",
"onlineNodes": "線上",
"offlineNodes": "離線",
"avgLatency": "平均延遲",
"name": "名稱",
"namePlaceholder": "例如de-frankfurt-1",
"addressPlaceholder": "panel.example.com 或 1.2.3.4",
"remark": "備註",
"scheme": "協議",
"address": "位址",
"port": "埠",
"basePath": "基礎路徑",
"apiToken": "API 權杖",
"apiTokenPlaceholder": "遠端面板設定頁中的權杖",
"apiTokenHint": "遠端面板在 設定 → API 權杖 中顯示其 API 權杖。",
"regenerate": "重新產生權杖",
"regenerateConfirm": "重新產生會使目前的權杖失效。任何使用該權杖的中央面板將失去存取權,直到更新為止。是否繼續?",
"allowPrivateAddress": "允許私有地址",
"allowPrivateAddressHint": "僅對私有網路或VPN上的節點啟用。",
"enable": "已啟用",
"status": "狀態",
"cpu": "CPU",
"mem": "記憶體",
"uptime": "執行時間",
"latency": "延遲",
"lastHeartbeat": "上次心跳",
"xrayVersion": "Xray 版本",
"panelVersion": "面板版本",
"actions": "操作",
"probe": "立即探測",
"testConnection": "測試連線",
"connectionOk": "連線正常 ({ms} ms)",
"connectionFailed": "連線失敗",
"never": "從未",
"justNow": "剛剛",
"deleteConfirmTitle": "刪除節點「{name}」?",
"deleteConfirmContent": "這將停止監控該節點。遠端面板本身不受影響。",
"statusValues": {
"online": "線上",
"offline": "離線",
"unknown": "未知"
},
"toasts": {
"list": "載入節點失敗",
"obtain": "載入節點失敗",
"add": "新增節點",
"update": "更新節點",
"delete": "刪除節點",
"deleted": "節點已刪除",
"test": "測試連線",
"fillRequired": "名稱、位址、埠與 API 權杖為必填",
"probeFailed": "探測失敗"
}
},
"settings": {
"title": "面板設定",
"save": "儲存",
"infoDesc": "此處的所有更改都需要儲存並重啟面板才能生效",
"restartPanel": "重啟面板",
"restartPanelDesc": "確定要重啟面板嗎?若重啟後無法訪問面板,請前往伺服器檢視面板日誌資訊",
"restartPanelSuccess": "面板已成功重新啟動",
"actions": "操作",
"resetDefaultConfig": "重置為預設配置",
"panelSettings": "常規",
"securitySettings": "安全設定",
"TGBotSettings": "Telegram 機器人配置",
"panelListeningIP": "面板監聽 IP",
"panelListeningIPDesc": "預設留空監聽所有 IP",
"panelListeningDomain": "面板監聽域名",
"panelListeningDomainDesc": "預設情況下留空以監視所有域名和 IP 地址",
"panelPort": "面板監聽埠",
"panelPortDesc": "重啟面板生效",
"publicKeyPath": "面板證書公鑰檔案路徑",
"publicKeyPathDesc": "填寫一個 '/' 開頭的絕對路徑",
"privateKeyPath": "面板證書金鑰檔案路徑",
"privateKeyPathDesc": "填寫一個 '/' 開頭的絕對路徑",
"panelUrlPath": "面板 url 根路徑",
"panelUrlPathDesc": "必須以 '/' 開頭,以 '/' 結尾",
"pageSize": "分頁大小",
"pageSizeDesc": "定義入站表的頁面大小。設定 0 表示禁用",
"remarkModel": "備註模型和分隔符",
"datepicker": "日期選擇器",
"datepickerPlaceholder": "選擇日期",
"datepickerDescription": "選擇器日曆類型指定到期日期",
"sampleRemark": "備註示例",
"oldUsername": "原使用者名稱",
"currentPassword": "原密碼",
"newUsername": "新使用者名稱",
"newPassword": "新密碼",
"telegramBotEnable": "啟用 Telegram 機器人",
"telegramBotEnableDesc": "啟用 Telegram 機器人功能",
"telegramToken": "Telegram 機器人令牌token",
"telegramTokenDesc": "從 '{'@'}BotFather' 獲取的 Telegram 機器人令牌",
"telegramProxy": "SOCKS5 Proxy",
"telegramProxyDesc": "啟用 SOCKS5 代理連線到 Telegram根據指南調整設定",
"telegramAPIServer": "Telegram API Server",
"telegramAPIServerDesc": "要使用的 Telegram API 伺服器。留空以使用預設伺服器。",
"telegramChatId": "管理員聊天 ID",
"telegramChatIdDesc": "Telegram 管理員聊天 ID (多個以逗號分隔)(可通過 {'@'}userinfobot 獲取,或在機器人中使用 '/id' 命令獲取)",
"telegramNotifyTime": "通知時間",
"telegramNotifyTimeDesc": "設定週期性的 Telegram 機器人通知時間(使用 crontab 時間格式)",
"tgNotifyBackup": "資料庫備份",
"tgNotifyBackupDesc": "傳送帶有報告的資料庫備份檔案",
"tgNotifyLogin": "登入通知",
"tgNotifyLoginDesc": "當有人試圖登入你的面板時顯示使用者名稱、IP 地址和時間",
"sessionMaxAge": "會話時長",
"sessionMaxAgeDesc": "保持登入狀態的時長(單位:分鐘)",
"expireTimeDiff": "到期通知閾值",
"expireTimeDiffDesc": "達到此閾值時,將收到有關到期時間的通知(單位:天)",
"trafficDiff": "流量耗盡閾值",
"trafficDiffDesc": "達到此閾值時將收到有關流量耗盡的通知單位GB",
"tgNotifyCpu": "CPU 負載通知閾值",
"tgNotifyCpuDesc": "CPU 負載超過此閾值時,將收到通知(單位:%",
"timeZone": "時區",
"timeZoneDesc": "定時任務將按照該時區的時間執行",
"subSettings": "訂閱設定",
"subEnable": "啟用訂閱服務",
"subEnableDesc": "啟用訂閱服務功能",
"subJsonEnable": "獨立啟用/停用 JSON 訂閱端點。",
"subTitle": "訂閱標題",
"subTitleDesc": "在VPN客戶端中顯示的標題",
"subSupportUrl": "支援連結",
"subSupportUrlDesc": "VPN 用戶端中顯示的技術支援連結",
"subProfileUrl": "個人資料連結",
"subProfileUrlDesc": "VPN 用戶端中顯示的網站連結",
"subAnnounce": "公告",
"subAnnounceDesc": "VPN 用戶端中顯示的公告文字",
"subEnableRouting": "啟用路由",
"subEnableRoutingDesc": "在 VPN 用戶端中啟用路由的全域設定。(僅限 Happ",
"subRoutingRules": "路由規則",
"subRoutingRulesDesc": "VPN 用戶端的全域路由規則。(僅限 Happ",
"subListen": "監聽 IP",
"subListenDesc": "訂閱服務監聽的 IP 地址(留空表示監聽所有 IP",
"subPort": "監聽埠",
"subPortDesc": "訂閱服務監聽的埠號(必須是未使用的埠)",
"subCertPath": "公鑰路徑",
"subCertPathDesc": "訂閱服務使用的公鑰檔案路徑(以 '/' 開頭)",
"subKeyPath": "私鑰路徑",
"subKeyPathDesc": "訂閱服務使用的私鑰檔案路徑(以 '/' 開頭)",
"subPath": "URI 路徑",
"subPathDesc": "訂閱服務使用的 URI 路徑(以 '/' 開頭,以 '/' 結尾)",
"subDomain": "監聽域名",
"subDomainDesc": "訂閱服務監聽的域名(留空表示監聽所有域名和 IP",
"subUpdates": "更新間隔",
"subUpdatesDesc": "客戶端應用中訂閱 URL 的更新間隔(單位:小時)",
"subEncrypt": "編碼",
"subEncryptDesc": "訂閱服務返回的內容將採用 Base64 編碼",
"subShowInfo": "顯示使用資訊",
"subShowInfoDesc": "客戶端應用中將顯示剩餘流量和日期資訊",
"subEmailInRemark": "在名稱中包含郵箱",
"subEmailInRemarkDesc": "在訂閱配置名稱中包含客戶端郵箱。",
"subURI": "反向代理 URI",
"subURIDesc": "用於代理後面的訂閱 URL 的 URI 路徑",
"externalTrafficInformEnable": "外部交通通知",
"externalTrafficInformEnableDesc": "每次流量更新時通知外部 API",
"externalTrafficInformURI": "外部流量通知 URI",
"externalTrafficInformURIDesc": "流量更新將會傳送到此 URI",
"restartXrayOnClientDisable": "用戶自動停用後重新啟動 Xray",
"restartXrayOnClientDisableDesc": "當用戶因到期或流量上限而被自動停用時,重新啟動 Xray。",
"fragment": "分片",
"fragmentDesc": "啟用 TLS hello 資料包分片",
"fragmentSett": "設定",
"noisesDesc": "啟用 Noises.",
"noisesSett": "Noises 設定",
"mux": "多路複用器",
"muxDesc": "在已建立的資料流內傳輸多個獨立的資料流",
"muxSett": "複用器設定",
"direct": "直接連線",
"directDesc": "直接與特定國家的域或IP範圍建立連線",
"notifications": "通知",
"certs": "證書",
"externalTraffic": "外部流量",
"dateAndTime": "日期和時間",
"proxyAndServer": "代理和伺服器",
"intervals": "間隔",
"information": "資訊",
"language": "語言",
"telegramBotLanguage": "Telegram 機器人語言",
"security": {
"admin": "管理員憑證",
"twoFactor": "雙重驗證",
"twoFactorEnable": "啟用2FA",
"twoFactorEnableDesc": "增加額外的驗證層以提高安全性。",
"twoFactorModalSetTitle": "啟用雙重認證",
"twoFactorModalDeleteTitle": "停用雙重認證",
"twoFactorModalSteps": "要設定雙重認證,請執行以下步驟:",
"twoFactorModalFirstStep": "1. 在認證應用程式中掃描此QR碼或複製QR碼附近的令牌並貼到應用程式中",
"twoFactorModalSecondStep": "2. 輸入應用程式中的驗證碼",
"twoFactorModalRemoveStep": "輸入應用程式中的驗證碼以移除雙重認證。",
"twoFactorModalChangeCredentialsTitle": "更改憑證",
"twoFactorModalChangeCredentialsStep": "輸入應用程式中的代碼以更改管理員憑證。",
"twoFactorModalSetSuccess": "雙重身份驗證已成功建立",
"twoFactorModalDeleteSuccess": "雙重身份驗證已成功刪除",
"twoFactorModalError": "驗證碼錯誤",
"show": "顯示",
"hide": "隱藏",
"apiTokenNew": "新增令牌",
"apiTokenName": "名稱",
"apiTokenNamePlaceholder": "例如 central-panel-a",
"apiTokenNameRequired": "名稱必填",
"apiTokenEmpty": "尚無令牌 — 建立一個以認證機器人或遠端面板。",
"apiTokenDeleteWarning": "使用此令牌的任何呼叫方將立即無法認證。"
},
"toasts": {
"modifySettings": "參數已更改。",
"getSettings": "取得參數時發生錯誤",
"modifyUserError": "變更管理員憑證時發生錯誤。",
"modifyUser": "您已成功變更管理員憑證。",
"originalUserPassIncorrect": "原使用者名稱或原密碼錯誤",
"userPassMustBeNotEmpty": "新使用者名稱和新密碼不能為空",
"getOutboundTrafficError": "取得出站流量錯誤",
"resetOutboundTrafficError": "重設出站流量錯誤"
}
},
"xray": {
"title": "Xray 配置",
"save": "儲存",
"restart": "重新啟動 Xray",
"restartSuccess": "Xray 已成功重新啟動",
"stopSuccess": "Xray 已成功停止",
"restartError": "重新啟動Xray時發生錯誤。",
"stopError": "停止Xray時發生錯誤。",
"basicTemplate": "基礎配置",
"advancedTemplate": "高階配置",
"generalConfigs": "常規配置",
"generalConfigsDesc": "這些選項將決定常規配置",
"logConfigs": "日誌",
"logConfigsDesc": "日誌可能會影響伺服器的效能,建議僅在需要時啟用",
"blockConfigsDesc": "這些選項將阻止使用者連線到特定協議和網站",
"basicRouting": "基本路由",
"blockConnectionsConfigsDesc": "這些選項將根據特定的請求國家阻止流量。",
"directConnectionsConfigsDesc": "直接連線確保特定的流量不會通過其他伺服器路由。",
"blockips": "阻止IP",
"blockdomains": "阻止域名",
"directips": "直接IP",
"directdomains": "直接域名",
"ipv4Routing": "IPv4 路由",
"ipv4RoutingDesc": "此選項將僅通過 IPv4 路由到目標域",
"warpRouting": "WARP 路由",
"warpRoutingDesc": "注意:在使用這些選項之前,請按照面板 GitHub 上的步驟在你的伺服器上以 socks5 代理模式安裝 WARP。WARP 將通過 Cloudflare 伺服器將流量路由到網站。",
"nordRouting": "NordVPN 路由",
"nordRoutingDesc": "這些選項將根據特定目的地通過 NordVPN 路由流量。",
"Template": "高階 Xray 配置模板",
"TemplateDesc": "最終的 Xray 配置檔案將基於此模板生成",
"FreedomStrategy": "Freedom 協議策略",
"FreedomStrategyDesc": "設定 Freedom 協議中網路的輸出策略",
"RoutingStrategy": "配置路由域策略",
"RoutingStrategyDesc": "設定 DNS 解析的整體路由策略",
"outboundTestUrl": "出站測試 URL",
"outboundTestUrlDesc": "測試出站連線時使用的 URL",
"Torrent": "遮蔽 BitTorrent 協議",
"Inbounds": "入站規則",
"InboundsDesc": "接受來自特定客戶端的流量",
"Outbounds": "出站規則",
"Balancers": "負載均衡",
"OutboundsDesc": "設定出站流量傳出方式",
"Routings": "路由規則",
"RoutingsDesc": "每條規則的優先順序都很重要",
"completeTemplate": "全部",
"logLevel": "日誌級別",
"logLevelDesc": "錯誤日誌的日誌級別,用於指示需要記錄的資訊",
"accessLog": "訪問日誌",
"accessLogDesc": "訪問日誌的檔案路徑。特殊值 'none' 禁用訪問日誌",
"errorLog": "錯誤日誌",
"errorLogDesc": "錯誤日誌的檔案路徑。特殊值 'none' 禁用錯誤日誌",
"dnsLog": "DNS 日誌",
"dnsLogDesc": "是否啟用 DNS 查詢日誌",
"maskAddress": "隱藏地址",
"maskAddressDesc": "IP 地址掩碼,啟用時會自動替換日誌中出現的 IP 地址。",
"statistics": "統計",
"statsInboundUplink": "入站上傳統計",
"statsInboundUplinkDesc": "啟用所有入站代理的上行流量統計收集。",
"statsInboundDownlink": "入站下載統計",
"statsInboundDownlinkDesc": "啟用所有入站代理的下行流量統計收集。",
"statsOutboundUplink": "出站上傳統計",
"statsOutboundUplinkDesc": "啟用所有出站代理的上行流量統計收集。",
"statsOutboundDownlink": "出站下載統計",
"statsOutboundDownlinkDesc": "啟用所有出站代理的下行流量統計收集。",
"rules": {
"first": "置頂",
"last": "置底",
"up": "向上",
"down": "向下",
"source": "來源",
"dest": "目的地址",
"inbound": "入站",
"outbound": "出站",
"balancer": "負載均衡",
"info": "資訊",
"add": "新增規則",
"edit": "編輯規則",
"useComma": "逗號分隔的項目"
},
"outbound": {
"addOutbound": "新增出站",
"addReverse": "新增反向",
"editOutbound": "編輯出站",
"editReverse": "編輯反向",
"reverseTag": "反向標籤",
"reverseTagDesc": "VLESS 簡易反向代理出站標籤。留空則停用。設定後,此客戶端的連線可作為反向代理隧道。",
"reverseTagPlaceholder": "出站標籤(留空則停用)",
"tag": "標籤",
"tagDesc": "唯一標籤",
"address": "地址",
"reverse": "反向",
"domain": "域名",
"type": "類型",
"bridge": "Bridge",
"portal": "Portal",
"link": "連結",
"intercon": "互連",
"settings": "設定",
"accountInfo": "帳戶資訊",
"outboundStatus": "出站狀態",
"sendThrough": "傳送通過",
"test": "測試",
"testResult": "測試結果",
"testing": "正在測試連接...",
"testSuccess": "測試成功",
"testFailed": "測試失敗",
"testError": "測試出站失敗",
"nordvpn": "NordVPN",
"accessToken": "訪問令牌",
"country": "國家",
"server": "伺服器",
"city": "城市",
"allCities": "所有城市",
"privateKey": "私密金鑰",
"load": "負載"
},
"balancer": {
"addBalancer": "新增負載均衡",
"editBalancer": "編輯負載均衡",
"balancerStrategy": "策略",
"balancerSelectors": "選擇器",
"tag": "標籤",
"tagDesc": "唯一標籤",
"balancerDesc": "無法同時使用 balancerTag 和 outboundTag。如果同時使用則只有 outboundTag 會生效。"
},
"wireguard": {
"secretKey": "金鑰",
"publicKey": "公鑰",
"allowedIPs": "允許的 IP",
"endpoint": "端點",
"psk": "共享金鑰",
"domainStrategy": "域策略"
},
"tun": {
"nameDesc": "TUN 介面的名稱。預設值為 'xray0'",
"mtuDesc": "最大傳輸單元。資料包的最大大小。預設值為 1500",
"userLevel": "用戶級別",
"userLevelDesc": "通過此入站的所有連接都將使用此用戶級別。預設值為 0"
},
"dns": {
"enable": "啟用 DNS",
"enableDesc": "啟用內建 DNS 伺服器",
"tag": "DNS 入站標籤",
"tagDesc": "此標籤將在路由規則中可用作入站標籤",
"clientIp": "客戶端IP",
"clientIpDesc": "用於在DNS查詢期間通知伺服器指定的IP位置",
"disableCache": "禁用快取",
"disableCacheDesc": "禁用DNS快取",
"disableFallback": "禁用回退",
"disableFallbackDesc": "禁用回退DNS查詢",
"disableFallbackIfMatch": "匹配時禁用回退",
"disableFallbackIfMatchDesc": "當DNS伺服器的匹配域名列表命中時禁用回退DNS查詢",
"enableParallelQuery": "啟用並行查詢",
"enableParallelQueryDesc": "啟用並行DNS查詢到多個伺服器以實現更快的解析",
"strategy": "查詢策略",
"strategyDesc": "解析域名的總體策略",
"add": "新增伺服器",
"edit": "編輯伺服器",
"domains": "域",
"expectIPs": "預期 IP",
"unexpectIPs": "意外IP",
"useSystemHosts": "使用系統Hosts",
"useSystemHostsDesc": "使用已安裝系統的hosts檔案",
"serveStale": "提供過期結果",
"serveStaleDesc": "在背景重新整理時傳回過期的快取結果",
"serveExpiredTTL": "過期TTL",
"serveExpiredTTLDesc": "過期快取項目的有效期0 = 永不過期",
"timeoutMs": "逾時 (毫秒)",
"skipFallback": "跳過回退",
"finalQuery": "最終查詢",
"hosts": "Hosts",
"hostsAdd": "新增 Host",
"hostsEmpty": "未定義任何 Host",
"hostsDomain": "網域 (例如 domain:example.com)",
"hostsValues": "IP 或網域 — 輸入後按 Enter",
"usePreset": "使用範本",
"dnsPresetTitle": "DNS範本",
"dnsPresetFamily": "家庭",
"clearAll": "全部刪除",
"clearAllTitle": "刪除所有 DNS 伺服器?",
"clearAllConfirm": "此操作將從清單中刪除所有 DNS 伺服器,無法復原。"
},
"fakedns": {
"add": "新增假 DNS",
"edit": "編輯假 DNS",
"ipPool": "IP 池子網",
"poolSize": "池大小"
}
}
},
"tgbot": {
"keyboardClosed": "❌ 自定義鍵盤已關閉!",
"noResult": "❗ 沒有結果!",
"noQuery": "❌ 未找到查詢!請再次使用該命令!",
"wentWrong": "❌ 出了點問題!",
"noIpRecord": "❗ 沒有IP記錄",
"noInbounds": "❗ 未找到入站!",
"unlimited": "♾ 無限(重置)",
"add": "添加",
"month": "月",
"months": "月",
"day": "天",
"days": "天",
"hours": "小時",
"minutes": "分鐘",
"unknown": "未知",
"inbounds": "入站",
"clients": "客戶端",
"offline": "🔴 離線",
"online": "🟢 在線",
"commands": {
"unknown": "❗ 未知命令",
"pleaseChoose": "👇 請選擇:\r\n",
"help": "🤖 歡迎使用本機器人!它旨在為您提供來自伺服器的特定資料,並允許您進行必要的修改。\r\n\r\n",
"start": "👋 你好,<i>{{ .Firstname }}</i>。\r\n",
"welcome": "🤖 歡迎來到 <b>{{ .Hostname }}</b> 管理機器人。\r\n",
"status": "✅ 機器人正常執行!",
"usage": "❗ 請輸入要搜尋的文字!",
"getID": "🆔 您的 ID 為:<code>{{ .ID }}</code>",
"helpAdminCommands": "要重新啟動 Xray Core\r\n<code>/restart</code>\r\n\r\n要搜尋客戶電子郵件\r\n<code>/usage [電子郵件]</code>\r\n\r\n要搜尋入站帶有客戶統計資料\r\n<code>/inbound [備註]</code>\r\n\r\nTelegram聊天ID\r\n<code>/id</code>",
"helpClientCommands": "要搜尋統計資料,請使用以下命令:\r\n<code>/usage [電子郵件]</code>\r\n\r\nTelegram聊天ID\r\n<code>/id</code>",
"restartUsage": "\r\n\r\n<code>/restart</code>",
"restartSuccess": "✅ 操作成功!",
"restartFailed": "❗ 操作錯誤。\r\n\r\n<code>錯誤: {{ .Error }}</code>.",
"xrayNotRunning": "❗ Xray Core 未運行。",
"startDesc": "顯示主選單",
"helpDesc": "機器人幫助",
"statusDesc": "檢查機器人狀態",
"idDesc": "顯示您的 Telegram ID"
},
"messages": {
"cpuThreshold": "🔴 CPU 使用率為 {{ .Percent }}%,超過閾值 {{ .Threshold }}%",
"selectUserFailed": "❌ 使用者選擇錯誤!",
"userSaved": "✅ 電報使用者已儲存。",
"loginSuccess": "✅ 成功登入到面板。\r\n",
"loginFailed": "❗️ 面板登入失敗。\r\n",
"2faFailed": "2FA 失敗",
"report": "🕰 定時報告:{{ .RunTime }}\r\n",
"datetime": "⏰ 日期時間:{{ .DateTime }}\r\n",
"hostname": "💻 主機名:{{ .Hostname }}\r\n",
"version": "🚀 X-UI 版本:{{ .Version }}\r\n",
"xrayVersion": "📡 Xray 版本: {{ .XrayVersion }}\r\n",
"ipv6": "🌐 IPv6{{ .IPv6 }}\r\n",
"ipv4": "🌐 IPv4{{ .IPv4 }}\r\n",
"ip": "🌐 IP{{ .IP }}\r\n",
"ips": "🔢 IP 地址:\r\n{{ .IPs }}\r\n",
"serverUpTime": "⏳ 伺服器執行時間:{{ .UpTime }} {{ .Unit }}\r\n",
"serverLoad": "📈 伺服器負載:{{ .Load1 }}, {{ .Load2 }}, {{ .Load3 }}\r\n",
"serverMemory": "📋 伺服器記憶體:{{ .Current }}/{{ .Total }}\r\n",
"tcpCount": "🔹 TCP 連線數:{{ .Count }}\r\n",
"udpCount": "🔸 UDP 連線數:{{ .Count }}\r\n",
"traffic": "🚦 流量:{{ .Total }} (↑{{ .Upload }},↓{{ .Download }})\r\n",
"xrayStatus": " Xray 狀態:{{ .State }}\r\n",
"username": "👤 使用者名稱:{{ .Username }}\r\n",
"reason": "❗️ 原因:{{ .Reason }}\r\n",
"time": "⏰ 時間:{{ .Time }}\r\n",
"inbound": "📍 入站:{{ .Remark }}\r\n",
"port": "🔌 埠:{{ .Port }}\r\n",
"expire": "📅 過期日期:{{ .Time }}\r\n",
"expireIn": "📅 剩餘時間:{{ .Time }}\r\n",
"active": "💡 啟用:{{ .Enable }}\r\n",
"enabled": "🚨 已啟用:{{ .Enable }}\r\n",
"online": "🌐 連線狀態:{{ .Status }}\r\n",
"lastOnline": "🔙 上次上線: {{ .Time }}\r\n",
"email": "📧 郵箱:{{ .Email }}\r\n",
"upload": "🔼 上傳↑:{{ .Upload }}\r\n",
"download": "🔽 下載↓:{{ .Download }}\r\n",
"total": "📊 總計:{{ .UpDown }} / {{ .Total }}\r\n",
"TGUser": "👤 電報使用者:{{ .TelegramID }}\r\n",
"exhaustedMsg": "🚨 耗盡的 {{ .Type }}\r\n",
"exhaustedCount": "🚨 耗盡的 {{ .Type }} 數量:\r\n",
"onlinesCount": "🌐 線上客戶:{{ .Count }}\r\n",
"disabled": "🛑 禁用:{{ .Disabled }}\r\n",
"depleteSoon": "🔜 即將耗盡:{{ .Deplete }}\r\n\r\n",
"backupTime": "🗄 備份時間:{{ .Time }}\r\n",
"refreshedOn": "\r\n📋🔄 重新整理時間:{{ .Time }}\r\n\r\n",
"yes": "✅ 是的",
"no": "❌ 沒有",
"received_id": "🔑📥 ID 已更新。",
"received_password": "🔑📥 密碼已更新。",
"received_email": "📧📥 電子郵件已更新。",
"received_comment": "💬📥 評論已更新。",
"id_prompt": "🔑 預設 ID: {{ .ClientId }}\n\n請輸入您的 ID。",
"pass_prompt": "🔑 預設密碼: {{ .ClientPassword }}\n\n請輸入您的密碼。",
"email_prompt": "📧 預設電子郵件: {{ .ClientEmail }}\n\n請輸入您的電子郵件。",
"comment_prompt": "💬 預設評論: {{ .ClientComment }}\n\n請輸入您的評論。",
"inbound_client_data_id": "🔄 入站: {{ .InboundRemark }}\n\n🔑 ID: {{ .ClientId }}\n📧 電子郵件: {{ .ClientEmail }}\n📊 流量: {{ .ClientTraffic }}\n📅 到期日: {{ .ClientExp }}\n🌐 IP 限制: {{ .IpLimit }}\n💬 備註: {{ .ClientComment }}\n\n你現在可以將客戶加入入站了",
"inbound_client_data_pass": "🔄 入站: {{ .InboundRemark }}\n\n🔑 密碼: {{ .ClientPass }}\n📧 電子郵件: {{ .ClientEmail }}\n📊 流量: {{ .ClientTraffic }}\n📅 到期日: {{ .ClientExp }}\n🌐 IP 限制: {{ .IpLimit }}\n💬 備註: {{ .ClientComment }}\n\n你現在可以將客戶加入入站了",
"cancel": "❌ 程序已取消!\n\n您可以隨時使用 /start 重新開始。 🔄",
"error_add_client": "⚠️ 錯誤:\n\n {{ .error }}",
"using_default_value": "好的,我會使用預設值。 😊",
"incorrect_input": "您的輸入無效。\n短語應連續輸入不能有空格。\n正確示例: aaaaaa\n錯誤示例: aaa aaa 🚫",
"AreYouSure": "你確定嗎?🤔",
"SuccessResetTraffic": "📧 電子郵件: {{ .ClientEmail }}\n🏁 結果: ✅ 成功",
"FailedResetTraffic": "📧 電子郵件: {{ .ClientEmail }}\n🏁 結果: ❌ 失敗 \n\n🛠 錯誤: [ {{ .ErrorMessage }} ]",
"FinishProcess": "🔚 所有客戶的流量重置已完成。"
},
"buttons": {
"closeKeyboard": "❌ 關閉鍵盤",
"cancel": "❌ 取消",
"cancelReset": "❌ 取消重置",
"cancelIpLimit": "❌ 取消 IP 限制",
"confirmResetTraffic": "✅ 確認重置流量?",
"confirmClearIps": "✅ 確認清除 IP",
"confirmRemoveTGUser": "✅ 確認移除 Telegram 使用者?",
"confirmToggle": "✅ 確認啟用/禁用使用者?",
"dbBackup": "獲取資料庫備份",
"serverUsage": "伺服器使用情況",
"getInbounds": "獲取入站資訊",
"depleteSoon": "即將耗盡",
"clientUsage": "獲取使用情況",
"onlines": "線上客戶端",
"commands": "命令",
"refresh": "🔄 重新整理",
"clearIPs": "❌ 清除 IP",
"removeTGUser": "❌ 移除 Telegram 使用者",
"selectTGUser": "👤 選擇 Telegram 使用者",
"selectOneTGUser": "👤 選擇一個 Telegram 使用者:",
"resetTraffic": "📈 重置流量",
"resetExpire": "📅 更改到期日期",
"ipLog": "🔢 IP 日誌",
"ipLimit": "🔢 IP 限制",
"setTGUser": "👤 設定 Telegram 使用者",
"toggle": "🔘 啟用/禁用",
"custom": "🔢 風俗",
"confirmNumber": "✅ 確認: {{ .Num }}",
"confirmNumberAdd": "✅ 確認新增:{{ .Num }}",
"limitTraffic": "🚧 流量限制",
"getBanLogs": "禁止日誌",
"allClients": "所有客戶",
"addClient": "新增客戶",
"submitDisable": "以停用方式送出 ☑️",
"submitEnable": "以啟用方式送出 ✅",
"use_default": "🏷️ 使用預設值",
"change_id": "⚙️🔑 ID",
"change_password": "⚙️🔑 密碼",
"change_email": "⚙️📧 電子郵件",
"change_comment": "⚙️💬 評論",
"change_flow": "⚙️🚦 流控",
"ResetAllTraffics": "重設所有流量",
"SortedTrafficUsageReport": "排序過的流量使用報告"
},
"answers": {
"successfulOperation": "✅ 成功!",
"errorOperation": "❗ 操作錯誤。",
"getInboundsFailed": "❌ 獲取入站資訊失敗。",
"getClientsFailed": "❌ 獲取客戶失敗。",
"canceled": "❌ {{ .Email }}:操作已取消。",
"clientRefreshSuccess": "✅ {{ .Email }}:客戶端重新整理成功。",
"IpRefreshSuccess": "✅ {{ .Email }}IP 重新整理成功。",
"TGIdRefreshSuccess": "✅ {{ .Email }}:客戶端的 Telegram 使用者重新整理成功。",
"resetTrafficSuccess": "✅ {{ .Email }}:流量已重置成功。",
"setTrafficLimitSuccess": "✅ {{ .Email }}: 流量限制儲存成功。",
"expireResetSuccess": "✅ {{ .Email }}:過期天數已重置成功。",
"resetIpSuccess": "✅ {{ .Email }}:成功儲存 IP 限制數量為 {{ .Count }}。",
"clearIpSuccess": "✅ {{ .Email }}IP 已成功清除。",
"getIpLog": "✅ {{ .Email }}:獲取 IP 日誌。",
"getUserInfo": "✅ {{ .Email }}:獲取 Telegram 使用者資訊。",
"removedTGUserSuccess": "✅ {{ .Email }}Telegram 使用者已成功移除。",
"enableSuccess": "✅ {{ .Email }}:已成功啟用。",
"disableSuccess": "✅ {{ .Email }}:已成功禁用。",
"askToAddUserId": "未找到您的配置!\r\n請向管理員詢問在您的配置中使用您的 Telegram 使用者 ChatID。\r\n\r\n您的使用者 ChatID<code>{{ .TgUserID }}</code>",
"chooseClient": "為入站 {{ .Inbound }} 選擇一個客戶",
"chooseInbound": "選擇一個入站"
}
}
}