mirror of
https://github.com/hansjone/netx.git
synced 2026-10-09 02:00:46 +08:00
Localize LLDP and topology classify English leftovers.
Translate job status/trigger/scope, item result, edge labels, and classify role/source option text in zh/en. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
617d4245b0
commit
7cd89aaae9
4 changed files with 167 additions and 24 deletions
|
|
@ -389,6 +389,34 @@ const en = {
|
|||
bPort: "Port B",
|
||||
lastSeen: "Last seen",
|
||||
},
|
||||
jobStatus: {
|
||||
running: "Running",
|
||||
pending: "Pending",
|
||||
paused: "Paused",
|
||||
done: "Done",
|
||||
failed: "Failed",
|
||||
cancelled: "Cancelled",
|
||||
},
|
||||
triggerMode: {
|
||||
manual: "Manual",
|
||||
schedule: "Scheduled",
|
||||
topology: "Topology canvas",
|
||||
},
|
||||
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",
|
||||
|
|
@ -1601,6 +1629,15 @@ const en = {
|
|||
linkUme: "UME",
|
||||
linkBoth: "Managed+UME",
|
||||
linkOrphaned: "Detached",
|
||||
roleCore: "Core",
|
||||
roleAggregation: "Aggregation",
|
||||
roleAccess: "Access",
|
||||
roleUnknown: "Unknown",
|
||||
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",
|
||||
|
|
|
|||
|
|
@ -385,6 +385,34 @@ const zh = {
|
|||
bPort: "对端口",
|
||||
lastSeen: "最近看见",
|
||||
},
|
||||
jobStatus: {
|
||||
running: "运行中",
|
||||
pending: "排队中",
|
||||
paused: "已暂停",
|
||||
done: "已完成",
|
||||
failed: "失败",
|
||||
cancelled: "已取消",
|
||||
},
|
||||
triggerMode: {
|
||||
manual: "手动",
|
||||
schedule: "周期",
|
||||
topology: "拓扑画布",
|
||||
},
|
||||
jobScope: {
|
||||
all: "全部网元",
|
||||
ne_ids: "指定网元",
|
||||
selected: "指定网元",
|
||||
},
|
||||
itemResult: {
|
||||
ok: "成功",
|
||||
warn: "警告",
|
||||
fail: "失败",
|
||||
},
|
||||
edgeSource: {
|
||||
lldp: "LLDP",
|
||||
manual: "手动",
|
||||
stale: "陈旧",
|
||||
},
|
||||
},
|
||||
configSync: {
|
||||
title: "配置同步",
|
||||
|
|
@ -1586,10 +1614,19 @@ const zh = {
|
|||
filterLinkLinked: "已关联库存",
|
||||
filterLinkOrphaned: "已解绑",
|
||||
colLink: "关联",
|
||||
linkManaged: "Managed",
|
||||
linkManaged: "托管",
|
||||
linkUme: "UME",
|
||||
linkBoth: "Managed+UME",
|
||||
linkBoth: "托管+UME",
|
||||
linkOrphaned: "已解绑",
|
||||
roleCore: "核心",
|
||||
roleAggregation: "汇聚",
|
||||
roleAccess: "接入",
|
||||
roleUnknown: "未知",
|
||||
sourceManual: "手动",
|
||||
sourceUmeSync: "UME 同步",
|
||||
sourceLldp: "LLDP 占位",
|
||||
sourceTopology: "拓扑占位",
|
||||
sourceWebcrt: "WebCRT",
|
||||
regexTitle: "正则查找",
|
||||
patternPh: "正则,如 ^CORE- 或 -BJ-",
|
||||
fieldName: "名称",
|
||||
|
|
|
|||
|
|
@ -25,6 +25,51 @@ const POLL_MS = 2500;
|
|||
const TARGET_PAGE_SIZE = 20;
|
||||
const SEP = " · ";
|
||||
|
||||
function lldpJobStatusLabel(t: (k: string) => string, status: string): string {
|
||||
const s = String(status || "").trim().toLowerCase();
|
||||
const key = `lldpLinks.jobStatus.${s}`;
|
||||
const label = t(key);
|
||||
return label === key ? status || "—" : label;
|
||||
}
|
||||
|
||||
function lldpTriggerLabel(t: (k: string) => string, mode: string): string {
|
||||
const s = String(mode || "").trim().toLowerCase();
|
||||
const key = `lldpLinks.triggerMode.${s}`;
|
||||
const label = t(key);
|
||||
return label === key ? mode || "—" : label;
|
||||
}
|
||||
|
||||
function lldpScopeLabel(t: (k: string) => string, scope: string): string {
|
||||
const s = String(scope || "").trim().toLowerCase();
|
||||
const key = `lldpLinks.jobScope.${s}`;
|
||||
const label = t(key);
|
||||
return label === key ? scope || "—" : label;
|
||||
}
|
||||
|
||||
function lldpItemResultLabel(
|
||||
t: (k: string) => string,
|
||||
it: { ok?: boolean; parser_stub?: boolean | string | null; unmatched_count?: number; unmatched?: unknown[] },
|
||||
): string {
|
||||
if (!it.ok) return t("lldpLinks.itemResult.fail");
|
||||
const unmatchedCount = it.unmatched_count ?? (it.unmatched?.length || 0);
|
||||
if (it.parser_stub || unmatchedCount > 0) return t("lldpLinks.itemResult.warn");
|
||||
return t("lldpLinks.itemResult.ok");
|
||||
}
|
||||
|
||||
function lldpEdgeStatusLabel(t: (k: string) => string, status: string): string {
|
||||
const s = status === "stale" ? "missing" : String(status || "").trim().toLowerCase();
|
||||
if (s === "active") return t("lldpLinks.edgeStatusActive");
|
||||
if (s === "missing") return t("lldpLinks.edgeStatusMissing");
|
||||
return status || "—";
|
||||
}
|
||||
|
||||
function lldpEdgeSourceLabel(t: (k: string) => string, source: string): string {
|
||||
const s = String(source || "").trim().toLowerCase();
|
||||
const key = `lldpLinks.edgeSource.${s}`;
|
||||
const label = t(key);
|
||||
return label === key ? source || "—" : label;
|
||||
}
|
||||
|
||||
export function LldpLinksPage() {
|
||||
const { t } = useI18n();
|
||||
const { showOk, showError } = useToast();
|
||||
|
|
@ -545,7 +590,7 @@ export function LldpLinksPage() {
|
|||
const st = e.status === "stale" ? "missing" : e.status;
|
||||
return (
|
||||
<tr key={e.id}>
|
||||
<td>{st}</td>
|
||||
<td>{lldpEdgeStatusLabel(t, st)}</td>
|
||||
<td>
|
||||
<div>{aLabel}</div>
|
||||
{e.a_ip && e.a_name ? <div className="muted">{e.a_ip}</div> : null}
|
||||
|
|
@ -556,7 +601,7 @@ export function LldpLinksPage() {
|
|||
{e.b_ip && e.b_name ? <div className="muted">{e.b_ip}</div> : null}
|
||||
</td>
|
||||
<td>{e.b_port || "—"}</td>
|
||||
<td>{e.source}</td>
|
||||
<td>{lldpEdgeSourceLabel(t, e.source)}</td>
|
||||
<td>
|
||||
{missCount || (st === "missing" ? 1 : 0)}
|
||||
{replaced ? (
|
||||
|
|
@ -635,8 +680,8 @@ export function LldpLinksPage() {
|
|||
</button>
|
||||
</td>
|
||||
<td className="mono">{job.id.slice(0, 8)}</td>
|
||||
<td>{job.trigger_mode}</td>
|
||||
<td>{job.scope}</td>
|
||||
<td>{lldpTriggerLabel(t, job.trigger_mode)}</td>
|
||||
<td>{lldpScopeLabel(t, job.scope)}</td>
|
||||
<td>
|
||||
<span
|
||||
className={
|
||||
|
|
@ -647,7 +692,7 @@ export function LldpLinksPage() {
|
|||
: undefined
|
||||
}
|
||||
>
|
||||
{job.status}
|
||||
{lldpJobStatusLabel(t, job.status)}
|
||||
</span>
|
||||
</td>
|
||||
<td>
|
||||
|
|
@ -744,7 +789,7 @@ export function LldpLinksPage() {
|
|||
{(() => {
|
||||
const job = jobDetailQuery.data;
|
||||
if (job) {
|
||||
return `${job.id.slice(0, 8)} · ${job.status} · ${job.done}/${job.total}`;
|
||||
return `${job.id.slice(0, 8)} · ${lldpJobStatusLabel(t, job.status)} · ${job.done}/${job.total}`;
|
||||
}
|
||||
if (jobDetailQuery.isLoading) {
|
||||
return `${expandedJobId.slice(0, 8)} · …`;
|
||||
|
|
@ -793,7 +838,7 @@ export function LldpLinksPage() {
|
|||
<td>{it.ne_name || it.ne_id || "—"}</td>
|
||||
<td>{it.ne_ip || "—"}</td>
|
||||
<td>
|
||||
{!it.ok ? "fail" : it.parser_stub || unmatchedCount > 0 ? "warn" : "ok"}
|
||||
{lldpItemResultLabel(t, it)}
|
||||
</td>
|
||||
<td>{it.neighbors}</td>
|
||||
<td>
|
||||
|
|
|
|||
|
|
@ -37,6 +37,27 @@ function isFabricNodeDeletable(n: FabricNodeSearchHit): boolean {
|
|||
return src === "lldp" || src === "topology" || src === "webcrt";
|
||||
}
|
||||
|
||||
const ROLE_OPTIONS = ["core", "aggregation", "access", "unknown"] as const;
|
||||
|
||||
function roleLabel(t: (k: string) => string, role: string): string {
|
||||
const s = String(role || "").trim().toLowerCase();
|
||||
if (s === "core") return t("topoClassify.roleCore");
|
||||
if (s === "aggregation") return t("topoClassify.roleAggregation");
|
||||
if (s === "access") return t("topoClassify.roleAccess");
|
||||
if (s === "unknown") return t("topoClassify.roleUnknown");
|
||||
return role || "-";
|
||||
}
|
||||
|
||||
function managedSourceLabel(t: (k: string) => string, source: string): string {
|
||||
const s = String(source || "").trim().toLowerCase();
|
||||
if (s === "manual") return t("topoClassify.sourceManual");
|
||||
if (s === "ume_sync") return t("topoClassify.sourceUmeSync");
|
||||
if (s === "lldp") return t("topoClassify.sourceLldp");
|
||||
if (s === "topology") return t("topoClassify.sourceTopology");
|
||||
if (s === "webcrt") return t("topoClassify.sourceWebcrt");
|
||||
return source;
|
||||
}
|
||||
|
||||
export function TopologyClassifyPage() {
|
||||
const { t } = useI18n();
|
||||
const { showOk, showError } = useToast();
|
||||
|
|
@ -285,10 +306,11 @@ export function TopologyClassifyPage() {
|
|||
}}
|
||||
>
|
||||
<option value="">{t("topoClassify.filterRoleAll")}</option>
|
||||
<option value="core">core</option>
|
||||
<option value="aggregation">aggregation</option>
|
||||
<option value="access">access</option>
|
||||
<option value="unknown">unknown</option>
|
||||
{ROLE_OPTIONS.map((r) => (
|
||||
<option key={r} value={r}>
|
||||
{roleLabel(t, r)}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
<select
|
||||
className="input"
|
||||
|
|
@ -380,10 +402,11 @@ export function TopologyClassifyPage() {
|
|||
value={assignRole}
|
||||
onChange={(e) => setAssignRole(e.target.value)}
|
||||
>
|
||||
<option value="core">core</option>
|
||||
<option value="aggregation">aggregation</option>
|
||||
<option value="access">access</option>
|
||||
<option value="unknown">unknown</option>
|
||||
{ROLE_OPTIONS.map((r) => (
|
||||
<option key={r} value={r}>
|
||||
{roleLabel(t, r)}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
) : null}
|
||||
{assignWhat !== "role" ? (
|
||||
|
|
@ -476,7 +499,7 @@ export function TopologyClassifyPage() {
|
|||
<td>{n.ip}</td>
|
||||
<td>
|
||||
{linkLabel(n.link_status)}
|
||||
{n.managed_source ? ` · ${n.managed_source}` : ""}
|
||||
{n.managed_source ? ` · ${managedSourceLabel(t, n.managed_source)}` : ""}
|
||||
</td>
|
||||
<td>
|
||||
{editing ? (
|
||||
|
|
@ -486,13 +509,14 @@ export function TopologyClassifyPage() {
|
|||
onChange={(e) => setEditRole(e.target.value)}
|
||||
>
|
||||
<option value="">-</option>
|
||||
<option value="core">core</option>
|
||||
<option value="aggregation">aggregation</option>
|
||||
<option value="access">access</option>
|
||||
<option value="unknown">unknown</option>
|
||||
{ROLE_OPTIONS.map((r) => (
|
||||
<option key={r} value={r}>
|
||||
{roleLabel(t, r)}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
) : (
|
||||
n.role || "-"
|
||||
roleLabel(t, n.role || "")
|
||||
)}
|
||||
</td>
|
||||
<td>
|
||||
|
|
@ -681,7 +705,7 @@ export function TopologyClassifyPage() {
|
|||
<li key={m.name}>
|
||||
<strong>{m.name}</strong>
|
||||
{" · "}
|
||||
{m.role}
|
||||
{roleLabel(t, m.role)}
|
||||
{" · "}
|
||||
{t("topoClassify.nodeCount").replace("{{count}}", String(m.node_count))}
|
||||
</li>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue