netx/web/src/i18n/en.ts
oliver 108c9da00f Add seconds unit to biz-state collection interval picker.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-17 19:03:37 +08:00

2157 lines
81 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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}}",
jumpPage: "Go to page",
pageSize: "Page size",
exportCsv: "Export CSV",
exporting: "Exporting…",
exportOk: "Exported {{count}} rows",
exportTruncated: "Exported first {{count}} of {{total}} rows (truncated)",
exportFailed: "Export failed",
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: "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: "OK",
healthWarn: "CHECK",
healthUnknown: "N/A",
},
statusTitle: "Runtime status",
hostHealth: "HOST",
platformCapacity: "Platform capacity",
channels: "CHANNELS",
fleet: "FLEET",
fleetNe: "NE",
fleetNeUnit: "units",
fleetNeKnown: "Known",
fleetNePass: "Pass",
fleetNeFail: "Fail",
fleetNeTesting: "Testing",
fleetNeUnknown: "Untested",
fleetNeNoTag: "No tag",
fleetNeTags: "Top tags",
fleetTopo: "Topology",
fleetTopoNodes: "nodes",
fleetTopoEdges: "edges",
fleetTopoOther: "Other regions",
fleetTopoUnassigned: "Unassigned",
fleetTopoNoRegion: "No regions",
fleetTopoDiscover: "Last discover",
fleetTopoUpdated: "Summary updated",
fleetSched: "Schedulers",
fleetSchedConfig: "Config sync",
fleetSchedLldp: "LLDP",
fleetSchedPort: "Port traffic",
fleetSchedNeCollect: "NE collect",
fleetSchedFabric: "Fabric reconcile",
fleetSchedOn: "ON",
fleetSchedOff: "OFF",
fleetSchedMode: "Mode",
fleetSchedHeartbeat: "Heartbeat",
fleetSchedFresh: "Fresh",
fleetSchedStale: "Stale",
sidePanel: "Console side panel",
consoleId: "OPS CONSOLE",
refreshedAt: "Updated {{time}}",
taskRail: "ACTIVE TASKS",
taskRailAll: "ALL",
taskRailEmpty: "No active tasks",
taskTotal: "POOL {{count}}",
announceTitle: "Announcements",
recentTitle: "Recent operations",
recentMore: "View all",
quickTitle: "Quick actions",
emptyRecent: "No recent audit entries.",
gauge: {
cpu: "CPU",
mem: "Memory",
storage: "Storage",
storageUsed: "Used",
storageUnavailable: "Unavailable",
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",
sessions: "Sessions",
},
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",
network: "Network Management",
managedNe: "NE Management",
collect: "Batch Collect",
webcrt: "WebCRT",
topology: "Topology",
portTrafficWall: "Traffic wall",
users: "Users",
audit: "Audit",
apiKeys: "API Keys",
sessions: "Login sessions",
},
},
network: {
title: "Network Management",
nav: {
detail: "Network details",
tasks: "Monitoring tasks",
topologyMgmt: "Topology management",
devices: "Device list",
topology: "Topology",
lldpLinks: "LLDP",
topoClassify: "Classify",
alarms: "Alarms",
configs: "Configurations",
webcrt: "WebCRT",
collectTasks: "Collection tasks",
configSync: "Config sync",
portTraffic: "Port traffic",
portTrafficWall: "Traffic wall",
bizState: "Business state",
bizCompare: "Business compare",
},
collapseNav: "Collapse sidebar",
expandNav: "Expand sidebar",
placeholder: {
title: "Coming soon",
body: "This capability is not available yet.",
},
},
bizState: {
title: "Business state",
create: "Create task",
createHint: "Pick any valid NE (managed / UME); search supported",
created: "Task created",
createFailed: "Create failed",
empty: "No business-state tasks yet.",
pickNe: "Select NE…",
neKeywordPh: "Name / IP / ID",
allSource: "All sources",
selectedNe: "Selected",
neEmpty: "No matching NEs.",
colSource: "Source",
colVendor: "Vendor",
colConnect: "Connect",
start: "Start schedule",
pause: "Pause",
collectNow: "Collect now",
delete: "Delete",
confirmDelete: "Delete this task and all batches?",
deleted: "Deleted",
started: "Schedule started",
paused: "Paused",
collecting: "Collecting",
detail: "Details",
interval: "Interval",
intervalUnitDays: "days",
intervalUnitHours: "hours",
intervalUnitSeconds: "seconds",
retention: "Keep batches",
saveSchedule: "Save schedule",
scheduleSaved: "Schedule saved",
colInterval: "Interval",
profiles: "Monitor items",
enable: "On",
command: "Command template",
params: "Bindings",
discoverVrf: "Discover VRFs",
saveBindings: "Save bindings",
cancel: "Close",
bindTitle: "Select VRF bindings",
unbound: "Not bound",
batches: "Batches",
viewBatch: "Open",
export: "Export",
noBatches: "No batches yet",
batchDetail: "Batch detail",
batchWorkbook: "Batch workbook",
sheetCommands: "Commands",
sheetLldp: "LLDP neighbors",
sheetVrfRoute: "VRF route summary",
sheetFilterPh: "Filter this sheet…",
sheetEmpty: "No rows in this sheet",
filterColumn: "Column",
filterAllCols: "All columns",
listFilterPh: "Filter NE / IP / vendor / status",
colNe: "NE",
colStatus: "Status",
colLast: "Last collect",
colTime: "Time",
colRows: "Rows",
colActions: "Actions",
colCommand: "Command",
colMessage: "Message",
pickTask: "Open details to manage a task",
noNeedBind: "This item needs no parameter bindings",
discoverFailed: "Discover failed",
bindingsSaved: "Bindings saved",
},
bizCompare: {
title: "Business compare",
templates: "Compare templates",
newTemplate: "New template",
editTemplate: "Edit template",
saveTemplate: "Save template",
templateSaved: "Template saved",
templateDeleted: "Template deleted",
confirmDeleteTemplate: "Delete this compare template?",
emptyTemplates: "No templates yet. Create one or use built-ins.",
templateHint:
"A template holds multiple metric sheets. Key: row identity (no separate compare). Interface: port mapping. Compare: value checks after key match. Leave compare empty for presence-only (entry set match).",
keyFields: "Key",
ifaceFields: "Interface",
compareFields: "Compare",
ignoreFields: "Ignore",
field: "Field",
note: "Note",
keyRequired: "Select at least one key field",
keyIsIdentity: "Key aligns rows; not used as a value-compare field",
metricsRequired: "Add at least one metric sheet",
noMetricFields: "No fields for this metric",
templateFilterPh: "Filter template / metric",
jobFilterPh: "Filter job / template / status",
addMetric: "Add metric",
pickMetric: "Pick metric…",
addSheet: "Add to template",
removeSheet: "Remove sheet",
colSheets: "Sheets",
sheetsUnit: "sheets",
presenceShort: "presence",
modeFields: "Mode: compare selected field values",
modePresence: "Mode: presence-only (entry set match)",
mapping: "Port mapping (optional)",
newMapping: "New mapping…",
mapName: "Mapping name",
saveMapping: "Save mapping",
validateMapping: "Validate",
validateNeed: "Select mapping and before/after batches first",
mapHint:
"Optional. Leave empty to ignore port renames (match on non-interface keys). Or one before_if,after_if pair per line",
mappingSaved: "Mapping saved",
mappingOptionalHint: "No port map: ignore local port changes; match on non-interface keys.",
createJob: "Create compare job",
defaultJobName: "Cutover compare",
jobName: "Name",
template: "Template",
mode: "Mode",
modeManual: "Manual batches",
modeAuto: "Auto (pin before; latest after batch)",
beforeTask: "Before task",
beforeBatch: "Before batch",
afterTask: "After task",
afterBatch: "After batch",
pick: "Select…",
sameAsBefore: "Same as before / select…",
autoHint: "auto: new after-task batches trigger compare",
createCompare: "Create job",
runNow: "Run now",
saveJob: "Save config",
jobSaved: "Job config saved",
jobList: "Compare jobs",
detail: "Details",
edit: "Edit",
delete: "Delete",
tabConfig: "Config / batches",
tabResult: "Result",
runs: "Run history",
noRuns: "No runs yet — run a compare first",
result: "Result",
pickBatchRun: "Select compare batch",
pickRun: "Select batch…",
resultEmpty: "No matching diff rows",
resultFilterPh: "Filter key / values…",
kindAll: "All",
kindDiff: "Diffs only",
kindAdded: "Added only",
kindRemoved: "Removed only",
kindChanged: "Changed only",
kindUnchanged: "Unchanged only",
kindAddedShort: "Added",
kindRemovedShort: "Removed",
kindChangedShort: "Changed",
kindUnchangedShort: "Same",
diffCount: "{{n}} diffs",
emptyJobs: "No compare jobs yet",
created: "Compare job created",
ran: "Compare finished",
colName: "Name",
colMode: "Mode",
colStatus: "Status",
colKind: "Kind",
colActions: "Actions",
colKey: "Key",
colChange: "Changes",
},
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",
statusDraft: "Draft",
collecting: "Collecting",
deleted: "Deleted",
confirmDelete: "Delete this device monitoring and its samples?",
empty: "No devices monitored yet. Pick a NE and interfaces to start.",
keywordPh: "Name / IP / note / ID",
allStatus: "All statuses",
exportDevicesName: "port-traffic-devices",
exportEventsName: "port-traffic-events",
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",
retryFailed: "Retry failed",
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: "Auto-add unmatched neighbors as placeholders on discover",
autoAddUnmatchedHint:
"Shared by scheduled collect and canvas discover. Off = record unmatched only, no placeholder NE. Default on.",
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",
edgeSourceAll: "All sources",
exportEdgesName: "lldp-fabric-edges",
exportJobsName: "lldp-collect-jobs",
exportItemsName: "lldp-job-items",
statusAll: "All statuses",
jobKeywordPh: "Job ID / error / scope",
itemKeywordPh: "NE name / IP / error",
itemResultAll: "All results",
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",
},
jobStatus: {
running: "Running",
pending: "Pending",
paused: "Paused",
done: "Done",
failed: "Failed",
cancelled: "Cancelled",
stopped: "Stopped",
},
triggerMode: {
manual: "Manual",
schedule: "Scheduled",
topology: "Topology canvas",
retry_failed: "Retry failed",
},
jobScope: {
all: "All NEs",
ne_ids: "Selected NEs",
selected: "Selected NEs",
},
itemResult: {
ok: "OK",
warn: "Warn",
fail: "Fail",
},
edgeSource: {
lldp: "LLDP",
manual: "Manual",
stale: "Stale",
},
},
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",
keywordPh: "ID / trigger / message",
statusAll: "All statuses",
allStatus: "All statuses",
exportCyclesName: "config-sync-cycles",
exportTasksName: "config-sync-tasks",
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.",
exportName: "network-devices",
col: {
source: "Source",
name: "Name",
vendor: "Vendor",
deviceType: "Device type",
connect: "Connect",
actions: "Actions",
},
},
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",
exportListName: "network-configs",
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",
titleSessions: "Login sessions",
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",
sessionsTitle: "Login sessions",
sessionsHint:
"Manage browser/device logins for this account. Behind a reverse proxy, IP is taken from trusted forwarded headers; use the Client column to tell browsers apart on the same public IP. Revoked sessions must sign in again.",
sessionsEmpty: "No active sessions.",
revokeOtherSessions: "Revoke other sessions",
revokeSession: "Revoke",
sessionCurrent: "Current",
sessionRevoked: "Session revoked",
sessionsRevokedOthers: "Revoked {{count}} other session(s)",
revokeCurrentConfirm: "This is your current session; revoking it requires signing in again. Continue?",
colSession: "Session",
colClient: "Client",
colLastSeen: "Last seen",
colCreated: "Created",
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. System-wide cap: {{max}} non-revoked keys (revoke to free a slot).",
tokenQuota: "Active {{active}} / {{max}}",
tokenLimitReached: "API key limit reached ({{max}}). Revoke unused keys first.",
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 8 characters",
passwordMismatch: "New passwords do not match",
passwordMustChange: "New password must differ from the default/old password",
},
collect: {
statusAll: "All statuses",
keywordPh: "Job title / ID",
exportJobsName: "collect-jobs",
exportRunsName: "collect-runs",
policyTitle: "Collect policy",
policyHint:
"Periodic schedule is off by default (one-shot / manual). Enable it to auto-create and run jobs on an interval. Keeps the last 3 finished jobs by default.",
policySaved: "Policy saved",
enabled: "Enable scheduled collect",
interval: "Collect interval",
intervalUnitDays: "days",
intervalUnitHours: "hours",
historyKeep: "Jobs to keep",
scope: "Scope",
scopeAll: "All NEs (managed + UME)",
scopeSelected: "Selected NEs only",
savePolicy: "Save policy",
collectNow: "Collect now",
selectedCount: "{{count}} selected",
colSource: "Source",
policyCommandsRequired: "Add commands and save the policy before enabling schedule or collecting now",
policyTargetsRequired: "No eligible NEs in scope; adjust scope or select NEs",
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.",
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",
listTitle: "Job list",
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",
nextDue: "Next due",
},
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? If its Fabric node is managed-only (no UME binding), that node and its direct links will also be removed.",
batchDelete: "Batch delete",
batchDeleting: "Deleting…",
batchDeleteConfirm:
"Delete {{n}} selected network element(s)? Cannot be undone. Managed-only Fabric nodes cascade (node + direct links); nodes still bound to UME keep topology.",
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",
topology: "Topology 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? Cannot be undone. Fabric nodes that become fully orphaned (no UME left) are removed with their direct links.",
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 IP or FQDN); CLI: ssh user@target@ip@bastion-host.\n" +
"· Bastion-managed: set Jump password (Vault); target password optional. Manual mode needs target password.\n" +
"· Jump host accepts IP (192.0.2.10) or domain (ssh-bastion.example.com). 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. Hop host may be an IP or FQDN. Examples: bastion-user@target-user@198.51.100.20@192.0.2.10 or bastion-user@target-user@198.51.100.20@ssh-bastion.example.com; 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 (IP or FQDN) goes in Jump host. CLI example: ssh bastion-user@target-user@198.51.100.20@ssh-bastion.example.com.",
host: "Jump host",
hostPlaceholderBastion: "192.0.2.10 or ssh-bastion.example.com",
hostHintBastion:
"IP or FQDN. You can paste a full SSH destination (e.g. bastion-user@target-user@198.51.100.20@ssh-bastion.example.com); on blur it splits into jump username + 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)",
enterSystemView: "Huawei system-view before jump",
enterSystemViewNo: "Stay in user-view (default)",
enterSystemViewYes: "Enter system-view first",
enterSystemViewHint:
"Some Huawei hops only accept stelnet/telnet in system-view. Choose explicitly; NetX will not auto-retry.",
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). Matched alerts are broadcast by NetX (server) to subscribed Netx Ops (DSH) clients.",
hub: "DSH subscribers",
hubConnected: "subscribed",
hubEmpty: "no subscribers",
hubSubscribers: "subscribers",
hubPublished: "published",
hubDeliverOk: "deliver ok",
hubDeliverFail: "deliver fail",
hubConnections: "Active connections",
hubColId: "id",
hubColUser: "user",
hubColRemote: "remote",
hubColClient: "client",
hubColConnected: "connected",
hubColLastSeen: "last seen",
hubEmptyConnections: "No Netx Ops clients are connected to the alarm hub",
hubPath: "path",
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 to subscribed Netx Ops clients.",
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…",
pulling_topology: "Pulling UME topology (nodes/links)…",
topology_sync_busy: "Topology sync already running; skipped",
ume_ws_disabled_no_base_url: "Disabled or UME_BASE_URL not configured",
resumed_sync_soon: "Resumed: skipping debounce wait, sync soon",
resumed_wss_reconnect: "Resumed: reconnecting 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",
},
runtimeTask: {
token_keepalive: "UME token keepalive",
alarms_current_auto_sync: "Current alarms auto sync",
alarms_current_ws_consumer: "UME alarm WSS",
inventory_auto_sync: "Inventory auto sync",
topology_auto_sync: "Topology auto sync",
},
},
syncStatus: {
title: "Sync history",
showPanel: "Details",
hidePanel: "Close",
empty: "No sync records",
summary: "{{total}} record(s), {{running}} running",
fabricGapTitle: "Topology dock is newer than Fabric / UME World",
fabricGapHint:
"Dock ME {{dock}}, Fabric UME {{fabric}}. Apply without pulling UME. Last status: {{status}}",
applyFabric: "Apply to topology",
applyingFabric: "Applying…",
applyFabricOk: "Applied to topology",
},
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: "Filter UME NEs (name / IP / ID)",
sampleNeRequired: "Select a UME NE first",
sampleNeEmpty: "No matching NE",
sampleNeLoading: "Loading…",
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: "Cleared: all",
clearedYes: "Cleared",
clearedNo: "Active",
clearTitle: "Clear filters and reset to page 1",
filterByHost: "Filter by this host",
noHostName: "No host_name (sync inventory first)",
col: {
time: "Time",
severity: "Severity",
neId: "NE ID",
hostName: "Host",
neType: "NE type",
cause: "Cause",
},
},
},
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",
claimTitle: "Complete placeholder session",
claimHint:
"Enter a reachable host. Session name defaults to the placeholder name. On success, source becomes WebCRT.",
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: "Login progress streams into the terminal; slow devices may take >10s",
},
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 (on HTTP use the paste box, or Ctrl+V)",
pasteBridgeHint:
"This page is not HTTPS, so the browser blocks clipboard reads. Press Ctrl+V in this box to send text to the terminal.",
pasteBridgePh: "Press Ctrl+V here…",
pasteBridgeCancel: "Cancel",
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…",
treeLoadFailed: "Failed to load topology tree",
treeRetry: "Retry",
graphLoading: "Loading topology…",
graphLoadFailed: "Failed to load topology",
graphRefreshing: "Refreshing…",
canvasEmpty: "No NEs on this map yet",
canvasEmptyHint: "Use Add NE, or pick a region that already has devices.",
treeHint: "Top level is a Root (nav only); its unique Root Map is the canvas. Open the root map or a sub-region to edit devices.",
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",
locateNotOnCanvas:
"This NE is missing from the world-map sample and has no world coordinates. Open a region map instead.",
treeSearchViewCount: "{{count}} map(s)",
treeSearchTruncated: "Showing first 30 of {{total}}",
rootName: "Roots",
selectRegion: "Select a root or region",
openMap: "Open map",
openRegion: "Open",
kindPhysical: "Physical",
kindCustom: "Custom",
layerNodeHint: "{{count}} NE(s)",
layerNodeEmpty: "Click to create & open",
rootCanvasHint: "Pick a root from the left tree.",
regionCanvasHint: "Open the root map to edit devices; create sub-regions on that canvas.",
regionBrowseHint: "Root folder: unique root map below. Click to open.",
browserTitle: "Topology folder",
browserRegionSub: "{{count}} map(s)",
browserRegionsSub: "{{count}} item(s)",
browserEmptyMaps: "No root map under this root yet.",
addNe: "Add NE",
more: "More",
backUp: "Back",
newRegion: "New root",
newRegionName: "New root",
newRegionPrompt: "Root name",
newSubRegion: "New sub-region",
newSubRegionName: "New sub-region",
newSubRegionPrompt: "Sub-region name",
subRegionNotOnFlat: "The world map has no regions. Create sub-regions on World or a lower-level canvas.",
worldMapNoDirectNes: "Cannot add NEs on the world map — add them on a sub-region canvas.",
worldMapName: "World map",
worldMapOffline: "World map is temporarily offline",
rootMapName: "Root map",
worldHud: "World {{total}} · close-up {{cached}} · z{{zoom}} · {{lod}}",
worldEmptyCoords: "World map has no coordinates yet",
worldEmptyCoordsHint:
"Dock has {{dock}} NEs, but Fabric / world coords are empty. Apply without pulling UME.",
worldApply: "Apply to world map",
worldApplying: "Applying…",
worldApplyOk: "Applied to world map",
subRegionNeedParent: "Open a root map or region canvas first, then create a sub-region.",
regionCreated: "Created",
regionDeleted: "Deleted",
deleteRegion: "Delete",
deleteRegionConfirm: "Delete \"{{name}}\"? Its maps will also be deleted.",
deleteUmeWorldConfirm:
"Delete \"{{name}}\"? This cascades all UME regions and maps under it. A later UME topology apply may recreate it.",
renameRegion: "Rename",
renameRegionPrompt: "Name",
regionRenamed: "Renamed",
collapseRegion: "Collapse",
expandRegion: "Expand",
newCustom: "Custom map",
newCustomName: "Custom topology",
newCustomPrompt: "Custom map name",
folderHint: "Creating a root auto-creates its unique root map. Add sub-regions on that canvas.",
folderHintShort: "Root folder",
outsidePeers: "{{count}} outside peer(s) linked but not on this map",
outsidePeersView: "Off-map NEs {{count}}",
outsidePeersTitle: "Outside peers",
outsidePeersHint: "Linked to this map but not placed. Select and add only what you need.",
outsidePeersVia: "via {{name}}",
outsidePeersFilterPh: "Filter: name / IP / via",
outsidePeersEmpty: "No matching outside peers",
projectNeighbors: "Project neighbors",
projectedNeighbors: "Projected {{count}} neighbor(s) onto the map",
localBrowserOnly: "this browser only",
deleteEdgeConfirm:
"Remove selected link(s) from the canvas? They are deleted from Fabric only when you Save. LLDP may recreate them on the next collect.",
edgeDeleted: "Queued {{count}} link delete(s) — Save to apply",
discoverCancelled: "Discovery cancelled",
truncatedMembership: "Membership cap reached; some neighbors were not placed. Use classify or a new map.",
truncatedFrozen: "This map membership is frozen; neighbors cannot be projected. Unfreeze via classify workflow or create a new map.",
truncatedNodes: "Too many view nodes; display truncated. Narrow membership or create another map.",
truncatedEdges: "Too many edges; display truncated. Filter status or create another map.",
truncatedGeneric: "Graph data truncated. Narrow scope or create another map.",
deleteSelectionConfirm:
"Remove {{nodes}} NE(s) from this map and {{edges}} link(s)? Link deletes apply on Save; NE remove from map applies on Save.",
selectionDeleted: "Queued: remove {{nodes}} NE(s), delete {{edges}} link(s) — Save to apply",
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 roots yet. Create a root 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",
saveFailed: "Save failed: {{detail}}",
readOnlyBanner: "Read-only (ne:write scope required to edit)",
readOnlyHint: "Read-only — no write permission",
discoverTimeout: "Discovery timed out; check LLDP tasks for progress",
policySyncFailed: "Failed to sync LLDP policy; local toggle still applies to canvas discovery",
discover: "Discover 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 as placeholders on discover",
discoverProjectNeighbors:
"After discover, project neighbors of scanned NEs onto the canvas (Save to keep permanently)",
undo: "Undo",
redo: "Redo",
discoverSelected: "Discover links ({{count}})",
removeSelected: "Remove selected ({{count}})",
openWebcrt: "Open terminal",
openNe: "NE details",
openAlarms: "View alarms",
noAlarmLink: "Cannot build an alarm filter for this node",
removeNode: "Remove from canvas",
deletePlaceholder: "Delete placeholder",
deletePlaceholderConfirm:
"Permanently delete this placeholder NE (inventory + Fabric links)? This cannot be undone with canvas Undo.",
deletePlaceholderDone: "Placeholder deleted",
removeEdge: "Delete edge",
nodeMenu: "Node",
paneMenu: "Canvas",
edgeMenu: "Edge",
createNe: "New NE",
createNeModeHint:
"Create a full managed NE, or a topology placeholder (credentials can be filled later).",
createNeManaged: "Managed NE",
createNeManagedHint: "Same full form as NE management; placed on the canvas after create.",
createNePlaceholder: "Placeholder NE",
createNePlaceholderHint: "Name/IP only; topology placeholder for sketching first.",
createNeTitle: "New topology placeholder NE",
createNeHint:
"Source is topology placeholder. Complete login via WebCRT, or promote in NE management.",
createNeName: "Name",
createNeNamePh: "e.g. SW-CORE-01",
createNeNameRequired: "NE name is required",
createNeIp: "IP (optional)",
createNeIpPh: "Can fill later during setup",
createNeDone: "Created placeholder {{name}}",
createManagedNeDone: "Created and placed {{name}}",
createManagedNePlaceFailed:
"{{name}} was created in inventory, but placing on the map failed: {{error}}. Add it again from NE management or the sidebar.",
createManagedNePosFailed:
"{{name}} is on the map, but could not move to the click position (kept default layout coords)",
createNeBusy: "Creating…",
connectTest: "Connectivity test",
connectTestSelected: "Connectivity test ({{count}})",
connectTestNeedManaged: "Node is not linked to a managed NE; cannot run connectivity test",
connectTestSkipped: "skipped {{count}} without managed link",
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",
edgeUme: "UME",
edgeDiscovered: "LLDP",
edgeStale: "Missing",
fit: "Fit view",
export: "Export",
exportSvg: "SVG vector",
exportXml: "XML data",
exporting: "Exporting…",
exportEmpty: "Nothing on the canvas to export",
exportedSvg: "SVG exported",
exportedXml: "XML exported",
exportFailed: "Export failed: {{detail}}",
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",
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",
showPlaceholderBadge: "Show placeholder badge (TOPO/LLDP)",
showConnectStatus: "Show connect-status dots",
showAlarmOverlay: "Show alarm overlay",
showLevelColors: "Color by classify level",
levelUnmatched: "Unclassified",
level: {
external: "External",
core: "Core",
aggregation: "Aggregation",
access: "Access",
edge: "Edge",
},
hidePorts: "Hide ports",
edgeFlow: "Edge flow",
collapseSidebar: "Collapse sidebar",
expandSidebar: "Expand sidebar",
resizeSidebar: "Drag to resize 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.",
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:
"Queue delete of red missing edges (applies on Save); scheduled collect also purges after 4 consecutive misses",
staleRemoved: "Queued {{count}} missing edge delete(s) — Save to apply",
connectHint: "Connect mode: drag from the NE center anchor to the target (Esc or Select to exit)",
},
topoClassify: {
title: "Classify",
openTopo: "Topology",
inventory: "NE inventory",
keywordPh: "Filter by name/IP",
filterLevelAll: "All levels",
filterRegionAll: "All regions",
filterUnmatchedOff: "No unmatched filter",
kindAny: "Missing level or region",
kindLevel: "Missing level",
kindRegion: "Missing region",
filterLinkAll: "All link states",
filterLinkLinked: "Linked inventory",
filterLinkOrphaned: "Detached",
colLink: "Link",
linkManaged: "Managed",
linkUme: "UME",
linkBoth: "Managed+UME",
linkOrphaned: "Detached",
levelExternal: "L0 external",
levelCore: "L1 core",
levelCoreSub: "L1.1 core′",
levelAgg: "L2 aggregation",
levelAggSub: "L2.1 agg′",
levelAccess: "L3 access",
levelAccessSub: "L3.1 deeper access",
levelInvalid: "Invalid level (0–99.9, one decimal)",
sourceManual: "Manual",
sourceUmeSync: "UME sync",
sourceLldp: "LLDP placeholder",
sourceTopology: "Topology placeholder",
sourceWebcrt: "WebCRT",
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",
assignLevel: "Level only",
assignRegion: "Region only",
assignBoth: "Level + 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",
colLevel: "Level",
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.",
},
audit: {
title: "Audit & ops",
logsTitle: "Operation logs",
colSummary: "Summary",
colDetail: "Detail",
showDetail: "Expand",
hideDetail: "Collapse",
filterBusiness: "Business",
filterAll: "All",
filterWebcrt: "Device terminal",
filterNeExec: "Device exec",
filterAuth: "Auth",
filterHttp: "HTTP",
summary: {
connecting: "Connecting to {{device}}",
loginOk: "Logged in to {{device}}",
loginFail: "Login failed {{device}}: {{error}}",
logout: "Disconnected {{device}}",
logoutReason: "Disconnected {{device}} ({{reason}})",
command: "{{device}} · cmd: {{command}}",
neExec: "{{device}} · exec: {{commands}}",
neExecBatch: "Batch exec {{n}} NE(s) (ok {{ok}} / fail {{fail}})",
},
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;