netx/web/src/i18n/locales/en/topology.ts
oliver 0d894efe5e Refactor web UI: feature-first structure, tokenized styles, modern blue theme.
Structure
- pages/ -> features/<domain>/{Page, components, hooks, lib}; 95 files moved,
  333 imports rewritten, verified with tsc.
- services/api.ts -> services/api/<domain>.ts (250 exports preserved),
  types.ts -> types/<domain>.ts, i18n zh/en -> locales/<lang>/<namespace>.ts
  (2696 messages deep-equal before/after).
- ESLint rule: shared layers must not import features/.

Styles
- index.css (16k lines) split losslessly into styles/{base,layout,components,
  pages,legacy,theme}; compiled CSS was byte-identical before tokenizing.
- ~930 hard-coded colors replaced by --nx-* tokens (theme/tokens.css); WebCRT
  off-palette colors unified; fixed dark-on-dark text introduced by mapping.
- theme/theme.css: modern blue design language (rounded shapes, gradients,
  unified buttons/fields/tables/modals), form-grid alignment fix, flat nav list
  without tree indent, soft selected state instead of left accent bar.

Docs: WEB.md documents code structure and style rules.
Not touched: the large Biz*/Webcrt/Topology page components (to be split later).

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-12 03:17:51 +08:00

344 lines
15 KiB
TypeScript

const 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)",
};
export default topology;