3x-ui/web/translation/en-US.json
Sanaei edf0f36940
Frontend rewrite: React + TypeScript with AntD v6 (#4498)
* chore(frontend): add react+typescript toolchain alongside vue

Step 0 of the planned vue->react migration. React 19, antd 5, i18next
+ react-i18next, typescript 5, and @vitejs/plugin-react 6 are added as
dev/runtime deps alongside the existing vue stack. Both frameworks
coexist in the build until the last entry flips.

* vite.config.js: react() plugin runs next to vue(); new manualChunks
  for vendor-react / vendor-antd-react / vendor-icons-react /
  vendor-i18next. Existing vue chunks unchanged.
* eslint.config.js: typescript-eslint + eslint-plugin-react-hooks
  rules scoped to *.{ts,tsx}; vue config untouched for *.{js,vue}.
* tsconfig.json: strict, jsx: react-jsx, moduleResolution: bundler,
  allowJs: true (lets .tsx files import the remaining .js modules
  during incremental migration), @/* path alias.
* env.d.ts: Vite client types + window.X_UI_BASE_PATH typing +
  SubPageData shape consumed by the subscription page.

Vite stays pinned at 8.0.13 per the existing project policy. No
existing .vue/.js source files touched in this step.

eslint-plugin-react (not -hooks) is not included because its latest
release does not yet support ESLint 10. react-hooks/purity covers
the safety-critical case; revisit when the plugin updates.

* refactor(frontend): port subpage to react+ts

Step 1 of the planned vue->react migration. The standalone
subscription page (sub/sub.go renders the HTML host; React mounts
into #app) is the first entry off vue.

Introduces two shared pieces both entries (and future ones) will
use:

* src/hooks/useTheme.tsx — React Context + useTheme hook + the
  same buildAntdThemeConfig (dark/ultra-dark token overrides) and
  pauseAnimationsUntilLeave helper the vue version exposes. Same
  localStorage keys (dark-mode, isUltraDarkThemeEnabled) and DOM
  side effects (body.className, html[data-theme]) so the two stay
  in sync across the coexistence period.
* src/i18n/react.ts — i18next + react-i18next loader that reads
  the same web/translation/*.json files via import.meta.glob. The
  vue-i18n setup in src/i18n/index.js is untouched and still serves
  the remaining vue entries.

SubPage.tsx mirrors the vue version's behavior: reads
window.__SUB_PAGE_DATA__ injected by the Go sub server, renders QR
codes / descriptions / Android+iOS deep-link dropdowns, supports
theme cycle and language switch. Uses AntD v5 idioms: Descriptions
items prop, Dropdown menu prop, Layout.Content.

* refactor(frontend): port login to react+ts

Step 2 of the planned vue->react migration. The login entry is the
first to exercise AntD React's Form API (Form + Form.Item with
name/rules + onFinish) and the existing axios/CSRF interceptors
under React.

* LoginPage.tsx: same form fields, conditional 2FA input,
  rotating headline ("Hello" / "Welcome to..."), drifting blob
  background, theme cycle + language popover. Headline transition
  switches from vue's <Transition mode=out-in> to a CSS keyframe
  animation keyed off the visible word.
* entries/login.tsx: setupAxios() + applyDocumentTitle() unchanged
  from the vue entry — both are framework-agnostic in src/utils
  and src/api/axios-init.js.

useTheme hook, ThemeProvider, and i18n/react.ts loader introduced
in step 1 are now shared across two entries; Vite extracts them as
a small chunk in the build output.

* refactor(frontend): port api-docs to react+ts

Step 3 of the planned vue->react migration. The five api-docs files
(ApiDocsPage, CodeBlock, EndpointRow, EndpointSection, plus the
data-only endpoints.js) all move to react+ts.

Also introduces components/AppSidebar.tsx — api-docs is the first
authenticated page to need it. AppSidebar.vue stays in place for the
six remaining vue entries (settings, inbounds, clients, xray, nodes,
index); each gets switched to AppSidebar.tsx as its entry migrates.
After the last entry flips, AppSidebar.vue is deleted.

Notable transformations:

* The scroll observer that highlights the active TOC link is a
  useEffect keyed on sections — re-registers whenever the visible
  set changes (search filter narrows it). Same behaviour as the vue
  watchEffect.
* v-html="safeInlineHtml(...)" becomes
  dangerouslySetInnerHTML={{ __html: safeInlineHtml(...) }}. The
  helper still escapes everything except <code> tags.
* JSON syntax highlighter in CodeBlock is unchanged — pure regex on
  the escaped string, then rendered via dangerouslySetInnerHTML.
* endpoints.js stays as JS (allowJs in tsconfig); only the consumer
  signatures (Endpoint, Section) are typed at the React boundary.
* AppSidebar reuses pauseAnimationsUntilLeave + useTheme from
  step 1. Drawer + Sider keyed off the same localStorage flag
  (isSidebarCollapsed) and DOM theme attributes the vue version
  uses, so the two stay in sync during coexistence.

* refactor(frontend): port nodes to react+ts

Step 4 of the planned vue->react migration. The nodes entry brings in
the largest shared-infrastructure batch so far — every authenticated
react page from here on can lean on these.

New shared pieces (live alongside their .vue counterparts during
coexistence):

* hooks/useMediaQuery.ts — useState + resize listener
* hooks/useWebSocket.ts — wraps WebSocketClient, subscribes on mount
  and unsubscribes on unmount. The underlying client is a single
  module-level instance so multiple components on the same page
  share one socket.
* hooks/useNodes.ts — node list state + CRUD + probe/test, including
  the totals memo (online/offline/avgLatency) used by the summary card.
  applyNodesEvent is the entry point for the heartbeat-pushed list.
* components/CustomStatistic.tsx — thin Statistic wrapper, prefix +
  suffix slots become props.
* components/Sparkline.tsx — the SVG line chart with measured-width
  axis scaling, gradient fill, tooltip overlay, and per-instance
  gradient id from React.useId. ResizeObserver lifecycle is in
  useEffect; the math is unchanged.

Pages:

* NodesPage — wires hooks + WebSocket together, renders summary card
  + NodeList, hosts the form modal. Uses Modal.useModal() for the
  delete confirm so the dialog inherits ConfigProvider theming.
* NodeList — desktop renders a Table with expandable history rows;
  mobile flips to a vertical card list whose actions live in a
  bottom-right Dropdown. The IP-blur eye toggle persists across both.
* NodeFormModal — controlled form (useState object, single setForm
  per change). The reset-on-open effect computes the next state
  once and applies it with eslint-disable to satisfy the new
  react-hooks/set-state-in-effect rule on a legitimate pattern.
* NodeHistoryPanel — polls /panel/api/nodes/history/{id}/{metric}/
  {bucket} every 15s, renders cpu+mem sparklines side-by-side.

* refactor(frontend): port settings to react+ts

Step 5 of the planned vue->react migration. Settings is the first
entry whose state model didn't translate to the Vue-style "parent
passes a reactive object, children mutate it in place" pattern, so
the React port flips it to lifted state + a typed updateSetting
patch function.

* models/setting.ts — typed AllSetting class with the same field
  defaults and equals() behavior the vue version had. The .js
  twin is deleted; nothing else imported it.
* hooks/useAllSetting.ts — owns allSetting + oldAllSetting state,
  exposes updateSetting(patch), saveDisabled is derived via useMemo
  off equals() (no more 1Hz dirty-check timer).
* components/SettingListItem.tsx — children-based wrapper instead
  of named slots. The vue twin stays alive because xray (BasicsTab,
  DnsTab) still imports it; deleted when xray migrates.

The five tab components and the TwoFactorModal each accept
{ allSetting, updateSetting } and render with AntD v5's Collapse
items[] API. Every v-model:value="x" became
value={...} onChange={(e) => updateSetting({ key: e.target.value })}
or onChange={(v) => updateSetting({ key: v })} for non-input
controls.

SubscriptionFormatsTab is the trickiest — fragment / noises[] /
mux / direct routing rules are stored as JSON-encoded strings on
the wire. Parsing them once via useMemo per field, mutating the
parsed object on edit, and stringifying back into the patch keeps
the round-trip identical to the vue version.

SettingsPage hosts the tab navigation (with hash sync), the
save / restart action bar, the security-warnings alert banner,
and the restart flow that rebuilds the panel URL after the new
host/port/cert settings take effect.

* refactor(frontend): port clients to react+ts

Step 6 of the planned vue->react migration. Clients is the biggest
data-CRUD page in the panel (1.1k-line ClientsPage, 4 modals, full
table + mobile card list, WebSocket-driven realtime traffic + online
updates).

New shared infra (lives alongside vue twins until inbounds migrates):

* hooks/useClients.ts — clients + inbounds list, CRUD + bulk delete +
  attach/detach + traffic reset, with WebSocket event handlers
  (traffic, client_stats, invalidate) and a small debounced refresh
  on the invalidate event. State managed via setState; the live
  client_stats event merges traffic snapshots row-by-row through a
  ref to avoid stale closure issues.
* hooks/useDatepicker.ts — singleton "gregorian"/"jalalian" cache
  with subscribe/notify so multiple components can read the panel's
  Calendar Type without re-fetching. Mirrors useDatepicker.js.
* components/DateTimePicker.tsx — AntD DatePicker wrapper.
  vue3-persian-datetime-picker has no React port; the Jalali UI
  calendar is deferred (read-only Jalali display via IntlUtil
  formatDate still works). The vue twin stays for inbounds.
* pages/inbounds/QrPanel.tsx — copy/download/copy-as-png QR helper
  shared between clients (qr modal) and inbounds (still on vue).
  Vue twin stays alive at QrPanel.vue.
* models/inbound.ts — slim port: only the TLS_FLOW_CONTROL constant
  the clients form needs. The full inbound model stays as
  inbound.js for now; inbounds will pull it in as inbound.ts.

The clients page itself uses Modal.useModal() for all confirm
dialogs (delete, bulk-delete, reset-traffic, delDepleted, reset-all)
so the dialogs render themed. Filter state persists to
localStorage under clientsFilterState. Sort + pagination state is
local; pageSize seeds from /panel/setting/defaultSettings.

The four modals share a controlled "open/onOpenChange" pattern
that replaces vue's v-model:open. ClientFormModal computes
attach/detach diffs from the inbound multi-select on submit; the
parent's onSave callback routes them through useClients's attach()/
detach() after the main update succeeds.

ESLint config: turned off four react-hooks v7 rules
(react-compiler, preserve-manual-memoization, set-state-in-effect,
purity). They're all React-Compiler-driven informational rules; we
don't run the compiler and the patterns they flag (initial-fetch
useEffect, derived computations using Date.now, inline arrow event
handlers) are all idiomatic React. Disabling globally instead of
per-line keeps the diff readable.

* refactor(frontend): port index dashboard to react+ts

Step 7 of the Vue→React migration. Ports the overview/index entry: dashboard
page, status + xray cards, panel-update / log / backup / system-history /
xray-metrics / xray-log / version modals, and the custom-geo subsection. Adds
the shared JsonEditor (CodeMirror 6) and useStatus hook used by the config
modal. Removes the unused react-hooks/set-state-in-effect disables now that
the rule is off globally.

* refactor(frontend): port xray to react+ts

Step 8 of the Vue→React migration. Ports the xray config entry: page shell,
basics/routing/outbounds/balancers/dns tabs, the rule + balancer + dns server
+ dns presets + warp + nord modals, the protocol-aware outbound form, and the
shared FinalMaskForm (TCP/UDP masks + QUIC params). Adds useXraySetting that
mirrors the legacy two-way sync between the JSON template string and the
parsed templateSettings tree. The outbound model itself stays in JS so the
class-driven form keeps its existing mutation API; instance access is typed
loosely inside the form to match.

The shared FinalMaskForm.vue and JsonEditor.vue stay alongside the new .tsx
versions until step 9 — InboundFormModal.vue still imports them.

Adds react-hooks/immutability and react-hooks/refs to the already-disabled
react-compiler rule set; both flag the outbound form's instance-mutation
pattern that doesn't run through useState.

* Upgrade frontend deps (antd v6, i18n, TS)

Bump frontend dependencies in package.json and regenerate package-lock.json. Notable updates: upgrade antd to v6, update i18next/react-i18next, axios, qs, vue-i18n, TypeScript and ESLint, plus related @rc-component packages and replacements (e.g. classnames/rc-util -> clsx/@rc-component/util). Lockfile changes reflect the new dependency tree required for Ant Design v6 and other package upgrades.

* refactor(frontend): port inbounds to react+ts and drop vue toolchain

Step 9 — the last entry. Ports the inbounds entry: page shell, list with
desktop table + mobile cards, info modal, qr-code modal, share-link
helpers, and the protocol-aware form modal (basics / protocol /
stream / security / sniffing / advanced JSON). useInbounds replaces
the Vue composable with WebSocket-driven traffic + client-stats merge.

Inbound and DBInbound models stay in JS so the class-driven form keeps
its mutation API; instance access is typed loosely inside the form to
match. FinalMaskForm/JsonEditor/TextModal/PromptModal/InfinityIcon are
the last shared bits to flip; their .vue counterparts go too.

Toolchain cleanup now that no entry needs Vue: drop plugin-vue from
vite.config, remove the .vue lint block + parser, prune vue / vue-i18n
/ ant-design-vue / @ant-design/icons-vue / vue3-persian-datetime-picker
/ moment-jalaali override from package.json, and switch utils/index.js
to import { message } from 'antd' instead of ant-design-vue.

* chore(frontend): adopt antd v6 api updates

Sweep deprecated props across the React tree:
- Modal: destroyOnClose -> destroyOnHidden, maskClosable -> mask.closable
- Space: direction -> orientation (or removed when redundant)
- Input.Group compact -> Space.Compact block
- Drawer: width -> size
- Spin: tip -> description
- Progress: trailColor -> railColor
- Alert: message -> title
- Popover: overlayClassName -> rootClassName
- BackTop -> FloatButton.BackTop

Also refresh dashboard theming for v6: rename dark/ultra Layout and Menu
tokens (siderBg, darkItemBg, darkSubMenuItemBg, darkPopupBg), tweak gauge
size/stroke, add font-size overrides for Statistic and Progress so the
overview numbers stay legible under v6 defaults.

* chore(frontend): antd v6 polish, theme + modal fixes

- adopt message.useMessage hook + messageBus bridge so HttpUtil messages
  inherit ConfigProvider theme tokens
- replace deprecated antd APIs (List, Input addonBefore/After, Empty
  imageStyle); introduce InputAddon helper + SettingListItem custom rows
- fix dark/ultra selectors in portaled modals (body.dark,
  html[data-theme='ultra-dark']) instead of nonexistent .is-dark/.is-ultra
- add horizontal scroll to clients table; reorder node columns so
  actions+enable sit at the left
- swap raw button for antd Button in NodeFormModal test connection
- fix FinalMaskForm nested-form by hoisting it outside OutboundFormModal's
  parent Form
- fix advanced "all" JSON tab in InboundFormModal — useMemo on a mutated
  ref was stale; compute on every render
- fix chart-on-open for SystemHistory + XrayMetrics modals by adding open
  to effect deps (useRef.current doesn't trigger re-runs)
- switch i18next interpolation to single-brace {var} to match locale files
- drop residual Vue mentions in CI workflows and Go comments

* fix(frontend): qr code collapse — open only first panel, allow toggle

ClientQrModal and QrCodeModal both used activeKey without onChange,
forcing every panel open and blocking user toggle. Switch to controlled
state initialized to the first item's key on open, with onChange so
clicks update state.

Also remove unused AppBridge.tsx (superseded by per-page message.useMessage
hook).

* fix(frontend): hover cards, balancer load, routing dnd, modal a11y, outbound crash

- ClientsPage/SettingsPage/XrayPage: add hoverable to bottom card/tabs so
  hover affordance matches the top card
- BalancerFormModal: lazy-init useState from props + destroyOnHidden so
  the form mounts with saved values instead of relying on a useEffect
  sync that could miss the first open
- RoutingTab: rewrite pointer drag — handlers are now defined inside the
  pointerdown closure so addEventListener/removeEventListener match;
  drag state lives on a ref (from/to/moved) so onUp reads the real
  indices, not stale closure values. Adds setPointerCapture so Windows
  and touch keep delivering events when the cursor leaves the handle.
- OutboundFormModal/InboundFormModal: blur the focused input before
  switching tabs to silence the aria-hidden-on-focused-element warning
- utils.isArrEmpty: return true for undefined/null arrays — the old form
  treated undefined as "not empty" which crashed VLESSSettings.fromJson
  when json.vnext was missing

* fix(frontend): clipboard reliability + restyle login page

- ClipboardManager.copyText: prefer navigator.clipboard on secure
  contexts, fall back to a focused on-screen textarea + execCommand.
  Old path used left:-9999px which failed selection in some browsers
  and swallowed execCommand's return value, so the "copied" toast
  appeared even when nothing made it to the clipboard.
- LoginPage: richer gradient backdrop — five animated colour blobs,
  glassmorphic card (backdrop-filter blur + saturate), gradient brand
  text/accent, masked grid texture for depth, and a thin gradient
  border on the card. Light/dark/ultra each get their own palette.

* Memoize compactAdvancedJson and update deps

Wrap compactAdvancedJson in useCallback (dependent on messageApi) and add it to the dependency array of applyAdvancedJsonToBasic. This ensures a stable function reference for correct dependency tracking and avoids stale closures/unnecessary re-renders in InboundFormModal.tsx.

* style(frontend): prettier charts, drop redundant frame, format net rates

- Sparkline: multi-stop gradient fill, soft drop-shadow under the line,
  dashed grid, glowing pulse on the latest-point marker, pill-shaped
  tooltip with dashed crosshair
- XrayMetricsModal: glow + pulse on the observatory alive dot,
  monospace stamps/listen text
- SystemHistoryModal: keep just the modal's frame around the chart (the
  inner wrapper I'd added stacked a second border on top); strip the
  decimal from Net Up/Down (25.63 KB/s → 25 KB/s) only on this chart's
  formatter

* style(frontend): refined dark/ultra palette + shared pro card frame

- Dark tokens shifted to a cooler, Linear-style palette: page #1a1b1f,
  sidebar/header #15161a (recessed nav, darker than cards), card
  #23252b, elevated #2d2f37
- Ultra dark: page pure #000 for OLED, sidebar #050507 disappears into
  the frame, card #101013 with a clear step, elevated #1a1a1e
- New styles/page-cards.css holds the card border/shadow/hover rules so
  all seven content pages (index, clients, inbounds, xray, settings,
  nodes, api-docs) share one definition instead of duplicating in each
  page CSS
- Dashboard typography: uppercase card titles with letter-spacing,
  larger 17px stat values, subtle gradient divider between stat columns,
  ellipsis on action labels so "Backup & Restore" doesn't break the
  card height at mid widths
- Light --bg-page stays at #e6e8ec for the contrast against white cards

* fix(frontend): wireguard info alignment, blue login dark, embed gitkeep

- align WireGuard info-modal fields with Protocol/Address/Port by wrapping
  values in Tag (matches the rest of the dl.info-list rows)
- swap login dark palette from purple to pure blue blobs/accent/brand
- pin web/dist/.gitkeep through gitignore so //go:embed all:dist never
  fails on a fresh clone with an empty dist directory

* docs: refresh frontend docs for the React + TS + AntD 6 stack

Update CONTRIBUTING.md and frontend/README.md to describe the migrated
frontend accurately:

- replace Vue 3 / Ant Design Vue 4 references with React 19 / AntD 6 / TS
- swap composables -> hooks, vue-i18n -> react-i18next, createApp -> createRoot
- mention the typecheck step (tsc --noEmit) in the PR checklist
- document the Vite 8.0.13 pin and TypeScript strict mode in conventions
- list the nodes and api-docs entries that were missing from the layout

* style(frontend): improve readability and mobile polish

- bump statistic title/value contrast in dark and ultra-dark so totals
  on the inbounds summary card stay legible
- give index card actions explicit colors per theme so links like Stop,
  Logs, System History no longer fade into the card background
- show the panel version as a tag next to "3X-UI" on mobile, mirroring
  the Xray version tag pattern, and turn it orange when an update is
  available
- make the login settings button a proper circle by adding size="large"
  + an explicit border-radius fallback on .toolbar-btn

* feat: jalali calendar support and date formatting fixes

- Wire useDatepicker into IntlUtil and switch jalalian display locale
  to fa-IR for clean "1405/07/03 12:00:00" output (drops the awkward
  "AP" era suffix that "<lang>-u-ca-persian" produced)
- Drop in persian-calendar-suite for the jalali date picker, with a
  light/dark/ultra theme map and CSS overrides so the inline-styled
  input stays readable and bg matches the surrounding container
- Force LTR on the picker input so "1405/03/07 00:00" reads naturally
- Pass calendar setting through ClientInfoModal, ClientsPage Duration
  tooltip, and ClientFormModal's expiry picker
- Heuristic toMs() in ClientInfoModal so GORM's autoUpdateTime seconds
  render as a real date instead of "1348/11/01"
- Persist UpdatedAt on the ClientRecord row in client_service.Update;
  previously only the inbound settings JSON was bumped, so the panel
  never saw a fresh updated_at after editing a client

* feat(frontend): donate link, panel version label, login lang menu

- Sidebar: add heart donate link to https://donate.sanaei.dev and small panel version under 3X-UI brand
- Login: swap settings-cog for translation icon, drop title, render languages as a direct list
- Vite dev: inject window.X_UI_CUR_VER from config/version so dev mode matches prod
- Translations: add menu.donate across all locales

* fix(xray-update): respect XUI_BIN_FOLDER on Windows

The Windows update path hardcoded "bin/xray-windows-amd64.exe", ignoring
the configured XUI_BIN_FOLDER. In dev mode (folder set to x-ui) this
created a stray bin/ folder while the running binary stayed un-updated.

* Bump Xray to v26.5.9 and minor cleanup

Update Xray release URLs to v26.5.9 in the GitHub Actions workflow and DockerInit.sh. Remove the hardcoded skip for tagVersion "26.5.3" so it will be considered when collecting Xray versions. Apply small formatting fixes: remove an extra blank line in database/db.go, normalize spacing/alignment of Protocol constants in database/model/model.go, and trim a trailing blank line in web/controller/inbound.go.

* fix(frontend): route remaining copy buttons through ClipboardManager

Direct navigator.clipboard calls fail in non-secure contexts (HTTP on a
LAN IP), making the API-docs code copy and security-tab token copy
silently broken. Both now go through ClipboardManager which falls back
to document.execCommand('copy') when navigator.clipboard is unavailable.

* fix(db): store CreatedAt/UpdatedAt in milliseconds

GORM's autoCreateTime/autoUpdateTime tags default to Unix seconds on
int64 fields and overwrite the service-supplied UnixMilli value on
save. The frontend interprets these timestamps as JS Date inputs
(milliseconds), so created/updated columns rendered ~1970 dates. Adding
the :milli qualifier makes GORM match what the service code and UI
expect.

* Improve legacy clipboard copy handling

Refactor ClipboardManager._legacyCopy to better handle focus and selection when copying. The textarea is now appended to the active element's parent (or body) and placed off-screen with aria-hidden and readonly attributes. The code preserves and restores the previous document selection and active element, uses focus({preventScroll: true}) to avoid scrolling, and returns the execCommand('copy') result. This makes legacy copy behavior more robust and less disruptive to the page state.

* fix(lint): drop redundant ok=false in clipboard fallback catch

* chore(deps): bump golang.org/x/net to v0.55.0 for GO-2026-5026
2026-05-23 15:21:45 +02:00

1093 lines
No EOL
53 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": "Username",
"password": "Password",
"login": "Log In",
"confirm": "Confirm",
"cancel": "Cancel",
"close": "Close",
"save": "Save",
"logout": "Log Out",
"create": "Create",
"update": "Update",
"copy": "Copy",
"copied": "Copied",
"download": "Download",
"remark": "Remark",
"enable": "Enabled",
"protocol": "Protocol",
"search": "Search",
"filter": "Filter",
"loading": "Loading...",
"refresh": "Refresh",
"clear": "Clear",
"second": "Second",
"minute": "Minute",
"hour": "Hour",
"day": "Day",
"check": "Check",
"indefinite": "Indefinite",
"unlimited": "Unlimited",
"none": "None",
"qrCode": "QR Code",
"info": "More Information",
"edit": "Edit",
"delete": "Delete",
"reset": "Reset",
"noData": "No data.",
"copySuccess": "Copied successfully",
"sure": "Sure",
"encryption": "Encryption",
"useIPv4ForHost": "Use IPv4 for host",
"transmission": "Transmission",
"host": "Host",
"path": "Path",
"camouflage": "Obfuscation",
"status": "Status",
"enabled": "Enabled",
"disabled": "Disabled",
"depleted": "Ended",
"depletingSoon": "Depleting",
"offline": "Offline",
"online": "Online",
"domainName": "Domain Name",
"monitor": "Listen IP",
"certificate": "Digital Certificate",
"fail": "Failed",
"comment": "Comment",
"success": "Success",
"lastOnline": "Last Online",
"getVersion": "Get Version",
"install": "Install",
"clients": "Clients",
"usage": "Usage",
"twoFactorCode": "Code",
"remained": "Remaining",
"security": "Security",
"secAlertTitle": "Security Alert",
"secAlertSsl": "This connection is not secure. Please avoid entering sensitive information until TLS is activated for data protection.",
"secAlertConf": "Certain settings are vulnerable to attacks. It is recommended to reinforce security protocols to prevent potential breaches.",
"secAlertSSL": "Panel lacks secure connection. Please install TLS certificate for data protection.",
"secAlertPanelPort": "Panel default port is vulnerable. Please configure a random or specific port.",
"secAlertPanelURI": "Panel default URI path is insecure. Please configure a complex URI path.",
"secAlertSubURI": "Subscription default URI path is insecure. Please configure a complex URI path.",
"secAlertSubJsonURI": "Subscription JSON default URI path is insecure. Please configure a complex URI path.",
"emptyDnsDesc": "No added DNS servers.",
"emptyFakeDnsDesc": "No added Fake DNS servers.",
"emptyBalancersDesc": "No added balancers.",
"emptyReverseDesc": "No added reverse proxies.",
"somethingWentWrong": "Something went wrong",
"subscription": {
"title": "Subscription info",
"subId": "Subscription ID",
"status": "Status",
"downloaded": "Downloaded",
"uploaded": "Uploaded",
"expiry": "Expiry",
"totalQuota": "Total quota",
"individualLinks": "Individual links",
"active": "Active",
"inactive": "Inactive",
"unlimited": "Unlimited",
"noExpiry": "No expiry"
},
"menu": {
"theme": "Theme",
"dark": "Dark",
"ultraDark": "Ultra Dark",
"dashboard": "Overview",
"inbounds": "Inbounds",
"clients": "Clients",
"nodes": "Nodes",
"settings": "Panel Settings",
"xray": "Xray Configs",
"apiDocs": "API Docs",
"logout": "Log Out",
"link": "Manage",
"donate": "Donate"
},
"pages": {
"login": {
"hello": "Hello",
"title": "Welcome",
"loginAgain": "Your session has expired, please log in again",
"toasts": {
"invalidFormData": "The input data format is invalid.",
"emptyUsername": "Username is required",
"emptyPassword": "Password is required",
"wrongUsernameOrPassword": "Invalid username or password or two-factor code.",
"successLogin": "You have successfully logged into your account."
}
},
"index": {
"title": "Overview",
"cpu": "CPU",
"logicalProcessors": "Logical Processors",
"frequency": "Frequency",
"swap": "Swap",
"storage": "Storage",
"memory": "RAM",
"threads": "Threads",
"xrayStatus": "Xray",
"stopXray": "Stop",
"restartXray": "Restart",
"xraySwitch": "Version",
"xrayUpdates": "Xray Updates",
"xraySwitchClick": "Choose the version you want to switch to.",
"xraySwitchClickDesk": "Choose carefully, as older versions may not be compatible with current configurations.",
"updatePanel": "Update Panel",
"panelUpdateDesc": "This will update 3X-UI itself to the latest release and restart the panel service.",
"currentPanelVersion": "Current panel version",
"latestPanelVersion": "Latest panel version",
"panelUpToDate": "Panel is up to date",
"upToDate": "Up to date",
"xrayStatusUnknown": "Unknown",
"xrayStatusRunning": "Running",
"xrayStatusStop": "Stopped",
"xrayStatusError": "Error",
"xrayErrorPopoverTitle": "An error occurred while running Xray",
"operationHours": "Uptime",
"systemHistoryTitle": "System History",
"charts": "Charts",
"xrayMetricsTitle": "Xray Metrics",
"xrayMetricsDisabled": "Xray metrics endpoint not configured",
"xrayMetricsHint": "Add a top-level metrics block to the xray config with tag metrics_out and listen 127.0.0.1:11111, then restart xray.",
"xrayObservatoryEmpty": "No observatory data yet",
"xrayObservatoryHint": "Add an observatory block to the xray config listing the outbound tags to probe, then restart xray.",
"xrayObservatoryTagPlaceholder": "Select outbound",
"xrayObservatoryAlive": "Alive",
"xrayObservatoryDead": "Down",
"xrayObservatoryLastSeen": "Last seen",
"xrayObservatoryLastTry": "Last try",
"trendLast2Min": "Last 2 minutes",
"systemLoad": "System Load",
"systemLoadDesc": "System load average for the past 1, 5, and 15 minutes",
"connectionCount": "Connection Stats",
"ipAddresses": "IP Addresses",
"toggleIpVisibility": "Toggle visibility of the IP",
"overallSpeed": "Overall Speed",
"upload": "Upload",
"download": "Download",
"totalData": "Total Data",
"sent": "Sent",
"received": "Received",
"documentation": "Documentation",
"xraySwitchVersionDialog": "Do you really want to change the Xray version?",
"xraySwitchVersionDialogDesc": "This will change the Xray version to #version#.",
"xraySwitchVersionPopover": "Xray updated successfully",
"panelUpdateDialog": "Do you really want to update the panel?",
"panelUpdateDialogDesc": "This will update 3X-UI to #version# and restart the panel service.",
"panelUpdateCheckPopover": "Panel update check failed",
"panelUpdateStartedPopover": "Panel update started",
"geofileUpdateDialog": "Do you really want to update the geofile?",
"geofileUpdateDialogDesc": "This will update the #filename# file.",
"geofilesUpdateDialogDesc": "This will update all geofiles.",
"geofilesUpdateAll": "Update all",
"geofileUpdatePopover": "Geofile updated successfully",
"customGeoTitle": "Custom GeoSite / GeoIP",
"customGeoAdd": "Add",
"customGeoType": "Type",
"customGeoAlias": "Alias",
"customGeoUrl": "URL",
"customGeoEnabled": "Enabled",
"customGeoLastUpdated": "Last updated",
"customGeoExtColumn": "Routing (ext:…)",
"customGeoToastUpdateAll": "All custom geo sources updated",
"customGeoActions": "Actions",
"customGeoEdit": "Edit",
"customGeoDelete": "Delete",
"customGeoDownload": "Update now",
"customGeoModalAdd": "Add custom geo",
"customGeoModalEdit": "Edit custom geo",
"customGeoModalSave": "Save",
"customGeoDeleteConfirm": "Delete this custom geo source?",
"customGeoRoutingHint": "In routing rules use the value column as ext:file.dat:tag (replace tag).",
"customGeoInvalidId": "Invalid resource id",
"customGeoAliasesError": "Failed to load custom geo aliases",
"customGeoValidationAlias": "Alias may only contain lowercase letters, digits, - and _",
"customGeoValidationUrl": "URL must start with http:// or https://",
"customGeoAliasPlaceholder": "a-z 0-9 _ -",
"customGeoAliasLabelSuffix": " (custom)",
"customGeoToastList": "Custom geo list",
"customGeoToastAdd": "Add custom geo",
"customGeoToastUpdate": "Update custom geo",
"customGeoToastDelete": "Custom geo file “{{ .fileName }}” deleted",
"customGeoToastDownload": "Geofile “{{ .fileName }}” updated",
"customGeoErrInvalidType": "Type must be geosite or geoip",
"customGeoErrAliasRequired": "Alias is required",
"customGeoErrAliasPattern": "Alias must match allowed characters",
"customGeoErrAliasReserved": "This alias is reserved",
"customGeoErrUrlRequired": "URL is required",
"customGeoErrInvalidUrl": "URL is invalid",
"customGeoErrUrlScheme": "URL must use http or https",
"customGeoErrUrlHost": "URL host is invalid",
"customGeoErrDuplicateAlias": "This alias is already used for this type",
"customGeoErrNotFound": "Custom geo source not found",
"customGeoErrDownload": "Download failed",
"customGeoErrUpdateAllIncomplete": "One or more custom geo sources failed to update",
"customGeoEmpty": "No custom geo sources yet — click Add to create one",
"dontRefresh": "Installation is in progress, please do not refresh this page",
"logs": "Logs",
"config": "Config",
"backup": "Backup",
"backupTitle": "Backup & Restore",
"exportDatabase": "Back Up",
"exportDatabaseDesc": "Click to download a .db file containing a backup of your current database to your device.",
"importDatabase": "Restore",
"importDatabaseDesc": "Click to select and upload a .db file from your device to restore your database from a backup.",
"importDatabaseSuccess": "The database has been successfully imported.",
"importDatabaseError": "An error occurred while importing the database.",
"readDatabaseError": "An error occurred while reading the database.",
"getDatabaseError": "An error occurred while retrieving the database.",
"getConfigError": "An error occurred while retrieving the config file."
},
"inbounds": {
"title": "Inbounds",
"totalDownUp": "Total Sent/Received",
"totalUsage": "Total Usage",
"inboundCount": "Total Inbounds",
"operate": "Menu",
"enable": "Enabled",
"remark": "Remark",
"node": "Node",
"deployTo": "Deploy to",
"localPanel": "Local panel",
"fallbacks": {
"title": "Fallbacks",
"help": "When a connection on this inbound does not match any client, route it to another inbound. Pick a child below and the routing fields (SNI / ALPN / path / xver) auto-fill from its transport — most setups need no further tweaking. Each child should listen on 127.0.0.1 with security=none.",
"empty": "No fallbacks yet",
"add": "Add fallback",
"pickInbound": "Pick an inbound",
"matchAny": "any",
"rederive": "Re-fill from child",
"rederived": "Re-filled from child",
"editAdvanced": "Edit routing fields",
"hideAdvanced": "Hide advanced",
"quickAddAll": "Quick add all eligible",
"quickAdded": "Added {n} fallback(s)",
"quickAddedNone": "No new eligible inbounds to add",
"routesWhen": "Routes when",
"defaultCatchAll": "Default — catches anything else"
},
"protocol": "Protocol",
"port": "Port",
"portMap": "Port Mapping",
"traffic": "Traffic",
"details": "Details",
"transportConfig": "Transport",
"expireDate": "Duration",
"createdAt": "Created",
"updatedAt": "Updated",
"resetTraffic": "Reset Traffic",
"addInbound": "Add Inbound",
"generalActions": "General Actions",
"modifyInbound": "Modify Inbound",
"deleteInbound": "Delete Inbound",
"deleteInboundContent": "Are you sure you want to delete this inbound?",
"deleteClient": "Delete Client",
"deleteClientContent": "Are you sure you want to delete this client?",
"resetTrafficContent": "Are you sure you want to reset traffic?",
"copyLink": "Copy URL",
"address": "Address",
"network": "Network",
"destinationPort": "Destination Port",
"targetAddress": "Target Address",
"monitorDesc": "Leave blank to listen on all IPs",
"meansNoLimit": "= Unlimited. (unit: GB)",
"totalFlow": "Total Flow",
"leaveBlankToNeverExpire": "Leave blank to never expire",
"noRecommendKeepDefault": "It is recommended to keep the default",
"certificatePath": "File Path",
"certificateContent": "File Content",
"publicKey": "Public Key",
"privatekey": "Private Key",
"clickOnQRcode": "Click on QR Code to Copy",
"client": "Client",
"export": "Export All URLs",
"clone": "Clone",
"cloneInbound": "Clone",
"cloneInboundContent": "All settings of this inbound, except Port, Listening IP, and Clients, will be applied to the clone.",
"cloneInboundOk": "Clone",
"resetAllTraffic": "Reset Traffic for All Inbounds",
"resetAllTrafficTitle": "Reset Traffic for All Inbounds",
"resetAllTrafficContent": "Are you sure you want to reset the traffic of all inbounds?",
"resetInboundClientTraffics": "Reset Clients' Traffic",
"resetInboundClientTrafficTitle": "Reset Clients' Traffic",
"resetInboundClientTrafficContent": "Are you sure you want to reset the traffic of this inbound's clients?",
"resetAllClientTraffics": "Reset All Clients' Traffic",
"resetAllClientTrafficTitle": "Reset All Clients' Traffic",
"resetAllClientTrafficContent": "Are you sure you want to reset the traffic of all clients?",
"delDepletedClients": "Delete Depleted Clients",
"delDepletedClientsTitle": "Delete Depleted Clients",
"delDepletedClientsContent": "Are you sure you want to delete all the depleted clients?",
"email": "Email",
"emailDesc": "Please provide a unique email address.",
"IPLimit": "IP Limit",
"IPLimitDesc": "Disables inbound if the count exceeds the set value. (0 = disable)",
"IPLimitlog": "IP Log",
"IPLimitlogDesc": "The IP history log. (to re-enable the inbound after disabling, clear the log)",
"IPLimitlogclear": "Clear the Log",
"setDefaultCert": "Set Cert from Panel",
"streamTab": "Stream",
"securityTab": "Security",
"sniffingTab": "Sniffing",
"sniffingMetadataOnly": "Metadata only",
"sniffingRouteOnly": "Route only",
"sniffingIpsExcluded": "IPs excluded",
"sniffingDomainsExcluded": "Domains excluded",
"decryption": "Decryption",
"encryption": "Encryption",
"vlessAuthX25519": "X25519 auth",
"vlessAuthMlkem768": "ML-KEM-768 auth",
"vlessAuthCustom": "Custom",
"vlessAuthSelected": "Selected: {auth}",
"advanced": {
"title": "Inbound JSON sections",
"subtitle": "Full inbound JSON and focused editors for settings, sniffing, and streamSettings.",
"all": "All",
"allHelp": "Full inbound object with all fields in one editor.",
"settings": "Settings",
"settingsHelp": "Xray settings block wrapper:",
"sniffing": "Sniffing",
"sniffingHelp": "Xray sniffing block wrapper:",
"stream": "Stream",
"streamHelp": "Xray stream block wrapper:",
"jsonErrorPrefix": "Advanced JSON"
},
"telegramDesc": "Please provide Telegram Chat ID. (use '/id' command in the bot) or ({'@'}userinfobot)",
"subscriptionDesc": "To find your subscription URL, navigate to the 'Details'. Additionally, you can use the same name for several clients.",
"info": "Info",
"same": "Same",
"inboundData": "Inbound's Data",
"exportInbound": "Export Inbound",
"import": "Import",
"importInbound": "Import an Inbound",
"periodicTrafficResetTitle": "Traffic Reset",
"periodicTrafficResetDesc": "Automatically reset traffic counter at specified intervals",
"lastReset": "Last Reset",
"periodicTrafficReset": {
"never": "Never",
"daily": "Daily",
"weekly": "Weekly",
"monthly": "Monthly",
"hourly": "Hourly"
},
"toasts": {
"obtain": "Obtain",
"updateSuccess": "The update was successful.",
"logCleanSuccess": "The log has been cleared.",
"inboundsUpdateSuccess": "Inbounds have been successfully updated.",
"inboundUpdateSuccess": "Inbound has been successfully updated.",
"inboundCreateSuccess": "Inbound has been successfully created.",
"inboundDeleteSuccess": "Inbound has been successfully deleted.",
"inboundClientAddSuccess": "Inbound client(s) have been added.",
"inboundClientDeleteSuccess": "Inbound client has been deleted.",
"inboundClientUpdateSuccess": "Inbound client has been updated.",
"delDepletedClientsSuccess": "All depleted clients have been deleted.",
"resetAllClientTrafficSuccess": "Traffic for all clients has been reset.",
"resetAllTrafficSuccess": "All traffic has been reset.",
"resetInboundClientTrafficSuccess": "Traffic has been reset.",
"resetInboundTrafficSuccess": "Inbound traffic has been reset.",
"trafficGetError": "Error getting traffic.",
"getNewX25519CertError": "Error while obtaining the X25519 certificate.",
"getNewmldsa65Error": "Error while obtaining mldsa65.",
"getNewVlessEncError": "Error while obtaining VlessEnc."
},
"stream": {
"general": {
"request": "Request",
"response": "Response",
"name": "Name",
"value": "Value"
},
"tcp": {
"version": "Version",
"method": "Method",
"path": "Path",
"status": "Status",
"statusDescription": "Status Desc",
"requestHeader": "Request Header",
"responseHeader": "Response Header"
}
}
},
"clients": {
"add": "Add Client",
"edit": "Edit Client",
"submitAdd": "Add Client",
"submitEdit": "Save Changes",
"clientCount": "Number of Clients",
"bulk": "Add Bulk",
"copyFromInbound": "Copy Clients from Inbound",
"copyToInbound": "Copy clients to",
"copySelected": "Copy Selected",
"copySource": "Source",
"copyEmailPreview": "Resulting email preview",
"copySelectSourceFirst": "Please select a source inbound first.",
"copyResult": "Copy result",
"copyResultSuccess": "Copied successfully",
"copyResultNone": "Nothing to copy: no clients selected or source is empty",
"copyResultErrors": "Copy errors",
"copyFlowLabel": "Flow for new clients (VLESS)",
"copyFlowHint": "Applied to all copied clients. Leave empty to skip.",
"selectAll": "Select all",
"clearAll": "Clear all",
"method": "Method",
"first": "First",
"last": "Last",
"ipLog": "IP Log",
"prefix": "Prefix",
"postfix": "Postfix",
"delayedStart": "Start After First Use",
"expireDays": "Duration",
"days": "Day(s)",
"renew": "Auto Renew",
"renewDesc": "Auto-renewal after expiration. (0 = disable)(unit: day)",
"title": "Clients",
"actions": "Actions",
"totalGB": "Total Sent/Received (GB)",
"expiryTime": "Expiry",
"addClients": "Add Clients",
"limitIp": "IP Limit",
"password": "Password",
"subId": "Subscription ID",
"online": "Online",
"email": "Email",
"comment": "Comment",
"traffic": "Traffic",
"offline": "Offline",
"addTitle": "Add Client",
"qrCode": "QR Code",
"moreInformation": "More Information",
"delete": "Delete",
"reset": "Reset Traffic",
"editTitle": "Edit Client",
"client": "Client",
"enabled": "Enabled",
"remaining": "Remaining",
"duration": "Duration",
"attachedInbounds": "Attached inbounds",
"selectInbound": "Select one or more inbounds",
"noSubId": "This client has no subId, no shareable link.",
"noLinks": "No shareable links — attach this client to a protocol-capable inbound first.",
"link": "Link",
"resetNotPossible": "Attach this client to an inbound first.",
"general": "General",
"resetAllTraffics": "Reset all client traffic",
"resetAllTrafficsTitle": "Reset all client traffic?",
"resetAllTrafficsContent": "Every client's up/down counter drops to zero. Quotas and expiry are not affected. This cannot be undone.",
"empty": "No clients yet — add one to get started.",
"deleteConfirmTitle": "Delete client {email}?",
"deleteConfirmContent": "This removes the client from every attached inbound and drops its traffic record. This cannot be undone.",
"deleteSelected": "Delete ({count})",
"bulkDeleteConfirmTitle": "Delete {count} clients?",
"bulkDeleteConfirmContent": "Each selected client is removed from every attached inbound and its traffic record is dropped. This cannot be undone.",
"delDepleted": "Delete depleted",
"delDepletedConfirmTitle": "Delete depleted clients?",
"delDepletedConfirmContent": "Removes every client whose traffic quota is exhausted or whose expiry has passed. This cannot be undone.",
"auth": "Auth",
"hysteriaAuth": "Hysteria Auth",
"uuid": "UUID",
"flow": "Flow",
"reverseTag": "Reverse tag",
"reverseTagPlaceholder": "Optional reverse tag",
"telegramId": "Telegram user ID",
"telegramIdPlaceholder": "Numeric Telegram user ID (0 = none)",
"created": "Created",
"updated": "Updated",
"ipLimit": "IP limit",
"toasts": {
"deleted": "Client deleted",
"trafficReset": "Traffic reset",
"allTrafficsReset": "All client traffic reset",
"bulkDeleted": "{count} clients deleted",
"bulkDeletedMixed": "{ok} deleted, {failed} failed",
"bulkCreated": "{count} clients created",
"bulkCreatedMixed": "{ok} created, {failed} failed",
"delDepleted": "{count} depleted clients deleted"
}
},
"nodes": {
"title": "Nodes",
"addNode": "Add Node",
"editNode": "Edit Node",
"totalNodes": "Total Nodes",
"onlineNodes": "Online",
"offlineNodes": "Offline",
"avgLatency": "Avg Latency",
"name": "Name",
"namePlaceholder": "e.g. de-frankfurt-1",
"addressPlaceholder": "panel.example.com or 1.2.3.4",
"remark": "Remark",
"scheme": "Scheme",
"address": "Address",
"port": "Port",
"basePath": "Base Path",
"apiToken": "API Token",
"apiTokenPlaceholder": "Token from the remote panel's Settings page",
"apiTokenHint": "The remote panel exposes its API token under Settings → API Token.",
"regenerate": "Regenerate Token",
"regenerateConfirm": "Regenerating invalidates the current token. Any central panel using it will lose access until updated. Continue?",
"allowPrivateAddress": "Allow private address",
"allowPrivateAddressHint": "Enable only for nodes on a private network or VPN.",
"enable": "Enabled",
"status": "Status",
"cpu": "CPU",
"mem": "Memory",
"uptime": "Uptime",
"latency": "Latency",
"lastHeartbeat": "Last Heartbeat",
"xrayVersion": "Xray Version",
"panelVersion": "Panel Version",
"actions": "Actions",
"probe": "Probe Now",
"testConnection": "Test Connection",
"connectionOk": "Connection OK ({ms} ms)",
"connectionFailed": "Connection failed",
"never": "never",
"justNow": "just now",
"deleteConfirmTitle": "Delete node \"{name}\"?",
"deleteConfirmContent": "This stops monitoring the node. The remote panel itself is unaffected.",
"statusValues": {
"online": "Online",
"offline": "Offline",
"unknown": "Unknown"
},
"toasts": {
"list": "Failed to load nodes",
"obtain": "Failed to load node",
"add": "Add node",
"update": "Update node",
"delete": "Delete node",
"deleted": "Node deleted",
"test": "Test connection",
"fillRequired": "Name, address, port and API token are required",
"probeFailed": "Probe failed"
}
},
"settings": {
"title": "Panel Settings",
"save": "Save",
"infoDesc": "Every change made here needs to be saved. Please restart the panel to apply changes.",
"restartPanel": "Restart Panel",
"restartPanelDesc": "Are you sure you want to restart the panel? If you cannot access the panel after restarting, please view the panel log info on the server.",
"restartPanelSuccess": "The panel was successfully restarted.",
"actions": "Actions",
"resetDefaultConfig": "Reset to Default",
"panelSettings": "General",
"securitySettings": "Authentication",
"TGBotSettings": "Telegram Bot",
"panelListeningIP": "Listen IP",
"panelListeningIPDesc": "The IP address for the web panel. (leave blank to listen on all IPs)",
"panelListeningDomain": "Listen Domain",
"panelListeningDomainDesc": "The domain name for the web panel. (leave blank to listen on all domains and IPs)",
"panelPort": "Listen Port",
"panelPortDesc": "The port number for the web panel. (must be an unused port)",
"publicKeyPath": "Public Key Path",
"publicKeyPathDesc": "The public key file path for the web panel. (begins with /)",
"privateKeyPath": "Private Key Path",
"privateKeyPathDesc": "The private key file path for the web panel. (begins with /)",
"panelUrlPath": "URI Path",
"panelUrlPathDesc": "The URI path for the web panel. (begins with / and concludes with /)",
"pageSize": "Pagination Size",
"pageSizeDesc": "Define page size for inbounds table. (0 = disable)",
"remarkModel": "Remark Model & Separation Character",
"datepicker": "Calendar Type",
"datepickerPlaceholder": "Select date",
"datepickerDescription": "Scheduled tasks will run based on this calendar.",
"sampleRemark": "Sample Remark",
"oldUsername": "Current Username",
"currentPassword": "Current Password",
"newUsername": "New Username",
"newPassword": "New Password",
"telegramBotEnable": "Enable Telegram Bot",
"telegramBotEnableDesc": "Enables the Telegram bot.",
"telegramToken": "Telegram Token",
"telegramTokenDesc": "The Telegram bot token obtained from '{'@'}BotFather'.",
"telegramProxy": "SOCKS Proxy",
"telegramProxyDesc": "Enables SOCKS5 proxy for connecting to Telegram. (adjust settings as per guide)",
"telegramAPIServer": "Telegram API Server",
"telegramAPIServerDesc": "The Telegram API server to use. Leave blank to use the default server.",
"telegramChatId": "Admin Chat ID",
"telegramChatIdDesc": "The Telegram Admin Chat ID(s). (comma-separated)(get it here {'@'}userinfobot) or (use '/id' command in the bot)",
"telegramNotifyTime": "Notification Time",
"telegramNotifyTimeDesc": "The Telegram bot notification time set for periodic reports. (use the crontab time format)",
"tgNotifyBackup": "Database Backup",
"tgNotifyBackupDesc": "Send a database backup file with a report.",
"tgNotifyLogin": "Login Notification",
"tgNotifyLoginDesc": "Get notified about the username, IP address, and time whenever someone attempts to log into your web panel.",
"sessionMaxAge": "Session Duration",
"sessionMaxAgeDesc": "The duration for which you can stay logged in. (unit: minute)",
"expireTimeDiff": "Expiration Date Notification",
"expireTimeDiffDesc": "Get notified about expiration date when reaching this threshold. (unit: day)",
"trafficDiff": "Traffic Cap Notification",
"trafficDiffDesc": "Get notified about traffic cap when reaching this threshold. (unit: GB)",
"tgNotifyCpu": "CPU Load Notification",
"tgNotifyCpuDesc": "Get notified if CPU load exceeds this threshold. (unit: %)",
"timeZone": "Time Zone",
"timeZoneDesc": "Scheduled tasks will run based on this time zone.",
"subSettings": "Subscription",
"subEnable": "Subscription Service",
"subEnableDesc": "Enable/Disable the subscription service.",
"subJsonEnable": "Enable/Disable the JSON subscription endpoint independently.",
"subTitle": "Subscription Title",
"subTitleDesc": "Title shown in VPN client",
"subSupportUrl": "Support URL",
"subSupportUrlDesc": "Technical support link shown in the VPN client",
"subProfileUrl": "Profile URL",
"subProfileUrlDesc": "A link to your website displayed in the VPN client",
"subAnnounce": "Announce",
"subAnnounceDesc": "The announcement text displayed in the VPN client",
"subEnableRouting": "Enable routing",
"subEnableRoutingDesc": "Global setting to enable routing in the VPN client. (Only for Happ)",
"subRoutingRules": "Routing rules",
"subRoutingRulesDesc": "Global routing rules for the VPN client. (Only for Happ)",
"subListen": "Listen IP",
"subListenDesc": "The IP address for the subscription service. (leave blank to listen on all IPs)",
"subPort": "Listen Port",
"subPortDesc": "The port number for the subscription service. (must be an unused port)",
"subCertPath": "Public Key Path",
"subCertPathDesc": "The public key file path for the subscription service. (begins with /)",
"subKeyPath": "Private Key Path",
"subKeyPathDesc": "The private key file path for the subscription service. (begins with /)",
"subPath": "URI Path",
"subPathDesc": "The URI path for the subscription service. (begins with / and concludes with /)",
"subDomain": "Listen Domain",
"subDomainDesc": "The domain name for the subscription service. (leave blank to listen on all domains and IPs)",
"subUpdates": "Update Intervals",
"subUpdatesDesc": "The update intervals of the subscription URL in the client apps. (unit: hour)",
"subEncrypt": "Encode",
"subEncryptDesc": "The returned content of subscription service will be Base64 encoded.",
"subShowInfo": "Show Usage Info",
"subShowInfoDesc": "The remaining traffic and date will be displayed in the client apps.",
"subEmailInRemark": "Include Email in Name",
"subEmailInRemarkDesc": "Include the client email in the subscription profile name.",
"subURI": "Reverse Proxy URI",
"subURIDesc": "The URI path of the subscription URL for use behind proxies.",
"externalTrafficInformEnable": "External Traffic Inform",
"externalTrafficInformEnableDesc": "Inform external API on every traffic update.",
"externalTrafficInformURI": "External Traffic Inform URI",
"externalTrafficInformURIDesc": "Traffic updates are sent to this URI.",
"restartXrayOnClientDisable": "Restart Xray After Auto Disable",
"restartXrayOnClientDisableDesc": "When a client is automatically disabled due to expiration or traffic limit, restart Xray.",
"fragment": "Fragmentation",
"fragmentDesc": "Enable fragmentation for TLS hello packet.",
"fragmentSett": "Fragmentation Settings",
"noisesDesc": "Enable Noises.",
"noisesSett": "Noises Settings",
"mux": "Mux",
"muxDesc": "Transmit multiple independent data streams within an established data stream.",
"muxSett": "Mux Settings",
"direct": "Direct Connection",
"directDesc": "Directly establishes connections with domains or IP ranges of a specific country.",
"notifications": "Notifications",
"certs": "Certificates",
"externalTraffic": "External Traffic",
"dateAndTime": "Date and Time",
"proxyAndServer": "Proxy and Server",
"intervals": "Intervals",
"information": "Information",
"language": "Language",
"telegramBotLanguage": "Telegram Bot Language",
"security": {
"admin": "Admin credentials",
"twoFactor": "Two-factor authentication",
"twoFactorEnable": "Enable 2FA",
"twoFactorEnableDesc": "Adds an additional layer of authentication to provide more security.",
"twoFactorModalSetTitle": "Enable two-factor authentication",
"twoFactorModalDeleteTitle": "Disable two-factor authentication",
"twoFactorModalSteps": "To set up two-factor authentication, perform a few steps:",
"twoFactorModalFirstStep": "1. Scan this QR code in the app for authentication or copy the token near the QR code and paste it into the app",
"twoFactorModalSecondStep": "2. Enter the code from the app",
"twoFactorModalRemoveStep": "Enter the code from the application to remove two-factor authentication.",
"twoFactorModalChangeCredentialsTitle": "Change credentials",
"twoFactorModalChangeCredentialsStep": "Enter the code from the application to change administrator credentials.",
"twoFactorModalSetSuccess": "Two-factor authentication has been successfully established",
"twoFactorModalDeleteSuccess": "Two-factor authentication has been successfully deleted",
"twoFactorModalError": "Wrong code",
"show": "Show",
"hide": "Hide",
"apiTokenNew": "New token",
"apiTokenName": "Name",
"apiTokenNamePlaceholder": "e.g. central-panel-a",
"apiTokenNameRequired": "Name is required",
"apiTokenEmpty": "No tokens yet — create one to authenticate bots or remote panels.",
"apiTokenDeleteWarning": "Any caller using this token will stop authenticating immediately."
},
"toasts": {
"modifySettings": "The parameters have been changed.",
"getSettings": "An error occurred while retrieving parameters.",
"modifyUserError": "An error occurred while changing administrator credentials.",
"modifyUser": "You have successfully changed the credentials of the administrator.",
"originalUserPassIncorrect": "The current username or password is invalid",
"userPassMustBeNotEmpty": "The new username and password are empty",
"getOutboundTrafficError": "Error getting traffic",
"resetOutboundTrafficError": "Error resetting outbound traffic"
}
},
"xray": {
"title": "Xray Configs",
"save": "Save",
"restart": "Restart Xray",
"restartSuccess": "Xray has been successfully relaunched.",
"stopSuccess": "Xray has been successfully stopped.",
"restartError": "There was an error when rebooting the Xray.",
"stopError": "There was an error when stopping the Xray.",
"basicTemplate": "Basics",
"advancedTemplate": "Advanced",
"generalConfigs": "General",
"generalConfigsDesc": "These options will determine general adjustments.",
"logConfigs": "Log",
"logConfigsDesc": "Logs may affect your server's efficiency. It is recommended to enable them wisely only when needed.",
"blockConfigsDesc": "These options will block traffic based on specific requested protocols and websites.",
"basicRouting": "Basic Routing",
"blockConnectionsConfigsDesc": "These options will block traffic based on the specific requested country.",
"directConnectionsConfigsDesc": "A direct connection ensures that specific traffic is not routed through another server.",
"blockips": "Block IPs",
"blockdomains": "Block Domains",
"directips": "Direct IPs",
"directdomains": "Direct Domains",
"ipv4Routing": "IPv4 Routing",
"ipv4RoutingDesc": "These options will route traffic based on a specific destination via IPv4.",
"warpRouting": "WARP Routing",
"warpRoutingDesc": "These options will route traffic based on a specific destination via WARP.",
"nordRouting": "NordVPN Routing",
"nordRoutingDesc": "These options will route traffic based on a specific destination via NordVPN.",
"Template": "Advanced Xray Configuration Template",
"TemplateDesc": "The final Xray config file will be generated based on this template.",
"FreedomStrategy": "Freedom Protocol Strategy",
"FreedomStrategyDesc": "Set the output strategy for the network in the Freedom Protocol.",
"RoutingStrategy": "Overall Routing Strategy",
"RoutingStrategyDesc": "Set the overall traffic routing strategy for resolving all requests.",
"outboundTestUrl": "Outbound Test URL",
"outboundTestUrlDesc": "URL used when testing outbound connectivity.",
"Torrent": "Block BitTorrent Protocol",
"Inbounds": "Inbounds",
"InboundsDesc": "Accepting the specific clients.",
"Outbounds": "Outbounds",
"Balancers": "Balancers",
"OutboundsDesc": "Set the outgoing traffic pathway.",
"Routings": "Routing Rules",
"RoutingsDesc": "The priority of each rule is important!",
"completeTemplate": "All",
"logLevel": "Log Level",
"logLevelDesc": "The log level for error logs, indicating the information that needs to be recorded.",
"accessLog": "Access Log",
"accessLogDesc": "The file path for the access log. The special value 'none' disables access logs",
"errorLog": "Error Log",
"errorLogDesc": "The file path for the error log. The special value 'none' disables error logs",
"dnsLog": "DNS Log",
"dnsLogDesc": "Whether to enable DNS query logs",
"maskAddress": "Mask Address",
"maskAddressDesc": "IP address mask, when enabled, will automatically replace the IP address that appears in the log.",
"statistics": "Statistics",
"statsInboundUplink": "Inbound Upload Statistics",
"statsInboundUplinkDesc": "Enables the statistics collection for upstream traffic of all inbound proxies.",
"statsInboundDownlink": "Inbound Download Statistics",
"statsInboundDownlinkDesc": "Enables the statistics collection for downstream traffic of all inbound proxies.",
"statsOutboundUplink": "Outbound Upload Statistics",
"statsOutboundUplinkDesc": "Enables the statistics collection for upstream traffic of all outbound proxies.",
"statsOutboundDownlink": "Outbound Download Statistics",
"statsOutboundDownlinkDesc": "Enables the statistics collection for downstream traffic of all outbound proxies.",
"rules": {
"first": "First",
"last": "Last",
"up": "Up",
"down": "Down",
"source": "Source",
"dest": "Destination",
"inbound": "Inbound",
"outbound": "Outbound",
"balancer": "Balancer",
"info": "Info",
"add": "Add Rule",
"edit": "Edit Rule",
"useComma": "Comma-separated items"
},
"outbound": {
"addOutbound": "Add Outbound",
"addReverse": "Add Reverse",
"editOutbound": "Edit Outbound",
"editReverse": "Edit Reverse",
"reverseTag": "Reverse Tag",
"reverseTagDesc": "VLESS simple reverse proxy tag. Leave empty to disable.",
"reverseTagPlaceholder": "reverse tag (leave empty to disable)",
"tag": "Tag",
"tagDesc": "Unique Tag",
"address": "Address",
"reverse": "Reverse",
"domain": "Domain",
"type": "Type",
"bridge": "Bridge",
"portal": "Portal",
"link": "Link",
"intercon": "Interconnection",
"settings": "Settings",
"accountInfo": "Account Information",
"outboundStatus": "Outbound Status",
"sendThrough": "Send Through",
"test": "Test",
"testResult": "Test Result",
"testing": "Testing connection...",
"testSuccess": "Test successful",
"testFailed": "Test failed",
"testError": "Failed to test outbound",
"nordvpn": "NordVPN",
"accessToken": "Access Token",
"country": "Country",
"server": "Server",
"city": "City",
"allCities": "All Cities",
"privateKey": "Private Key",
"load": "Load"
},
"balancer": {
"addBalancer": "Add Balancer",
"editBalancer": "Edit Balancer",
"balancerStrategy": "Strategy",
"balancerSelectors": "Selectors",
"tag": "Tag",
"tagDesc": "Unique Tag",
"balancerDesc": "It is not possible to use balancerTag and outboundTag at the same time. If used at the same time, only outboundTag will work."
},
"wireguard": {
"secretKey": "Secret Key",
"publicKey": "Public Key",
"allowedIPs": "Allowed IPs",
"endpoint": "Endpoint",
"psk": "PreShared Key",
"domainStrategy": "Domain Strategy"
},
"tun": {
"nameDesc": "The name of the TUN interface. Default is 'xray0'",
"mtuDesc": "Maximum Transmission Unit. The maximum size of data packets. Default is 1500",
"userLevel": "User Level",
"userLevelDesc": "All connections made through this inbound will use this user level. Default is 0"
},
"dns": {
"enable": "Enable DNS",
"enableDesc": "Enable built-in DNS server",
"tag": "DNS Inbound Tag",
"tagDesc": "This tag will be available as an Inbound tag in routing rules.",
"clientIp": "Client IP",
"clientIpDesc": "Used to notify the server of the specified IP location during DNS queries",
"disableCache": "Disable cache",
"disableCacheDesc": "Disables DNS caching",
"disableFallback": "Disable Fallback",
"disableFallbackDesc": "Disables fallback DNS queries",
"disableFallbackIfMatch": "Disable Fallback If Match",
"disableFallbackIfMatchDesc": "Disables fallback DNS queries when the matching domain list of the DNS server is hit",
"enableParallelQuery": "Enable Parallel Query",
"enableParallelQueryDesc": "Enable parallel DNS queries to multiple servers for faster resolution",
"strategy": "Query Strategy",
"strategyDesc": "Overall strategy to resolve domain names",
"add": "Add Server",
"edit": "Edit Server",
"domains": "Domains",
"expectIPs": "Expect IPs",
"unexpectIPs": "Unexpected IPs",
"useSystemHosts": "Use System Hosts",
"useSystemHostsDesc": "Use the operating system's hosts file",
"serveStale": "Serve Stale",
"serveStaleDesc": "Return expired cached results while refreshing in the background",
"serveExpiredTTL": "Serve Expired TTL",
"serveExpiredTTLDesc": "Validity (seconds) of stale cache entries; 0 = never expire",
"timeoutMs": "Timeout (ms)",
"skipFallback": "Skip Fallback",
"finalQuery": "Final Query",
"hosts": "Hosts",
"hostsAdd": "Add Host",
"hostsEmpty": "No host overrides defined",
"hostsDomain": "Domain (e.g. domain:example.com)",
"hostsValues": "IP or domain — type and press Enter",
"usePreset": "Use Preset",
"dnsPresetTitle": "DNS Presets",
"dnsPresetFamily": "Family",
"clearAll": "Delete All",
"clearAllTitle": "Delete all DNS servers?",
"clearAllConfirm": "This removes every DNS server from the list. This cannot be undone."
},
"fakedns": {
"add": "Add Fake DNS",
"edit": "Edit Fake DNS",
"ipPool": "IP Pool Subnet",
"poolSize": "Pool Size"
}
}
},
"tgbot": {
"keyboardClosed": "❌ Custom keyboard closed!",
"noResult": "❗ No result!",
"noQuery": "❌ Query not found! Please use the command again!",
"wentWrong": "❌ Something went wrong!",
"noIpRecord": "❗ No IP Record!",
"noInbounds": "❗ No inbound found!",
"unlimited": "♾ Unlimited(Reset)",
"add": "Add",
"month": "Month",
"months": "Months",
"day": "Day",
"days": "Days",
"hours": "Hours",
"minutes": "Minutes",
"unknown": "Unknown",
"inbounds": "Inbounds",
"clients": "Clients",
"offline": "🔴 Offline",
"online": "🟢 Online",
"commands": {
"unknown": "❗ Unknown command.",
"pleaseChoose": "👇 Please choose:\r\n",
"help": "🤖 Welcome to this bot! It's designed to offer specific data from the web panel and allows you to make modifications as needed.\r\n\r\n",
"start": "👋 Hello <i>{{ .Firstname }}</i>.\r\n",
"welcome": "🤖 Welcome to <b>{{ .Hostname }}</b> management bot.\r\n",
"status": "✅ Bot is OK!",
"usage": "❗ Please provide a text to search!",
"getID": "🆔 Your ID: <code>{{ .ID }}</code>",
"helpAdminCommands": "To restart Xray Core:\r\n<code>/restart</code>\r\n\r\nTo search for a client email:\r\n<code>/usage [Email]</code>\r\n\r\nTo search for inbounds (with client stats):\r\n<code>/inbound [Remark]</code>\r\n\r\nTelegram Chat ID:\r\n<code>/id</code>",
"helpClientCommands": "To search for statistics, use the following command:\r\n\r\n<code>/usage [Email]</code>\r\n\r\nTelegram Chat ID:\r\n<code>/id</code>",
"restartUsage": "\r\n\r\n<code>/restart</code>",
"restartSuccess": "✅ Operation successful!",
"restartFailed": "❗ Error in operation.\r\n\r\n<code>Error: {{ .Error }}</code>.",
"xrayNotRunning": "❗ Xray Core is not running.",
"startDesc": "Show the main menu",
"helpDesc": "Bot help",
"statusDesc": "Check bot status",
"idDesc": "Show your Telegram ID"
},
"messages": {
"cpuThreshold": "🔴 CPU Load {{ .Percent }}% exceeds the threshold of {{ .Threshold }}%",
"selectUserFailed": "❌ Error in user selection!",
"userSaved": "✅ Telegram User saved.",
"loginSuccess": "✅ Logged in to the panel successfully.\r\n",
"loginFailed": "❗Login attempt to the panel failed.\r\n",
"2faFailed": "2FA Failed",
"report": "🕰 Scheduled Reports: {{ .RunTime }}\r\n",
"datetime": "⏰ Date&Time: {{ .DateTime }}\r\n",
"hostname": "💻 Host: {{ .Hostname }}\r\n",
"version": "🚀 3X-UI Version: {{ .Version }}\r\n",
"xrayVersion": "📡 Xray Version: {{ .XrayVersion }}\r\n",
"ipv6": "🌐 IPv6: {{ .IPv6 }}\r\n",
"ipv4": "🌐 IPv4: {{ .IPv4 }}\r\n",
"ip": "🌐 IP: {{ .IP }}\r\n",
"ips": "🔢 IPs:\r\n{{ .IPs }}\r\n",
"serverUpTime": "⏳ Uptime: {{ .UpTime }} {{ .Unit }}\r\n",
"serverLoad": "📈 System Load: {{ .Load1 }}, {{ .Load2 }}, {{ .Load3 }}\r\n",
"serverMemory": "📋 RAM: {{ .Current }}/{{ .Total }}\r\n",
"tcpCount": "🔹 TCP: {{ .Count }}\r\n",
"udpCount": "🔸 UDP: {{ .Count }}\r\n",
"traffic": "🚦 Traffic: {{ .Total }} (↑{{ .Upload }},↓{{ .Download }})\r\n",
"xrayStatus": " Status: {{ .State }}\r\n",
"username": "👤 Username: {{ .Username }}\r\n",
"reason": "❗️ Reason: {{ .Reason }}\r\n",
"time": "⏰ Time: {{ .Time }}\r\n",
"inbound": "📍 Inbound: {{ .Remark }}\r\n",
"port": "🔌 Port: {{ .Port }}\r\n",
"expire": "📅 Expire Date: {{ .Time }}\r\n",
"expireIn": "📅 Expire In: {{ .Time }}\r\n",
"active": "💡 Active: {{ .Enable }}\r\n",
"enabled": "🚨 Enabled: {{ .Enable }}\r\n",
"online": "🌐 Connection status: {{ .Status }}\r\n",
"lastOnline": "🔙 Last online: {{ .Time }}\r\n",
"email": "📧 Email: {{ .Email }}\r\n",
"upload": "🔼 Upload: ↑{{ .Upload }}\r\n",
"download": "🔽 Download: ↓{{ .Download }}\r\n",
"total": "📊 Total: ↑↓{{ .UpDown }} / {{ .Total }}\r\n",
"TGUser": "👤 Telegram User: {{ .TelegramID }}\r\n",
"exhaustedMsg": "🚨 Exhausted {{ .Type }}:\r\n",
"exhaustedCount": "🚨 Exhausted {{ .Type }} count:\r\n",
"onlinesCount": "🌐 Online Clients: {{ .Count }}\r\n",
"disabled": "🛑 Disabled: {{ .Disabled }}\r\n",
"depleteSoon": "🔜 Deplete Soon: {{ .Deplete }}\r\n\r\n",
"backupTime": "🗄 Backup Time: {{ .Time }}\r\n",
"refreshedOn": "\r\n📋🔄 Refreshed On: {{ .Time }}\r\n\r\n",
"yes": "✅ Yes",
"no": "❌ No",
"received_id": "🔑📥 ID updated.",
"received_password": "🔑📥 Password updated.",
"received_email": "📧📥 Email updated.",
"received_comment": "💬📥 Comment updated.",
"id_prompt": "🔑 Default ID: {{ .ClientId }}\n\nEnter your ID.",
"pass_prompt": "🔑 Default Password: {{ .ClientPassword }}\n\nEnter your password.",
"email_prompt": "📧 Default Email: {{ .ClientEmail }}\n\nEnter your email.",
"comment_prompt": "💬 Default Comment: {{ .ClientComment }}\n\nEnter your comment.",
"inbound_client_data_id": "🔄 Inbound: {{ .InboundRemark }}\n\n🔑 ID: {{ .ClientId }}\n📧 Email: {{ .ClientEmail }}\n📊 Traffic: {{ .ClientTraffic }}\n📅 Expire Date: {{ .ClientExp }}\n🌐 IP Limit: {{ .IpLimit }}\n💬 Comment: {{ .ClientComment }}\n\nYou can add the client to inbound now!",
"inbound_client_data_pass": "🔄 Inbound: {{ .InboundRemark }}\n\n🔑 Password: {{ .ClientPass }}\n📧 Email: {{ .ClientEmail }}\n📊 Traffic: {{ .ClientTraffic }}\n📅 Expire Date: {{ .ClientExp }}\n🌐 IP Limit: {{ .IpLimit }}\n💬 Comment: {{ .ClientComment }}\n\nYou can add the client to inbound now!",
"cancel": "❌ Process Canceled! \n\nYou can /start again anytime. 🔄",
"error_add_client": "⚠️ Error:\n\n {{ .error }}",
"using_default_value": "Okay, I'll stick with the default value. 😊",
"incorrect_input": "Your input is not valid.\nThe phrases should be continuous without spaces.\nCorrect example: aaaaaa\nIncorrect example: aaa aaa 🚫",
"AreYouSure": "Are you sure? 🤔",
"SuccessResetTraffic": "📧 Email: {{ .ClientEmail }}\n🏁 Result: ✅ Success",
"FailedResetTraffic": "📧 Email: {{ .ClientEmail }}\n🏁 Result: ❌ Failed \n\n🛠 Error: [ {{ .ErrorMessage }} ]",
"FinishProcess": "🔚 Traffic reset process finished for all clients."
},
"buttons": {
"closeKeyboard": "❌ Close Keyboard",
"cancel": "❌ Cancel",
"cancelReset": "❌ Cancel Reset",
"cancelIpLimit": "❌ Cancel IP Limit",
"confirmResetTraffic": "✅ Confirm Reset Traffic?",
"confirmClearIps": "✅ Confirm Clear IPs?",
"confirmRemoveTGUser": "✅ Confirm Remove Telegram User?",
"confirmToggle": "✅ Confirm Enable/Disable User?",
"dbBackup": "Get DB Backup",
"serverUsage": "Server Usage",
"getInbounds": "Get Inbounds",
"depleteSoon": "Deplete Soon",
"clientUsage": "Get Usage",
"onlines": "Online Clients",
"commands": "Commands",
"refresh": "🔄 Refresh",
"clearIPs": "❌ Clear IPs",
"removeTGUser": "❌ Remove Telegram User",
"selectTGUser": "👤 Select Telegram User",
"selectOneTGUser": "👤 Select a Telegram User:",
"resetTraffic": "📈 Reset Traffic",
"resetExpire": "📅 Change Expiry Date",
"ipLog": "🔢 IP Log",
"ipLimit": "🔢 IP Limit",
"setTGUser": "👤 Set Telegram User",
"toggle": "🔘 Enable / Disable",
"custom": "🔢 Custom",
"confirmNumber": "✅ Confirm: {{ .Num }}",
"confirmNumberAdd": "✅ Confirm adding: {{ .Num }}",
"limitTraffic": "🚧 Traffic Limit",
"getBanLogs": "Get Ban Logs",
"allClients": "All Clients",
"addClient": "Add Client",
"submitDisable": "Submit As Disable ☑️",
"submitEnable": "Submit As Enable ✅",
"use_default": "🏷️ Use default",
"change_id": "⚙️🔑 ID",
"change_password": "⚙️🔑 Password",
"change_email": "⚙️📧 Email",
"change_comment": "⚙️💬 Comment",
"change_flow": "⚙️🚦 Flow",
"ResetAllTraffics": "Reset All Traffic",
"SortedTrafficUsageReport": "Sorted Traffic Usage Report"
},
"answers": {
"successfulOperation": "✅ Operation successful!",
"errorOperation": "❗ Error in operation.",
"getInboundsFailed": "❌ Failed to get inbounds.",
"getClientsFailed": "❌ Failed to get clients.",
"canceled": "❌ {{ .Email }}: Operation canceled.",
"clientRefreshSuccess": "✅ {{ .Email }}: Client refreshed successfully.",
"IpRefreshSuccess": "✅ {{ .Email }}: IPs refreshed successfully.",
"TGIdRefreshSuccess": "✅ {{ .Email }}: Client's Telegram User refreshed successfully.",
"resetTrafficSuccess": "✅ {{ .Email }}: Traffic reset successfully.",
"setTrafficLimitSuccess": "✅ {{ .Email }}: Traffic limit saved successfully.",
"expireResetSuccess": "✅ {{ .Email }}: Expire days reset successfully.",
"resetIpSuccess": "✅ {{ .Email }}: IP limit {{ .Count }} saved successfully.",
"clearIpSuccess": "✅ {{ .Email }}: IPs cleared successfully.",
"getIpLog": "✅ {{ .Email }}: Get IP Log.",
"getUserInfo": "✅ {{ .Email }}: Get Telegram User Info.",
"removedTGUserSuccess": "✅ {{ .Email }}: Telegram User removed successfully.",
"enableSuccess": "✅ {{ .Email }}: Enabled successfully.",
"disableSuccess": "✅ {{ .Email }}: Disabled successfully.",
"askToAddUserId": "Your configuration is not found!\r\nPlease ask your admin to use your Telegram ChatID in your configuration(s).\r\n\r\nYour ChatID: <code>{{ .TgUserID }}</code>",
"chooseClient": "Choose a Client for Inbound {{ .Inbound }}",
"chooseInbound": "Choose an Inbound"
}
}
}