netx/web/src/i18n/en.ts
oliver 6642f4b8b5 Add collect dashboard KPIs and create-job modal.
Align collection tasks with LLDP/config-sync chrome, and refresh active-task overview when jobs change.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-05 14:08:13 +08:00

1694 lines
63 KiB
TypeScript

const en = {
common: {
help: "Help",
refresh: "Refresh",
refreshing: "Refreshing…",
query: "Search",
clearFilters: "Clear filters",
prevPage: "Previous",
nextPage: "Next",
perPage: "{{n}}/page",
pagerMeta: "{{total}} items · page {{page}}/{{pages}}",
opFailed: "Operation failed",
empty: "-",
cancel: "Cancel",
},
workbench: {
title: "Workbench",
modules: "Modules",
sections: "Sections",
moduleCount: "{{count}} modules",
backToWorkbench: "Back to Workbench",
openInNewTab: "Open in new tab",
openModule: "Open or switch to this module tab",
monitoring: "System integration",
operations: "Operations",
system: "System",
welcomeEyebrow: "Workbench",
welcomeTitle: "Welcome back, {{name}}",
welcomeSub: "Monitor sync, topology, and ops tasks from one place.",
today: "Today · {{date}}",
kpi: {
uptime: "API uptime",
health: "System health",
tasks: "Active tasks",
healthOk: "Schedulers healthy",
healthWarn: "Check schedulers",
healthUnknown: "Metrics unavailable",
},
statusTitle: "Runtime status",
announceTitle: "Announcements",
recentTitle: "Recent operations",
recentMore: "View all",
quickTitle: "Quick actions",
emptyRecent: "No recent audit entries.",
gauge: {
cpu: "CPU",
mem: "Memory",
cli: "CLI budget",
db: "DB pool",
webcrt: "WebCRT",
tasks: "Active tasks",
low: "Low load",
normal: "Normal",
high: "Busy",
},
quick: {
tasks: "Task overview",
logs: "Audit logs",
users: "User admin",
apiKeys: "API keys",
},
announce: {
a1: "Dark workbench shell is live — other modules follow the same palette.",
a1Date: "2026-08-04",
a2: "LLDP collect supports pause / resume / stop; interrupted jobs resume after restart.",
a2Date: "2026-08-04",
a3: "Config sync and LLDP KPI boards refresh while jobs are running.",
a3Date: "2026-08-03",
},
recentCols: {
action: "Action",
module: "Module",
time: "Time",
user: "User",
},
cards: {
umeSync: "UME Sync",
umeSyncDesc: "UME alarms, subscription & inventory",
network: "Network Management",
networkDesc: "NE inventory, alarms, config sync, and monitoring tasks",
managedNe: "NE Management",
managedNeDesc: "Multi-vendor SSH/Telnet NEs & connectivity test",
collect: "Batch Collect",
collectDesc: "Run custom CLI commands on connected NEs and save logs",
webcrt: "WebCRT",
webcrtDesc: "Interactive browser login to connected NEs (SSH/Telnet)",
topology: "Topology",
topologyDesc: "Fabric topology views; LLDP physical discovery; MCP paged graph query",
portTrafficWall: "Traffic wall",
portTrafficWallDesc: "Named traffic boards in dedicated tabs",
users: "Users",
usersDesc: "Admin: create, disable users and reset passwords",
audit: "Audit",
auditDesc: "Live task overview and operation logs",
apiKeys: "API Keys",
apiKeysDesc: "Issue MCP/script tokens per user with expiry",
},
},
network: {
title: "Network Management",
nav: {
detail: "Network details",
tasks: "Monitoring tasks",
topologyMgmt: "Topology management",
devices: "Device list",
topology: "Topology",
lldpLinks: "LLDP",
topoClassify: "Classify / slices",
alarms: "Alarms",
configs: "Configurations",
webcrt: "WebCRT",
collectTasks: "Collection tasks",
configSync: "Config sync",
portTraffic: "Port traffic",
portTrafficWall: "Traffic wall",
},
collapseNav: "Collapse sidebar",
expandNav: "Expand sidebar",
placeholder: {
title: "Coming soon",
body: "This capability is not available yet.",
},
},
portTraffic: {
title: "Port traffic",
create: "Monitor device",
backList: "Back to list",
created: "Device monitoring added",
createFailed: "Create failed",
updated: "Saved",
interfacesSaved: "Interfaces updated",
started: "Started",
paused: "Paused",
stopped: "Stopped",
statusRunning: "Running",
statusPaused: "Paused",
statusStopped: "Stopped",
collecting: "Collecting",
deleted: "Deleted",
confirmDelete: "Delete this device monitoring and its samples?",
empty: "No devices monitored yet. Pick a NE and interfaces to start.",
wall: "Ops wall",
wallTitle: "Port traffic wall",
toDevices: "Manage devices",
wallEmpty: "No monitored devices yet. Add a device and pick interfaces first.",
fullscreen: "Fullscreen",
exitFullscreen: "Exit fullscreen",
board: "Board",
boardPick: "Select board",
boardPickHint: "Select or create a traffic wall board.",
boardEmpty: "No boards yet. Create one for cutover or duty monitoring.",
boardDefaultName: "Untitled board",
boardName: "Name",
boardCols: "Columns",
boardCreate: "New board",
boardEdit: "Edit layout",
boardSave: "Save",
boardCancel: "Cancel",
boardSaved: "Board saved",
boardCreated: "Board created",
boardDeleted: "Board deleted",
boardConfirmDelete: "Delete this board and its panels?",
boardUnsavedLeave: "You have unsaved changes. Leave anyway?",
boardOpenTab: "Open in new tab",
boardOpen: "Open wall",
boardListHint: "Open a board in its own browser tab; launching Network from the workbench will not refresh open walls.",
boardPanelCount: "Panels",
boardUpdated: "Updated",
boardClosedHint: "You can close this tab, or reopen from Network → Traffic wall list.",
boardAddPanel: "Add panel",
boardNoPanels: "No panels yet. Edit the board and add interfaces.",
boardPickDevice: "Select device",
boardPanelSettings: "Panel settings",
boardPanelTitle: "Title (optional)",
boardColSpan: "Column span",
boardPanelStale: "Interface missing or inactive. Please reselect.",
boardRetarget: "Reselect port",
edit: "Edit",
save: "Save",
start: "Start",
pause: "Pause",
stop: "Stop",
delete: "Delete",
wizStep1: "Device",
wizStep2: "Interfaces",
wizStep3: "Params",
step1: "Params",
step2: "NEs",
step3: "Interfaces",
step4: "Confirm",
prev: "Back",
next: "Next",
fieldTitle: "Task name",
titlePh: "e.g. Access uplinks",
titleRequired: "Task name is required",
portsRequired: "Select at least one interface",
interval: "Interval (sec)",
retention: "Retention (days)",
retentionHintWizard: "Use ≥8 days for week-over-week compare",
concurrency: "Concurrency",
note: "Note",
notePh: "Optional",
startNow: "Start after create",
neKeywordPh: "Name / IP / vendor",
deepLinkHint: "Opened from topology: NE {{ne}}{{ifname}}. Confirm selection, then continue.",
deepLinkIfname: ", interface {{ifname}}",
selectedNe: "{{count}} NE(s) selected",
selectedOneNe: "Selected",
selectedPorts: "{{count}} interface(s) selected",
pickNeFirst: "Select one NE first",
discover: "Discover interfaces",
discoverDetail: "Discover detail",
discoverCommand: "Command",
discoverRaw: "Raw log",
discoverOk: "Discover succeeded",
discoverFail: "Discover failed",
confirmCreate: "Start monitoring",
wallTask: "Task",
wallPort: "Interface",
wallDevice: "Device",
wallDeviceLabel: "Device",
pickPort: "Select interface",
range: "Range",
compare: "Compare",
compareOff: "Off",
compareDay: "Yesterday (same TOD)",
compareWeek: "Last week (same weekday)",
compareShift: "Previous window",
compareCustom: "Custom offset",
offsetHours: "Offset (hours)",
aheadHours: "Extend past now (hours)",
aheadHoursHint:
"Push the chart end past now. With period compare enabled, the overlay can show trend after the current clock; 0 ends at now.",
mapBaselineHint:
"Mapped baseline can be combined with period compare; turn on Yesterday yourself when you need that contrast.",
baseline: "Baseline",
baselineAt: "Baseline time",
baselineDevice: "Baseline device",
baselinePort: "Baseline interface",
seriesCurrentIn: "Current In",
seriesCurrentOut: "Current Out",
seriesBaselineIn: "Baseline In",
seriesBaselineOut: "Baseline Out",
mapBaselinePort: "Mapped baseline interface",
mapBaselineTask: "Mapped baseline task",
mapBaselineNone: "None",
retentionHint: "Week compare needs retention_days ≥ 8 or the baseline may be empty.",
collectNow: "Collect now",
collectStarted: "Collect triggered",
collectBusy: "Collect already running",
targetError: "Last interface error",
waitSamples: "Samples appear after the device runs; or click Collect now. Chart auto-refreshes.",
waitAfterError: "Collect failed — open Logs under Traffic monitoring for details, then Collect now.",
log: "Logs",
logTitle: "Collect logs",
logEmpty: "No log entries yet.",
logDevice: "Device",
logLevel: "Level",
logTime: "Time",
logMessage: "Message",
logClose: "Close",
logHasError: "Has error",
rebind: "Rebind NE",
rebindTitle: "Rebind network element",
rebindOk: "Rebound to selected NE; collect can resume",
rebindFailed: "Rebind failed",
rebindHint:
"Pick the correct NE and confirm. IPs may not be unique, and a matching IP does not mean the same device. History is kept.",
rebindCurrent: "Current binding",
rebindEmpty: "No NEs found — add the device back under inventory first.",
rebindSameIp: "Same IP",
rebindSameIpHint: "IP matches current monitor — still verify name and ID",
rebindSubmit: "Confirm rebind",
rebindConfirm: "Bind monitoring to {{ne}}? History samples are kept.",
err: {
managedNeNotFound:
"Managed NE not found (often deleted then re-created with a new id). Use Rebind NE and pick manually.",
targetErrors: "{{count}} interface(s) failed",
unsupportedVendor: "Unsupported vendor / device type",
invalidSource: "Invalid NE source",
rebindPickFirst: "Select an NE first",
rebindNeMissing: "Selected NE not found — refresh and try again",
rebindSourceMismatch: "NE source does not match this monitor device",
alreadyMonitored: "That NE is already monitored by another device entry",
},
kpiLatest: "latest",
kpiBw: "Bandwidth",
latestAt: "Latest sample",
hoverPoint: "Cursor",
wallLiveTitle: "Live traffic",
filterView: "View",
filterMap: "Mapped baseline",
yMode: "Y axis",
yModeAuto: "Auto (incl. baseline)",
yModeCurrent: "Fit current",
yModeUtil: "Utilization %",
kpi: {
tasks: "Tasks",
devices: "Devices",
running: "Running",
ports: "Interfaces",
samples24h: "Samples 24h",
},
col: {
title: "Task",
device: "Device",
status: "Status",
ports: "Interfaces",
interval: "Interval",
last: "Last collect",
actions: "Actions",
source: "Source",
name: "Device",
vendor: "Vendor",
ifname: "Interface",
bw: "BW",
upDown: "Admin / Phy / Prot",
admin: "Admin",
phy: "Phy",
prot: "Prot",
description: "Description",
},
},
lldpLinks: {
title: "LLDP links",
collectNow: "Collect now",
started: "LLDP collect started",
pause: "Pause",
resume: "Resume",
stop: "Stop",
confirmStop: "Stop this job and skip remaining NEs?",
paused: "Paused",
resumed: "Resumed",
stopped: "Collect stopped",
policyTitle: "Collect policy",
policySaved: "Policy saved",
enabled: "Enable scheduled collect",
autoAddUnmatched: "Create SSH placeholder NEs for unmatched neighbors",
intervalDays: "Interval (days)",
interval: "Collect interval",
intervalUnitDays: "days",
intervalUnitHours: "hours",
concurrency: "Concurrency",
historyKeep: "Jobs to keep",
scope: "Scope",
scopeAll: "All NEs (managed + UME)",
scopeSelected: "Selected NEs",
savePolicy: "Save policy",
selectedCount: "{{count}} selected",
targetKeywordPh: "Name / IP / vendor",
jobsTitle: "Collect jobs",
jobDetailTitle: "Job details",
expand: "Details",
edgesTitle: "Fabric links",
edgeKeywordPh: "Endpoint name or IP",
edgeStatusAll: "All statuses",
edgeStatusActive: "Active",
edgeStatusMissing: "Missing",
missCount: "Miss cycles",
replacedBy: "Replaced",
kpi: {
nodes: "Fabric nodes",
edges: "Active links",
missing: "Missing",
running: "In progress",
idle: "Idle",
last: "Last job",
nextDue: "Next due",
},
col: {
source: "Source",
name: "Device",
vendor: "Vendor",
trigger: "Trigger",
scope: "Scope",
status: "Status",
progress: "Progress",
edges: "Edges",
missingDelta: "Missing/purged",
started: "Started",
ended: "Ended",
neighbors: "Neighbors",
error: "Error",
aSide: "Side A",
bSide: "Side B",
aPort: "Port A",
bPort: "Port B",
lastSeen: "Last seen",
},
},
configSync: {
title: "Config sync",
syncNow: "Sync now",
retryFailed: "Retry failed",
pause: "Pause",
resume: "Resume",
stop: "Stop",
confirmStop: "Stop this cycle and cancel remaining NE tasks?",
started: "Sync cycle started",
paused: "Paused",
resumed: "Resumed",
stopped: "Sync cycle stopped",
policyTitle: "Sync policy",
policySaved: "Policy saved",
enabled: "Enable scheduled sync",
intervalDays: "Interval (days)",
concurrency: "Concurrency",
historyKeep: "Config versions to keep",
cycleKeep: "Jobs to keep",
scope: "Scope",
scopeAll: "All NEs",
scopeSelected: "Selected NEs",
savePolicy: "Save policy",
selectedCount: "{{count}} selected",
targetKeywordPh: "Name / IP / vendor",
cyclesTitle: "Sync cycles",
cyclesEmpty: "No sync cycles yet",
tasksTitle: "Cycle tasks",
taskKeywordPh: "Name / IP / message",
allStatus: "All statuses",
expand: "Details",
collapse: "Collapse",
kpi: {
snapshots: "Snapshots",
running: "In progress",
idle: "Idle",
last: "Last cycle",
nextDue: "Next due",
},
col: {
source: "Source",
name: "Device",
vendor: "Vendor",
trigger: "Trigger",
status: "Status",
progress: "Progress",
started: "Started",
ended: "Ended",
message: "Message",
actions: "Actions",
},
},
networkDevices: {
title: "Device list",
keywordPh: "Name / IP / ID",
allSource: "All sources",
empty: "No network elements.",
col: {
source: "Source",
name: "Name",
vendor: "Vendor",
deviceType: "Device type",
connect: "Connect",
},
},
networkConfigs: {
title: "Configurations",
keywordPh: "Name / IP / ID",
vendorPh: "Vendor",
allSource: "All sources",
empty: "No synced configs yet. Run Config sync first.",
view: "View",
export: "Export",
exportBoth: "Export all",
exportOk: "Config exported",
close: "Close",
tabSet: "Set format",
tabHier: "Hierarchical",
col: {
name: "Device",
vendor: "Vendor",
source: "Source",
size: "Size",
collected: "Collected",
},
},
layout: {
title: "UME Integration",
titleUme: "UME Sync",
titleNetwork: "Network Management",
titleManagedNe: "NE Management",
titleCollect: "Batch Collect",
titleWebcrt: "WebCRT",
titleTopology: "Topology",
titlePortTrafficWall: "Traffic wall",
titleUsers: "Users",
titleAudit: "Audit",
titleApiKeys: "API Keys",
navUme: "UME",
netxApi: "netx api",
oclawBridge: "oclaw WSS",
activeTasks: "Active {{count}}",
activeTasksHint: "Open Audit · Task overview",
language: "Language",
switchLanguage: "Switch language / 切换语言",
moreMenu: "More",
langZh: "中文",
langEn: "English",
},
auth: {
loginTitle: "Sign in",
username: "Username",
password: "Password",
login: "Sign in",
loggingIn: "Signing in…",
loginFailed: "Login failed",
logout: "Sign out",
usersTitle: "User management",
usersHint: "Only admins can create and manage local accounts.",
addUser: "Add user",
role: "Role",
roleAdmin: "Admin",
roleUser: "User",
status: "Status",
active: "Active",
disabled: "Disabled",
enable: "Enable",
disable: "Disable",
actions: "Actions",
newPassword: "New password",
resetPassword: "Reset password",
userCreated: "User created",
userUpdated: "User updated",
auditTitle: "Audit log",
auditHintAdmin: "View login and operation records for all users.",
auditHintUser: "View your own operation records.",
filterUsername: "Filter username",
filterAction: "Filter action",
colTime: "Time",
colUser: "User",
colAction: "Action",
colMethod: "Method",
colPath: "Path",
colStatus: "Status",
colIp: "IP",
apiKeysTitle: "API Key management",
apiKeysHelp:
"The web UI uses login sessions, not API keys — keys are for MCP/scripts. Defaults on create: alarms:read + ne:read + ne:write + ne:exec; refine later with Edit scopes (takes effect immediately, no need to regenerate). Secret is shown once. Admins may issue for other users; scopes cannot exceed the owner's. Unused scopes are reserved for future MCP tools.",
tokenName: "Name",
expiresIn: "Expiry",
expire7d: "7 days",
expire30d: "30 days",
expire90d: "90 days",
expire365d: "1 year",
expireNever: "Never",
tokenOwner: "Owner",
tokenOwnerSelf: "Myself ({{user}})",
createToken: "Create key",
tokenCreated: "API key created",
tokenUpdated: "Scopes updated",
tokenRevoked: "Revoked",
tokenOnceHint: "Copy and store this secret now; it will not be shown again:",
copyToken: "Copy",
tokenCopied: "Copied",
tokenCopyFailed: "Copy failed",
expiresAt: "Expires",
lastUsed: "Last used",
tokenStatusActive: "Active",
tokenStatusExpired: "Expired",
tokenStatusRevoked: "Revoked",
revokeToken: "Revoke",
revokeConfirm: "Revoke this API key?",
editScopes: "Edit scopes",
saveScopes: "Save scopes",
scopesUnsetLegacy: "Unrestricted (same as owner)",
scopesCol: "Scopes",
scopesRequired: "Select at least one scope",
scopesNoneAvailable: "This owner has no grantable scopes",
scopeAlarmsRead: "alarms:read — Alarms read (netx MCP)",
scopeNeRead: "ne:read — NE / topology read (MCP)",
scopeNeWrite: "ne:write — NE / topology write / draw (MCP)",
scopeNeExec: "ne:exec — Managed NE CLI (netx MCP)",
scopeSql: "sql:query — UME SQL (netx MCP)",
scopeWebcrt: "webcrt:session — WebCRT (reserved MCP)",
scopeAdminUsers: "admin:users — User admin (reserved MCP)",
scopeOpsWrite: "ops:write — Ops write / subscriptions (reserved MCP)",
forceChangeTitle: "Change initial password",
forceChangeHint: "Account {{user}} is still using the default password. You must change it before continuing.",
oldPassword: "Current password",
confirmPassword: "Confirm new password",
savePassword: "Save new password",
savingPassword: "Saving…",
passwordTooShort: "New password must be at least 6 characters",
passwordMismatch: "New passwords do not match",
passwordMustChange: "New password must differ from the default/old password",
},
collect: {
create: {
title: "New collection job",
hint: "Configure commands and NEs, then create the job. Click Start in the job list to run collection.",
selectedTitle: "Selected NEs",
selectedEmpty: "Filter below and add NEs one by one.",
selectedCount: "{{count}} selected",
clearSelected: "Clear selection",
remove: "Remove",
pickTitle: "Eligible NEs",
pickHint: "All managed and UME inventory NEs are selectable (any connect status). UME uses CLI connect profile credentials.",
pickEmpty: "No matching NEs.",
filterKeyword: "Filter",
filterKeywordPh: "Name / IP / vendor / device type",
add: "Add",
addBatch: "Add selected ({{count}})",
added: "Added",
create: "Create job",
creating: "Creating…",
expand: "New",
collapse: "Close",
meta: "{{ne}} NE(s) selected · {{cmd}} command(s)",
},
eligible: {
title: "Eligible NEs",
hint: "All managed and UME inventory NEs are selectable (any connect status).",
empty: "No NEs.",
},
form: {
title: "Collection setup",
jobTitle: "Job title",
jobTitlePh: "Auto-generated if empty",
commands: "Commands (one per line, # for comments)",
commandsPh: "display version\ndisplay current-configuration",
commandsHint: "Commands run in order on each device; output is saved as a text log (legacy NetX format).",
meta: "{{ne}} NE(s) selected · {{cmd}} command(s)",
start: "Start collection",
starting: "Submitting…",
},
created: "Collection job created: {{id}}",
started: "Collection started: {{id}}",
paused: "Job paused",
restarted: "Collection restarted",
retryFailedDone: "Retrying failed devices",
deleted: "Job deleted",
nothingToRetry: "No failed devices to retry",
neBusy: "Some NEs are busy in another collection job; wait for it to finish before starting",
jobRunning: "This job is already running",
confirmDelete: "Delete this collection job and its log files?",
jobs: {
title: "Collection jobs",
col: {
title: "Job",
status: "Status",
progress: "Progress",
created: "Created",
lastRun: "Last run",
actions: "Actions",
},
ok: "ok",
fail: "fail",
expand: "Details",
collapse: "Hide",
detailTitle: "Job details",
pause: "Pause",
start: "Start",
starting: "Starting…",
restart: "Restart",
retryFailed: "Retry failed",
downloadResults: "Download results",
delete: "Delete",
autoRefresh: "Auto-refresh every 2s while jobs are running",
runsInProgress: "Some devices are still being collected…",
},
kpi: {
jobs: "Jobs",
running: "In progress",
idle: "Idle",
last: "Last job",
active: "Active jobs",
},
runs: {
status: "Status",
message: "Message",
download: "Log",
downloadFile: "Download",
filterStatus: "Status filter",
filterDevice: "Device filter",
deviceKeywordPh: "Name / IP",
allStatus: "All statuses",
},
},
managedNe: {
title: "Network Elements",
credsNotConfigured:
"NETX_CREDENTIAL_SECRET_KEY is not set. Configure a Fernet key in .env and restart the API to store passwords.",
add: "Add NE",
importBtn: "Import",
downloadTemplate: "Download template",
importing: "Importing…",
edit: "Edit",
delete: "Delete",
openTerminal: "Terminal",
confirmDelete: "Delete this network element?",
batchDelete: "Batch delete",
batchDeleting: "Deleting…",
batchDeleteConfirm: "Delete {{n}} selected network element(s)? This cannot be undone.",
batchDeleteDone: "Deleted {{n}} network element(s)",
batchDeleteSelectRequired: "Select network elements to delete",
keywordPh: "name / IP / user / tags",
allVendors: "All vendors",
allConnectStatus: "All connect status",
stats: {
title: "Overview",
total: "{{n}} NEs managed",
pass: "Connected",
fail: "Failed",
unknown: "Untested",
testing: "Testing",
batchProxy: "Batch add proxy",
batchTest: "Batch connect-test",
tagFilter: "Filter by tag",
all: "All",
allTag: "All (no tag filter)",
noTag: "No tags",
tagStats: "Top tags",
confirm: "Apply to {{n}} NE(s). Continue?",
proxyDone: "Proxy applied to {{n}} NE(s)",
testDone: "Connect-test submitted for {{n}} NE(s)",
loadingIds: "Loading…",
},
col: {
name: "Name",
vendor: "Vendor",
deviceType: "Device type",
source: "Source",
ip: "Management IP",
port: "Port",
protocol: "Protocol",
user: "Username",
password: "Password",
tags: "Tags",
remark: "Remark",
connect: "Connect",
testedAt: "Tested at",
actions: "Actions",
},
source: {
manual: "Manual",
ume_sync: "UME sync",
webcrt: "WebCRT",
lldp: "LLDP placeholder",
},
connect: {
run: "Connectivity test",
running: "Testing…",
submitted: "Submitted tests for {{n}} device(s)",
retest: "Test again",
},
connectDetail: "Details",
connectDetailTitle: "Connectivity test log",
connectDetailEmpty:
"No log yet. Run a connectivity test first; failures store full errors and hop context (passwords excluded).",
umeSync: {
sync: "Sync UME NEs",
syncing: "Syncing…",
delete: "Delete UME NEs",
deleting: "Deleting…",
deleteConfirm: "Delete all managed NEs created from UME sync? This cannot be undone.",
done: "UME sync done: {{inserted}} inserted, {{updated}} updated, {{deleted}} deleted, {{total}} inventory total",
deletedDone: "Deleted {{n}} UME-synced NEs",
},
account: {
batchAdd: "Batch add account",
batchByTag: "Batch account by tag",
batchTitle: "Batch add account",
batchHint: "Apply the account info below to {{n}} selected NE(s).",
batchByTagHint: "Apply the account info below to NEs under the selected tag; leave password blank to update only username.",
batchDone: "Account updated for {{n}} NE(s)",
selectRequired: "Select network elements first",
applying: "Applying…",
apply: "Apply to NEs",
usernameOrPasswordRequired: "Enter at least a username or a password",
passwordOptionalBatch: "leave blank to keep unchanged",
},
help:
"[Bulk import]\n" +
"· Required columns: device_type, ip, username, port, protocol, name, vendor. Download the template first.\n" +
"· password may be empty (required for direct login; optional for bastion-managed or batch proxy later).\n" +
"· Recommended flow: import NEs first, select rows, then use Batch add proxy.\n\n" +
"[Jump / bastion]\n" +
"· Bastion SSH username template: {hop_user}@{target_user}@{target_ip} (bastion host is separate); CLI: ssh user@target@ip@bastion-host.\n" +
"· Bastion-managed: set Jump password (Vault); target password optional. Manual mode needs target password.\n" +
"· JumpServer/CBH often use port 2222; some sites use 22.\n\n" +
"[Connectivity / edit]\n" +
"· NETX_CREDENTIAL_SECRET_KEY required to store passwords.\n" +
"· Leave password blank on edit to keep unchanged; open Details after failed connect-test for hop context (no secrets).",
importResult: {
done: "Import done: {{inserted}} inserted, {{updated}} updated, {{failed}} failed row(s)",
},
form: {
requiredHint: "Fields marked with * are required",
nameConnectHint: "After a successful connectivity test, name is replaced with the device hostname",
createTitle: "Add network element",
editTitle: "Edit network element",
save: "Save",
saving: "Saving…",
cancel: "Cancel",
created: "Network element created",
updated: "Network element updated",
deleted: "Network element deleted",
passwordRequired: "Password is required",
passwordOptional: "leave blank to keep unchanged",
},
hop: {
sectionTitle: "Jump host / proxy",
type: "Jump type",
enable: "Connect to target via jump host",
vendor: {
zte: "ZTE device (CLI jump)",
huawei: "Huawei device (CLI jump)",
cisco: "Cisco device (CLI jump)",
linux: "Linux server (SSH tunnel)",
bastion: "Bastion host (SSH protocol proxy)",
},
zteHint: "Run ssh/telnet on the ZTE device to reach the target; target credentials use secondary auth.",
huaweiHint: "Run telnet / stelnet (SSH) on the Huawei hop; stelnet is SSH. Target credentials use secondary auth.",
ciscoHint: "Run Cisco ssh -vrf / telnet /vrf jump commands; target credentials use secondary auth.",
linuxHint: "SSH to the Linux bastion, then direct-tcpip tunnel to target IP:port (ProxyJump-style).",
bastionHint:
"SSH with composite username via bastion protocol proxy. Example: bastion-user@target-user@2.2.2.2@1.1.1.1; password is the bastion/Vault password. JumpServer/CBH often use port 2222.",
targetAuthMode: "Target credentials",
targetAuthBastionManaged: "Bastion-managed (target password optional)",
targetAuthManual: "Manual (secondary auth after connect)",
targetAuthHint: "Bastion-managed needs only bastion password; manual mode requires target NE password.",
usernameTemplate: "SSH username template",
templateHintBastion:
"Default {hop_user}@{target_user}@{target_ip}. Bastion address is the hop host field. CLI example: ssh bastion-user@target-user@2.2.2.2@1.1.1.1.",
host: "Jump host",
port: "Jump port",
protocol: "Jump protocol",
protocolSshStelnet: "ssh (stelnet)",
username: "Jump username",
password: "Jump password",
vrf: "Mgmt VRF (optional)",
vpnInstance: "VPN-Instance (optional)",
vrfCisco: "VRF (optional, e.g. MGMT)",
commandTemplate: "Jump command template",
templateHint:
"ZTE CLI: telnet {target_ip}, telnet {target_ip} vrf {vrf}, ssh {target_ip}, ssh {target_ip} vrf {vrf}. Auto-suggested from jump protocol/VRF; same when left blank. Target credentials via secondary auth prompts.",
templateHintHuawei:
"Huawei CLI: telnet {target_ip}, telnet vpn-instance {vrf} {target_ip}, stelnet {target_ip}, stelnet {target_ip} -vpn-instance {vrf}. SSH protocol maps to stelnet. Auto-suggested when left blank.",
templateHintCisco:
"Cisco CLI: ssh {target_ip}, ssh -vrf {vrf} {target_ip}, telnet {target_ip}, telnet {target_ip} /vrf {vrf}. Auto-suggested when left blank.",
badge: {
zte: "ZTE hop",
huawei: "Huawei hop",
cisco: "Cisco hop",
linux: "Linux hop",
bastion: "Bastion",
},
hostRequired: "Jump host is required",
userRequired: "Jump username is required",
passwordRequired: "Jump password is required",
batchAdd: "Add proxy",
batchTitle: "Batch add jump proxy",
batchHint: "Apply the jump-host settings below to {{n}} selected NE(s) (overwrites existing proxy config).",
batchDone: "Proxy applied to {{n}} NE(s)",
selectRequired: "Select network elements first",
applying: "Applying…",
apply: "Apply to selected",
},
},
ume: {
token: {
title: "UME Token",
disconnected: "disconnected",
connected: "connected",
connectedRenew: "connected (renew required)",
expiresIn: "expires_in",
needsRenew: "renew / 0s",
preview: "preview",
refreshStatus: "Refresh status",
renewLogin: "Renew / login",
renewing: "Renewing…",
disconnect: "Disconnect token",
disconnecting: "Disconnecting…",
renewOkChanged: "Token renewed and updated",
renewOkSame: "Renew completed (unchanged)",
disconnectOk: "UME token disconnected",
renewHint: "Token missing expiry or expired. Use Renew / login or wait for background keepalive.",
},
subscription: {
title: "UME alarm subscription (WebSocket)",
help:
"Subscriptions must be created/cancelled manually. After establish (or on restart if a valid subscription exists in DB), the backend auto-connects WSS for live alarms. Pause/resume on runtime task alarms_current_ws_consumer stops/resumes WSS only (does not cancel the UME subscription).",
sub: "subscription",
subActive: "active",
subInactive: "inactive",
wssPaused: "paused",
wssModeBanner:
"Current alarms are maintained via WSS (mode: {{mode}}); scheduled REST full sync is paused. Use Sync current alarms below for full reconcile.",
serverLostBanner:
"UME server subscription is missing or expired; local state may still show active. Clear local record and re-subscribe.",
establish: "Establish subscription",
establishing: "Establishing…",
reestablish: "Clear local & re-subscribe",
reestablishing: "Re-subscribing…",
cancel: "Cancel subscription",
cancelling: "Cancelling…",
refreshStatus: "Refresh subscription",
clearLocalOnly: "Clear local only",
clearing: "Clearing…",
opFailed: "Subscription operation failed",
confirmClearDefault:
"UME alarm subscription no longer exists or has expired.\n\nClear local subscription record? You can establish again afterwards.",
confirmReestablish: "Clear local record and re-establish subscription on UME. Continue?",
titleLoginFirst: "Log in to UME token first",
titleReestablish: "Clear stale local record and re-establish on UME",
establishInvalid: "Establish did not return a valid id/uri",
establishExists: "Subscription already exists (WSS will stay/connect)",
establishOk: "Subscription established; WSS will connect",
clearLocalOk: "Local subscription cleared; you can establish again",
serverLostMsg: "UME subscription lost. Clear local record and re-subscribe?",
cancelFailed: "Cancel failed: subscription may still exist on UME",
cancelOkMissing: "Local cleared (UME subscription was already missing)",
cancelOk: "Subscription cancelled",
wsLogs: "WSS runtime log",
wsActivity: "Last activity {{time}}",
wsAutoRefresh: "Auto refresh",
wsLogsEmpty: "No logs yet (connect WSS after establishing subscription)",
},
keyAlert: {
title: "AI alarm monitor",
showPanel: "Details",
hidePanel: "Close",
help: "Exact match by notificationId, or substring match on alarm description (case-insensitive; nativeProbableCause, etc.). Optionally restrict to UME inventory ne_type values (multi-select; empty = all devices). Pushed via OClaw WebSocket. Shares NETX_OCLAW_ANALYZE_TOKEN.",
ws: "OClaw WSS",
wsConnected: "connected",
wsDisconnected: "disconnected",
wsDisabled: "disabled",
wsPaused: "paused",
publishedOk: "published ok",
publishedFail: "published fail",
queue: "queue",
matchType: "Match by",
matchNotificationId: "notificationId",
matchKeyword: "description keyword",
matchValuePhNotificationId: "Enter notificationId",
matchValuePhKeyword: "Enter keyword (case-insensitive)",
label: "Rule description",
labelPh: "Required, e.g. BGP Down",
neTypesLabel: "Device types (optional, multi-select)",
neTypesHint: "Leave empty to match all device types. Options come from synced UME Inventory ne_type.",
neTypesEmpty: "No device types yet — sync Inventory first",
neTypesAll: "All",
forwardOnClear: "Push on alarm clear",
forwardOnClearHelp: "Global switch for all monitor rules. By default only new/updated alarms are pushed; when enabled, clear notifications are also sent when UME reports alarms cleared.",
add: "Add rules",
adding: "Adding…",
delete: "Delete",
edit: "Edit",
editTitle: "Edit monitor rule",
save: "Save",
saving: "Saving…",
cancel: "Cancel",
editOk: "Rule updated",
pickFromAlarms: "Load alarm hints",
emptyRules: "No monitor rules yet",
colType: "type",
colMatch: "match",
colLabel: "description",
colNeTypes: "device types",
colForwardClear: "on clear",
colPublished: "published",
colAttempts: "attempts",
colLast: "last push",
colActions: "actions",
colMonitor: "monitor",
monitorOn: "on",
monitorOff: "off",
filterKeywordPh: "Filter match / label",
filterMatchAll: "All types",
filterEnabledAll: "All states",
filterEnabledOn: "Monitoring on",
filterEnabledOff: "Monitoring off",
addOk: "Monitor rule added",
deleteOk: "Deleted",
confirmDelete: "Delete this monitor rule?",
yes: "yes",
no: "no",
},
sync: {
title: "UME sync",
inventory: "Sync inventory",
alarmsCurrent: "Sync current alarms",
full: "Full sync",
failed: "Sync failed",
},
tasks: {
currentTitle: "Running jobs",
running: "running",
noRunning: "No running jobs",
runtimeTitle: "Background tasks",
runtimeOpFailed: "Background task operation failed",
noRuntime: "No background task status",
intervalTitle: "Configured sleep between loop iterations (clamped at process start)",
lastRunTitle: "Last finished sync for scheduled tasks; refreshed at each loop tick",
deletedTitle: "Reconcile deletes: inventory = NE rows; alarms_current = current alarm rows",
lastErrorCol: "last_error",
runtimeError: {
startup_alarm_sync_before_ws: "Starting up: syncing current alarms (WSS connects after completion)…",
startup_gate_waiting: "Startup REST snapshot pending; scheduled REST and WSS on standby",
wss_active_skip_rest: "WSS receiving live alarms; REST sync skipped",
pulling_alarms_current: "Pulling UME current alarms…",
alarms_sync_in_progress_skip: "Another current-alarm REST sync in progress; skipped",
pulling_inventory: "Pulling UME inventory…",
ume_ws_disabled_no_base_url: "Disabled or UME_BASE_URL not configured",
oclaw_fwd_disabled: "Disabled or NETX_OCLAW_ALARM_WS / token / url not configured",
resumed_sync_soon: "Resumed: skipping debounce wait, sync soon",
resumed_wss_reconnect: "Resumed: reconnecting WSS soon",
resumed_oclaw_wss_reconnect: "Resumed: reconnecting OClaw WSS soon",
resumed: "Resumed",
keepalive_failed: "Token keepalive failed",
},
wsState: {
init: "Initializing",
connected: "Connected",
connecting: "Connecting",
disconnected: "Disconnected",
no_subscription: "No subscription",
waiting_token: "Waiting for token",
paused: "Paused",
error: "Connection error",
reconnecting: "Reconnecting",
subscription_lost: "UME subscription lost",
disabled_no_base_url: "Disabled or UME_BASE_URL not configured",
},
fwdState: {
paused: "Paused",
disabled: "Disabled",
connecting: "Connecting",
connected: "Connected",
},
runtimeTask: {
token_keepalive: "UME token keepalive",
alarms_current_auto_sync: "Current alarms auto sync",
alarms_current_ws_consumer: "UME alarm WSS",
oclaw_alarm_forwarder: "OClaw key-alert WSS",
inventory_auto_sync: "Inventory auto sync",
},
},
syncStatus: {
title: "Sync history",
showPanel: "Details",
hidePanel: "Close",
empty: "No sync records",
summary: "{{total}} record(s), {{running}} running",
},
ne: {
title: "Network elements",
showPanel: "Details",
hidePanel: "Close",
keywordPh: "keyword(ne_id/ne_name/user_label/ip/host_name)",
clearTitle: "Clear keyword and reset to page 1",
expand: "Details",
collapse: "Close",
cliConnect: "CLI connect",
summary: "{{total}} inventory NE(s)",
},
tabs: {
main: "UME sync",
cli: "CLI connect",
},
cli: {
title: "UME CLI connection profile",
showPanel: "Details",
hidePanel: "Close",
hint: "Unified SSH credentials and hop/bastion for UME inventory NEs. OClaw MCP can exec show commands via ume_ne_id without per-device managed_ne entries.",
statusReady: "CLI profile is ready for UME NE command execution.",
statusNotReady: "Set target username and hop/bastion settings (if enabled).",
profileName: "Profile name",
targetUsername: "Target SSH username",
targetPassword: "Target SSH password",
passwordKeep: "Leave blank to keep unchanged",
newProfile: "New profile",
deleteConfirm: "Delete this CLI profile?",
deleted: "Profile deleted",
saved: "CLI profile saved",
connectTestTitle: "Sample NE connectivity test",
sampleNePh: "ume_ne_id (copy from NE list below)",
sampleNeRequired: "ume_ne_id is required",
connectTestSubmitted: "Connectivity test submitted",
connectTestWaiting: "Waiting for test result…",
connectTestRunning: "Connectivity test in progress…",
connectTestResult: "Test result",
existingProfiles: "Existing profiles",
default: "default",
},
alarms: {
title: "Current alarms",
movedHint: "Current alarm query moved to Network Management →",
showPanel: "Expand",
hidePanel: "Collapse",
keywordPh: "keyword(alarm key/cause/ne_name/host_name/ip…)",
hostNamePh: "host_name (contains)",
allSeverity: "All severities",
clearedAll: "is_cleared: all",
clearTitle: "Clear filters and reset to page 1",
filterByHost: "Filter by this host",
noHostName: "No host_name (sync inventory first)",
},
},
webcrt: {
title: "WebCRT",
deviceList: "Device list",
sessionManager: "Device list",
connect: "Connect",
hint: "Pick an NE and open an interactive terminal in the browser. Credentials stay on the server.",
hintCrt: "Click an NE on the left to open a terminal. Includes managed, Sessions, and UME; or use New Session for a quick login.",
filterKeyword: "Filter",
filterKeywordPh: "Filter by name / IP / tag",
empty: "No matching NEs. Switch source or refine the search.",
open: "Open terminal",
opening: "Connecting…",
opened: "Opened {{name}}",
disconnect: "Disconnect",
closed: "Session closed",
termPlaceholder: "No terminal session open",
sessionMeta: "{{name}} · {{ip}}",
source: {
all: "All",
managed: "Managed",
webcrt: "Sessions",
ume: "UME",
},
newSession: {
title: "New Session",
connect: "Connect",
connecting: "Connecting…",
protocol: "Protocol",
host: "Hostname",
port: "Port",
sessionName: "Session name",
sessionNamePh: "Optional; defaults to IP",
authTitle: "SSH Authentication",
savePassword: "Save password",
ipRequired: "Hostname is required",
userRequired: "Username is required",
passwordRequired: "Password is required",
},
status: {
idle: "Idle",
connecting: "Connecting",
connected: "Connected",
error: "Error",
closed: "Closed",
},
phase: {
creating: "Creating session",
authenticating: "Authenticating",
waitingPrompt: "Waiting for device prompt",
hint: "Slow devices may take more than 10 seconds",
},
tabMenu: {
close: "Close",
closeOthers: "Close others",
closeAll: "Close all",
copyIp: "Copy IP",
copyIpOk: "Copied {{ip}}",
copyIpEmpty: "No IP on this tab",
},
treeMenu: {
rename: "Rename",
renameLabel: "Session name",
renameSave: "Save",
renameRequired: "Session name is required",
renamed: "Session renamed",
delete: "Delete session",
deleteConfirm: "Delete session \"{{name}}\"? This cannot be undone.",
deleted: "Session deleted",
},
keywordHl: {
title: "Keyword Highlighting",
enabled: "Enabled",
newKeyword: "New keyword",
newKeywordPh: "Keyword or regex",
regex: "Regular expression",
add: "Add",
colKeyword: "Keyword",
colRegex: "Regex",
empty: "No keywords yet",
moveUp: "Move up",
moveDown: "Move down",
remove: "Remove",
caseSensitive: "Case sensitive",
color: "Text color",
saved: "Keyword highlighting saved",
},
actions: {
reconnect: "Reconnect",
recordLog: "Record log",
clear: "Clear",
copy: "Copy",
recording: "Recording",
recordingOn: "Log recording started",
recordingSaved: "Log saved",
copied: "Copied to clipboard",
copyEmpty: "Terminal is empty",
copyFailed: "Copy failed",
break: "Break",
breakSent: "Break sent",
broadcast: "Broadcast",
broadcastSent: "Sent to all sessions",
postLogin: "Post-login cmds",
},
favorites: "Favorites",
recent: "Recent",
favorite: "Toggle favorite",
encoding: "Encoding",
fontSize: "Font size",
theme: "Theme",
themeDark: "Dark",
themeLight: "Light",
colorScheme: "Color scheme",
backgroundColor: "Background",
foregroundColor: "Font color",
scheme: {
dark: "Deep blue (default)",
blackWhite: "Black on white",
whiteBlack: "White on black",
greenBlack: "Green on black",
amberBlack: "Amber on black",
custom: "Custom",
},
copyOnSelect: "Copy on select",
pasteDelay: "Paste max line wait",
pasteDelayOff: "No delay (bulk send)",
keepalive: "SSH keepalive",
keepaliveOff: "Off",
keepaliveSec: "{{n}} sec",
options: "Options",
globalSessionOptions: "Global Session Options",
sessionOptionsSave: "Save",
sessionOptionsCancel: "Cancel",
sessionOptionsSaved: "Global session options saved",
disconnectBanner: "Session disconnected",
disconnectBannerError: "Session error or connection lost",
broadcastPh: "Send to all tabs…",
buttonBarHint: "One per line: label|command",
postLoginPrompt: "Post-login commands (one per line, this device)",
postLoginSaved: "Post-login commands saved",
sftp: {
title: "SFTP",
refresh: "Refresh",
up: "Up",
upload: "Upload",
uploaded: "Upload complete",
uploading: "Upload {{done}}/{{total}} · {{name}} {{pct}}",
downloading: "Download {{done}}/{{total}} · {{name}} {{pct}}",
downloaded: "Downloaded {{count}} file(s)",
downloadPartial: "Downloaded {{ok}}, failed {{failed}}",
uploadPartial: "Uploaded {{ok}}, failed {{failed}}",
retrying: "Retrying {{name}} ({{attempt}}/{{max}})…",
cancel: "Cancel",
dropHint: "Drop to upload into current folder",
dropEmpty: "No uploadable files found",
mkdir: "New",
mkdirPrompt: "New folder name",
mkdirInvalid: "Invalid folder name (no / or ..)",
mkdirOk: "Folder created",
rename: "Rename",
renamePrompt: "New name",
renameInvalid: "Invalid name (no / or ..)",
renamed: "Renamed",
download: "Download",
chmod: "chmod",
chmodPrompt: "Mode (e.g. 755 or rwxr-xr-x)",
chmodOk: "Permissions updated",
chmodPartial: "chmod ok {{ok}}, failed {{failed}}",
delete: "Delete",
deleteConfirm: "Delete {{name}}?",
deleteRecursiveConfirm: "{{name}} includes folders; non-empty folders will be removed recursively. Continue?",
deletePartial: "Deleted {{ok}}, failed {{failed}}",
deleted: "Deleted",
selectedCount: "{{count}} selected",
empty: "This folder is empty",
listTruncated: "Folder is large; showing first {{count}} entries (limit {{max}})",
dir: "dir",
loading: "Loading…",
enterHint: "Click to select; Ctrl/Shift multi-select; double-click to open",
downloadHint: "Click to select; Ctrl/Shift multi-select; double-click to download",
colName: "Name",
colSize: "Size",
colMtime: "Modified",
colOwner: "Owner",
colGroup: "Group",
colMode: "Access",
resizeHint: "Drag to resize SFTP / terminal",
colResizeHint: "Drag to resize column",
},
term: {
findPh: "Find…",
findPrev: "Previous",
findNext: "Next",
findClose: "Close find",
copy: "Copy",
paste: "Paste",
pasteEmpty: "Clipboard is empty",
pasteFailed: "Cannot read clipboard (try Ctrl+Shift+V or check browser permission)",
clear: "Clear",
pasting: "Pasting {{done}}/{{total}} lines…",
wsError: "[WebSocket error]",
wsClosed: "[WebSocket closed code={{code}}]",
sessionStatus: "[session {{state}}{{detail}}]",
outputTruncated: "[output truncated: {{count}} chunk(s) dropped]",
},
err: {
deviceType: "Unsupported device type",
ipConflictRestart: "Database still enforces unique IP; restart the netx API and retry",
sessionLimit: "Session limit reached; close other terminals first",
neNotFound: "Network element not found",
credsIncomplete: "Incomplete username/password on this NE",
authRejected: "Wrong username or password. Please try again.",
cliProfile: "UME NEs require a CLI connect profile (default account / hop)",
connectFailed: "Device login failed: {{detail}}",
sftpHop: "SFTP does not support hop devices yet (direct SSH only)",
sftpSsh: "SFTP requires SSH",
sftpNeedPassword: "SFTP needs a saved SSH password (one-shot dialog passwords are not reused for files)",
sftpUnsupported: "This SSH session has no SFTP channel (device may not offer it)",
sftpTooLarge: "File exceeds size limit (default 512MB)",
sftpChmodInvalid: "Invalid mode (use 755 or rwxr-xr-x)",
sftpAborted: "Transfer cancelled",
queueDropped: "Terminal output too fast; about {{count}} chunk(s) dropped — transcript may be incomplete",
websocket: "WebSocket connection failed",
},
},
topology: {
title: "Topology",
maps: "Maps",
tree: "Topology tree",
treeLoading: "Loading tree…",
treeHint: "Regions hold maps you create; open a map to edit the canvas.",
treeSearch: "Search NEs across maps",
treeSearchPh: "Name / IP",
treeSearchEmpty: "No matching NEs",
treeSearchNoViews: "Not on any map",
treeSearchNotOnMap: "This NE is not placed on any topology map yet",
treeSearchViewCount: "{{count}} map(s)",
treeSearchTruncated: "Showing first 30 of {{total}}",
rootName: "Regions",
selectRegion: "Select a region",
openMap: "Open map",
openRegion: "Open region",
kindPhysical: "Physical",
kindCustom: "Custom",
regionNodeHint: "{{count}}P",
layerNodeHint: "{{count}} NE(s)",
layerNodeEmpty: "Click to create & open",
rootCanvasHint: "Pick a region from the left tree.",
regionCanvasHint: "Open a map to edit devices, or create a custom map.",
regionBrowseHint: "Region folder: maps you add. Click to open.",
browserTitle: "Topology folder",
browserRegionSub: "{{count}} map(s)",
browserRegionsSub: "{{count}} region(s)",
browserEmptyMaps: "No maps in this region yet.",
addNe: "Add NE",
backUp: "Back to region",
newRegion: "New region",
newRegionName: "New region",
newRegionPrompt: "Region name",
regionCreated: "Region created",
regionDeleted: "Region deleted",
deleteRegion: "Delete region",
deleteRegionConfirm: "Delete region \"{{name}}\"? Its maps will also be deleted.",
renameRegion: "Rename region",
renameRegionPrompt: "Region name",
regionRenamed: "Region renamed",
collapseRegion: "Collapse region",
expandRegion: "Expand region",
newCustom: "Custom map",
newCustomName: "Custom topology",
newCustomPrompt: "Custom map name",
folderHint: "Regions group maps. After creating a region, add a physical or custom map yourself.",
folderHintShort: "Region folder",
outsidePeers: "{{count}} outside peer(s) linked but not on this map",
newMap: "New",
newMapName: "New topology",
rename: "Rename",
renamePrompt: "Topology map name",
renameHint: "Double-click to rename",
renamed: "Topology map renamed",
deleteMap: "Delete map",
deleteMapConfirm: "Delete map \"{{name}}\"? Nodes and edges will be removed.",
emptyMaps: "No regions yet. Create a region first.",
palette: "NE library",
paletteHint: "Select multiple to add in batch, or drag one onto the canvas",
paletteDragHint: "Drag onto canvas to add",
selectAllVisible: "Select all visible",
addSelected: "Add selected ({{count}})",
addSelectedDone: "Added {{count}} NE(s)",
addingNe: "Adding…",
filterPh: "Type to filter: name / IP / vendor",
save: "Save",
saveDirty: "Save *",
unsavedConfirm: "You have unsaved changes. Discard and switch maps?",
saving: "Saving…",
saved: "Topology saved",
discover: "Discover all links",
discovering: "Discovering…",
discoverOne: "Discover links",
discovered: "Discovery done: +{{added}} updated {{updated}} missing {{stale}}",
discoverFail: "Discovery failed: {{detail}}",
discoverOneNeedNe: "This node is not linked to a scannable NE",
selectMap: "Select or create a topology map",
renameNode: "Rename",
renameNodePrompt: "Node display name",
sourcePort: "Local port",
targetPort: "Remote port",
endpointA: "Endpoint A",
endpointB: "Endpoint B",
port: "port",
expandPhysicalLinks: "Expand physical links",
scaleBundleWidth: "Thicken bundles by link count",
linkMembers: "Physical members ({{count}})",
canvasHint:
"Left tree lists regions; add maps yourself, then open a map to edit devices with Add NE and save positions.",
selected: "Selected",
selectedEdge: "Selected edge",
selectedCount: "{{count}} selected",
toolModes: "Edit modes",
toolSelect: "Select",
toolPan: "Pan",
toolDrag: "Drag",
toolConnect: "Connect",
layout: "Layout",
layoutHierarchicalTb: "Hierarchical · top-bottom",
layoutHierarchicalLr: "Hierarchical · left-right",
layoutForce: "Force-directed",
layoutGrid: "Grid",
layoutRadial: "Radial",
layoutSelected: "Selected only (hierarchical)",
layoutNeedSelection: "Select nodes to lay out first",
align: "Align",
alignLeft: "Align left",
alignRight: "Align right",
alignTop: "Align top",
alignBottom: "Align bottom",
alignHCenter: "Align center X",
alignVCenter: "Align center Y",
alignHDistribute: "Distribute horizontally",
alignVDistribute: "Distribute vertically",
alignNeedSelection: "Select at least two nodes",
snapGrid: "Snap to grid",
autoLayoutDiscover: "Auto-layout after discover",
discoverAutoAddUnmatched: "Auto-add unmatched NEs to fabric on discover",
discoverProjectNeighbors: "Project direct neighbors onto canvas after discover",
undo: "Undo",
redo: "Redo",
discoverSelected: "Discover selected ({{count}})",
removeSelected: "Remove selected ({{count}})",
openWebcrt: "Open terminal",
openNe: "NE details",
removeNode: "Remove from canvas",
removeEdge: "Delete edge",
nodeMenu: "Node",
selectionMenu: "Selection",
edgeStyle: "Edge style",
edgeColor: "Color",
edgeLineStyle: "Line",
edgeLineSolid: "Solid",
edgeLineDashed: "Dashed",
edgeLineDotted: "Dotted",
edgeWidth: "Width",
edgeStyleReset: "Reset style",
edgeDefaults: "Default edge styles",
edgeDefaultsReset: "Factory reset",
noNeLink: "Not linked to a managed NE",
completeNeFirst:
"Login details are incomplete (vendor, hop/proxy, etc. belong in NE inventory). Complete the NE first, then open the terminal.",
edgeManual: "Manual",
edgeDiscovered: "Discovered",
edgeStale: "Missing",
fit: "Fit view",
liveSync: "Live sync",
liveSyncOn: "Syncing…",
liveSyncHint: "Poll the tree about every 5s and the open map every 3s (watch MCP create/draw). Off by default. No need to open a map first",
fullscreen: "Fullscreen",
exitFullscreen: "Exit fullscreen",
display: "Display",
canvasBg: "Canvas background",
canvasBgColor: "Background color",
canvasBgReset: "Reset default",
textColors: "Text colors",
textColorsReset: "Reset default",
labelColor: "NE name",
edgeLabelColor: "Link numbers",
vendorColors: "Vendor icon colors",
vendorColorsReset: "Reset default",
vendorTone: {
cisco: "Cisco",
huawei: "Huawei",
zte: "ZTE",
juniper: "Juniper",
nokia: "Nokia",
ericsson: "Ericsson",
h3c: "H3C",
ruijie: "Ruijie",
mikrotik: "MikroTik",
gray: "Unknown / unmanaged",
},
hideIp: "Hide IP",
hideVendor: "Hide vendor",
hidePorts: "Hide ports",
edgeFlow: "Edge flow",
collapseSidebar: "Collapse sidebar",
expandSidebar: "Expand sidebar",
hideAdded: "Hide added",
paletteEmpty: "No matching NEs.",
paletteHiddenAll: "Added NEs are hidden; uncheck to show them again.",
paletteSource: "NE source",
paletteManaged: "Managed",
paletteUme: "UME",
paletteLoading: "Loading…",
discoverReport: "Discovery progress",
discoverClose: "Close",
discoverCancel: "Cancel discovery",
discoverProgress: "Scanning {{count}} managed NE(s) on the map (serial LLDP; please wait)…",
discoverProgressLive: "Scanning {{index}}/{{total}}: {{name}}",
discoverSummary: "Scanned {{scanned}} · added {{added}} · updated {{updated}} · missing {{stale}} · failed {{failed}}",
discoverNeOk: "neighbors {{neighbors}} · added {{added}} · updated {{updated}}",
discoverNeFail: "Collect failed",
discoverUnmatched: "unmatched {{count}}",
discoverUnmatchedMore: "{{count}} more not listed",
discoverVendorRequired: "Vendor/device type missing; skipped LLDP command",
discoverParserStub: "Parser \"{{parser}}\" is still a stub; output was collected but neighbors were not parsed",
discoverNoTargets:
"No scannable NEs on the map. Add managed NEs, or UME NEs with a CLI connect profile.",
discoverFilter: "Result filter",
discoverFilterIssues: "Issues",
discoverFilterFail: "Failed",
discoverFilterWarn: "Warnings",
discoverFilterOk: "OK",
discoverFilterAll: "All",
discoverFilterDetail: "Details",
discoverListTitle: "Discovery details",
discoverListEmpty: "No NEs under the current filter.",
discoverShowLimited: "Showing first {{shown}} / {{total}} (narrow with filters)",
discoverViewDetail: "Detail",
discoverLinkCount: "{{count}} link(s)",
discoverLinksTitle: "Matched links ({{count}})",
discoverLinksEmpty: "No on-map peer links matched in this scan.",
discoverUnmatchedTitle: "Unmatched neighbors ({{count}})",
discoverUnmatchedEmpty: "No unmatched neighbors.",
discoverColPeer: "Peer NE",
discoverColRemote: "Neighbor",
discoverColLocalPort: "Local port",
discoverColRemotePort: "Remote port",
discoverColProtocol: "Protocol",
discoverColAction: "Result",
discoverLinkAdded: "Added",
discoverLinkUpdated: "Updated",
discoverLinkKeptManual: "Kept manual",
discoverLocatePeer: "Locate peer",
discoverLocateNe: "Locate NE",
discoverRawPreview: "Raw output preview (truncated when long; parsing uses full output)",
discoverGoLldp: "Details → LLDP links",
findNode: "Find",
findNodePh: "Type to search: name / IP / vendor",
findNoMatch: "No matching node on the canvas",
findMore: "{{count}} more matches…",
locateOnCanvas: "Click to locate on canvas",
openPortTraffic: "Open port traffic",
removeStale: "Clear missing ({{count}})",
removeStaleHint: "Manually delete red missing edges; scheduled collect also purges after 4 consecutive misses",
staleRemoved: "Removed {{count}} missing edge(s)",
connectHint: "Connect mode: click source node, then target (Esc or Select to exit)",
},
topoClassify: {
title: "Classify & slices",
openTopo: "Topology",
inventory: "NE inventory",
keywordPh: "Filter by name/IP",
filterRoleAll: "All roles",
filterRegionAll: "All regions",
filterUnmatchedOff: "No unmatched filter",
kindAny: "Role or region empty",
kindRole: "Role empty",
kindRegion: "Region empty",
filterLinkAll: "All link states",
filterLinkLinked: "Linked inventory",
filterLinkOrphaned: "Detached",
colLink: "Link",
linkManaged: "Managed",
linkUme: "UME",
linkBoth: "Managed+UME",
linkOrphaned: "Detached",
regexTitle: "Regex find",
patternPh: "Regex, e.g. ^CORE- or -BJ-",
fieldName: "Name",
fieldNameIp: "Name+IP",
findMatches: "Find matches",
matchOk: "Matched {{count}}; selected",
matchHint: "{{count}} matched",
assignRole: "Role only",
assignRegion: "Region only",
assignBoth: "Role + region",
clearRegion: "Clear region",
confirmAssign: "Confirm assign",
bulkConfirm: "Write tags to {{count}} selected/matched NE(s)?",
bulkOk: "Updated {{count}}",
selectedCount: "{{count}} selected",
selectPage: "Select page",
colNe: "NE",
colRole: "Role",
colRegion: "Region",
colActions: "Actions",
edit: "Edit",
save: "Save",
cancel: "Cancel",
delete: "Delete",
deleteSelected: "Delete nodes ({{count}})",
deleteConfirm:
"Delete {{count}} fabric node(s) (placements and edges)? Managed/UME inventory is not touched.",
deleteOk: "Deleted {{count}} node(s)",
deleteHint:
"Only orphaned or LLDP/WebCRT placeholder fabric nodes can be deleted; UME-only and real managed NEs cannot",
deleteBlocked: "UME-only or still linked to real managed inventory — cannot delete here",
rowSaved: "Saved",
loading: "Loading…",
empty: "No NEs.",
showSlices: "Show slice maps",
hideSlices: "Hide slice maps",
slices: "Slice maps",
pickRegion: "Pick region",
tplCore: "Core only",
tplCoreAgg: "Core + neighbor Agg",
tplAggAcc: "Agg + neighbor Access",
seedPhysical: "Also place Cores on physical overview",
slicePreview: "Preview slices",
sliceApply: "Generate maps",
sliceConfirm: "Create custom maps and freeze placements?",
sliceOk: "Created {{count}} slice map(s)",
sliceStats: "{{maps}} map(s) planned, {{overlap}} NE(s) on multiple maps",
nodeCount: "{{count}} NE(s)",
},
audit: {
title: "Audit & ops",
logsTitle: "Operation logs",
nav: {
tasks: "Task overview",
logs: "Operation logs",
},
tasks: {
title: "Task overview",
hint: "Live view of running or pending background work. Actor prefers recent audit entries; scheduled work shows as scheduler/system.",
empty: "No tasks to show right now.",
open: "Open",
inflight: "running {{n}}",
kpiActive: "Active / in-flight",
kpiTotal: "Total rows",
kpiKinds: "By kind",
kpiStatuses: "By status",
colKind: "Kind",
colTitle: "Task",
colStatus: "Status",
colActor: "Actor",
colTrigger: "Trigger",
colProgress: "Progress",
colStarted: "Started",
colUpdated: "Updated",
colLink: "Open",
kind: {
port_traffic: "Port traffic",
config_sync: "Config sync",
ne_collect: "Batch collect",
lldp_discover: "LLDP discover",
ume_sync: "UME sync",
ne_connect: "Connect test",
ume_runtime: "UME runtime",
webcrt: "WebCRT",
},
trigger: {
manual: "Manual",
schedule: "Schedule",
topology: "Topology",
system: "System",
retry_failed: "Retry failed",
},
status: {
collecting: "Collecting",
running: "Running",
testing: "Testing",
connecting: "Logging in",
ready: "Connected",
detached: "Detached (grace)",
pending: "Pending",
paused: "Paused",
idle: "Idle",
error: "Error",
failed: "Failed",
},
},
},
};
export default en;