i18n(feeder): replace all English UI text with Chinese

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
caoqianming 2026-04-20 09:12:15 +08:00
parent 268c5f76af
commit f8757a757e
10 changed files with 63 additions and 63 deletions

View File

@ -38,7 +38,7 @@
</div>
<div class="toolbar">
<select id="pointSourceSelect"></select>
<div class="muted" id="pointSourceNodeCount">Nodes: 0</div>
<div class="muted" id="pointSourceNodeCount">节点: 0</div>
<button id="browseNodes">加载节点</button>
<button class="secondary" id="refreshTree">刷新树</button>
</div>

View File

@ -42,7 +42,7 @@
</div>
<div class="toolbar">
<select id="pointSourceSelect"></select>
<div class="muted" id="pointSourceNodeCount">Nodes: 0</div>
<div class="muted" id="pointSourceNodeCount">节点: 0</div>
<button id="browseNodes">加载节点</button>
<button class="secondary" id="refreshTree">刷新树</button>
</div>

View File

@ -105,8 +105,8 @@ function bindEvents() {
dom.closeEquipmentModalBtn.addEventListener("click", closeEquipmentModal);
dom.openPointModalBtn.addEventListener("click", openPointCreateModal);
dom.pointSourceSelect.addEventListener("change", () => {
dom.nodeTree.innerHTML = '<div class="muted">Click "Load Nodes" to fetch node tree</div>';
dom.pointSourceNodeCount.textContent = "Nodes: 0";
dom.nodeTree.innerHTML = '<div class="muted">点击"加载节点"获取节点树</div>';
dom.pointSourceNodeCount.textContent = "节点: 0";
});
dom.browseNodesBtn.addEventListener("click", () => withStatus(browseAndLoadTree()));
dom.refreshTreeBtn.addEventListener("click", () => withStatus(loadTree()));

View File

@ -52,8 +52,8 @@ function formatTimeLabel(timestamp) {
export async function openChart(pointId, pointName) {
state.chartPointId = pointId;
state.chartPointName = pointName || "Point";
dom.chartTitle.textContent = `${state.chartPointName} Chart`;
state.chartPointName = pointName || "点位";
dom.chartTitle.textContent = `${state.chartPointName} 趋势图`;
const items = await apiFetch(`/api/point/${pointId}/history?limit=120`);
state.chartData = (items || [])
@ -99,8 +99,8 @@ export function renderChart() {
if (!state.chartData.length) {
ctx.fillStyle = "#94a3b8";
ctx.font = "14px Segoe UI";
ctx.fillText("Click a point row to view its chart", 24, 40);
dom.chartSummary.textContent = "Click a point row to view its chart";
ctx.fillText("点击点位行查看图表", 24, 40);
dom.chartSummary.textContent = "点击点位行查看图表";
return;
}
@ -158,9 +158,9 @@ export function renderChart() {
ctx.translate(16, padding.top + plotHeight / 2);
ctx.rotate(-Math.PI / 2);
ctx.fillStyle = "#64748b";
ctx.fillText("Value", 0, 0);
ctx.fillText("数值", 0, 0);
ctx.restore();
ctx.fillText("Time", width / 2 - 12, height - 28);
ctx.fillText("时间", width / 2 - 12, height - 28);
ctx.strokeStyle = "#2563eb";
ctx.lineWidth = 2;

View File

@ -41,7 +41,7 @@ function renderEquipmentUnitOptions(selected = "", target = dom.equipmentUnitId)
}
export function renderBindingEquipmentOptions(selected = "", target = dom.bindingEquipmentId) {
const options = ['<option value="">Unbound</option>'];
const options = ['<option value="">未绑定</option>'];
filteredEquipments().forEach((item) => {
const equipment = equipmentOf(item);
const isSelected = equipment.id === selected ? "selected" : "";
@ -112,7 +112,7 @@ export function renderEquipments() {
const items = filteredEquipments();
if (!items.length) {
dom.equipmentList.innerHTML = '<div class="list-item"><div class="muted">No equipment</div></div>';
dom.equipmentList.innerHTML = '<div class="list-item"><div class="muted">暂无设备</div></div>';
return;
}
@ -126,7 +126,7 @@ export function renderEquipments() {
<span class="badge">${item.point_count ?? 0} pts</span>
</div>
<div>${equipment.name}</div>
<div class="muted">${equipment.kind || "No type"}</div>
<div class="muted">${equipment.kind || "未分类"}</div>
<div class="muted">单元: ${currentUnitLabel(equipment.unit_id)}</div>
<div class="row equipment-card-actions"></div>
`;
@ -141,7 +141,7 @@ export function renderEquipments() {
const editBtn = document.createElement("button");
editBtn.className = "secondary";
editBtn.textContent = "Edit";
editBtn.textContent = "编辑";
editBtn.addEventListener("click", (event) => {
event.stopPropagation();
openEditEquipmentModal(equipment);
@ -149,7 +149,7 @@ export function renderEquipments() {
const deleteBtn = document.createElement("button");
deleteBtn.className = "danger";
deleteBtn.textContent = "Delete";
deleteBtn.textContent = "删除";
deleteBtn.addEventListener("click", (event) => {
event.stopPropagation();
deleteEquipment(equipment.id).catch((error) => {
@ -218,7 +218,7 @@ export async function saveEquipment(event) {
}
export async function deleteEquipment(equipmentId) {
if (!window.confirm("Delete this equipment?")) {
if (!window.confirm("确认删除该设备?")) {
return;
}

View File

@ -58,7 +58,7 @@ export function renderOpsUnits() {
const startBlocked = !isAutoOn && (runtime?.fault_locked || runtime?.manual_ack_required || runtime?.rem_local);
const autoBtn = document.createElement("button");
autoBtn.className = isAutoOn ? "danger" : "secondary";
autoBtn.textContent = isAutoOn ? "Stop Auto" : "Start Auto";
autoBtn.textContent = isAutoOn ? "停止自动" : "启动自动";
autoBtn.disabled = startBlocked;
autoBtn.title = startBlocked
? (runtime?.fault_locked ? "设备故障中,无法启动自动控制"
@ -75,7 +75,7 @@ export function renderOpsUnits() {
if (runtime?.manual_ack_required) {
const ackBtn = document.createElement("button");
ackBtn.className = "danger";
ackBtn.textContent = "Ack Fault";
ackBtn.textContent = "故障确认";
ackBtn.title = "人工确认解除故障锁定";
ackBtn.addEventListener("click", (e) => {
e.stopPropagation();
@ -153,13 +153,13 @@ function renderOpsEquipments(equipments) {
const startBtn = document.createElement("button");
startBtn.className = "secondary";
startBtn.textContent = "Start";
startBtn.textContent = "启动";
startBtn.addEventListener("click", () =>
apiFetch(`/api/control/equipment/${eq.id}/start`, { method: "POST" }).catch(() => {})
);
const stopBtn = document.createElement("button");
stopBtn.className = "danger";
stopBtn.textContent = "Stop";
stopBtn.textContent = "停止";
stopBtn.addEventListener("click", () =>
apiFetch(`/api/control/equipment/${eq.id}/stop`, { method: "POST" }).catch(() => {})
);

View File

@ -11,7 +11,7 @@ import { state } from "./state.js";
function updatePointSourceNodeCount() {
const count = dom.nodeTree.querySelectorAll("details").length;
dom.pointSourceNodeCount.textContent = `Nodes: ${count}`;
dom.pointSourceNodeCount.textContent = `节点: ${count}`;
}
export function formatValue(monitor) {
@ -28,13 +28,13 @@ export function formatValue(monitor) {
}
export function renderSelectedNodes() {
dom.selectedCount.textContent = `Selected ${state.selectedNodeIds.size} nodes`;
dom.selectedCount.textContent = `已选中 ${state.selectedNodeIds.size} 个节点`;
}
export function updateSelectedPointSummary() {
const count = state.selectedPointIds.size;
dom.selectedPointCount.textContent = `Selected ${count} points`;
dom.batchBindingSummary.textContent = `Selected ${count} points`;
dom.selectedPointCount.textContent = `已选中 ${count} 个点位`;
dom.batchBindingSummary.textContent = `已选中 ${count} 个点位`;
dom.openBatchBindingBtn.disabled = count === 0;
}
@ -42,16 +42,16 @@ export function updatePointFilterSummary() {
const filters = [];
if (state.selectedEquipmentId) {
const equipment = state.equipmentMap.get(state.selectedEquipmentId);
filters.push(`Equipment:${equipment?.name || equipment?.code || "Unknown"}`);
filters.push(`设备:${equipment?.name || equipment?.code || "未知"}`);
}
if (state.selectedSourceId) {
const source = state.sources.find((item) => item.id === state.selectedSourceId);
filters.push(`Source:${source?.name || "Unknown"}`);
filters.push(`数据源:${source?.name || "未知"}`);
}
dom.pointFilterSummary.textContent = filters.length
? `Current filter: ${filters.join(" / ")}`
: "Current filter: All points";
? `当前筛选: ${filters.join(" / ")}`
: "当前筛选: 全部点位";
}
export function clearSelectedPoints() {
@ -102,8 +102,8 @@ export function openPointCreateModal() {
if (dom.pointSourceSelect) {
dom.pointSourceSelect.value = state.selectedSourceId || "";
}
dom.nodeTree.innerHTML = '<div class="muted">Select a source and load nodes</div>';
dom.pointSourceNodeCount.textContent = "Nodes: 0";
dom.nodeTree.innerHTML = '<div class="muted">选择数据源并加载节点</div>';
dom.pointSourceNodeCount.textContent = "节点: 0";
state.selectedNodeIds.clear();
renderSelectedNodes();
}
@ -111,8 +111,8 @@ export function openPointCreateModal() {
export async function loadTree() {
const sourceId = dom.pointSourceSelect.value || state.selectedSourceId;
if (!sourceId) {
dom.nodeTree.innerHTML = '<div class="muted">Select a source</div>';
dom.pointSourceNodeCount.textContent = "Nodes: 0";
dom.nodeTree.innerHTML = '<div class="muted">请选择数据源</div>';
dom.pointSourceNodeCount.textContent = "节点: 0";
return;
}
@ -126,7 +126,7 @@ export async function loadTree() {
export async function browseAndLoadTree() {
const sourceId = dom.pointSourceSelect.value || state.selectedSourceId;
if (!sourceId) {
throw new Error("Select a source first");
throw new Error("请先选择数据源");
}
state.selectedSourceId = sourceId;
@ -178,7 +178,7 @@ export async function loadPoints() {
dom.pointList.innerHTML = "";
if (!items.length) {
dom.pointList.innerHTML = '<tr><td colspan="7" class="empty-state">No points</td></tr>';
dom.pointList.innerHTML = '<tr><td colspan="7" class="empty-state">暂无点位</td></tr>';
dom.pointsPageInfo.textContent = `${state.pointsPage} / 1`;
clearSelectedPoints();
updatePointFilterSummary();
@ -207,7 +207,7 @@ export async function loadPoints() {
<td><span class="badge quality-${(monitor?.quality || "unknown").toLowerCase()}">${(monitor?.quality || "unknown").toUpperCase()}</span></td>
<td>
<div class="point-meta">
<div>${equipment ? equipment.name : '<span class="muted">Unbound</span>'}</div>
<div>${equipment ? equipment.name : '<span class="muted">未绑定</span>'}</div>
<div class="point-role">${point.signal_role || "--"}</div>
</div>
</td>
@ -228,7 +228,7 @@ export async function loadPoints() {
actionCell.className = "point-actions";
const editBtn = document.createElement("button");
editBtn.className = "secondary";
editBtn.textContent = "Edit";
editBtn.textContent = "编辑";
editBtn.addEventListener("click", (event) => {
event.stopPropagation();
openPointBinding(point);
@ -236,7 +236,7 @@ export async function loadPoints() {
const deleteBtn = document.createElement("button");
deleteBtn.className = "danger";
deleteBtn.textContent = "Delete";
deleteBtn.textContent = "删除";
deleteBtn.addEventListener("click", (event) => {
event.stopPropagation();
deletePoint(point.id).catch((error) => {
@ -270,14 +270,14 @@ export function openPointBinding(point) {
dom.bindingPointName.disabled = false;
const modalTitle = dom.pointBindingModal.querySelector("h3");
if (modalTitle) {
modalTitle.textContent = "Edit Point";
modalTitle.textContent = "编辑点位";
}
if (dom.clearPointBindingBtn) {
dom.clearPointBindingBtn.textContent = "Clear Equipment";
dom.clearPointBindingBtn.textContent = "清除设备";
}
const saveButton = dom.pointBindingForm?.querySelector('button[type="submit"]');
if (saveButton) {
saveButton.textContent = "Save";
saveButton.textContent = "保存";
}
renderBindingEquipmentOptions(point.equipment_id || "");
dom.bindingSignalRole.innerHTML = renderRoleOptions(point.signal_role || "");
@ -353,7 +353,7 @@ export async function clearBatchBinding() {
}
export async function deletePoint(pointId) {
if (!window.confirm("Delete this point?")) {
if (!window.confirm("确认删除该点位?")) {
return;
}

View File

@ -1,17 +1,17 @@
export const SIGNAL_ROLE_OPTIONS = [
{ value: "", label: "Unset" },
{ value: "rem", label: "REM Remote Enable" },
{ value: "run", label: "RUN Running" },
{ value: "flt", label: "FLT Fault" },
{ value: "ii", label: "II Current" },
{ value: "start_cmd", label: "Start Command" },
{ value: "stop_cmd", label: "Stop Command" },
{ value: "", label: "未设置" },
{ value: "rem", label: "REM 远程使能" },
{ value: "run", label: "RUN 运行" },
{ value: "flt", label: "FLT 故障" },
{ value: "ii", label: "II 电流" },
{ value: "start_cmd", label: "启动命令" },
{ value: "stop_cmd", label: "停止命令" },
];
export const EQUIPMENT_KIND_OPTIONS = [
{ value: "", label: "Unset" },
{ value: "coal_feeder", label: "Coal Feeder" },
{ value: "distributor", label: "Distributor" },
{ value: "", label: "未设置" },
{ value: "coal_feeder", label: "投煤器" },
{ value: "distributor", label: "布料机" },
];
export function renderRoleOptions(selected = "") {

View File

@ -8,7 +8,7 @@ function renderPointSourceOptions() {
return;
}
const options = ['<option value="">Select source</option>'];
const options = ['<option value="">选择数据源</option>'];
state.sources.forEach((source) => {
const selected = source.id === state.selectedSourceId ? "selected" : "";
options.push(`<option value="${source.id}" ${selected}>${source.name}</option>`);
@ -41,7 +41,7 @@ export function renderSources() {
const editBtn = document.createElement("button");
editBtn.className = "secondary";
editBtn.textContent = "Edit";
editBtn.textContent = "编辑";
editBtn.addEventListener("click", (event) => {
event.stopPropagation();
dom.sourceId.value = source.id;
@ -53,7 +53,7 @@ export function renderSources() {
const reconnectBtn = document.createElement("button");
reconnectBtn.className = "secondary";
reconnectBtn.textContent = "Reconnect";
reconnectBtn.textContent = "重连";
reconnectBtn.addEventListener("click", (event) => {
event.stopPropagation();
reconnectSource(source.id, source.name).catch((error) => {
@ -63,7 +63,7 @@ export function renderSources() {
const deleteBtn = document.createElement("button");
deleteBtn.className = "danger";
deleteBtn.textContent = "Delete";
deleteBtn.textContent = "删除";
deleteBtn.addEventListener("click", (event) => {
event.stopPropagation();
deleteSource(source.id).catch((error) => {
@ -118,14 +118,14 @@ export async function saveSource(event) {
}
export async function reconnectSource(sourceId, name) {
dom.statusText.textContent = `Reconnecting ${name || "Source"}...`;
dom.statusText.textContent = `正在重连 ${name || "数据源"}...`;
await apiFetch(`/api/source/${sourceId}/reconnect`, { method: "POST" });
await loadSources();
dom.statusText.textContent = "Ready";
dom.statusText.textContent = "就绪";
}
export async function deleteSource(sourceId) {
if (!window.confirm("Delete this source?")) {
if (!window.confirm("确认删除该数据源?")) {
return;
}

View File

@ -125,8 +125,8 @@ function buildUnitCard(unit, mode) {
<span class="badge ${unit.enabled ? "" : "offline"}">${unit.enabled ? "EN" : "DIS"}</span>
</div>
<div>${unit.name}</div>
<div class="muted">设备 ${bound.length} | Acc ${runtime ? Math.floor(runtime.display_acc_sec / 1000) : 0}s</div>
<div class="muted">Run ${unit.run_time_sec}s / Stop ${unit.stop_time_sec}s / Acc ${unit.acc_time_sec}s / BL ${unit.bl_time_sec}s</div>
<div class="muted">设备 ${bound.length} | 累计 ${runtime ? Math.floor(runtime.display_acc_sec / 1000) : 0}s</div>
<div class="muted">运行 ${unit.run_time_sec}s / 停止 ${unit.stop_time_sec}s / 累计 ${unit.acc_time_sec}s / 间隔 ${unit.bl_time_sec}s</div>
${mode === "config" ? `<div class="unit-equipment-tags">${equipTags}</div>` : ""}
<div class="row unit-card-actions"></div>
`;
@ -143,7 +143,7 @@ function buildUnitCard(unit, mode) {
const editBtn = document.createElement("button");
editBtn.className = "secondary";
editBtn.textContent = "Edit";
editBtn.textContent = "编辑";
editBtn.addEventListener("click", (event) => {
event.stopPropagation();
openEditUnitModal(unit);
@ -151,7 +151,7 @@ function buildUnitCard(unit, mode) {
const deleteBtn = document.createElement("button");
deleteBtn.className = "danger";
deleteBtn.textContent = "Delete";
deleteBtn.textContent = "删除";
deleteBtn.addEventListener("click", (event) => {
event.stopPropagation();
deleteUnit(unit.id).catch((error) => {
@ -240,7 +240,7 @@ export async function saveUnit(event) {
}
export async function deleteUnit(unitId) {
if (!window.confirm("Delete this unit?")) {
if (!window.confirm("确认删除该单元?")) {
return;
}