mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-05-31 18:24:10 +00:00
* 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
1093 lines
No EOL
54 KiB
JSON
1093 lines
No EOL
54 KiB
JSON
{
|
||
"username": "Nama Pengguna",
|
||
"password": "Kata Sandi",
|
||
"login": "Masuk",
|
||
"confirm": "Konfirmasi",
|
||
"cancel": "Batal",
|
||
"close": "Tutup",
|
||
"save": "Simpan",
|
||
"logout": "Keluar",
|
||
"create": "Buat",
|
||
"update": "Perbarui",
|
||
"copy": "Salin",
|
||
"copied": "Tersalin",
|
||
"download": "Unduh",
|
||
"remark": "Catatan",
|
||
"enable": "Aktifkan",
|
||
"protocol": "Protokol",
|
||
"search": "Cari",
|
||
"filter": "Filter",
|
||
"loading": "Memuat...",
|
||
"refresh": "Segarkan",
|
||
"clear": "Bersihkan",
|
||
"second": "Detik",
|
||
"minute": "Menit",
|
||
"hour": "Jam",
|
||
"day": "Hari",
|
||
"check": "Centang",
|
||
"indefinite": "Tak Terbatas",
|
||
"unlimited": "Tanpa Batas",
|
||
"none": "None",
|
||
"qrCode": "Kode QR",
|
||
"info": "Informasi Lebih Lanjut",
|
||
"edit": "Edit",
|
||
"delete": "Hapus",
|
||
"reset": "Reset",
|
||
"noData": "Tidak ada data.",
|
||
"copySuccess": "Berhasil Disalin",
|
||
"sure": "Yakin",
|
||
"encryption": "Enkripsi",
|
||
"useIPv4ForHost": "Gunakan IPv4 untuk host",
|
||
"transmission": "Transmisi",
|
||
"host": "Host",
|
||
"path": "Jalur",
|
||
"camouflage": "Obfuscation",
|
||
"status": "Status",
|
||
"enabled": "Aktif",
|
||
"disabled": "Nonaktif",
|
||
"depleted": "Habis",
|
||
"depletingSoon": "Akan Habis",
|
||
"offline": "Offline",
|
||
"online": "Online",
|
||
"domainName": "Nama Domain",
|
||
"monitor": "IP Pemantauan",
|
||
"certificate": "Sertifikat Digital",
|
||
"fail": "Gagal",
|
||
"comment": "Komentar",
|
||
"success": "Berhasil",
|
||
"lastOnline": "Terakhir online",
|
||
"getVersion": "Dapatkan Versi",
|
||
"install": "Instal",
|
||
"clients": "Klien",
|
||
"usage": "Penggunaan",
|
||
"twoFactorCode": "Kode",
|
||
"remained": "Tersisa",
|
||
"security": "Keamanan",
|
||
"secAlertTitle": "Peringatan keamanan",
|
||
"secAlertSsl": "Koneksi ini tidak aman. Harap hindari memasukkan informasi sensitif sampai TLS diaktifkan untuk perlindungan data.",
|
||
"secAlertConf": "Beberapa pengaturan rentan terhadap serangan. Disarankan untuk memperkuat protokol keamanan guna mencegah pelanggaran potensial.",
|
||
"secAlertSSL": "Panel kekurangan koneksi yang aman. Harap instal sertifikat TLS untuk perlindungan data.",
|
||
"secAlertPanelPort": "Port default panel rentan. Harap konfigurasi port acak atau tertentu.",
|
||
"secAlertPanelURI": "Jalur URI default panel tidak aman. Harap konfigurasi jalur URI kompleks.",
|
||
"secAlertSubURI": "Jalur URI default langganan tidak aman. Harap konfigurasi jalur URI kompleks.",
|
||
"secAlertSubJsonURI": "Jalur URI default JSON langganan tidak aman. Harap konfigurasikan jalur URI kompleks.",
|
||
"emptyDnsDesc": "Tidak ada server DNS yang ditambahkan.",
|
||
"emptyFakeDnsDesc": "Tidak ada server Fake DNS yang ditambahkan.",
|
||
"emptyBalancersDesc": "Tidak ada penyeimbang yang ditambahkan.",
|
||
"emptyReverseDesc": "Tidak ada proxy terbalik yang ditambahkan.",
|
||
"somethingWentWrong": "Terjadi kesalahan",
|
||
"subscription": {
|
||
"title": "Info langganan",
|
||
"subId": "ID langganan",
|
||
"status": "Status",
|
||
"downloaded": "Diunduh",
|
||
"uploaded": "Diunggah",
|
||
"expiry": "Kedaluwarsa",
|
||
"totalQuota": "Kuota total",
|
||
"individualLinks": "Tautan individual",
|
||
"active": "Aktif",
|
||
"inactive": "Nonaktif",
|
||
"unlimited": "Tanpa batas",
|
||
"noExpiry": "Tanpa kedaluwarsa"
|
||
},
|
||
"menu": {
|
||
"theme": "Tema",
|
||
"dark": "Gelap",
|
||
"ultraDark": "Sangat Gelap",
|
||
"dashboard": "Ikhtisar",
|
||
"inbounds": "Masuk",
|
||
"clients": "Klien",
|
||
"nodes": "Node",
|
||
"settings": "Pengaturan Panel",
|
||
"xray": "Konfigurasi Xray",
|
||
"apiDocs": "Dokumentasi API",
|
||
"logout": "Keluar",
|
||
"link": "Kelola",
|
||
"donate": "Donasi"
|
||
},
|
||
"pages": {
|
||
"login": {
|
||
"hello": "Halo",
|
||
"title": "Selamat Datang",
|
||
"loginAgain": "Sesi Anda telah berakhir, harap masuk kembali",
|
||
"toasts": {
|
||
"invalidFormData": "Format data input tidak valid.",
|
||
"emptyUsername": "Nama Pengguna diperlukan",
|
||
"emptyPassword": "Kata Sandi diperlukan",
|
||
"wrongUsernameOrPassword": "Username, kata sandi, atau kode dua faktor tidak valid.",
|
||
"successLogin": "Anda telah berhasil masuk ke akun Anda."
|
||
}
|
||
},
|
||
"index": {
|
||
"title": "Ikhtisar",
|
||
"cpu": "CPU",
|
||
"logicalProcessors": "Prosesor logis",
|
||
"frequency": "Frekuensi",
|
||
"swap": "Swap",
|
||
"storage": "Penyimpanan",
|
||
"memory": "RAM",
|
||
"threads": "Thread",
|
||
"xrayStatus": "Xray",
|
||
"stopXray": "Stop",
|
||
"restartXray": "Restart",
|
||
"xraySwitch": "Versi",
|
||
"xrayUpdates": "Pembaruan Xray",
|
||
"xraySwitchClick": "Pilih versi yang ingin Anda pindah.",
|
||
"xraySwitchClickDesk": "Pilih dengan hati-hati, karena versi yang lebih lama mungkin tidak kompatibel dengan konfigurasi saat ini.",
|
||
"updatePanel": "Perbarui Panel",
|
||
"panelUpdateDesc": "Ini akan memperbarui 3X-UI ke rilis terbaru dan me-restart layanan panel.",
|
||
"currentPanelVersion": "Versi panel saat ini",
|
||
"latestPanelVersion": "Versi panel terbaru",
|
||
"panelUpToDate": "Panel sudah terbaru",
|
||
"upToDate": "Terbaru",
|
||
"xrayStatusUnknown": "Tidak diketahui",
|
||
"xrayStatusRunning": "Berjalan",
|
||
"xrayStatusStop": "Berhenti",
|
||
"xrayStatusError": "Kesalahan",
|
||
"xrayErrorPopoverTitle": "Terjadi kesalahan saat menjalankan Xray",
|
||
"operationHours": "Waktu Aktif",
|
||
"systemHistoryTitle": "Riwayat Sistem",
|
||
"charts": "Grafik",
|
||
"xrayMetricsTitle": "Metrik Xray",
|
||
"xrayMetricsDisabled": "Endpoint metrik Xray belum dikonfigurasi",
|
||
"xrayMetricsHint": "Tambahkan blok metrics tingkat atas ke konfigurasi xray dengan tag metrics_out dan listen 127.0.0.1:11111, lalu mulai ulang xray.",
|
||
"xrayObservatoryEmpty": "Belum ada data Observatory",
|
||
"xrayObservatoryHint": "Tambahkan blok observatory ke konfigurasi xray yang mencantumkan tag outbound untuk diuji, lalu mulai ulang xray.",
|
||
"xrayObservatoryTagPlaceholder": "Pilih outbound",
|
||
"xrayObservatoryAlive": "Aktif",
|
||
"xrayObservatoryDead": "Mati",
|
||
"xrayObservatoryLastSeen": "Terakhir terlihat",
|
||
"xrayObservatoryLastTry": "Percobaan terakhir",
|
||
"trendLast2Min": "2 menit terakhir",
|
||
"systemLoad": "Beban Sistem",
|
||
"systemLoadDesc": "Rata-rata beban sistem selama 1, 5, dan 15 menit terakhir",
|
||
"connectionCount": "Statistik Koneksi",
|
||
"ipAddresses": "Alamat IP",
|
||
"toggleIpVisibility": "Alihkan visibilitas IP",
|
||
"overallSpeed": "Kecepatan keseluruhan",
|
||
"upload": "Unggah",
|
||
"download": "Unduh",
|
||
"totalData": "Total data",
|
||
"sent": "Dikirim",
|
||
"received": "Diterima",
|
||
"documentation": "Dokumentasi",
|
||
"xraySwitchVersionDialog": "Apakah Anda yakin ingin mengubah versi Xray?",
|
||
"xraySwitchVersionDialogDesc": "Ini akan mengubah versi Xray ke #version#.",
|
||
"xraySwitchVersionPopover": "Xray berhasil diperbarui",
|
||
"panelUpdateDialog": "Apakah Anda benar-benar ingin memperbarui panel?",
|
||
"panelUpdateDialogDesc": "Ini akan memperbarui 3X-UI ke #version# dan me-restart layanan panel.",
|
||
"panelUpdateCheckPopover": "Pemeriksaan pembaruan panel gagal",
|
||
"panelUpdateStartedPopover": "Pembaruan panel dimulai",
|
||
"geofileUpdateDialog": "Apakah Anda yakin ingin memperbarui geofile?",
|
||
"geofileUpdateDialogDesc": "Ini akan memperbarui file #filename#.",
|
||
"geofilesUpdateDialogDesc": "Ini akan memperbarui semua berkas.",
|
||
"geofilesUpdateAll": "Perbarui semua",
|
||
"geofileUpdatePopover": "Geofile berhasil diperbarui",
|
||
"customGeoTitle": "GeoSite / GeoIP kustom",
|
||
"customGeoAdd": "Tambah",
|
||
"customGeoType": "Jenis",
|
||
"customGeoAlias": "Alias",
|
||
"customGeoUrl": "URL",
|
||
"customGeoEnabled": "Aktif",
|
||
"customGeoLastUpdated": "Terakhir diperbarui",
|
||
"customGeoExtColumn": "Routing (ext:…)",
|
||
"customGeoToastUpdateAll": "Semua sumber kustom telah diperbarui",
|
||
"customGeoActions": "Aksi",
|
||
"customGeoEdit": "Edit",
|
||
"customGeoDelete": "Hapus",
|
||
"customGeoDownload": "Perbarui sekarang",
|
||
"customGeoModalAdd": "Tambah geo kustom",
|
||
"customGeoModalEdit": "Edit geo kustom",
|
||
"customGeoModalSave": "Simpan",
|
||
"customGeoDeleteConfirm": "Hapus sumber geo kustom ini?",
|
||
"customGeoRoutingHint": "Pada aturan routing gunakan kolom nilai sebagai ext:file.dat:tag (ganti tag).",
|
||
"customGeoInvalidId": "ID sumber tidak valid",
|
||
"customGeoAliasesError": "Gagal memuat alias geo kustom",
|
||
"customGeoValidationAlias": "Alias hanya huruf kecil, angka, - dan _",
|
||
"customGeoValidationUrl": "URL harus diawali http:// atau https://",
|
||
"customGeoAliasPlaceholder": "a-z 0-9 _ -",
|
||
"customGeoAliasLabelSuffix": " (kustom)",
|
||
"customGeoToastList": "Daftar geo kustom",
|
||
"customGeoToastAdd": "Tambah geo kustom",
|
||
"customGeoToastUpdate": "Perbarui geo kustom",
|
||
"customGeoToastDelete": "Geofile kustom “{{ .fileName }}” dihapus",
|
||
"customGeoToastDownload": "Geofile “{{ .fileName }}” diperbarui",
|
||
"customGeoErrInvalidType": "Jenis harus geosite atau geoip",
|
||
"customGeoErrAliasRequired": "Alias wajib diisi",
|
||
"customGeoErrAliasPattern": "Alias berisi karakter yang tidak diizinkan",
|
||
"customGeoErrAliasReserved": "Alias ini dicadangkan",
|
||
"customGeoErrUrlRequired": "URL wajib diisi",
|
||
"customGeoErrInvalidUrl": "URL tidak valid",
|
||
"customGeoErrUrlScheme": "URL harus memakai http atau https",
|
||
"customGeoErrUrlHost": "Host URL tidak valid",
|
||
"customGeoErrDuplicateAlias": "Alias ini sudah dipakai untuk jenis ini",
|
||
"customGeoErrNotFound": "Sumber geo kustom tidak ditemukan",
|
||
"customGeoErrDownload": "Unduh gagal",
|
||
"customGeoErrUpdateAllIncomplete": "Satu atau lebih sumber geo kustom gagal diperbarui",
|
||
"customGeoEmpty": "Belum ada sumber geo kustom — klik Tambah untuk membuatnya",
|
||
"dontRefresh": "Instalasi sedang berlangsung, harap jangan menyegarkan halaman ini",
|
||
"logs": "Log",
|
||
"config": "Konfigurasi",
|
||
"backup": "Cadangan",
|
||
"backupTitle": "Cadangan & Pulihkan",
|
||
"exportDatabase": "Cadangkan",
|
||
"exportDatabaseDesc": "Klik untuk mengunduh file .db yang berisi cadangan dari database Anda saat ini ke perangkat Anda.",
|
||
"importDatabase": "Pulihkan",
|
||
"importDatabaseDesc": "Klik untuk memilih dan mengunggah file .db dari perangkat Anda untuk memulihkan database dari cadangan.",
|
||
"importDatabaseSuccess": "Database berhasil diimpor",
|
||
"importDatabaseError": "Terjadi kesalahan saat mengimpor database",
|
||
"readDatabaseError": "Terjadi kesalahan saat membaca database",
|
||
"getDatabaseError": "Terjadi kesalahan saat mengambil database",
|
||
"getConfigError": "Terjadi kesalahan saat mengambil file konfigurasi"
|
||
},
|
||
"inbounds": {
|
||
"title": "Masuk",
|
||
"totalDownUp": "Total Terkirim/Diterima",
|
||
"totalUsage": "Penggunaan Total",
|
||
"inboundCount": "Total Masuk",
|
||
"operate": "Menu",
|
||
"enable": "Aktifkan",
|
||
"remark": "Catatan",
|
||
"node": "Node",
|
||
"deployTo": "Terapkan ke",
|
||
"localPanel": "Panel lokal",
|
||
"fallbacks": {
|
||
"title": "Fallback",
|
||
"help": "Saat koneksi pada inbound ini tidak cocok dengan client mana pun, arahkan ke inbound lain. Pilih child di bawah dan field routing (SNI / ALPN / Path / xver) terisi otomatis dari transport-nya — sebagian besar konfigurasi tidak perlu disesuaikan lagi. Setiap child harus listen di 127.0.0.1 dengan security=none.",
|
||
"empty": "Belum ada fallback",
|
||
"add": "Tambah fallback",
|
||
"pickInbound": "Pilih inbound",
|
||
"matchAny": "apa pun",
|
||
"rederive": "Isi ulang dari child",
|
||
"rederived": "Diisi ulang dari child",
|
||
"editAdvanced": "Edit field routing",
|
||
"hideAdvanced": "Sembunyikan lanjutan",
|
||
"quickAddAll": "Tambah cepat semua yang memenuhi syarat",
|
||
"quickAdded": "Menambahkan {n} fallback",
|
||
"quickAddedNone": "Tidak ada inbound baru yang memenuhi syarat",
|
||
"routesWhen": "Diarahkan ketika",
|
||
"defaultCatchAll": "Default — menangkap apa pun lainnya"
|
||
},
|
||
"protocol": "Protokol",
|
||
"port": "Port",
|
||
"portMap": "Port Mapping",
|
||
"traffic": "Traffic",
|
||
"details": "Rincian",
|
||
"transportConfig": "Transport",
|
||
"expireDate": "Durasi",
|
||
"createdAt": "Dibuat",
|
||
"updatedAt": "Diperbarui",
|
||
"resetTraffic": "Reset Traffic",
|
||
"addInbound": "Tambahkan Masuk",
|
||
"generalActions": "Tindakan Umum",
|
||
"modifyInbound": "Ubah Masuk",
|
||
"deleteInbound": "Hapus Masuk",
|
||
"deleteInboundContent": "Apakah Anda yakin ingin menghapus masuk?",
|
||
"deleteClient": "Hapus Klien",
|
||
"deleteClientContent": "Apakah Anda yakin ingin menghapus klien?",
|
||
"resetTrafficContent": "Apakah Anda yakin ingin mereset traffic?",
|
||
"copyLink": "Salin URL",
|
||
"address": "Alamat",
|
||
"network": "Jaringan",
|
||
"destinationPort": "Port Tujuan",
|
||
"targetAddress": "Alamat Target",
|
||
"monitorDesc": "Biarkan kosong untuk mendengarkan semua IP",
|
||
"meansNoLimit": "= Unlimited. (unit: GB)",
|
||
"totalFlow": "Total Aliran",
|
||
"leaveBlankToNeverExpire": "Biarkan kosong untuk tidak pernah kedaluwarsa",
|
||
"noRecommendKeepDefault": "Disarankan untuk tetap menggunakan pengaturan default",
|
||
"certificatePath": "Path Berkas",
|
||
"certificateContent": "Konten Berkas",
|
||
"publicKey": "Kunci Publik",
|
||
"privatekey": "Kunci Pribadi",
|
||
"clickOnQRcode": "Klik pada Kode QR untuk Menyalin",
|
||
"client": "Klien",
|
||
"export": "Ekspor Semua URL",
|
||
"clone": "Duplikat",
|
||
"cloneInbound": "Duplikat",
|
||
"cloneInboundContent": "Semua pengaturan masuk ini, kecuali Port, Listening IP, dan Klien, akan diterapkan pada duplikat.",
|
||
"cloneInboundOk": "Duplikat",
|
||
"resetAllTraffic": "Reset Semua Traffic Masuk",
|
||
"resetAllTrafficTitle": "Reset Semua Traffic Masuk",
|
||
"resetAllTrafficContent": "Apakah Anda yakin ingin mereset traffic semua masuk?",
|
||
"resetInboundClientTraffics": "Reset Traffic Klien Masuk",
|
||
"resetInboundClientTrafficTitle": "Reset Traffic Klien Masuk",
|
||
"resetInboundClientTrafficContent": "Apakah Anda yakin ingin mereset traffic klien masuk ini?",
|
||
"resetAllClientTraffics": "Reset Traffic Semua Klien",
|
||
"resetAllClientTrafficTitle": "Reset Traffic Semua Klien",
|
||
"resetAllClientTrafficContent": "Apakah Anda yakin ingin mereset traffic semua klien?",
|
||
"delDepletedClients": "Hapus Klien Habis",
|
||
"delDepletedClientsTitle": "Hapus Klien Habis",
|
||
"delDepletedClientsContent": "Apakah Anda yakin ingin menghapus semua klien yang habis?",
|
||
"email": "Email",
|
||
"emailDesc": "Harap berikan alamat email yang unik.",
|
||
"IPLimit": "Batas IP",
|
||
"IPLimitDesc": "Menonaktifkan masuk jika jumlah melebihi nilai yang ditetapkan. (0 = nonaktif)",
|
||
"IPLimitlog": "Log IP",
|
||
"IPLimitlogDesc": "Log histori IP. (untuk mengaktifkan masuk setelah menonaktifkan, hapus log)",
|
||
"IPLimitlogclear": "Hapus Log",
|
||
"setDefaultCert": "Atur Sertifikat dari Panel",
|
||
"streamTab": "Stream",
|
||
"securityTab": "Keamanan",
|
||
"sniffingTab": "Sniffing",
|
||
"sniffingMetadataOnly": "Hanya metadata",
|
||
"sniffingRouteOnly": "Hanya routing",
|
||
"sniffingIpsExcluded": "IP yang dikecualikan",
|
||
"sniffingDomainsExcluded": "Domain yang dikecualikan",
|
||
"decryption": "Dekripsi",
|
||
"encryption": "Enkripsi",
|
||
"vlessAuthX25519": "Auth X25519",
|
||
"vlessAuthMlkem768": "Auth ML-KEM-768",
|
||
"vlessAuthCustom": "Khusus",
|
||
"vlessAuthSelected": "Dipilih: {auth}",
|
||
"advanced": {
|
||
"title": "Bagian JSON inbound",
|
||
"subtitle": "JSON inbound lengkap dan editor fokus untuk settings, sniffing, dan streamSettings.",
|
||
"all": "Semua",
|
||
"allHelp": "Objek inbound lengkap dengan semua bidang dalam satu editor.",
|
||
"settings": "Pengaturan",
|
||
"settingsHelp": "Pembungkus blok settings Xray:",
|
||
"sniffing": "Sniffing",
|
||
"sniffingHelp": "Pembungkus blok sniffing Xray:",
|
||
"stream": "Stream",
|
||
"streamHelp": "Pembungkus blok stream Xray:",
|
||
"jsonErrorPrefix": "JSON lanjutan"
|
||
},
|
||
"telegramDesc": "Harap berikan ID Obrolan Telegram. (gunakan perintah '/id' di bot) atau ({'@'}userinfobot)",
|
||
"subscriptionDesc": "Untuk menemukan URL langganan Anda, buka 'Rincian'. Selain itu, Anda dapat menggunakan nama yang sama untuk beberapa klien.",
|
||
"info": "Info",
|
||
"same": "Sama",
|
||
"inboundData": "Data Masuk",
|
||
"exportInbound": "Ekspor Masuk",
|
||
"import": "Impor",
|
||
"importInbound": "Impor Masuk",
|
||
"periodicTrafficResetTitle": "Reset Trafik Berkala",
|
||
"periodicTrafficResetDesc": "Reset otomatis penghitung trafik pada interval tertentu",
|
||
"lastReset": "Reset Terakhir",
|
||
"periodicTrafficReset": {
|
||
"never": "Tidak Pernah",
|
||
"daily": "Harian",
|
||
"weekly": "Mingguan",
|
||
"monthly": "Bulanan",
|
||
"hourly": "Setiap jam"
|
||
},
|
||
"toasts": {
|
||
"obtain": "Dapatkan",
|
||
"updateSuccess": "Pembaruan berhasil",
|
||
"logCleanSuccess": "Log telah dibersihkan",
|
||
"inboundsUpdateSuccess": "Inbound berhasil diperbarui",
|
||
"inboundUpdateSuccess": "Inbound berhasil diperbarui",
|
||
"inboundCreateSuccess": "Inbound berhasil dibuat",
|
||
"inboundDeleteSuccess": "Inbound berhasil dihapus",
|
||
"inboundClientAddSuccess": "Klien inbound telah ditambahkan",
|
||
"inboundClientDeleteSuccess": "Klien inbound telah dihapus",
|
||
"inboundClientUpdateSuccess": "Klien inbound telah diperbarui",
|
||
"delDepletedClientsSuccess": "Semua klien yang habis telah dihapus",
|
||
"resetAllClientTrafficSuccess": "Semua lalu lintas klien telah direset",
|
||
"resetAllTrafficSuccess": "Semua lalu lintas telah direset",
|
||
"resetInboundClientTrafficSuccess": "Lalu lintas telah direset",
|
||
"resetInboundTrafficSuccess": "Lalu lintas masuk telah direset",
|
||
"trafficGetError": "Gagal mendapatkan data lalu lintas",
|
||
"getNewX25519CertError": "Terjadi kesalahan saat mendapatkan sertifikat X25519.",
|
||
"getNewmldsa65Error": "Terjadi kesalahan saat mendapatkan sertifikat mldsa65.",
|
||
"getNewVlessEncError": "Terjadi kesalahan saat mendapatkan sertifikat VlessEnc."
|
||
},
|
||
"stream": {
|
||
"general": {
|
||
"request": "Permintaan",
|
||
"response": "Respons",
|
||
"name": "Nama",
|
||
"value": "Nilai"
|
||
},
|
||
"tcp": {
|
||
"version": "Versi",
|
||
"method": "Metode",
|
||
"path": "Path",
|
||
"status": "Status",
|
||
"statusDescription": "Deskripsi Status",
|
||
"requestHeader": "Header Permintaan",
|
||
"responseHeader": "Header Respons"
|
||
}
|
||
}
|
||
},
|
||
"clients": {
|
||
"add": "Tambah klien",
|
||
"edit": "Ubah klien",
|
||
"submitAdd": "Tambah klien",
|
||
"submitEdit": "Simpan perubahan",
|
||
"clientCount": "Jumlah klien",
|
||
"bulk": "Tambah massal",
|
||
"copyFromInbound": "Salin klien dari inbound",
|
||
"copyToInbound": "Salin klien ke",
|
||
"copySelected": "Salin terpilih",
|
||
"copySource": "Sumber",
|
||
"copyEmailPreview": "Pratinjau email hasil",
|
||
"copySelectSourceFirst": "Pilih inbound sumber terlebih dahulu.",
|
||
"copyResult": "Hasil salinan",
|
||
"copyResultSuccess": "Berhasil disalin",
|
||
"copyResultNone": "Tidak ada yang disalin: tidak ada klien terpilih atau sumber kosong",
|
||
"copyResultErrors": "Kesalahan salin",
|
||
"copyFlowLabel": "Flow untuk klien baru (VLESS)",
|
||
"copyFlowHint": "Diterapkan ke semua klien yang disalin. Kosongkan untuk dilewati.",
|
||
"selectAll": "Pilih semua",
|
||
"clearAll": "Hapus semua",
|
||
"method": "Metode",
|
||
"first": "Pertama",
|
||
"last": "Terakhir",
|
||
"ipLog": "Log IP",
|
||
"prefix": "Awalan",
|
||
"postfix": "Akhiran",
|
||
"delayedStart": "Mulai setelah penggunaan pertama",
|
||
"expireDays": "Durasi",
|
||
"days": "Hari",
|
||
"renew": "Perpanjangan otomatis",
|
||
"renewDesc": "Perpanjangan otomatis setelah kedaluwarsa. (0 = nonaktif) (satuan: hari)",
|
||
"title": "Klien",
|
||
"actions": "Aksi",
|
||
"totalGB": "Total Kirim/Terima (GB)",
|
||
"expiryTime": "Kedaluwarsa",
|
||
"addClients": "Tambah klien",
|
||
"limitIp": "Batas IP",
|
||
"password": "Kata sandi",
|
||
"subId": "ID Langganan",
|
||
"online": "Online",
|
||
"email": "Email",
|
||
"comment": "Komentar",
|
||
"traffic": "Lalu lintas",
|
||
"offline": "Offline",
|
||
"addTitle": "Tambah klien",
|
||
"qrCode": "Kode QR",
|
||
"moreInformation": "Informasi lebih lanjut",
|
||
"delete": "Hapus",
|
||
"reset": "Reset lalu lintas",
|
||
"editTitle": "Ubah klien",
|
||
"client": "Klien",
|
||
"enabled": "Aktif",
|
||
"remaining": "Sisa",
|
||
"duration": "Durasi",
|
||
"attachedInbounds": "Inbound terlampir",
|
||
"selectInbound": "Pilih satu atau lebih inbound",
|
||
"noSubId": "Klien ini tidak punya subId, tidak ada tautan yang bisa dibagikan.",
|
||
"noLinks": "Tidak ada tautan yang bisa dibagikan — lampirkan klien ini ke inbound yang mendukung protokol terlebih dahulu.",
|
||
"link": "Tautan",
|
||
"resetNotPossible": "Lampirkan klien ini ke inbound terlebih dahulu.",
|
||
"general": "Umum",
|
||
"resetAllTraffics": "Reset lalu lintas semua klien",
|
||
"resetAllTrafficsTitle": "Reset lalu lintas semua klien?",
|
||
"resetAllTrafficsContent": "Penghitung kirim/terima setiap klien turun ke nol. Kuota dan kedaluwarsa tidak terpengaruh. Tidak dapat dibatalkan.",
|
||
"empty": "Belum ada klien — tambahkan satu untuk memulai.",
|
||
"deleteConfirmTitle": "Hapus klien {email}?",
|
||
"deleteConfirmContent": "Tindakan ini menghapus klien dari setiap inbound terlampir dan menghapus catatan lalu lintasnya. Tidak dapat dibatalkan.",
|
||
"deleteSelected": "Hapus ({count})",
|
||
"bulkDeleteConfirmTitle": "Hapus {count} klien?",
|
||
"bulkDeleteConfirmContent": "Setiap klien yang dipilih dihapus dari semua inbound terlampir dan catatan lalu lintasnya dihapus. Tidak dapat dibatalkan.",
|
||
"delDepleted": "Hapus yang habis",
|
||
"delDepletedConfirmTitle": "Hapus klien yang habis?",
|
||
"delDepletedConfirmContent": "Hapus setiap klien yang kuota lalu lintasnya habis atau yang masa berlakunya telah berakhir. Tidak dapat dibatalkan.",
|
||
"auth": "Auth",
|
||
"hysteriaAuth": "Auth Hysteria",
|
||
"uuid": "UUID",
|
||
"flow": "Flow",
|
||
"reverseTag": "Reverse tag",
|
||
"reverseTagPlaceholder": "Reverse tag opsional",
|
||
"telegramId": "ID pengguna Telegram",
|
||
"telegramIdPlaceholder": "ID numerik pengguna Telegram (0 = tidak ada)",
|
||
"created": "Dibuat",
|
||
"updated": "Diperbarui",
|
||
"ipLimit": "Batas IP",
|
||
"toasts": {
|
||
"deleted": "Klien dihapus",
|
||
"trafficReset": "Lalu lintas direset",
|
||
"allTrafficsReset": "Lalu lintas semua klien direset",
|
||
"bulkDeleted": "{count} klien dihapus",
|
||
"bulkDeletedMixed": "{ok} dihapus, {failed} gagal",
|
||
"bulkCreated": "{count} klien dibuat",
|
||
"bulkCreatedMixed": "{ok} dibuat, {failed} gagal",
|
||
"delDepleted": "{count} klien habis dihapus"
|
||
}
|
||
},
|
||
"nodes": {
|
||
"title": "Node",
|
||
"addNode": "Tambah Node",
|
||
"editNode": "Edit Node",
|
||
"totalNodes": "Total Node",
|
||
"onlineNodes": "Online",
|
||
"offlineNodes": "Offline",
|
||
"avgLatency": "Latensi Rata-rata",
|
||
"name": "Nama",
|
||
"namePlaceholder": "mis. de-frankfurt-1",
|
||
"addressPlaceholder": "panel.example.com atau 1.2.3.4",
|
||
"remark": "Catatan",
|
||
"scheme": "Skema",
|
||
"address": "Alamat",
|
||
"port": "Port",
|
||
"basePath": "Base Path",
|
||
"apiToken": "Token API",
|
||
"apiTokenPlaceholder": "Token dari halaman Pengaturan panel jarak jauh",
|
||
"apiTokenHint": "Panel jarak jauh menampilkan token API-nya di Pengaturan → Token API.",
|
||
"regenerate": "Buat Ulang Token",
|
||
"regenerateConfirm": "Membuat ulang akan membatalkan token saat ini. Setiap panel pusat yang menggunakannya akan kehilangan akses sampai diperbarui. Lanjutkan?",
|
||
"allowPrivateAddress": "Izinkan alamat pribadi",
|
||
"allowPrivateAddressHint": "Aktifkan hanya untuk node di jaringan pribadi atau VPN.",
|
||
"enable": "Aktif",
|
||
"status": "Status",
|
||
"cpu": "CPU",
|
||
"mem": "Memori",
|
||
"uptime": "Uptime",
|
||
"latency": "Latensi",
|
||
"lastHeartbeat": "Heartbeat Terakhir",
|
||
"xrayVersion": "Versi Xray",
|
||
"panelVersion": "Versi panel",
|
||
"actions": "Aksi",
|
||
"probe": "Probe Sekarang",
|
||
"testConnection": "Tes Koneksi",
|
||
"connectionOk": "Koneksi OK ({ms} ms)",
|
||
"connectionFailed": "Koneksi gagal",
|
||
"never": "tidak pernah",
|
||
"justNow": "baru saja",
|
||
"deleteConfirmTitle": "Hapus node \"{name}\"?",
|
||
"deleteConfirmContent": "Ini menghentikan pemantauan node. Panel jarak jauh itu sendiri tidak terpengaruh.",
|
||
"statusValues": {
|
||
"online": "Online",
|
||
"offline": "Offline",
|
||
"unknown": "Tidak diketahui"
|
||
},
|
||
"toasts": {
|
||
"list": "Gagal memuat node",
|
||
"obtain": "Gagal memuat node",
|
||
"add": "Tambah node",
|
||
"update": "Perbarui node",
|
||
"delete": "Hapus node",
|
||
"deleted": "Node dihapus",
|
||
"test": "Tes koneksi",
|
||
"fillRequired": "Nama, alamat, port, dan token API wajib diisi",
|
||
"probeFailed": "Probe gagal"
|
||
}
|
||
},
|
||
"settings": {
|
||
"title": "Pengaturan Panel",
|
||
"save": "Simpan",
|
||
"infoDesc": "Setiap perubahan yang dibuat di sini perlu disimpan. Harap restart panel untuk menerapkan perubahan.",
|
||
"restartPanel": "Restart Panel",
|
||
"restartPanelDesc": "Apakah Anda yakin ingin merestart panel? Jika Anda tidak dapat mengakses panel setelah merestart, lihat info log panel di server.",
|
||
"restartPanelSuccess": "Panel berhasil dimulai ulang",
|
||
"actions": "Tindakan",
|
||
"resetDefaultConfig": "Reset ke Default",
|
||
"panelSettings": "Umum",
|
||
"securitySettings": "Otentikasi",
|
||
"TGBotSettings": "Bot Telegram",
|
||
"panelListeningIP": "IP Pendengar",
|
||
"panelListeningIPDesc": "Alamat IP untuk panel web. (biarkan kosong untuk mendengarkan semua IP)",
|
||
"panelListeningDomain": "Domain Pendengar",
|
||
"panelListeningDomainDesc": "Nama domain untuk panel web. (biarkan kosong untuk mendengarkan semua domain dan IP)",
|
||
"panelPort": "Port Pendengar",
|
||
"panelPortDesc": "Nomor port untuk panel web. (harus menjadi port yang tidak digunakan)",
|
||
"publicKeyPath": "Path Kunci Publik",
|
||
"publicKeyPathDesc": "Path berkas kunci publik untuk panel web. (dimulai dengan ‘/‘)",
|
||
"privateKeyPath": "Path Kunci Privat",
|
||
"privateKeyPathDesc": "Path berkas kunci privat untuk panel web. (dimulai dengan ‘/‘)",
|
||
"panelUrlPath": "URI Path",
|
||
"panelUrlPathDesc": "URI path untuk panel web. (dimulai dengan ‘/‘ dan diakhiri dengan ‘/‘)",
|
||
"pageSize": "Ukuran Halaman",
|
||
"pageSizeDesc": "Tentukan ukuran halaman untuk tabel masuk. (0 = nonaktif)",
|
||
"remarkModel": "Model Catatan & Karakter Pemisah",
|
||
"datepicker": "Jenis Kalender",
|
||
"datepickerPlaceholder": "Pilih tanggal",
|
||
"datepickerDescription": "Tugas terjadwal akan berjalan berdasarkan kalender ini.",
|
||
"sampleRemark": "Contoh Catatan",
|
||
"oldUsername": "Username Saat Ini",
|
||
"currentPassword": "Kata Sandi Saat Ini",
|
||
"newUsername": "Username Baru",
|
||
"newPassword": "Kata Sandi Baru",
|
||
"telegramBotEnable": "Aktifkan Bot Telegram",
|
||
"telegramBotEnableDesc": "Mengaktifkan bot Telegram.",
|
||
"telegramToken": "Token Telegram",
|
||
"telegramTokenDesc": "Token bot Telegram yang diperoleh dari '{'@'}BotFather'.",
|
||
"telegramProxy": "Proxy SOCKS",
|
||
"telegramProxyDesc": "Mengaktifkan proxy SOCKS5 untuk terhubung ke Telegram. (sesuaikan pengaturan sesuai panduan)",
|
||
"telegramAPIServer": "Telegram API Server",
|
||
"telegramAPIServerDesc": "Server API Telegram yang akan digunakan. Biarkan kosong untuk menggunakan server default.",
|
||
"telegramChatId": "ID Obrolan Admin",
|
||
"telegramChatIdDesc": "ID Obrolan Admin Telegram. (dipisahkan koma)(dapatkan di sini {'@'}userinfobot) atau (gunakan perintah '/id' di bot)",
|
||
"telegramNotifyTime": "Waktu Notifikasi",
|
||
"telegramNotifyTimeDesc": "Waktu notifikasi bot Telegram yang diatur untuk laporan berkala. (gunakan format waktu crontab)",
|
||
"tgNotifyBackup": "Cadangan Database",
|
||
"tgNotifyBackupDesc": "Kirim berkas cadangan database dengan laporan.",
|
||
"tgNotifyLogin": "Notifikasi Login",
|
||
"tgNotifyLoginDesc": "Dapatkan notifikasi tentang username, alamat IP, dan waktu setiap kali seseorang mencoba masuk ke panel web Anda.",
|
||
"sessionMaxAge": "Durasi Sesi",
|
||
"sessionMaxAgeDesc": "Durasi di mana Anda dapat tetap masuk. (unit: menit)",
|
||
"expireTimeDiff": "Notifikasi Tanggal Kedaluwarsa",
|
||
"expireTimeDiffDesc": "Dapatkan notifikasi tentang tanggal kedaluwarsa saat mencapai ambang batas ini. (unit: hari)",
|
||
"trafficDiff": "Notifikasi Batas Traffic",
|
||
"trafficDiffDesc": "Dapatkan notifikasi tentang batas traffic saat mencapai ambang batas ini. (unit: GB)",
|
||
"tgNotifyCpu": "Notifikasi Beban CPU",
|
||
"tgNotifyCpuDesc": "Dapatkan notifikasi jika beban CPU melebihi ambang batas ini. (unit: %)",
|
||
"timeZone": "Zone Waktu",
|
||
"timeZoneDesc": "Tugas terjadwal akan berjalan berdasarkan zona waktu ini.",
|
||
"subSettings": "Langganan",
|
||
"subEnable": "Aktifkan Layanan Langganan",
|
||
"subEnableDesc": "Mengaktifkan layanan langganan.",
|
||
"subJsonEnable": "Aktifkan/Nonaktifkan endpoint langganan JSON secara mandiri.",
|
||
"subTitle": "Judul Langganan",
|
||
"subTitleDesc": "Judul yang ditampilkan di klien VPN",
|
||
"subSupportUrl": "URL Dukungan",
|
||
"subSupportUrlDesc": "Tautan dukungan teknis yang ditampilkan di klien VPN",
|
||
"subProfileUrl": "URL Profil",
|
||
"subProfileUrlDesc": "Tautan ke situs web Anda yang ditampilkan di klien VPN",
|
||
"subAnnounce": "Pengumuman",
|
||
"subAnnounceDesc": "Teks pengumuman yang ditampilkan di klien VPN",
|
||
"subEnableRouting": "Aktifkan perutean",
|
||
"subEnableRoutingDesc": "Pengaturan global untuk mengaktifkan perutean (routing) di klien VPN. (Hanya untuk Happ)",
|
||
"subRoutingRules": "Aturan routing",
|
||
"subRoutingRulesDesc": "Aturan routing global untuk klien VPN. (Hanya untuk Happ)",
|
||
"subListen": "IP Pendengar",
|
||
"subListenDesc": "Alamat IP untuk layanan langganan. (biarkan kosong untuk mendengarkan semua IP)",
|
||
"subPort": "Port Pendengar",
|
||
"subPortDesc": "Nomor port untuk layanan langganan. (harus menjadi port yang tidak digunakan)",
|
||
"subCertPath": "Path Kunci Publik",
|
||
"subCertPathDesc": "Path berkas kunci publik untuk layanan langganan. (dimulai dengan ‘/‘)",
|
||
"subKeyPath": "Path Kunci Privat",
|
||
"subKeyPathDesc": "Path berkas kunci privat untuk layanan langganan. (dimulai dengan ‘/‘)",
|
||
"subPath": "URI Path",
|
||
"subPathDesc": "URI path untuk layanan langganan. (dimulai dengan ‘/‘ dan diakhiri dengan ‘/‘)",
|
||
"subDomain": "Domain Pendengar",
|
||
"subDomainDesc": "Nama domain untuk layanan langganan. (biarkan kosong untuk mendengarkan semua domain dan IP)",
|
||
"subUpdates": "Interval Pembaruan",
|
||
"subUpdatesDesc": "Interval pembaruan URL langganan dalam aplikasi klien. (unit: jam)",
|
||
"subEncrypt": "Encode",
|
||
"subEncryptDesc": "Konten yang dikembalikan dari layanan langganan akan dienkripsi Base64.",
|
||
"subShowInfo": "Tampilkan Info Penggunaan",
|
||
"subShowInfoDesc": "Sisa traffic dan tanggal akan ditampilkan di aplikasi klien.",
|
||
"subEmailInRemark": "Sertakan Email dalam Nama",
|
||
"subEmailInRemarkDesc": "Sertakan email klien dalam nama profil langganan.",
|
||
"subURI": "URI Proxy Terbalik",
|
||
"subURIDesc": "Path URI dari URL langganan untuk digunakan di belakang proxy.",
|
||
"externalTrafficInformEnable": "Informasikan API eksternal pada setiap pembaruan lalu lintas.",
|
||
"externalTrafficInformEnableDesc": "Inform external API on every traffic update.",
|
||
"externalTrafficInformURI": "Lalu Lintas Eksternal Menginformasikan URI",
|
||
"externalTrafficInformURIDesc": "Pembaruan lalu lintas dikirim ke URI ini.",
|
||
"restartXrayOnClientDisable": "Nyalakan Ulang Xray Setelah Nonaktif Otomatis",
|
||
"restartXrayOnClientDisableDesc": "Saat klien otomatis dinonaktifkan karena kedaluwarsa atau batas trafik, mulai ulang Xray.",
|
||
"fragment": "Fragmentasi",
|
||
"fragmentDesc": "Aktifkan fragmentasi untuk paket hello TLS",
|
||
"fragmentSett": "Pengaturan Fragmentasi",
|
||
"noisesDesc": "Aktifkan Noises.",
|
||
"noisesSett": "Pengaturan Noises",
|
||
"mux": "Mux",
|
||
"muxDesc": "Mengirimkan beberapa aliran data independen dalam aliran data yang sudah ada.",
|
||
"muxSett": "Pengaturan Mux",
|
||
"direct": "Koneksi langsung",
|
||
"directDesc": "Secara langsung membuat koneksi dengan domain atau rentang IP negara tertentu.",
|
||
"notifications": "Notifikasi",
|
||
"certs": "Sertifikat",
|
||
"externalTraffic": "Lalu Lintas Eksternal",
|
||
"dateAndTime": "Tanggal dan Waktu",
|
||
"proxyAndServer": "Proxy dan Server",
|
||
"intervals": "Interval",
|
||
"information": "Informasi",
|
||
"language": "Bahasa",
|
||
"telegramBotLanguage": "Bahasa Bot Telegram",
|
||
"security": {
|
||
"admin": "Kredensial admin",
|
||
"twoFactor": "Autentikasi dua faktor",
|
||
"twoFactorEnable": "Aktifkan 2FA",
|
||
"twoFactorEnableDesc": "Menambahkan lapisan autentikasi tambahan untuk keamanan lebih.",
|
||
"twoFactorModalSetTitle": "Aktifkan autentikasi dua faktor",
|
||
"twoFactorModalDeleteTitle": "Nonaktifkan autentikasi dua faktor",
|
||
"twoFactorModalSteps": "Untuk menyiapkan autentikasi dua faktor, lakukan beberapa langkah:",
|
||
"twoFactorModalFirstStep": "1. Pindai kode QR ini di aplikasi autentikasi atau salin token di dekat kode QR dan tempelkan ke aplikasi",
|
||
"twoFactorModalSecondStep": "2. Masukkan kode dari aplikasi",
|
||
"twoFactorModalRemoveStep": "Masukkan kode dari aplikasi untuk menghapus autentikasi dua faktor.",
|
||
"twoFactorModalChangeCredentialsTitle": "Ubah kredensial",
|
||
"twoFactorModalChangeCredentialsStep": "Masukkan kode dari aplikasi untuk mengubah kredensial administrator.",
|
||
"twoFactorModalSetSuccess": "Autentikasi dua faktor telah berhasil dibuat",
|
||
"twoFactorModalDeleteSuccess": "Autentikasi dua faktor telah berhasil dihapus",
|
||
"twoFactorModalError": "Kode salah",
|
||
"show": "Tampilkan",
|
||
"hide": "Sembunyikan",
|
||
"apiTokenNew": "Token baru",
|
||
"apiTokenName": "Nama",
|
||
"apiTokenNamePlaceholder": "misalnya central-panel-a",
|
||
"apiTokenNameRequired": "Nama wajib diisi",
|
||
"apiTokenEmpty": "Belum ada token — buat satu untuk mengautentikasi bot atau panel jarak jauh.",
|
||
"apiTokenDeleteWarning": "Setiap pemanggil yang menggunakan token ini akan berhenti terautentikasi segera."
|
||
},
|
||
"toasts": {
|
||
"modifySettings": "Parameter telah diubah.",
|
||
"getSettings": "Terjadi kesalahan saat mengambil parameter.",
|
||
"modifyUserError": "Terjadi kesalahan saat mengubah kredensial administrator.",
|
||
"modifyUser": "Anda telah berhasil mengubah kredensial administrator.",
|
||
"originalUserPassIncorrect": "Username atau password saat ini tidak valid",
|
||
"userPassMustBeNotEmpty": "Username dan password baru tidak boleh kosong",
|
||
"getOutboundTrafficError": "Gagal mendapatkan lalu lintas keluar",
|
||
"resetOutboundTrafficError": "Gagal mereset lalu lintas keluar"
|
||
}
|
||
},
|
||
"xray": {
|
||
"title": "Konfigurasi Xray",
|
||
"save": "Simpan",
|
||
"restart": "Restart Xray",
|
||
"restartSuccess": "Xray berhasil diluncurkan ulang",
|
||
"stopSuccess": "Xray telah berhasil dihentikan",
|
||
"restartError": "Terjadi kesalahan saat memulai ulang Xray.",
|
||
"stopError": "Terjadi kesalahan saat menghentikan Xray.",
|
||
"basicTemplate": "Dasar",
|
||
"advancedTemplate": "Lanjutan",
|
||
"generalConfigs": "Strategi Umum",
|
||
"generalConfigsDesc": "Opsi ini akan menentukan penyesuaian strategi umum.",
|
||
"logConfigs": "Catatan",
|
||
"logConfigsDesc": "Log dapat mempengaruhi efisiensi server Anda. Disarankan untuk mengaktifkannya dengan bijak hanya jika diperlukan",
|
||
"blockConfigsDesc": "Opsi ini akan memblokir lalu lintas berdasarkan protokol dan situs web yang diminta.",
|
||
"basicRouting": "Perutean Dasar",
|
||
"blockConnectionsConfigsDesc": "Opsi ini akan memblokir lalu lintas berdasarkan negara yang diminta.",
|
||
"directConnectionsConfigsDesc": "Koneksi langsung memastikan bahwa lalu lintas tertentu tidak dialihkan melalui server lain.",
|
||
"blockips": "Blokir IP",
|
||
"blockdomains": "Blokir Domain",
|
||
"directips": "IP Langsung",
|
||
"directdomains": "Domain Langsung",
|
||
"ipv4Routing": "Perutean IPv4",
|
||
"ipv4RoutingDesc": "Opsi ini akan mengalihkan lalu lintas berdasarkan tujuan tertentu melalui IPv4.",
|
||
"warpRouting": "Perutean WARP",
|
||
"warpRoutingDesc": "Opsi ini akan mengalihkan lalu lintas berdasarkan tujuan tertentu melalui WARP.",
|
||
"nordRouting": "Routing NordVPN",
|
||
"nordRoutingDesc": "Opsi ini akan mengalihkan lalu lintas berdasarkan tujuan tertentu melalui NordVPN.",
|
||
"Template": "Template Konfigurasi Xray Lanjutan",
|
||
"TemplateDesc": "File konfigurasi Xray akhir akan dibuat berdasarkan template ini.",
|
||
"FreedomStrategy": "Strategi Protokol Freedom",
|
||
"FreedomStrategyDesc": "Atur strategi output untuk jaringan dalam Protokol Freedom.",
|
||
"RoutingStrategy": "Strategi Pengalihan Keseluruhan",
|
||
"RoutingStrategyDesc": "Atur strategi pengalihan lalu lintas keseluruhan untuk menyelesaikan semua permintaan.",
|
||
"outboundTestUrl": "URL tes outbound",
|
||
"outboundTestUrlDesc": "URL yang digunakan saat menguji konektivitas outbound",
|
||
"Torrent": "Blokir Protokol BitTorrent",
|
||
"Inbounds": "Masuk",
|
||
"InboundsDesc": "Menerima klien tertentu.",
|
||
"Outbounds": "Keluar",
|
||
"Balancers": "Penyeimbang",
|
||
"OutboundsDesc": "Atur jalur lalu lintas keluar.",
|
||
"Routings": "Aturan Pengalihan",
|
||
"RoutingsDesc": "Prioritas setiap aturan penting!",
|
||
"completeTemplate": "Semua",
|
||
"logLevel": "Tingkat Log",
|
||
"logLevelDesc": "Tingkat log untuk log kesalahan, menunjukkan informasi yang perlu dicatat.",
|
||
"accessLog": "Log Akses",
|
||
"accessLogDesc": "Jalur file untuk log akses. Nilai khusus 'tidak ada' menonaktifkan log akses",
|
||
"errorLog": "Catatan eror",
|
||
"errorLogDesc": "Jalur file untuk log kesalahan. Nilai khusus 'tidak ada' menonaktifkan log kesalahan",
|
||
"dnsLog": "Log DNS",
|
||
"dnsLogDesc": "Apakah akan mengaktifkan log kueri DNS",
|
||
"maskAddress": "Alamat Masker",
|
||
"maskAddressDesc": "Masker alamat IP, ketika diaktifkan, akan secara otomatis mengganti alamat IP yang muncul di log.",
|
||
"statistics": "Statistik",
|
||
"statsInboundUplink": "Statistik Unggah Masuk",
|
||
"statsInboundUplinkDesc": "Mengaktifkan pengumpulan statistik untuk lalu lintas unggah dari semua proxy masuk.",
|
||
"statsInboundDownlink": "Statistik Unduh Masuk",
|
||
"statsInboundDownlinkDesc": "Mengaktifkan pengumpulan statistik untuk lalu lintas unduh dari semua proxy masuk.",
|
||
"statsOutboundUplink": "Statistik Unggah Keluar",
|
||
"statsOutboundUplinkDesc": "Mengaktifkan pengumpulan statistik untuk lalu lintas unggah dari semua proxy keluar.",
|
||
"statsOutboundDownlink": "Statistik Unduh Keluar",
|
||
"statsOutboundDownlinkDesc": "Mengaktifkan pengumpulan statistik untuk lalu lintas unduh dari semua proxy keluar.",
|
||
"rules": {
|
||
"first": "Pertama",
|
||
"last": "Terakhir",
|
||
"up": "Naik",
|
||
"down": "Turun",
|
||
"source": "Sumber",
|
||
"dest": "Tujuan",
|
||
"inbound": "Masuk",
|
||
"outbound": "Keluar",
|
||
"balancer": "Pengimbang",
|
||
"info": "Info",
|
||
"add": "Tambahkan Aturan",
|
||
"edit": "Edit Aturan",
|
||
"useComma": "Item yang dipisahkan koma"
|
||
},
|
||
"outbound": {
|
||
"addOutbound": "Tambahkan Keluar",
|
||
"addReverse": "Tambahkan Revers",
|
||
"editOutbound": "Edit Keluar",
|
||
"editReverse": "Edit Revers",
|
||
"reverseTag": "Tag Revers",
|
||
"reverseTagDesc": "Tag outbound proxy revers sederhana VLESS. Kosongkan untuk menonaktifkan.",
|
||
"reverseTagPlaceholder": "tag outbound (kosong untuk menonaktifkan)",
|
||
"tag": "Tag",
|
||
"tagDesc": "Tag Unik",
|
||
"address": "Alamat",
|
||
"reverse": "Revers",
|
||
"domain": "Domain",
|
||
"type": "Tipe",
|
||
"bridge": "Jembatan",
|
||
"portal": "Portal",
|
||
"link": "Tautan",
|
||
"intercon": "Interkoneksi",
|
||
"settings": "Pengaturan",
|
||
"accountInfo": "Informasi Akun",
|
||
"outboundStatus": "Status Keluar",
|
||
"sendThrough": "Kirim Melalui",
|
||
"test": "Tes",
|
||
"testResult": "Hasil Tes",
|
||
"testing": "Menguji koneksi...",
|
||
"testSuccess": "Tes berhasil",
|
||
"testFailed": "Tes gagal",
|
||
"testError": "Gagal menguji outbound",
|
||
"nordvpn": "NordVPN",
|
||
"accessToken": "Token Akses",
|
||
"country": "Negara",
|
||
"server": "Server",
|
||
"city": "Kota",
|
||
"allCities": "Semua Kota",
|
||
"privateKey": "Kunci Privat",
|
||
"load": "Beban"
|
||
},
|
||
"balancer": {
|
||
"addBalancer": "Tambahkan Penyeimbang",
|
||
"editBalancer": "Sunting Penyeimbang",
|
||
"balancerStrategy": "Strategi",
|
||
"balancerSelectors": "Penyeleksi",
|
||
"tag": "Menandai",
|
||
"tagDesc": "Label Unik",
|
||
"balancerDesc": "BalancerTag dan outboundTag tidak dapat digunakan secara bersamaan. Jika digunakan secara bersamaan, hanya outboundTag yang akan berfungsi."
|
||
},
|
||
"wireguard": {
|
||
"secretKey": "Kunci Rahasia",
|
||
"publicKey": "Kunci Publik",
|
||
"allowedIPs": "IP yang Diizinkan",
|
||
"endpoint": "Titik Akhir",
|
||
"psk": "Kunci Pra-Bagi",
|
||
"domainStrategy": "Strategi Domain"
|
||
},
|
||
"tun": {
|
||
"nameDesc": "Nama antarmuka TUN. Standar adalah 'xray0'",
|
||
"mtuDesc": "Unit Transmisi Maksimum. Ukuran maksimum paket data. Standar adalah 1500",
|
||
"userLevel": "Level Pengguna",
|
||
"userLevelDesc": "Semua koneksi yang dibuat melalui inbound ini akan menggunakan level pengguna ini. Standar adalah 0"
|
||
},
|
||
"dns": {
|
||
"enable": "Aktifkan DNS",
|
||
"enableDesc": "Aktifkan server DNS bawaan",
|
||
"tag": "Tanda DNS Masuk",
|
||
"tagDesc": "Tanda ini akan tersedia sebagai tanda masuk dalam aturan penataan.",
|
||
"clientIp": "IP Klien",
|
||
"clientIpDesc": "Digunakan untuk memberi tahu server tentang lokasi IP yang ditentukan selama kueri DNS",
|
||
"disableCache": "Nonaktifkan cache",
|
||
"disableCacheDesc": "Menonaktifkan caching DNS",
|
||
"disableFallback": "Nonaktifkan Fallback",
|
||
"disableFallbackDesc": "Menonaktifkan kueri DNS fallback",
|
||
"disableFallbackIfMatch": "Nonaktifkan Fallback Jika Cocok",
|
||
"disableFallbackIfMatchDesc": "Menonaktifkan kueri DNS fallback ketika daftar domain yang cocok dari server DNS terpenuhi",
|
||
"enableParallelQuery": "Aktifkan Kueri Paralel",
|
||
"enableParallelQueryDesc": "Aktifkan kueri DNS paralel ke beberapa server untuk resolusi yang lebih cepat",
|
||
"strategy": "Strategi Kueri",
|
||
"strategyDesc": "Strategi keseluruhan untuk menyelesaikan nama domain",
|
||
"add": "Tambahkan Server",
|
||
"edit": "Sunting Server",
|
||
"domains": "Domains",
|
||
"expectIPs": "IP yang Diharapkan",
|
||
"unexpectIPs": "IP tak terduga",
|
||
"useSystemHosts": "Gunakan Hosts Sistem",
|
||
"useSystemHostsDesc": "Gunakan file hosts dari sistem yang terinstal",
|
||
"serveStale": "Sajikan Kedaluwarsa",
|
||
"serveStaleDesc": "Mengembalikan hasil cache yang kedaluwarsa saat memperbarui di latar belakang",
|
||
"serveExpiredTTL": "TTL Kedaluwarsa",
|
||
"serveExpiredTTLDesc": "Masa berlaku (detik) entri cache kedaluwarsa; 0 = tidak pernah kedaluwarsa",
|
||
"timeoutMs": "Batas waktu (ms)",
|
||
"skipFallback": "Lewati Fallback",
|
||
"finalQuery": "Kueri Akhir",
|
||
"hosts": "Hosts",
|
||
"hostsAdd": "Tambah Host",
|
||
"hostsEmpty": "Tidak ada Host yang ditentukan",
|
||
"hostsDomain": "Domain (mis. domain:example.com)",
|
||
"hostsValues": "IP atau domain — ketik dan tekan Enter",
|
||
"usePreset": "Gunakan templat",
|
||
"dnsPresetTitle": "Templat DNS",
|
||
"dnsPresetFamily": "Keluarga",
|
||
"clearAll": "Hapus Semua",
|
||
"clearAllTitle": "Hapus semua server DNS?",
|
||
"clearAllConfirm": "Ini akan menghapus semua server DNS dari daftar. Tidak dapat dibatalkan."
|
||
},
|
||
"fakedns": {
|
||
"add": "Tambahkan DNS Palsu",
|
||
"edit": "Edit DNS Palsu",
|
||
"ipPool": "Subnet Kumpulan IP",
|
||
"poolSize": "Ukuran Kolam"
|
||
}
|
||
}
|
||
},
|
||
"tgbot": {
|
||
"keyboardClosed": "❌ Keyboard ditutup!",
|
||
"noResult": "❗ Tidak ada hasil!",
|
||
"noQuery": "❌ Kueri tidak ditemukan! Silakan gunakan perintah lagi!",
|
||
"wentWrong": "❌ Terjadi kesalahan!",
|
||
"noIpRecord": "❗ Tidak ada Catatan IP!",
|
||
"noInbounds": "❗ Tidak ada inbound yang ditemukan!",
|
||
"unlimited": "♾ Tidak terbatas (Reset)",
|
||
"add": "Tambah",
|
||
"month": "Bulan",
|
||
"months": "Bulan",
|
||
"day": "Hari",
|
||
"days": "Hari",
|
||
"hours": "Jam",
|
||
"minutes": "Menit",
|
||
"unknown": "Tidak diketahui",
|
||
"inbounds": "Inbound",
|
||
"clients": "Klien",
|
||
"offline": "🔴 Offline",
|
||
"online": "🟢 Online",
|
||
"commands": {
|
||
"unknown": "❗ Perintah tidak dikenal.",
|
||
"pleaseChoose": "👇 Harap pilih:\r\n",
|
||
"help": "🤖 Selamat datang di bot ini! Ini dirancang untuk menyediakan data tertentu dari panel web dan memungkinkan Anda melakukan modifikasi sesuai kebutuhan.\r\n\r\n",
|
||
"start": "👋 Halo <i>{{ .Firstname }}</i>.\r\n",
|
||
"welcome": "🤖 Selamat datang di <b>{{.Hostname }}</b> bot managemen.\r\n",
|
||
"status": "✅ Bot dalam keadaan baik!",
|
||
"usage": "❗ Harap berikan teks untuk mencari!",
|
||
"getID": "🆔 ID Anda: <code>{{ .ID }}</code>",
|
||
"helpAdminCommands": "Untuk memulai ulang Xray Core:\r\n<code>/restart</code>\r\n\r\nUntuk mencari email klien:\r\n<code>/usage [Email]</code>\r\n\r\nUntuk mencari inbound (dengan statistik klien):\r\n<code>/inbound [Catatan]</code>\r\n\r\nID Obrolan Telegram:\r\n<code>/id</code>",
|
||
"helpClientCommands": "Untuk mencari statistik, gunakan perintah berikut:\r\n<code>/usage [Email]</code>\r\n\r\nID Obrolan Telegram:\r\n<code>/id</code>",
|
||
"restartUsage": "\r\n\r\n<code>/restart</code>",
|
||
"restartSuccess": "✅ Operasi berhasil!",
|
||
"restartFailed": "❗ Kesalahan dalam operasi.\r\n\r\n<code>Error: {{ .Error }}</code>.",
|
||
"xrayNotRunning": "❗ Xray Core tidak berjalan.",
|
||
"startDesc": "Tampilkan menu utama",
|
||
"helpDesc": "Bantuan bot",
|
||
"statusDesc": "Periksa status bot",
|
||
"idDesc": "Tampilkan ID Telegram Anda"
|
||
},
|
||
"messages": {
|
||
"cpuThreshold": "🔴 Beban CPU {{ .Percent }}% melebihi batas {{ .Threshold }}%",
|
||
"selectUserFailed": "❌ Kesalahan dalam pemilihan pengguna!",
|
||
"userSaved": "✅ Pengguna Telegram tersimpan.",
|
||
"loginSuccess": "✅ Berhasil masuk ke panel.\r\n",
|
||
"loginFailed": "❗️ Gagal masuk ke panel.\r\n",
|
||
"2faFailed": "2FA Gagal",
|
||
"report": "🕰 Laporan Terjadwal: {{ .RunTime }}\r\n",
|
||
"datetime": "⏰ Tanggal & Waktu: {{ .DateTime }}\r\n",
|
||
"hostname": "💻 Host: {{ .Hostname }}\r\n",
|
||
"version": "🚀 Versi 3X-UI: {{ .Version }}\r\n",
|
||
"xrayVersion": "📡 Versi 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": "⏳ Waktu Aktif: {{ .UpTime }} {{ .Unit }}\r\n",
|
||
"serverLoad": "📈 Beban Sistem: {{ .Load1 }}, {{ .Load2 }}, {{ .Load3 }}\r\n",
|
||
"serverMemory": "📋 RAM: {{ .Current }}/{{ .Total }}\r\n",
|
||
"tcpCount": "🔹 TCP: {{ .Count }}\r\n",
|
||
"udpCount": "🔸 UDP: {{ .Count }}\r\n",
|
||
"traffic": "🚦 Lalu Lintas: {{ .Total }} (↑{{ .Upload }},↓{{ .Download }})\r\n",
|
||
"xrayStatus": "ℹ️ Status: {{ .State }}\r\n",
|
||
"username": "👤 Nama Pengguna: {{ .Username }}\r\n",
|
||
"reason": "❗️ Alasan: {{ .Reason }}\r\n",
|
||
"time": "⏰ Waktu: {{ .Time }}\r\n",
|
||
"inbound": "📍 Inbound: {{ .Remark }}\r\n",
|
||
"port": "🔌 Port: {{ .Port }}\r\n",
|
||
"expire": "📅 Tanggal Kadaluarsa: {{ .Time }}\r\n",
|
||
"expireIn": "📅 Kadaluarsa Dalam: {{ .Time }}\r\n",
|
||
"active": "💡 Aktif: {{ .Enable }}\r\n",
|
||
"enabled": "🚨 Diaktifkan: {{ .Enable }}\r\n",
|
||
"online": "🌐 Status Koneksi: {{ .Status }}\r\n",
|
||
"lastOnline": "🔙 Terakhir online: {{ .Time }}\r\n",
|
||
"email": "📧 Email: {{ .Email }}\r\n",
|
||
"upload": "🔼 Unggah: ↑{{ .Upload }}\r\n",
|
||
"download": "🔽 Unduh: ↓{{ .Download }}\r\n",
|
||
"total": "📊 Total: ↑↓{{ .UpDown }} / {{ .Total }}\r\n",
|
||
"TGUser": "👤 Pengguna Telegram: {{ .TelegramID }}\r\n",
|
||
"exhaustedMsg": "🚨 Habis {{ .Type }}:\r\n",
|
||
"exhaustedCount": "🚨 Jumlah Habis {{ .Type }}:\r\n",
|
||
"onlinesCount": "🌐 Klien Online: {{ .Count }}\r\n",
|
||
"disabled": "🛑 Dinonaktifkan: {{ .Disabled }}\r\n",
|
||
"depleteSoon": "🔜 Habis Sebentar: {{ .Deplete }}\r\n\r\n",
|
||
"backupTime": "🗄 Waktu Backup: {{ .Time }}\r\n",
|
||
"refreshedOn": "\r\n📋🔄 Diperbarui Pada: {{ .Time }}\r\n\r\n",
|
||
"yes": "✅ Ya",
|
||
"no": "❌ Tidak",
|
||
"received_id": "🔑📥 ID diperbarui.",
|
||
"received_password": "🔑📥 Kata sandi diperbarui.",
|
||
"received_email": "📧📥 Email diperbarui.",
|
||
"received_comment": "💬📥 Komentar diperbarui.",
|
||
"id_prompt": "🔑 ID Default: {{ .ClientId }}\n\nMasukkan ID Anda.",
|
||
"pass_prompt": "🔑 Kata Sandi Default: {{ .ClientPassword }}\n\nMasukkan kata sandi Anda.",
|
||
"email_prompt": "📧 Email Default: {{ .ClientEmail }}\n\nMasukkan email Anda.",
|
||
"comment_prompt": "💬 Komentar Default: {{ .ClientComment }}\n\nMasukkan komentar Anda.",
|
||
"inbound_client_data_id": "🔄 Masuk: {{ .InboundRemark }}\n\n🔑 ID: {{ .ClientId }}\n📧 Email: {{ .ClientEmail }}\n📊 Lalu lintas: {{ .ClientTraffic }}\n📅 Tanggal Kedaluwarsa: {{ .ClientExp }}\n🌐 Batas IP: {{ .IpLimit }}\n💬 Komentar: {{ .ClientComment }}\n\nSekarang kamu bisa menambahkan klien ke inbound!",
|
||
"inbound_client_data_pass": "🔄 Masuk: {{ .InboundRemark }}\n\n🔑 Kata sandi: {{ .ClientPass }}\n📧 Email: {{ .ClientEmail }}\n📊 Lalu lintas: {{ .ClientTraffic }}\n📅 Tanggal Kedaluwarsa: {{ .ClientExp }}\n🌐 Batas IP: {{ .IpLimit }}\n💬 Komentar: {{ .ClientComment }}\n\nSekarang kamu bisa menambahkan klien ke inbound!",
|
||
"cancel": "❌ Proses Dibatalkan! \n\nAnda dapat /start lagi kapan saja. 🔄",
|
||
"error_add_client": "⚠️ Kesalahan:\n\n {{ .error }}",
|
||
"using_default_value": "Oke, saya akan tetap menggunakan nilai default. 😊",
|
||
"incorrect_input": "Masukan Anda tidak valid.\nFrasa harus berlanjut tanpa spasi.\nContoh benar: aaaaaa\nContoh salah: aaa aaa 🚫",
|
||
"AreYouSure": "Apakah kamu yakin? 🤔",
|
||
"SuccessResetTraffic": "📧 Email: {{ .ClientEmail }}\n🏁 Hasil: ✅ Berhasil",
|
||
"FailedResetTraffic": "📧 Email: {{ .ClientEmail }}\n🏁 Hasil: ❌ Gagal \n\n🛠️ Kesalahan: [ {{ .ErrorMessage }} ]",
|
||
"FinishProcess": "🔚 Proses reset traffic selesai untuk semua klien."
|
||
},
|
||
"buttons": {
|
||
"closeKeyboard": "❌ Tutup Papan Ketik",
|
||
"cancel": "❌ Batal",
|
||
"cancelReset": "❌ Batal Reset",
|
||
"cancelIpLimit": "❌ Batal Batas IP",
|
||
"confirmResetTraffic": "✅ Konfirmasi Reset Lalu Lintas?",
|
||
"confirmClearIps": "✅ Konfirmasi Hapus IPs?",
|
||
"confirmRemoveTGUser": "✅ Konfirmasi Hapus Pengguna Telegram?",
|
||
"confirmToggle": "✅ Konfirmasi Aktifkan/Nonaktifkan Pengguna?",
|
||
"dbBackup": "Dapatkan Cadangan DB",
|
||
"serverUsage": "Penggunaan Server",
|
||
"getInbounds": "Dapatkan Inbounds",
|
||
"depleteSoon": "Habis Sebentar",
|
||
"clientUsage": "Dapatkan Penggunaan",
|
||
"onlines": "Klien Online",
|
||
"commands": "Perintah",
|
||
"refresh": "🔄 Perbarui",
|
||
"clearIPs": "❌ Hapus IPs",
|
||
"removeTGUser": "❌ Hapus Pengguna Telegram",
|
||
"selectTGUser": "👤 Pilih Pengguna Telegram",
|
||
"selectOneTGUser": "👤 Pilih Pengguna Telegram:",
|
||
"resetTraffic": "📈 Reset Lalu Lintas",
|
||
"resetExpire": "📅 Ubah Tanggal Kadaluarsa",
|
||
"ipLog": "🔢 Log IP",
|
||
"ipLimit": "🔢 Batas IP",
|
||
"setTGUser": "👤 Set Pengguna Telegram",
|
||
"toggle": "🔘 Aktifkan / Nonaktifkan",
|
||
"custom": "🔢 Kustom",
|
||
"confirmNumber": "✅ Konfirmasi: {{ .Num }}",
|
||
"confirmNumberAdd": "✅ Konfirmasi menambahkan: {{ .Num }}",
|
||
"limitTraffic": "🚧 Batas Lalu Lintas",
|
||
"getBanLogs": "Dapatkan Log Pemblokiran",
|
||
"allClients": "Semua Klien",
|
||
"addClient": "Tambah Klien",
|
||
"submitDisable": "Kirim Sebagai Nonaktif ☑️",
|
||
"submitEnable": "Kirim Sebagai Aktif ✅",
|
||
"use_default": "🏷️ Gunakan Default",
|
||
"change_id": "⚙️🔑 ID",
|
||
"change_password": "⚙️🔑 Kata Sandi",
|
||
"change_email": "⚙️📧 Email",
|
||
"change_comment": "⚙️💬 Komentar",
|
||
"change_flow": "⚙️🚦 Flow",
|
||
"ResetAllTraffics": "Reset Semua Lalu Lintas",
|
||
"SortedTrafficUsageReport": "Laporan Penggunaan Lalu Lintas yang Terurut"
|
||
},
|
||
"answers": {
|
||
"successfulOperation": "✅ Operasi berhasil!",
|
||
"errorOperation": "❗ Kesalahan dalam operasi.",
|
||
"getInboundsFailed": "❌ Gagal mendapatkan inbounds.",
|
||
"getClientsFailed": "❌ Gagal mendapatkan klien.",
|
||
"canceled": "❌ {{ .Email }}: Operasi dibatalkan.",
|
||
"clientRefreshSuccess": "✅ {{ .Email }}: Klien diperbarui dengan berhasil.",
|
||
"IpRefreshSuccess": "✅ {{ .Email }}: IP diperbarui dengan berhasil.",
|
||
"TGIdRefreshSuccess": "✅ {{ .Email }}: Pengguna Telegram Klien diperbarui dengan berhasil.",
|
||
"resetTrafficSuccess": "✅ {{ .Email }}: Lalu lintas direset dengan berhasil.",
|
||
"setTrafficLimitSuccess": "✅ {{ .Email }}: Batas lalu lintas disimpan dengan berhasil.",
|
||
"expireResetSuccess": "✅ {{ .Email }}: Hari kadaluarsa direset dengan berhasil.",
|
||
"resetIpSuccess": "✅ {{ .Email }}: Batas IP {{ .Count }} disimpan dengan berhasil.",
|
||
"clearIpSuccess": "✅ {{ .Email }}: IP dihapus dengan berhasil.",
|
||
"getIpLog": "✅ {{ .Email }}: Dapatkan Log IP.",
|
||
"getUserInfo": "✅ {{ .Email }}: Dapatkan Info Pengguna Telegram.",
|
||
"removedTGUserSuccess": "✅ {{ .Email }}: Pengguna Telegram dihapus dengan berhasil.",
|
||
"enableSuccess": "✅ {{ .Email }}: Diaktifkan dengan berhasil.",
|
||
"disableSuccess": "✅ {{ .Email }}: Dinonaktifkan dengan berhasil.",
|
||
"askToAddUserId": "Konfigurasi Anda tidak ditemukan!\r\nSilakan minta admin Anda untuk menggunakan ChatID Telegram Anda dalam konfigurasi Anda.\r\n\r\nChatID Pengguna Anda: <code>{{ .TgUserID }}</code>",
|
||
"chooseClient": "Pilih Klien untuk Inbound {{ .Inbound }}",
|
||
"chooseInbound": "Pilih Inbound"
|
||
}
|
||
}
|
||
} |