From d55192f3f7efb7c619ae9b5299772cb81554e72e Mon Sep 17 00:00:00 2001 From: u8array Date: Thu, 4 Jun 2026 11:32:32 +0200 Subject: [PATCH 1/3] feat(zpl): maintenance batch (^MA / ^MI / ^MW / ^JH) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds four Setup-Script-channel commands and a dedicated Maintenance tab in the printer-settings modal. - ^MA (Maintenance Alert): type (H/R), print (Y/N), threshold, frequency, units (M/I/C). Blank slot in `print`/`units` falls back to spec defaults (Y / M) so partial dumps round-trip. frequency min=1 by deliberate parser stance; out-of-range and invalid type discarded. - ^MI (Maintenance Info Message): paired with ^MA via a shared type discriminator. Enforced cross-field at the schema layer (superRefine) so a mismatched import is rejected; the UI cascades alert-type changes into the existing message so the invariant never trips silently. - ^MW (Head Cold Warning): single Y/N flag. - ^JH (Early Warning Settings): only f-slot (early-warning gate) and g-slot (head-cleaning interval, `M`) modelled; sibling slots emit empty. The registry places ^JH BEFORE ^MA so the gate is in effect when the alert lands. The g-slot wire format is marked TODO HW-verify (Zebra docs are ambiguous between raw `M` and an indexed 0..16 table). UI surface: - New `maintenance` PrinterSettingsTab + MaintenanceTab component - Alert is fully clearable via the type select's default option; message text empty clears the message - Defaults sourced from MAINTENANCE_ALERT_DEFAULTS (single source of truth shared by UI placeholder + parser blank-slot fallback) - Hint shows the 63-char cap via `({MAINTENANCE_MESSAGE_MAX_LEN})` template, not a hardcoded string Locales seeded English across 32 files; translators can refine. Roadmap 98→102 / 202; READMEs Media-&-feed 8/10 and Print-quality 5/14. --- README.md | 6 +- docs/zpl-roadmap.md | 8 +- .../PrinterSettings/MaintenanceTab.tsx | 266 ++++++++++++++++++ .../PrinterSettings/PrinterSettingsModal.tsx | 3 + src/lib/zplParser/handlers/setupScript.ts | 49 +++- src/lib/zplSetupScript.test.ts | 144 ++++++++++ src/lib/zplSetupScript.ts | 45 +++ src/locales/ar.ts | 28 ++ src/locales/bg.ts | 28 ++ src/locales/cs.ts | 28 ++ src/locales/da.ts | 28 ++ src/locales/de.ts | 28 ++ src/locales/el.ts | 28 ++ src/locales/en.ts | 28 ++ src/locales/es.ts | 28 ++ src/locales/et.ts | 28 ++ src/locales/fa.ts | 28 ++ src/locales/fi.ts | 28 ++ src/locales/fr.ts | 28 ++ src/locales/he.ts | 28 ++ src/locales/hr.ts | 28 ++ src/locales/hu.ts | 28 ++ src/locales/it.ts | 28 ++ src/locales/ja.ts | 28 ++ src/locales/ko.ts | 28 ++ src/locales/lt.ts | 28 ++ src/locales/lv.ts | 28 ++ src/locales/nl.ts | 28 ++ src/locales/no.ts | 28 ++ src/locales/pl.ts | 28 ++ src/locales/pt.ts | 28 ++ src/locales/ro.ts | 28 ++ src/locales/sk.ts | 28 ++ src/locales/sl.ts | 28 ++ src/locales/sr.ts | 28 ++ src/locales/sv.ts | 28 ++ src/locales/tr.ts | 28 ++ src/locales/zh-hans.ts | 28 ++ src/locales/zh-hant.ts | 28 ++ src/store/slices/uiSlice.ts | 3 +- src/types/PrinterProfile.ts | 74 +++++ 41 files changed, 1484 insertions(+), 10 deletions(-) create mode 100644 src/components/PrinterSettings/MaintenanceTab.tsx diff --git a/README.md b/README.md index 57ca0bf6..6341b5ff 100644 --- a/README.md +++ b/README.md @@ -116,7 +116,7 @@ Both `.zpl` and `.json` round-trip cleanly. `.zpl` preserves all printable conte ## Coverage -98 of the 202 ZPL II commands tracked in the [roadmap](docs/zpl-roadmap.md) are supported today. Categorical breakdown: +102 of the 202 ZPL II commands tracked in the [roadmap](docs/zpl-roadmap.md) are supported today. Categorical breakdown: | Area | Supported | |---|---| @@ -128,9 +128,9 @@ Both `.zpl` and `.json` round-trip cleanly. `.zpl` preserves all printable conte | Clock & time | 2 / 3 | | Identity & access | 3 / 3 | | Graphics | 6 / 12 | -| Media & feed | 5 / 10 | +| Media & feed | 8 / 10 | | Text & fonts | 6 / 14 | -| Print quality | 4 / 14 | +| Print quality | 5 / 14 | | Configuration & persistence | 3 / 5 | | Hardware / Host comm / RFID / Network | 0 / 67 | diff --git a/docs/zpl-roadmap.md b/docs/zpl-roadmap.md index 303174ca..25176209 100644 --- a/docs/zpl-roadmap.md +++ b/docs/zpl-roadmap.md @@ -158,12 +158,12 @@ The Printer Settings Modal (Media & Feed / Print Quality / Clock & Time / Encodi | `[x]` | `^ML` | maximum label length | | | `[x]` | `^MF` | media feed | | | `[x]` | `^XB` | suppress backfeed | | -| `[ ]` | `^MA` | maintenance alert | `Coming soon` | +| `[x]` | `^MA` | maintenance alert | | | `[ ]` | `^MC` | map clear | `Coming soon` | -| `[ ]` | `^MI` | maintenance info message | `Coming soon` | +| `[x]` | `^MI` | maintenance info message | | | `[ ]` | `^MP` | mode protection | `Coming soon` | | `[x]` | `^MU` | units of measure | | -| `[ ]` | `^MW` | head cold warning | `Coming soon` | +| `[x]` | `^MW` | head cold warning | | ## Print quality @@ -173,7 +173,7 @@ The Printer Settings Modal (Media & Feed / Print Quality / Clock & Time / Encodi | `[x]` | `^JT` | head test interval | | | `[x]` | `~TA` | tear off adjust | | | `[ ]` | `^PA` | advanced text properties | `Coming soon` | -| `[ ]` | `^JH` | early warning settings | `Coming soon` | +| `[x]` | `^JH` | early warning settings | | | `[ ]` | `^PF` | slew dot rows | `Coming soon` | | `[ ]` | `^PH` / `~PH` | slew to home | `Coming soon` | | `[ ]` | `^PN` | present now | `Coming soon` | diff --git a/src/components/PrinterSettings/MaintenanceTab.tsx b/src/components/PrinterSettings/MaintenanceTab.tsx new file mode 100644 index 00000000..a63805cf --- /dev/null +++ b/src/components/PrinterSettings/MaintenanceTab.tsx @@ -0,0 +1,266 @@ +import { useT } from "../../lib/useT"; +import { useLabelStore } from "../../store/labelStore"; +import { inputCls, labelCls } from "../ui/formStyles"; +import type { PrinterProfile } from "../../types/PrinterProfile"; +import { + HEAD_CLEANING_INTERVAL_RANGE, + MAINTENANCE_ALERT_DEFAULTS, + MAINTENANCE_ALERT_PRINT_VALUES, + MAINTENANCE_ALERT_TYPES, + MAINTENANCE_ALERT_UNITS, + MAINTENANCE_DISTANCE_MAX, + MAINTENANCE_MESSAGE_MAX_LEN, + isMaintenanceAlertPrint, + isMaintenanceAlertType, + isMaintenanceAlertUnit, + type MaintenanceAlertType, + type MaintenanceAlertUnit, +} from "../../types/PrinterProfile"; +import { + ZplBoundedIntInput, + ZplCommandLabel, + ZplEnumSelect, + ZplField, +} from "./zplFieldPrimitives"; + +type LocMaintenance = ReturnType["printerSettings"]["maintenance"]; +type MaintenanceAlert = NonNullable; + +const ALERT_TYPE_LABEL_KEYS = { + H: 'alertTypeH', + R: 'alertTypeR', +} as const satisfies Record; + +const ALERT_UNIT_LABEL_KEYS = { + M: 'alertUnitsM', + I: 'alertUnitsI', + C: 'alertUnitsC', +} as const satisfies Record; + +/** Maintenance tab (^MA/^MI/^MW/^JH). */ +export function MaintenanceTab() { + const t = useT(); + const profile = useLabelStore((s) => s.printerProfile); + const patchPrinterProfile = useLabelStore((s) => s.patchPrinterProfile); + const loc = t.printerSettings.maintenance; + const alert = profile.maintenanceAlert; + const message = profile.maintenanceMessage; + + // Centralised mutation: any change to maintenanceAlert.type also + // cascades into maintenanceMessage.type so the cross-field invariant + // (superRefine in PrinterProfile.ts) never trips and a silent patch + // rollback never strands the UI. + const updateAlert = (key: K, value: MaintenanceAlert[K]) => { + if (!alert) return; + const nextAlert = { ...alert, [key]: value }; + const next: Partial = { maintenanceAlert: nextAlert }; + if (key === "type" && message) { + next.maintenanceMessage = { ...message, type: nextAlert.type }; + } + patchPrinterProfile(next); + }; + + return ( +
+ + +
+
+
+ + +
+
+ + +
+
+
+ { + if (threshold !== undefined) updateAlert("threshold", threshold); + }} + disabled={!alert} + /> + { + if (frequency !== undefined) updateAlert("frequency", frequency); + }} + disabled={!alert} + /> +
+ + +
+
+
+
+ + + +
+
+ + { + const text = e.target.value; + if (!text) { + patchPrinterProfile({ maintenanceMessage: undefined }); + return; + } + // Type sticks to alert.type when an alert exists; otherwise + // falls back to the default so superRefine stays satisfied. + const type = message?.type ?? alert?.type ?? MAINTENANCE_ALERT_DEFAULTS.type; + patchPrinterProfile({ maintenanceMessage: { type, text } }); + }} + /> + + {loc.messageTextHint} ({MAINTENANCE_MESSAGE_MAX_LEN}) + +
+
+
+ + v === 'E' || v === 'D'} + value={profile.earlyWarningMaintenance} + onChange={(earlyWarningMaintenance) => patchPrinterProfile({ earlyWarningMaintenance })} + defaultLabel={t.printerSettings.defaultOption} + optionLabel={(v) => v === 'E' ? loc.earlyWarningE : loc.earlyWarningD} + /> + + {loc.earlyWarningGateHint} + + + + patchPrinterProfile({ headCleaningIntervalMeters }) + } + unit={loc.alertUnitsM} + /> + + {loc.headCleaningIntervalHint} + + + v === 'Y' || v === 'N'} + value={profile.headColdWarning} + onChange={(headColdWarning) => patchPrinterProfile({ headColdWarning })} + defaultLabel={t.printerSettings.defaultOption} + optionLabel={(v) => v === 'Y' ? loc.headColdWarningOn : loc.headColdWarningOff} + /> +
+ ); +} + +function BoundedIntCell({ + label, + min, + max, + value, + onChange, + disabled, +}: { + label: string; + min: number; + max: number; + value: number | undefined; + onChange: (next: number | undefined) => void; + disabled?: boolean; +}) { + return ( +
+ + { + const raw = e.target.value; + if (raw === "") return; + const n = Number.parseInt(raw, 10); + if (Number.isNaN(n)) return; + onChange(Math.max(min, Math.min(max, n))); + }} + /> +
+ ); +} diff --git a/src/components/PrinterSettings/PrinterSettingsModal.tsx b/src/components/PrinterSettings/PrinterSettingsModal.tsx index 2fffb19e..b0731d2e 100644 --- a/src/components/PrinterSettings/PrinterSettingsModal.tsx +++ b/src/components/PrinterSettings/PrinterSettingsModal.tsx @@ -11,6 +11,7 @@ import { ZplLine } from "../Output/ZplLine"; import { ClockAndTimeTab } from "./ClockAndTimeTab"; import { EncodingAndLanguageTab } from "./EncodingAndLanguageTab"; import { IdentityTab } from "./IdentityTab"; +import { MaintenanceTab } from "./MaintenanceTab"; import { MediaFeedTab } from "./MediaFeedTab"; import { PrintQualityTab } from "./PrintQualityTab"; @@ -24,6 +25,7 @@ const TOP_TAB_OF = { clockTime: 'setupScript', encodingLanguage: 'setupScript', identity: 'setupScript', + maintenance: 'setupScript', } as const satisfies Record; const TOP_TAB_ORDER: readonly TopTabId[] = ['perLabel', 'setupScript']; @@ -50,6 +52,7 @@ const TAB_COMPONENTS: Partial> = { clockTime: ClockAndTimeTab, encodingLanguage: EncodingAndLanguageTab, identity: IdentityTab, + maintenance: MaintenanceTab, }; const MODAL_BOX_CLS = diff --git a/src/lib/zplParser/handlers/setupScript.ts b/src/lib/zplParser/handlers/setupScript.ts index 2d4bfe92..1a44a24f 100644 --- a/src/lib/zplParser/handlers/setupScript.ts +++ b/src/lib/zplParser/handlers/setupScript.ts @@ -1,4 +1,4 @@ -import { CLOCK_TOLERANCE_RANGE, HEAD_TEST_INTERVAL_RANGE, PRINTER_NAME_MAX_LEN, PRINTER_PASSWORD_REGEX, TEAR_OFF_ADJUST_RANGE, isClockFormat, isClockLanguage, isConfigUpdateAction, isPrinterLocale, isZplMode, setupScriptUnsafeCharRegex } from "../../../types/PrinterProfile"; +import { CLOCK_TOLERANCE_RANGE, HEAD_CLEANING_INTERVAL_RANGE, HEAD_TEST_INTERVAL_RANGE, MAINTENANCE_ALERT_DEFAULTS, MAINTENANCE_DISTANCE_MAX, MAINTENANCE_MESSAGE_MAX_LEN, PRINTER_NAME_MAX_LEN, PRINTER_PASSWORD_REGEX, TEAR_OFF_ADJUST_RANGE, isClockFormat, isClockLanguage, isConfigUpdateAction, isMaintenanceAlertPrint, isMaintenanceAlertType, isMaintenanceAlertUnit, isPrinterLocale, isZplMode, setupScriptUnsafeCharRegex } from "../../../types/PrinterProfile"; import { parseIntOrUndef } from "../../inputParse"; import { parseRealtimeClock } from "../../realtimeClock"; import type { ParserState } from "../context"; @@ -6,7 +6,8 @@ import { inRange, strParam } from "../helpers"; import type { Handler } from "../types"; /** Setup-Script commands (^JZ, ^JT, ~TA, ^ST, ^KD, ^KL, ^SE, ^SZ, - * ^KN, ^SL, ^KP, ^JU): all write the shared `printerProfile` slice. */ + * ^KN, ^SL, ^KP, ^MA, ^MI, ^MW, ^JH, ^JU): all write the shared + * `printerProfile` slice. */ export function createSetupScriptHandlers(s: ParserState): Record { const printerProfile = s.result.printerProfile; return { @@ -80,5 +81,49 @@ export function createSetupScriptHandlers(s: ParserState): Record=1; 0 ("alert every 0 labels") is degenerate. + const frequency = inRange(parseIntOrUndef(p[3]), { min: 1, max: MAINTENANCE_DISTANCE_MAX }); + if (threshold === undefined || frequency === undefined) return; + const unitsRaw = strParam(p[4]); + const units = unitsRaw === "" ? MAINTENANCE_ALERT_DEFAULTS.units : unitsRaw; + if (!isMaintenanceAlertUnit(units)) return; + printerProfile.maintenanceAlert = { type, print, threshold, frequency, units }; + }, + MI(p) { + const type = strParam(p[0]); + if (!isMaintenanceAlertType(type)) return; + // Rejoin so a message with embedded commas trips the + // unsafe-char guard below instead of getting silently + // truncated at the first comma. + const text = (p.slice(1).join(",")).trim(); + if (!text || text.length > MAINTENANCE_MESSAGE_MAX_LEN) return; + if (setupScriptUnsafeCharRegex.test(text)) return; + printerProfile.maintenanceMessage = { type, text }; + }, + MW(_, rest) { + const v = rest.trim().toUpperCase(); + if (v === "Y" || v === "N") printerProfile.headColdWarning = v; + }, + // ^JH: only f/g modelled; other slots are runtime reset flags. + JH(p) { + const JH_SLOT_F = 5; + const JH_SLOT_G = 6; + const f = strParam(p[JH_SLOT_F]); + if (f === "E" || f === "D") printerProfile.earlyWarningMaintenance = f; + const gMatch = /^(\d+)M$/i.exec((p[JH_SLOT_G] ?? "").trim()); + if (gMatch && gMatch[1] !== undefined) { + const meters = inRange(parseIntOrUndef(gMatch[1]), HEAD_CLEANING_INTERVAL_RANGE); + if (meters !== undefined) printerProfile.headCleaningIntervalMeters = meters; + } + }, }; } diff --git a/src/lib/zplSetupScript.test.ts b/src/lib/zplSetupScript.test.ts index f56844fb..b7865f63 100644 --- a/src/lib/zplSetupScript.test.ts +++ b/src/lib/zplSetupScript.test.ts @@ -410,6 +410,11 @@ describe("generateSetupScript — output shape", () => { "clockMode", "clockTolerance", "clockLanguage", + "earlyWarningMaintenance", + "headCleaningIntervalMeters", + "maintenanceAlert", + "maintenanceMessage", + "headColdWarning", "configurationUpdate", ]); }); @@ -447,6 +452,145 @@ describe("generateSetupScript — output shape", () => { }); }); +describe("generateSetupScript — maintenance commands", () => { + it("emits ^MA with the 5 positional params", () => { + const script = generateSetupScript({ + ...base, + maintenanceAlert: { type: 'H', print: 'Y', threshold: 5, frequency: 1, units: 'M' }, + }); + expect(script).toBe("^XA\n^MAH,Y,5,1,M\n^XZ"); + }); + + it("emits ^MI as type + text", () => { + const script = generateSetupScript({ + ...base, + maintenanceMessage: { type: 'H', text: 'Clean head soon' }, + }); + expect(script).toBe("^XA\n^MIH,Clean head soon\n^XZ"); + }); + + it("emits ^MW for the head-cold-warning flag", () => { + expect(generateSetupScript({ ...base, headColdWarning: 'N' })).toBe("^XA\n^MWN\n^XZ"); + }); + + it("emits ^JH with f-slot filled and other slots empty", () => { + const script = generateSetupScript({ ...base, earlyWarningMaintenance: 'E' }); + expect(script).toBe("^XA\n^JH,,,,,E,,,,\n^XZ"); + }); + + it("emits ^JH with both f and g slots when both set", () => { + const script = generateSetupScript({ + ...base, + earlyWarningMaintenance: 'E', + headCleaningIntervalMeters: 150, + }); + expect(script).toBe("^XA\n^JH,,,,,E,150M,,,\n^XZ"); + }); + + it("emits ^JH with only g-slot when only the interval is set", () => { + const script = generateSetupScript({ ...base, headCleaningIntervalMeters: 200 }); + expect(script).toBe("^XA\n^JH,,,,,,200M,,,\n^XZ"); + }); + + it("parses ^MA / ^MI / ^MW / ^JH back into the profile", () => { + const zpl = [ + "^XA", + "^MAH,Y,5,1,M", + "^MIH,Clean me", + "^MWN", + "^JH,,,,,E,150M,,,", + "^XZ", + ].join("\n"); + const { printerProfile } = parseZPL(zpl); + expect(printerProfile.maintenanceAlert).toEqual({ + type: 'H', print: 'Y', threshold: 5, frequency: 1, units: 'M', + }); + expect(printerProfile.maintenanceMessage).toEqual({ type: 'H', text: 'Clean me' }); + expect(printerProfile.headColdWarning).toBe('N'); + expect(printerProfile.earlyWarningMaintenance).toBe('E'); + expect(printerProfile.headCleaningIntervalMeters).toBe(150); + }); + + it("round-trips the maintenance batch", () => { + const profile: PrinterProfile = { + maintenanceAlert: { type: 'R', print: 'N', threshold: 50, frequency: 5, units: 'I' }, + maintenanceMessage: { type: 'R', text: 'Replace head' }, + headColdWarning: 'Y', + earlyWarningMaintenance: 'E', + headCleaningIntervalMeters: 300, + }; + const script = generateSetupScript(profile); + const { printerProfile } = parseZPL(script); + expect(printerProfile).toEqual(profile); + }); + + it("emits ^JH before ^MA so the alert gate is set first", () => { + const script = generateSetupScript({ + ...base, + earlyWarningMaintenance: 'E', + maintenanceAlert: { type: 'H', print: 'Y', threshold: 5, frequency: 1, units: 'M' }, + }); + expect(script.indexOf("^JH")).toBeLessThan(script.indexOf("^MA")); + }); + + it("defaults ^MA print to Y when the slot is blank", () => { + const { printerProfile } = parseZPL("^XA^MAH,,5,1,M^XZ"); + expect(printerProfile.maintenanceAlert?.print).toBe("Y"); + }); + + it("defaults ^MA units to M when the slot is blank", () => { + const { printerProfile } = parseZPL("^XA^MAH,Y,5,1^XZ"); + expect(printerProfile.maintenanceAlert?.units).toBe("M"); + }); + + it("drops ^MA with invalid type", () => { + const { printerProfile } = parseZPL("^XA^MAX,Y,5,1,M^XZ"); + expect(printerProfile.maintenanceAlert).toBeUndefined(); + }); + + it("drops ^MA with out-of-range threshold", () => { + const { printerProfile } = parseZPL("^XA^MAH,Y,999999,1,M^XZ"); + expect(printerProfile.maintenanceAlert).toBeUndefined(); + }); + + it("drops ^MA with frequency=0 (out of spec example range)", () => { + // Documents the parser's min=1 stance: older firmware dumps with + // `frequency=0` (intended as a "disabled" idiom) are discarded + // rather than imported as a degenerate "alert every 0 labels". + const { printerProfile } = parseZPL("^XA^MAH,Y,5,0,M^XZ"); + expect(printerProfile.maintenanceAlert).toBeUndefined(); + }); + + it("drops ^MI with embedded comma", () => { + const { printerProfile } = parseZPL("^XA^MIH,Hi, there^XZ"); + expect(printerProfile.maintenanceMessage).toBeUndefined(); + }); + + it("drops ^MI exceeding the message length cap", () => { + const longText = "A".repeat(64); + const { printerProfile } = parseZPL(`^XA^MIH,${longText}^XZ`); + expect(printerProfile.maintenanceMessage).toBeUndefined(); + }); + + it("accepts ^JH g-slot in lowercase m suffix", () => { + const { printerProfile } = parseZPL("^XA^JH,,,,,E,150m,,,^XZ"); + expect(printerProfile.headCleaningIntervalMeters).toBe(150); + }); + + it("drops ^JH g-slot without M suffix", () => { + const { printerProfile } = parseZPL("^XA^JH,,,,,E,150,,,^XZ"); + expect(printerProfile.headCleaningIntervalMeters).toBeUndefined(); + }); + + it("rejects mismatched ^MA/^MI type via schema", () => { + const result = printerProfileSchema.safeParse({ + maintenanceAlert: { type: 'H', print: 'Y', threshold: 5, frequency: 1, units: 'M' }, + maintenanceMessage: { type: 'R', text: 'Mismatched' }, + }); + expect(result.success).toBe(false); + }); +}); + describe("SETUP_SCRIPT_EMITTERS structural invariants", () => { it("every foldedInto.target points at a kind:'emit' entry, never another foldedInto", () => { // A target chain (foldedInto → foldedInto) would mean no entry diff --git a/src/lib/zplSetupScript.ts b/src/lib/zplSetupScript.ts index cc6cda2f..d08a9432 100644 --- a/src/lib/zplSetupScript.ts +++ b/src/lib/zplSetupScript.ts @@ -21,6 +21,10 @@ type SetupScriptEntry = target: keyof PrinterProfile; }; +/** Named indices for the two ^JH slots we model (a..j = 0..9). */ +const JH_SLOT_F = 5; +const JH_SLOT_G = 6; + const SETUP_SCRIPT_EMITTERS = { tearOffAdjust: { kind: 'emit', @@ -120,6 +124,47 @@ const SETUP_SCRIPT_EMITTERS = { // Drop bare `^SL,` (mode unset): spec leaves the shape // implementation-defined. clockLanguage: { kind: 'foldedInto', target: 'clockMode' }, + // ^JH precedes ^MA so the master gate is on when the alert lands; + // otherwise the printer discards the alert until ^JUS + reboot. + // earlyWarningMaintenance owns the composite emit; + // headCleaningIntervalMeters folds in (cf. ^KN). + earlyWarningMaintenance: { + kind: 'emit', + channel: 'block', + emit: (p) => { + if (p.earlyWarningMaintenance === undefined && p.headCleaningIntervalMeters === undefined) { + return null; + } + const slots = ['', '', '', '', '', '', '', '', '', '']; + slots[JH_SLOT_F] = p.earlyWarningMaintenance ?? ''; + if (p.headCleaningIntervalMeters !== undefined) { + slots[JH_SLOT_G] = `${p.headCleaningIntervalMeters}M`; + } + return `^JH${slots.join(',')}`; + }, + }, + headCleaningIntervalMeters: { kind: 'foldedInto', target: 'earlyWarningMaintenance' }, + maintenanceAlert: { + kind: 'emit', + channel: 'block', + emit: (p) => { + const m = p.maintenanceAlert; + if (!m) return null; + return `^MA${m.type},${m.print},${m.threshold},${m.frequency},${m.units}`; + }, + }, + maintenanceMessage: { + kind: 'emit', + channel: 'block', + emit: (p) => p.maintenanceMessage + ? `^MI${p.maintenanceMessage.type},${p.maintenanceMessage.text}` + : null, + }, + headColdWarning: { + kind: 'emit', + channel: 'block', + emit: (p) => p.headColdWarning !== undefined ? `^MW${p.headColdWarning}` : null, + }, // configurationUpdate is the registry's last entry on purpose: a // commit (`^JUS`) needs to follow every other persistent write so // the EEPROM lands the rest of the block before being asked to diff --git a/src/locales/ar.ts b/src/locales/ar.ts index 03d02d41..54fa0591 100644 --- a/src/locales/ar.ts +++ b/src/locales/ar.ts @@ -128,6 +128,7 @@ const ar = { clockTime: 'التاريخ والوقت', encodingLanguage: 'الترميز واللغة', identity: 'الهوية', + maintenance: 'Maintenance', }, railGroupPerLabel: 'لكل ملصق', railGroupSetupScript: 'نص الإعداد', @@ -244,6 +245,33 @@ const ar = { configurationUpdateOptionN: 'N — إعادة تحميل إعدادات الشبكة المصنعية (مدمر)', configurationUpdateOptionF: 'F — إعادة الضبط للمصنع (مدمر)', }, + maintenance: { + maintenanceAlertHeading: 'Maintenance alert', + alertType: 'Alert type', + alertTypeH: 'Head cleaning', + alertTypeR: 'Head replacement', + alertPrint: 'Print warning label', + alertPrintY: 'Print', + alertPrintN: 'Skip', + alertThreshold: 'Threshold', + alertFrequency: 'Repeat frequency', + alertUnits: 'Units', + alertUnitsM: 'Meters', + alertUnitsI: 'Inches', + alertUnitsC: 'Centimeters', + maintenanceMessageHeading: 'Custom message', + messageText: 'Message text', + messageTextHint: 'Characters printed on the alert label.', + headColdWarning: 'Head cold warning', + headColdWarningOn: 'On', + headColdWarningOff: 'Off', + earlyWarningMaintenance: 'Early warning maintenance', + earlyWarningE: 'Enabled', + earlyWarningD: 'Disabled', + headCleaningIntervalMeters: 'Head cleaning interval (m)', + headCleaningIntervalHint: 'Distance after which the printer flags the head for cleaning.', + earlyWarningGateHint: 'Master gate for the maintenance alert system; required for ^MA to fire.', + }, }, app: { diff --git a/src/locales/bg.ts b/src/locales/bg.ts index e1ccd5ab..4e9565dd 100644 --- a/src/locales/bg.ts +++ b/src/locales/bg.ts @@ -128,6 +128,7 @@ const bg = { clockTime: 'Дата и час', encodingLanguage: 'Кодиране и език', identity: 'Идентичност', + maintenance: 'Maintenance', }, railGroupPerLabel: 'На етикет', railGroupSetupScript: 'Инициализиращ скрипт', @@ -244,6 +245,33 @@ const bg = { configurationUpdateOptionN: 'N — Зареди фабрични мрежови настройки (разрушително)', configurationUpdateOptionF: 'F — Фабрично нулиране (разрушително)', }, + maintenance: { + maintenanceAlertHeading: 'Maintenance alert', + alertType: 'Alert type', + alertTypeH: 'Head cleaning', + alertTypeR: 'Head replacement', + alertPrint: 'Print warning label', + alertPrintY: 'Print', + alertPrintN: 'Skip', + alertThreshold: 'Threshold', + alertFrequency: 'Repeat frequency', + alertUnits: 'Units', + alertUnitsM: 'Meters', + alertUnitsI: 'Inches', + alertUnitsC: 'Centimeters', + maintenanceMessageHeading: 'Custom message', + messageText: 'Message text', + messageTextHint: 'Characters printed on the alert label.', + headColdWarning: 'Head cold warning', + headColdWarningOn: 'On', + headColdWarningOff: 'Off', + earlyWarningMaintenance: 'Early warning maintenance', + earlyWarningE: 'Enabled', + earlyWarningD: 'Disabled', + headCleaningIntervalMeters: 'Head cleaning interval (m)', + headCleaningIntervalHint: 'Distance after which the printer flags the head for cleaning.', + earlyWarningGateHint: 'Master gate for the maintenance alert system; required for ^MA to fire.', + }, }, app: { diff --git a/src/locales/cs.ts b/src/locales/cs.ts index 6bbb09bc..5b9bee2a 100644 --- a/src/locales/cs.ts +++ b/src/locales/cs.ts @@ -128,6 +128,7 @@ const cs = { clockTime: 'Datum a čas', encodingLanguage: 'Kódování a jazyk', identity: 'Identita', + maintenance: 'Maintenance', }, railGroupPerLabel: 'Na štítek', railGroupSetupScript: 'Inicializační skript', @@ -244,6 +245,33 @@ const cs = { configurationUpdateOptionN: 'N — Načíst tovární síťová nastavení (destruktivní)', configurationUpdateOptionF: 'F — Tovární reset (destruktivní)', }, + maintenance: { + maintenanceAlertHeading: 'Maintenance alert', + alertType: 'Alert type', + alertTypeH: 'Head cleaning', + alertTypeR: 'Head replacement', + alertPrint: 'Print warning label', + alertPrintY: 'Print', + alertPrintN: 'Skip', + alertThreshold: 'Threshold', + alertFrequency: 'Repeat frequency', + alertUnits: 'Units', + alertUnitsM: 'Meters', + alertUnitsI: 'Inches', + alertUnitsC: 'Centimeters', + maintenanceMessageHeading: 'Custom message', + messageText: 'Message text', + messageTextHint: 'Characters printed on the alert label.', + headColdWarning: 'Head cold warning', + headColdWarningOn: 'On', + headColdWarningOff: 'Off', + earlyWarningMaintenance: 'Early warning maintenance', + earlyWarningE: 'Enabled', + earlyWarningD: 'Disabled', + headCleaningIntervalMeters: 'Head cleaning interval (m)', + headCleaningIntervalHint: 'Distance after which the printer flags the head for cleaning.', + earlyWarningGateHint: 'Master gate for the maintenance alert system; required for ^MA to fire.', + }, }, app: { diff --git a/src/locales/da.ts b/src/locales/da.ts index 1839036c..67bd3433 100644 --- a/src/locales/da.ts +++ b/src/locales/da.ts @@ -128,6 +128,7 @@ const da = { clockTime: 'Dato og tid', encodingLanguage: 'Kodning og sprog', identity: 'Identitet', + maintenance: 'Maintenance', }, railGroupPerLabel: 'Pr. etiket', railGroupSetupScript: 'Opsætningsscript', @@ -244,6 +245,33 @@ const da = { configurationUpdateOptionN: 'N — Indlæs fabrik-netværksindstillinger (destruktiv)', configurationUpdateOptionF: 'F — Fabriksnulstilling (destruktiv)', }, + maintenance: { + maintenanceAlertHeading: 'Maintenance alert', + alertType: 'Alert type', + alertTypeH: 'Head cleaning', + alertTypeR: 'Head replacement', + alertPrint: 'Print warning label', + alertPrintY: 'Print', + alertPrintN: 'Skip', + alertThreshold: 'Threshold', + alertFrequency: 'Repeat frequency', + alertUnits: 'Units', + alertUnitsM: 'Meters', + alertUnitsI: 'Inches', + alertUnitsC: 'Centimeters', + maintenanceMessageHeading: 'Custom message', + messageText: 'Message text', + messageTextHint: 'Characters printed on the alert label.', + headColdWarning: 'Head cold warning', + headColdWarningOn: 'On', + headColdWarningOff: 'Off', + earlyWarningMaintenance: 'Early warning maintenance', + earlyWarningE: 'Enabled', + earlyWarningD: 'Disabled', + headCleaningIntervalMeters: 'Head cleaning interval (m)', + headCleaningIntervalHint: 'Distance after which the printer flags the head for cleaning.', + earlyWarningGateHint: 'Master gate for the maintenance alert system; required for ^MA to fire.', + }, }, app: { diff --git a/src/locales/de.ts b/src/locales/de.ts index 75718a45..cdccff71 100644 --- a/src/locales/de.ts +++ b/src/locales/de.ts @@ -128,6 +128,7 @@ const de = { clockTime: 'Datum & Uhrzeit', encodingLanguage: 'Kodierung & Sprache', identity: 'Identität', + maintenance: 'Maintenance', }, railGroupPerLabel: 'Pro Etikett', railGroupSetupScript: 'Setup-Skript', @@ -244,6 +245,33 @@ const de = { configurationUpdateOptionN: 'N — Werks-Netzwerkeinstellungen laden (destruktiv)', configurationUpdateOptionF: 'F — Werkseinstellungen (destruktiv)', }, + maintenance: { + maintenanceAlertHeading: 'Maintenance alert', + alertType: 'Alert type', + alertTypeH: 'Head cleaning', + alertTypeR: 'Head replacement', + alertPrint: 'Print warning label', + alertPrintY: 'Print', + alertPrintN: 'Skip', + alertThreshold: 'Threshold', + alertFrequency: 'Repeat frequency', + alertUnits: 'Units', + alertUnitsM: 'Meters', + alertUnitsI: 'Inches', + alertUnitsC: 'Centimeters', + maintenanceMessageHeading: 'Custom message', + messageText: 'Message text', + messageTextHint: 'Characters printed on the alert label.', + headColdWarning: 'Head cold warning', + headColdWarningOn: 'On', + headColdWarningOff: 'Off', + earlyWarningMaintenance: 'Early warning maintenance', + earlyWarningE: 'Enabled', + earlyWarningD: 'Disabled', + headCleaningIntervalMeters: 'Head cleaning interval (m)', + headCleaningIntervalHint: 'Distance after which the printer flags the head for cleaning.', + earlyWarningGateHint: 'Master gate for the maintenance alert system; required for ^MA to fire.', + }, }, app: { diff --git a/src/locales/el.ts b/src/locales/el.ts index 2b099162..d13522d9 100644 --- a/src/locales/el.ts +++ b/src/locales/el.ts @@ -128,6 +128,7 @@ const el = { clockTime: 'Ημερομηνία και ώρα', encodingLanguage: 'Κωδικοποίηση και γλώσσα', identity: 'Ταυτότητα', + maintenance: 'Maintenance', }, railGroupPerLabel: 'Ανά ετικέτα', railGroupSetupScript: 'Σενάριο εγκατάστασης', @@ -244,6 +245,33 @@ const el = { configurationUpdateOptionN: 'N — Φόρτωση εργοστασιακών ρυθμίσεων δικτύου (καταστροφικό)', configurationUpdateOptionF: 'F — Επαναφορά εργοστασίου (καταστροφικό)', }, + maintenance: { + maintenanceAlertHeading: 'Maintenance alert', + alertType: 'Alert type', + alertTypeH: 'Head cleaning', + alertTypeR: 'Head replacement', + alertPrint: 'Print warning label', + alertPrintY: 'Print', + alertPrintN: 'Skip', + alertThreshold: 'Threshold', + alertFrequency: 'Repeat frequency', + alertUnits: 'Units', + alertUnitsM: 'Meters', + alertUnitsI: 'Inches', + alertUnitsC: 'Centimeters', + maintenanceMessageHeading: 'Custom message', + messageText: 'Message text', + messageTextHint: 'Characters printed on the alert label.', + headColdWarning: 'Head cold warning', + headColdWarningOn: 'On', + headColdWarningOff: 'Off', + earlyWarningMaintenance: 'Early warning maintenance', + earlyWarningE: 'Enabled', + earlyWarningD: 'Disabled', + headCleaningIntervalMeters: 'Head cleaning interval (m)', + headCleaningIntervalHint: 'Distance after which the printer flags the head for cleaning.', + earlyWarningGateHint: 'Master gate for the maintenance alert system; required for ^MA to fire.', + }, }, app: { diff --git a/src/locales/en.ts b/src/locales/en.ts index 9970efc6..a48216a9 100644 --- a/src/locales/en.ts +++ b/src/locales/en.ts @@ -128,6 +128,7 @@ const en = { clockTime: 'Clock & Time', encodingLanguage: 'Encoding & Language', identity: 'Identity', + maintenance: 'Maintenance', }, railGroupPerLabel: 'Per Label', railGroupSetupScript: 'Setup Script', @@ -244,6 +245,33 @@ const en = { configurationUpdateOptionN: 'N — Reload factory network settings (destructive)', configurationUpdateOptionF: 'F — Factory reset (destructive)', }, + maintenance: { + maintenanceAlertHeading: 'Maintenance alert', + alertType: 'Alert type', + alertTypeH: 'Head cleaning', + alertTypeR: 'Head replacement', + alertPrint: 'Print warning label', + alertPrintY: 'Print', + alertPrintN: 'Skip', + alertThreshold: 'Threshold', + alertFrequency: 'Repeat frequency', + alertUnits: 'Units', + alertUnitsM: 'Meters', + alertUnitsI: 'Inches', + alertUnitsC: 'Centimeters', + maintenanceMessageHeading: 'Custom message', + messageText: 'Message text', + messageTextHint: 'Characters printed on the alert label.', + headColdWarning: 'Head cold warning', + headColdWarningOn: 'On', + headColdWarningOff: 'Off', + earlyWarningMaintenance: 'Early warning maintenance', + earlyWarningE: 'Enabled', + earlyWarningD: 'Disabled', + headCleaningIntervalMeters: 'Head cleaning interval (m)', + headCleaningIntervalHint: 'Distance after which the printer flags the head for cleaning.', + earlyWarningGateHint: 'Master gate for the maintenance alert system; required for ^MA to fire.', + }, }, app: { diff --git a/src/locales/es.ts b/src/locales/es.ts index 905b9765..660ffe41 100644 --- a/src/locales/es.ts +++ b/src/locales/es.ts @@ -128,6 +128,7 @@ const es = { clockTime: 'Fecha y hora', encodingLanguage: 'Codificación e idioma', identity: 'Identidad', + maintenance: 'Maintenance', }, railGroupPerLabel: 'Por etiqueta', railGroupSetupScript: 'Script de configuración', @@ -244,6 +245,33 @@ const es = { configurationUpdateOptionN: 'N — Restablecer red de fábrica (destructivo)', configurationUpdateOptionF: 'F — Restablecer de fábrica (destructivo)', }, + maintenance: { + maintenanceAlertHeading: 'Maintenance alert', + alertType: 'Alert type', + alertTypeH: 'Head cleaning', + alertTypeR: 'Head replacement', + alertPrint: 'Print warning label', + alertPrintY: 'Print', + alertPrintN: 'Skip', + alertThreshold: 'Threshold', + alertFrequency: 'Repeat frequency', + alertUnits: 'Units', + alertUnitsM: 'Meters', + alertUnitsI: 'Inches', + alertUnitsC: 'Centimeters', + maintenanceMessageHeading: 'Custom message', + messageText: 'Message text', + messageTextHint: 'Characters printed on the alert label.', + headColdWarning: 'Head cold warning', + headColdWarningOn: 'On', + headColdWarningOff: 'Off', + earlyWarningMaintenance: 'Early warning maintenance', + earlyWarningE: 'Enabled', + earlyWarningD: 'Disabled', + headCleaningIntervalMeters: 'Head cleaning interval (m)', + headCleaningIntervalHint: 'Distance after which the printer flags the head for cleaning.', + earlyWarningGateHint: 'Master gate for the maintenance alert system; required for ^MA to fire.', + }, }, app: { diff --git a/src/locales/et.ts b/src/locales/et.ts index 538a79d2..15947b36 100644 --- a/src/locales/et.ts +++ b/src/locales/et.ts @@ -128,6 +128,7 @@ const et = { clockTime: 'Kuupäev ja kellaaeg', encodingLanguage: 'Kodeering ja keel', identity: 'Identiteet', + maintenance: 'Maintenance', }, railGroupPerLabel: 'Sildi kohta', railGroupSetupScript: 'Seadistusskript', @@ -244,6 +245,33 @@ const et = { configurationUpdateOptionN: 'N — Tehase võrgusätete taastamine (hävitav)', configurationUpdateOptionF: 'F — Tehase lähtestamine (hävitav)', }, + maintenance: { + maintenanceAlertHeading: 'Maintenance alert', + alertType: 'Alert type', + alertTypeH: 'Head cleaning', + alertTypeR: 'Head replacement', + alertPrint: 'Print warning label', + alertPrintY: 'Print', + alertPrintN: 'Skip', + alertThreshold: 'Threshold', + alertFrequency: 'Repeat frequency', + alertUnits: 'Units', + alertUnitsM: 'Meters', + alertUnitsI: 'Inches', + alertUnitsC: 'Centimeters', + maintenanceMessageHeading: 'Custom message', + messageText: 'Message text', + messageTextHint: 'Characters printed on the alert label.', + headColdWarning: 'Head cold warning', + headColdWarningOn: 'On', + headColdWarningOff: 'Off', + earlyWarningMaintenance: 'Early warning maintenance', + earlyWarningE: 'Enabled', + earlyWarningD: 'Disabled', + headCleaningIntervalMeters: 'Head cleaning interval (m)', + headCleaningIntervalHint: 'Distance after which the printer flags the head for cleaning.', + earlyWarningGateHint: 'Master gate for the maintenance alert system; required for ^MA to fire.', + }, }, app: { diff --git a/src/locales/fa.ts b/src/locales/fa.ts index 5418dbd9..bc41d2df 100644 --- a/src/locales/fa.ts +++ b/src/locales/fa.ts @@ -128,6 +128,7 @@ const fa = { clockTime: 'تاریخ و زمان', encodingLanguage: 'کدگذاری و زبان', identity: 'هویت', + maintenance: 'Maintenance', }, railGroupPerLabel: 'برای هر برچسب', railGroupSetupScript: 'اسکریپت راه‌اندازی', @@ -244,6 +245,33 @@ const fa = { configurationUpdateOptionN: 'N — بازنشانی تنظیمات شبکه کارخانه (مخرب)', configurationUpdateOptionF: 'F — بازنشانی کارخانه (مخرب)', }, + maintenance: { + maintenanceAlertHeading: 'Maintenance alert', + alertType: 'Alert type', + alertTypeH: 'Head cleaning', + alertTypeR: 'Head replacement', + alertPrint: 'Print warning label', + alertPrintY: 'Print', + alertPrintN: 'Skip', + alertThreshold: 'Threshold', + alertFrequency: 'Repeat frequency', + alertUnits: 'Units', + alertUnitsM: 'Meters', + alertUnitsI: 'Inches', + alertUnitsC: 'Centimeters', + maintenanceMessageHeading: 'Custom message', + messageText: 'Message text', + messageTextHint: 'Characters printed on the alert label.', + headColdWarning: 'Head cold warning', + headColdWarningOn: 'On', + headColdWarningOff: 'Off', + earlyWarningMaintenance: 'Early warning maintenance', + earlyWarningE: 'Enabled', + earlyWarningD: 'Disabled', + headCleaningIntervalMeters: 'Head cleaning interval (m)', + headCleaningIntervalHint: 'Distance after which the printer flags the head for cleaning.', + earlyWarningGateHint: 'Master gate for the maintenance alert system; required for ^MA to fire.', + }, }, app: { diff --git a/src/locales/fi.ts b/src/locales/fi.ts index f7b31a99..2ba454c9 100644 --- a/src/locales/fi.ts +++ b/src/locales/fi.ts @@ -128,6 +128,7 @@ const fi = { clockTime: 'Päivämäärä ja aika', encodingLanguage: 'Koodaus ja kieli', identity: 'Tunniste', + maintenance: 'Maintenance', }, railGroupPerLabel: 'Per etiketti', railGroupSetupScript: 'Asennusskripti', @@ -244,6 +245,33 @@ const fi = { configurationUpdateOptionN: 'N — Palauta tehtaan verkkoasetukset (tuhoava)', configurationUpdateOptionF: 'F — Tehdasasetusten palautus (tuhoava)', }, + maintenance: { + maintenanceAlertHeading: 'Maintenance alert', + alertType: 'Alert type', + alertTypeH: 'Head cleaning', + alertTypeR: 'Head replacement', + alertPrint: 'Print warning label', + alertPrintY: 'Print', + alertPrintN: 'Skip', + alertThreshold: 'Threshold', + alertFrequency: 'Repeat frequency', + alertUnits: 'Units', + alertUnitsM: 'Meters', + alertUnitsI: 'Inches', + alertUnitsC: 'Centimeters', + maintenanceMessageHeading: 'Custom message', + messageText: 'Message text', + messageTextHint: 'Characters printed on the alert label.', + headColdWarning: 'Head cold warning', + headColdWarningOn: 'On', + headColdWarningOff: 'Off', + earlyWarningMaintenance: 'Early warning maintenance', + earlyWarningE: 'Enabled', + earlyWarningD: 'Disabled', + headCleaningIntervalMeters: 'Head cleaning interval (m)', + headCleaningIntervalHint: 'Distance after which the printer flags the head for cleaning.', + earlyWarningGateHint: 'Master gate for the maintenance alert system; required for ^MA to fire.', + }, }, app: { diff --git a/src/locales/fr.ts b/src/locales/fr.ts index cd798b64..1e602710 100644 --- a/src/locales/fr.ts +++ b/src/locales/fr.ts @@ -128,6 +128,7 @@ const fr = { clockTime: 'Date et heure', encodingLanguage: 'Encodage et langue', identity: 'Identité', + maintenance: 'Maintenance', }, railGroupPerLabel: 'Par étiquette', railGroupSetupScript: 'Script d\'installation', @@ -244,6 +245,33 @@ const fr = { configurationUpdateOptionN: 'N — Recharger les réglages réseau d\'usine (destructif)', configurationUpdateOptionF: 'F — Réinitialisation usine (destructif)', }, + maintenance: { + maintenanceAlertHeading: 'Maintenance alert', + alertType: 'Alert type', + alertTypeH: 'Head cleaning', + alertTypeR: 'Head replacement', + alertPrint: 'Print warning label', + alertPrintY: 'Print', + alertPrintN: 'Skip', + alertThreshold: 'Threshold', + alertFrequency: 'Repeat frequency', + alertUnits: 'Units', + alertUnitsM: 'Meters', + alertUnitsI: 'Inches', + alertUnitsC: 'Centimeters', + maintenanceMessageHeading: 'Custom message', + messageText: 'Message text', + messageTextHint: 'Characters printed on the alert label.', + headColdWarning: 'Head cold warning', + headColdWarningOn: 'On', + headColdWarningOff: 'Off', + earlyWarningMaintenance: 'Early warning maintenance', + earlyWarningE: 'Enabled', + earlyWarningD: 'Disabled', + headCleaningIntervalMeters: 'Head cleaning interval (m)', + headCleaningIntervalHint: 'Distance after which the printer flags the head for cleaning.', + earlyWarningGateHint: 'Master gate for the maintenance alert system; required for ^MA to fire.', + }, }, app: { diff --git a/src/locales/he.ts b/src/locales/he.ts index b957fec6..fe001b58 100644 --- a/src/locales/he.ts +++ b/src/locales/he.ts @@ -128,6 +128,7 @@ const he = { clockTime: 'תאריך ושעה', encodingLanguage: 'קידוד ושפה', identity: 'זהות', + maintenance: 'Maintenance', }, railGroupPerLabel: 'לכל מדבקה', railGroupSetupScript: 'סקריפט הגדרה', @@ -244,6 +245,33 @@ const he = { configurationUpdateOptionN: 'N — טען מחדש הגדרות רשת יצרן (הרסני)', configurationUpdateOptionF: 'F — איפוס יצרן (הרסני)', }, + maintenance: { + maintenanceAlertHeading: 'Maintenance alert', + alertType: 'Alert type', + alertTypeH: 'Head cleaning', + alertTypeR: 'Head replacement', + alertPrint: 'Print warning label', + alertPrintY: 'Print', + alertPrintN: 'Skip', + alertThreshold: 'Threshold', + alertFrequency: 'Repeat frequency', + alertUnits: 'Units', + alertUnitsM: 'Meters', + alertUnitsI: 'Inches', + alertUnitsC: 'Centimeters', + maintenanceMessageHeading: 'Custom message', + messageText: 'Message text', + messageTextHint: 'Characters printed on the alert label.', + headColdWarning: 'Head cold warning', + headColdWarningOn: 'On', + headColdWarningOff: 'Off', + earlyWarningMaintenance: 'Early warning maintenance', + earlyWarningE: 'Enabled', + earlyWarningD: 'Disabled', + headCleaningIntervalMeters: 'Head cleaning interval (m)', + headCleaningIntervalHint: 'Distance after which the printer flags the head for cleaning.', + earlyWarningGateHint: 'Master gate for the maintenance alert system; required for ^MA to fire.', + }, }, app: { diff --git a/src/locales/hr.ts b/src/locales/hr.ts index ac1efccb..9e7cae28 100644 --- a/src/locales/hr.ts +++ b/src/locales/hr.ts @@ -128,6 +128,7 @@ const hr = { clockTime: 'Datum i vrijeme', encodingLanguage: 'Kodiranje i jezik', identity: 'Identitet', + maintenance: 'Maintenance', }, railGroupPerLabel: 'Po naljepnici', railGroupSetupScript: 'Skripta postavljanja', @@ -244,6 +245,33 @@ const hr = { configurationUpdateOptionN: 'N — Vrati tvorničke mrežne postavke (destruktivno)', configurationUpdateOptionF: 'F — Tvornički reset (destruktivno)', }, + maintenance: { + maintenanceAlertHeading: 'Maintenance alert', + alertType: 'Alert type', + alertTypeH: 'Head cleaning', + alertTypeR: 'Head replacement', + alertPrint: 'Print warning label', + alertPrintY: 'Print', + alertPrintN: 'Skip', + alertThreshold: 'Threshold', + alertFrequency: 'Repeat frequency', + alertUnits: 'Units', + alertUnitsM: 'Meters', + alertUnitsI: 'Inches', + alertUnitsC: 'Centimeters', + maintenanceMessageHeading: 'Custom message', + messageText: 'Message text', + messageTextHint: 'Characters printed on the alert label.', + headColdWarning: 'Head cold warning', + headColdWarningOn: 'On', + headColdWarningOff: 'Off', + earlyWarningMaintenance: 'Early warning maintenance', + earlyWarningE: 'Enabled', + earlyWarningD: 'Disabled', + headCleaningIntervalMeters: 'Head cleaning interval (m)', + headCleaningIntervalHint: 'Distance after which the printer flags the head for cleaning.', + earlyWarningGateHint: 'Master gate for the maintenance alert system; required for ^MA to fire.', + }, }, app: { diff --git a/src/locales/hu.ts b/src/locales/hu.ts index c59120f0..1adc5a21 100644 --- a/src/locales/hu.ts +++ b/src/locales/hu.ts @@ -128,6 +128,7 @@ const hu = { clockTime: 'Dátum és idő', encodingLanguage: 'Kódolás és nyelv', identity: 'Azonosító', + maintenance: 'Maintenance', }, railGroupPerLabel: 'Címkénként', railGroupSetupScript: 'Beállító szkript', @@ -244,6 +245,33 @@ const hu = { configurationUpdateOptionN: 'N — Gyári hálózati beállítások visszaállítása (destruktív)', configurationUpdateOptionF: 'F — Gyári visszaállítás (destruktív)', }, + maintenance: { + maintenanceAlertHeading: 'Maintenance alert', + alertType: 'Alert type', + alertTypeH: 'Head cleaning', + alertTypeR: 'Head replacement', + alertPrint: 'Print warning label', + alertPrintY: 'Print', + alertPrintN: 'Skip', + alertThreshold: 'Threshold', + alertFrequency: 'Repeat frequency', + alertUnits: 'Units', + alertUnitsM: 'Meters', + alertUnitsI: 'Inches', + alertUnitsC: 'Centimeters', + maintenanceMessageHeading: 'Custom message', + messageText: 'Message text', + messageTextHint: 'Characters printed on the alert label.', + headColdWarning: 'Head cold warning', + headColdWarningOn: 'On', + headColdWarningOff: 'Off', + earlyWarningMaintenance: 'Early warning maintenance', + earlyWarningE: 'Enabled', + earlyWarningD: 'Disabled', + headCleaningIntervalMeters: 'Head cleaning interval (m)', + headCleaningIntervalHint: 'Distance after which the printer flags the head for cleaning.', + earlyWarningGateHint: 'Master gate for the maintenance alert system; required for ^MA to fire.', + }, }, app: { diff --git a/src/locales/it.ts b/src/locales/it.ts index e9600175..3fbfb6a3 100644 --- a/src/locales/it.ts +++ b/src/locales/it.ts @@ -128,6 +128,7 @@ const it = { clockTime: 'Data e ora', encodingLanguage: 'Codifica e lingua', identity: 'Identità', + maintenance: 'Maintenance', }, railGroupPerLabel: 'Per etichetta', railGroupSetupScript: 'Script di configurazione', @@ -244,6 +245,33 @@ const it = { configurationUpdateOptionN: 'N — Ricarica impostazioni di rete di fabbrica (distruttivo)', configurationUpdateOptionF: 'F — Ripristino di fabbrica (distruttivo)', }, + maintenance: { + maintenanceAlertHeading: 'Maintenance alert', + alertType: 'Alert type', + alertTypeH: 'Head cleaning', + alertTypeR: 'Head replacement', + alertPrint: 'Print warning label', + alertPrintY: 'Print', + alertPrintN: 'Skip', + alertThreshold: 'Threshold', + alertFrequency: 'Repeat frequency', + alertUnits: 'Units', + alertUnitsM: 'Meters', + alertUnitsI: 'Inches', + alertUnitsC: 'Centimeters', + maintenanceMessageHeading: 'Custom message', + messageText: 'Message text', + messageTextHint: 'Characters printed on the alert label.', + headColdWarning: 'Head cold warning', + headColdWarningOn: 'On', + headColdWarningOff: 'Off', + earlyWarningMaintenance: 'Early warning maintenance', + earlyWarningE: 'Enabled', + earlyWarningD: 'Disabled', + headCleaningIntervalMeters: 'Head cleaning interval (m)', + headCleaningIntervalHint: 'Distance after which the printer flags the head for cleaning.', + earlyWarningGateHint: 'Master gate for the maintenance alert system; required for ^MA to fire.', + }, }, app: { diff --git a/src/locales/ja.ts b/src/locales/ja.ts index 1c1fee0b..6c5e928f 100644 --- a/src/locales/ja.ts +++ b/src/locales/ja.ts @@ -128,6 +128,7 @@ const ja = { clockTime: '日付と時刻', encodingLanguage: 'エンコードと言語', identity: '識別情報', + maintenance: 'Maintenance', }, railGroupPerLabel: 'ラベルごと', railGroupSetupScript: 'セットアップスクリプト', @@ -244,6 +245,33 @@ const ja = { configurationUpdateOptionN: 'N — 工場ネットワーク設定の再読み込み (破壊的)', configurationUpdateOptionF: 'F — 工場リセット (破壊的)', }, + maintenance: { + maintenanceAlertHeading: 'Maintenance alert', + alertType: 'Alert type', + alertTypeH: 'Head cleaning', + alertTypeR: 'Head replacement', + alertPrint: 'Print warning label', + alertPrintY: 'Print', + alertPrintN: 'Skip', + alertThreshold: 'Threshold', + alertFrequency: 'Repeat frequency', + alertUnits: 'Units', + alertUnitsM: 'Meters', + alertUnitsI: 'Inches', + alertUnitsC: 'Centimeters', + maintenanceMessageHeading: 'Custom message', + messageText: 'Message text', + messageTextHint: 'Characters printed on the alert label.', + headColdWarning: 'Head cold warning', + headColdWarningOn: 'On', + headColdWarningOff: 'Off', + earlyWarningMaintenance: 'Early warning maintenance', + earlyWarningE: 'Enabled', + earlyWarningD: 'Disabled', + headCleaningIntervalMeters: 'Head cleaning interval (m)', + headCleaningIntervalHint: 'Distance after which the printer flags the head for cleaning.', + earlyWarningGateHint: 'Master gate for the maintenance alert system; required for ^MA to fire.', + }, }, app: { diff --git a/src/locales/ko.ts b/src/locales/ko.ts index b11a53a7..326060ca 100644 --- a/src/locales/ko.ts +++ b/src/locales/ko.ts @@ -128,6 +128,7 @@ const ko = { clockTime: '날짜 및 시간', encodingLanguage: '인코딩 및 언어', identity: '식별', + maintenance: 'Maintenance', }, railGroupPerLabel: '라벨별', railGroupSetupScript: '설정 스크립트', @@ -244,6 +245,33 @@ const ko = { configurationUpdateOptionN: 'N — 공장 네트워크 설정 재로드 (파괴적)', configurationUpdateOptionF: 'F — 공장 초기화 (파괴적)', }, + maintenance: { + maintenanceAlertHeading: 'Maintenance alert', + alertType: 'Alert type', + alertTypeH: 'Head cleaning', + alertTypeR: 'Head replacement', + alertPrint: 'Print warning label', + alertPrintY: 'Print', + alertPrintN: 'Skip', + alertThreshold: 'Threshold', + alertFrequency: 'Repeat frequency', + alertUnits: 'Units', + alertUnitsM: 'Meters', + alertUnitsI: 'Inches', + alertUnitsC: 'Centimeters', + maintenanceMessageHeading: 'Custom message', + messageText: 'Message text', + messageTextHint: 'Characters printed on the alert label.', + headColdWarning: 'Head cold warning', + headColdWarningOn: 'On', + headColdWarningOff: 'Off', + earlyWarningMaintenance: 'Early warning maintenance', + earlyWarningE: 'Enabled', + earlyWarningD: 'Disabled', + headCleaningIntervalMeters: 'Head cleaning interval (m)', + headCleaningIntervalHint: 'Distance after which the printer flags the head for cleaning.', + earlyWarningGateHint: 'Master gate for the maintenance alert system; required for ^MA to fire.', + }, }, app: { diff --git a/src/locales/lt.ts b/src/locales/lt.ts index b8736a81..bcd8ad52 100644 --- a/src/locales/lt.ts +++ b/src/locales/lt.ts @@ -128,6 +128,7 @@ const lt = { clockTime: 'Data ir laikas', encodingLanguage: 'Kodavimas ir kalba', identity: 'Tapatybė', + maintenance: 'Maintenance', }, railGroupPerLabel: 'Etiketei', railGroupSetupScript: 'Sąrankos scenarijus', @@ -244,6 +245,33 @@ const lt = { configurationUpdateOptionN: 'N — Atkurti gamyklinius tinklo nustatymus (destruktyvus)', configurationUpdateOptionF: 'F — Gamyklinis atstatymas (destruktyvus)', }, + maintenance: { + maintenanceAlertHeading: 'Maintenance alert', + alertType: 'Alert type', + alertTypeH: 'Head cleaning', + alertTypeR: 'Head replacement', + alertPrint: 'Print warning label', + alertPrintY: 'Print', + alertPrintN: 'Skip', + alertThreshold: 'Threshold', + alertFrequency: 'Repeat frequency', + alertUnits: 'Units', + alertUnitsM: 'Meters', + alertUnitsI: 'Inches', + alertUnitsC: 'Centimeters', + maintenanceMessageHeading: 'Custom message', + messageText: 'Message text', + messageTextHint: 'Characters printed on the alert label.', + headColdWarning: 'Head cold warning', + headColdWarningOn: 'On', + headColdWarningOff: 'Off', + earlyWarningMaintenance: 'Early warning maintenance', + earlyWarningE: 'Enabled', + earlyWarningD: 'Disabled', + headCleaningIntervalMeters: 'Head cleaning interval (m)', + headCleaningIntervalHint: 'Distance after which the printer flags the head for cleaning.', + earlyWarningGateHint: 'Master gate for the maintenance alert system; required for ^MA to fire.', + }, }, app: { diff --git a/src/locales/lv.ts b/src/locales/lv.ts index 38a1d192..40a3caf5 100644 --- a/src/locales/lv.ts +++ b/src/locales/lv.ts @@ -128,6 +128,7 @@ const lv = { clockTime: 'Datums un laiks', encodingLanguage: 'Kodējums un valoda', identity: 'Identitāte', + maintenance: 'Maintenance', }, railGroupPerLabel: 'Uz uzlīmi', railGroupSetupScript: 'Iestatīšanas skripts', @@ -244,6 +245,33 @@ const lv = { configurationUpdateOptionN: 'N — Atjaunot rūpnīcas tīkla iestatījumus (destruktīvi)', configurationUpdateOptionF: 'F — Rūpnīcas atiestatīšana (destruktīvi)', }, + maintenance: { + maintenanceAlertHeading: 'Maintenance alert', + alertType: 'Alert type', + alertTypeH: 'Head cleaning', + alertTypeR: 'Head replacement', + alertPrint: 'Print warning label', + alertPrintY: 'Print', + alertPrintN: 'Skip', + alertThreshold: 'Threshold', + alertFrequency: 'Repeat frequency', + alertUnits: 'Units', + alertUnitsM: 'Meters', + alertUnitsI: 'Inches', + alertUnitsC: 'Centimeters', + maintenanceMessageHeading: 'Custom message', + messageText: 'Message text', + messageTextHint: 'Characters printed on the alert label.', + headColdWarning: 'Head cold warning', + headColdWarningOn: 'On', + headColdWarningOff: 'Off', + earlyWarningMaintenance: 'Early warning maintenance', + earlyWarningE: 'Enabled', + earlyWarningD: 'Disabled', + headCleaningIntervalMeters: 'Head cleaning interval (m)', + headCleaningIntervalHint: 'Distance after which the printer flags the head for cleaning.', + earlyWarningGateHint: 'Master gate for the maintenance alert system; required for ^MA to fire.', + }, }, app: { diff --git a/src/locales/nl.ts b/src/locales/nl.ts index 519239d8..12628b44 100644 --- a/src/locales/nl.ts +++ b/src/locales/nl.ts @@ -128,6 +128,7 @@ const nl = { clockTime: 'Datum en tijd', encodingLanguage: 'Codering en taal', identity: 'Identiteit', + maintenance: 'Maintenance', }, railGroupPerLabel: 'Per etiket', railGroupSetupScript: 'Setup-script', @@ -244,6 +245,33 @@ const nl = { configurationUpdateOptionN: 'N — Fabrieksnetwerkinstellingen opnieuw laden (destructief)', configurationUpdateOptionF: 'F — Fabrieksreset (destructief)', }, + maintenance: { + maintenanceAlertHeading: 'Maintenance alert', + alertType: 'Alert type', + alertTypeH: 'Head cleaning', + alertTypeR: 'Head replacement', + alertPrint: 'Print warning label', + alertPrintY: 'Print', + alertPrintN: 'Skip', + alertThreshold: 'Threshold', + alertFrequency: 'Repeat frequency', + alertUnits: 'Units', + alertUnitsM: 'Meters', + alertUnitsI: 'Inches', + alertUnitsC: 'Centimeters', + maintenanceMessageHeading: 'Custom message', + messageText: 'Message text', + messageTextHint: 'Characters printed on the alert label.', + headColdWarning: 'Head cold warning', + headColdWarningOn: 'On', + headColdWarningOff: 'Off', + earlyWarningMaintenance: 'Early warning maintenance', + earlyWarningE: 'Enabled', + earlyWarningD: 'Disabled', + headCleaningIntervalMeters: 'Head cleaning interval (m)', + headCleaningIntervalHint: 'Distance after which the printer flags the head for cleaning.', + earlyWarningGateHint: 'Master gate for the maintenance alert system; required for ^MA to fire.', + }, }, app: { diff --git a/src/locales/no.ts b/src/locales/no.ts index d0c5ce68..d448bdff 100644 --- a/src/locales/no.ts +++ b/src/locales/no.ts @@ -128,6 +128,7 @@ const no = { clockTime: 'Dato og tid', encodingLanguage: 'Koding og språk', identity: 'Identitet', + maintenance: 'Maintenance', }, railGroupPerLabel: 'Per etikett', railGroupSetupScript: 'Oppsettsskript', @@ -244,6 +245,33 @@ const no = { configurationUpdateOptionN: 'N — Last fabrikknettverksinnstillinger (destruktiv)', configurationUpdateOptionF: 'F — Fabrikknullstilling (destruktiv)', }, + maintenance: { + maintenanceAlertHeading: 'Maintenance alert', + alertType: 'Alert type', + alertTypeH: 'Head cleaning', + alertTypeR: 'Head replacement', + alertPrint: 'Print warning label', + alertPrintY: 'Print', + alertPrintN: 'Skip', + alertThreshold: 'Threshold', + alertFrequency: 'Repeat frequency', + alertUnits: 'Units', + alertUnitsM: 'Meters', + alertUnitsI: 'Inches', + alertUnitsC: 'Centimeters', + maintenanceMessageHeading: 'Custom message', + messageText: 'Message text', + messageTextHint: 'Characters printed on the alert label.', + headColdWarning: 'Head cold warning', + headColdWarningOn: 'On', + headColdWarningOff: 'Off', + earlyWarningMaintenance: 'Early warning maintenance', + earlyWarningE: 'Enabled', + earlyWarningD: 'Disabled', + headCleaningIntervalMeters: 'Head cleaning interval (m)', + headCleaningIntervalHint: 'Distance after which the printer flags the head for cleaning.', + earlyWarningGateHint: 'Master gate for the maintenance alert system; required for ^MA to fire.', + }, }, app: { diff --git a/src/locales/pl.ts b/src/locales/pl.ts index 6dca9fba..c58a1097 100644 --- a/src/locales/pl.ts +++ b/src/locales/pl.ts @@ -128,6 +128,7 @@ const pl = { clockTime: 'Data i godzina', encodingLanguage: 'Kodowanie i język', identity: 'Identyfikacja', + maintenance: 'Maintenance', }, railGroupPerLabel: 'Na etykietę', railGroupSetupScript: 'Skrypt konfiguracji', @@ -244,6 +245,33 @@ const pl = { configurationUpdateOptionN: 'N — Wczytaj fabryczne ustawienia sieci (destrukcyjny)', configurationUpdateOptionF: 'F — Reset fabryczny (destrukcyjny)', }, + maintenance: { + maintenanceAlertHeading: 'Maintenance alert', + alertType: 'Alert type', + alertTypeH: 'Head cleaning', + alertTypeR: 'Head replacement', + alertPrint: 'Print warning label', + alertPrintY: 'Print', + alertPrintN: 'Skip', + alertThreshold: 'Threshold', + alertFrequency: 'Repeat frequency', + alertUnits: 'Units', + alertUnitsM: 'Meters', + alertUnitsI: 'Inches', + alertUnitsC: 'Centimeters', + maintenanceMessageHeading: 'Custom message', + messageText: 'Message text', + messageTextHint: 'Characters printed on the alert label.', + headColdWarning: 'Head cold warning', + headColdWarningOn: 'On', + headColdWarningOff: 'Off', + earlyWarningMaintenance: 'Early warning maintenance', + earlyWarningE: 'Enabled', + earlyWarningD: 'Disabled', + headCleaningIntervalMeters: 'Head cleaning interval (m)', + headCleaningIntervalHint: 'Distance after which the printer flags the head for cleaning.', + earlyWarningGateHint: 'Master gate for the maintenance alert system; required for ^MA to fire.', + }, }, app: { diff --git a/src/locales/pt.ts b/src/locales/pt.ts index 54dca83f..a521fcac 100644 --- a/src/locales/pt.ts +++ b/src/locales/pt.ts @@ -128,6 +128,7 @@ const pt = { clockTime: 'Data e hora', encodingLanguage: 'Codificação e idioma', identity: 'Identidade', + maintenance: 'Maintenance', }, railGroupPerLabel: 'Por etiqueta', railGroupSetupScript: 'Script de configuração', @@ -244,6 +245,33 @@ const pt = { configurationUpdateOptionN: 'N — Recarregar configurações de rede de fábrica (destrutivo)', configurationUpdateOptionF: 'F — Restaurar fábrica (destrutivo)', }, + maintenance: { + maintenanceAlertHeading: 'Maintenance alert', + alertType: 'Alert type', + alertTypeH: 'Head cleaning', + alertTypeR: 'Head replacement', + alertPrint: 'Print warning label', + alertPrintY: 'Print', + alertPrintN: 'Skip', + alertThreshold: 'Threshold', + alertFrequency: 'Repeat frequency', + alertUnits: 'Units', + alertUnitsM: 'Meters', + alertUnitsI: 'Inches', + alertUnitsC: 'Centimeters', + maintenanceMessageHeading: 'Custom message', + messageText: 'Message text', + messageTextHint: 'Characters printed on the alert label.', + headColdWarning: 'Head cold warning', + headColdWarningOn: 'On', + headColdWarningOff: 'Off', + earlyWarningMaintenance: 'Early warning maintenance', + earlyWarningE: 'Enabled', + earlyWarningD: 'Disabled', + headCleaningIntervalMeters: 'Head cleaning interval (m)', + headCleaningIntervalHint: 'Distance after which the printer flags the head for cleaning.', + earlyWarningGateHint: 'Master gate for the maintenance alert system; required for ^MA to fire.', + }, }, app: { diff --git a/src/locales/ro.ts b/src/locales/ro.ts index ccda4d22..72cff53d 100644 --- a/src/locales/ro.ts +++ b/src/locales/ro.ts @@ -128,6 +128,7 @@ const ro = { clockTime: 'Data și ora', encodingLanguage: 'Codare și limbă', identity: 'Identitate', + maintenance: 'Maintenance', }, railGroupPerLabel: 'Pe etichetă', railGroupSetupScript: 'Script de configurare', @@ -244,6 +245,33 @@ const ro = { configurationUpdateOptionN: 'N — Reîncărcare setări de rețea din fabrică (distructiv)', configurationUpdateOptionF: 'F — Resetare din fabrică (distructiv)', }, + maintenance: { + maintenanceAlertHeading: 'Maintenance alert', + alertType: 'Alert type', + alertTypeH: 'Head cleaning', + alertTypeR: 'Head replacement', + alertPrint: 'Print warning label', + alertPrintY: 'Print', + alertPrintN: 'Skip', + alertThreshold: 'Threshold', + alertFrequency: 'Repeat frequency', + alertUnits: 'Units', + alertUnitsM: 'Meters', + alertUnitsI: 'Inches', + alertUnitsC: 'Centimeters', + maintenanceMessageHeading: 'Custom message', + messageText: 'Message text', + messageTextHint: 'Characters printed on the alert label.', + headColdWarning: 'Head cold warning', + headColdWarningOn: 'On', + headColdWarningOff: 'Off', + earlyWarningMaintenance: 'Early warning maintenance', + earlyWarningE: 'Enabled', + earlyWarningD: 'Disabled', + headCleaningIntervalMeters: 'Head cleaning interval (m)', + headCleaningIntervalHint: 'Distance after which the printer flags the head for cleaning.', + earlyWarningGateHint: 'Master gate for the maintenance alert system; required for ^MA to fire.', + }, }, app: { diff --git a/src/locales/sk.ts b/src/locales/sk.ts index 308a843b..39739ce7 100644 --- a/src/locales/sk.ts +++ b/src/locales/sk.ts @@ -128,6 +128,7 @@ const sk = { clockTime: 'Dátum a čas', encodingLanguage: 'Kódovanie a jazyk', identity: 'Identita', + maintenance: 'Maintenance', }, railGroupPerLabel: 'Na štítok', railGroupSetupScript: 'Inicializačný skript', @@ -244,6 +245,33 @@ const sk = { configurationUpdateOptionN: 'N — Načítať továrenské sieťové nastavenia (deštruktívne)', configurationUpdateOptionF: 'F — Továrenský reset (deštruktívny)', }, + maintenance: { + maintenanceAlertHeading: 'Maintenance alert', + alertType: 'Alert type', + alertTypeH: 'Head cleaning', + alertTypeR: 'Head replacement', + alertPrint: 'Print warning label', + alertPrintY: 'Print', + alertPrintN: 'Skip', + alertThreshold: 'Threshold', + alertFrequency: 'Repeat frequency', + alertUnits: 'Units', + alertUnitsM: 'Meters', + alertUnitsI: 'Inches', + alertUnitsC: 'Centimeters', + maintenanceMessageHeading: 'Custom message', + messageText: 'Message text', + messageTextHint: 'Characters printed on the alert label.', + headColdWarning: 'Head cold warning', + headColdWarningOn: 'On', + headColdWarningOff: 'Off', + earlyWarningMaintenance: 'Early warning maintenance', + earlyWarningE: 'Enabled', + earlyWarningD: 'Disabled', + headCleaningIntervalMeters: 'Head cleaning interval (m)', + headCleaningIntervalHint: 'Distance after which the printer flags the head for cleaning.', + earlyWarningGateHint: 'Master gate for the maintenance alert system; required for ^MA to fire.', + }, }, app: { diff --git a/src/locales/sl.ts b/src/locales/sl.ts index 647784fb..94f9395b 100644 --- a/src/locales/sl.ts +++ b/src/locales/sl.ts @@ -128,6 +128,7 @@ const sl = { clockTime: 'Datum in čas', encodingLanguage: 'Kodiranje in jezik', identity: 'Identiteta', + maintenance: 'Maintenance', }, railGroupPerLabel: 'Na nalepko', railGroupSetupScript: 'Skripta za nastavitev', @@ -244,6 +245,33 @@ const sl = { configurationUpdateOptionN: 'N — Ponovno naloži tovarniške omrežne nastavitve (destruktivno)', configurationUpdateOptionF: 'F — Tovarniška ponastavitev (destruktivno)', }, + maintenance: { + maintenanceAlertHeading: 'Maintenance alert', + alertType: 'Alert type', + alertTypeH: 'Head cleaning', + alertTypeR: 'Head replacement', + alertPrint: 'Print warning label', + alertPrintY: 'Print', + alertPrintN: 'Skip', + alertThreshold: 'Threshold', + alertFrequency: 'Repeat frequency', + alertUnits: 'Units', + alertUnitsM: 'Meters', + alertUnitsI: 'Inches', + alertUnitsC: 'Centimeters', + maintenanceMessageHeading: 'Custom message', + messageText: 'Message text', + messageTextHint: 'Characters printed on the alert label.', + headColdWarning: 'Head cold warning', + headColdWarningOn: 'On', + headColdWarningOff: 'Off', + earlyWarningMaintenance: 'Early warning maintenance', + earlyWarningE: 'Enabled', + earlyWarningD: 'Disabled', + headCleaningIntervalMeters: 'Head cleaning interval (m)', + headCleaningIntervalHint: 'Distance after which the printer flags the head for cleaning.', + earlyWarningGateHint: 'Master gate for the maintenance alert system; required for ^MA to fire.', + }, }, app: { diff --git a/src/locales/sr.ts b/src/locales/sr.ts index 0c58493d..d856ed74 100644 --- a/src/locales/sr.ts +++ b/src/locales/sr.ts @@ -128,6 +128,7 @@ const sr = { clockTime: 'Датум и време', encodingLanguage: 'Кодирање и језик', identity: 'Идентитет', + maintenance: 'Maintenance', }, railGroupPerLabel: 'По налепници', railGroupSetupScript: 'Скрипта подешавања', @@ -244,6 +245,33 @@ const sr = { configurationUpdateOptionN: 'N — Učitaj fabričke mrežne postavke (destruktivno)', configurationUpdateOptionF: 'F — Fabričko resetovanje (destruktivno)', }, + maintenance: { + maintenanceAlertHeading: 'Maintenance alert', + alertType: 'Alert type', + alertTypeH: 'Head cleaning', + alertTypeR: 'Head replacement', + alertPrint: 'Print warning label', + alertPrintY: 'Print', + alertPrintN: 'Skip', + alertThreshold: 'Threshold', + alertFrequency: 'Repeat frequency', + alertUnits: 'Units', + alertUnitsM: 'Meters', + alertUnitsI: 'Inches', + alertUnitsC: 'Centimeters', + maintenanceMessageHeading: 'Custom message', + messageText: 'Message text', + messageTextHint: 'Characters printed on the alert label.', + headColdWarning: 'Head cold warning', + headColdWarningOn: 'On', + headColdWarningOff: 'Off', + earlyWarningMaintenance: 'Early warning maintenance', + earlyWarningE: 'Enabled', + earlyWarningD: 'Disabled', + headCleaningIntervalMeters: 'Head cleaning interval (m)', + headCleaningIntervalHint: 'Distance after which the printer flags the head for cleaning.', + earlyWarningGateHint: 'Master gate for the maintenance alert system; required for ^MA to fire.', + }, }, app: { diff --git a/src/locales/sv.ts b/src/locales/sv.ts index 0b38908e..c3dc070a 100644 --- a/src/locales/sv.ts +++ b/src/locales/sv.ts @@ -128,6 +128,7 @@ const sv = { clockTime: 'Datum och tid', encodingLanguage: 'Kodning och språk', identity: 'Identitet', + maintenance: 'Maintenance', }, railGroupPerLabel: 'Per etikett', railGroupSetupScript: 'Konfigurationsskript', @@ -244,6 +245,33 @@ const sv = { configurationUpdateOptionN: 'N — Läs in fabriksnätverksinställningar (destruktiv)', configurationUpdateOptionF: 'F — Återställ till fabrik (destruktiv)', }, + maintenance: { + maintenanceAlertHeading: 'Maintenance alert', + alertType: 'Alert type', + alertTypeH: 'Head cleaning', + alertTypeR: 'Head replacement', + alertPrint: 'Print warning label', + alertPrintY: 'Print', + alertPrintN: 'Skip', + alertThreshold: 'Threshold', + alertFrequency: 'Repeat frequency', + alertUnits: 'Units', + alertUnitsM: 'Meters', + alertUnitsI: 'Inches', + alertUnitsC: 'Centimeters', + maintenanceMessageHeading: 'Custom message', + messageText: 'Message text', + messageTextHint: 'Characters printed on the alert label.', + headColdWarning: 'Head cold warning', + headColdWarningOn: 'On', + headColdWarningOff: 'Off', + earlyWarningMaintenance: 'Early warning maintenance', + earlyWarningE: 'Enabled', + earlyWarningD: 'Disabled', + headCleaningIntervalMeters: 'Head cleaning interval (m)', + headCleaningIntervalHint: 'Distance after which the printer flags the head for cleaning.', + earlyWarningGateHint: 'Master gate for the maintenance alert system; required for ^MA to fire.', + }, }, app: { diff --git a/src/locales/tr.ts b/src/locales/tr.ts index f3a282d4..e93dcef6 100644 --- a/src/locales/tr.ts +++ b/src/locales/tr.ts @@ -128,6 +128,7 @@ const tr = { clockTime: 'Tarih ve saat', encodingLanguage: 'Kodlama ve dil', identity: 'Kimlik', + maintenance: 'Maintenance', }, railGroupPerLabel: 'Etiket başına', railGroupSetupScript: 'Kurulum betiği', @@ -244,6 +245,33 @@ const tr = { configurationUpdateOptionN: 'N — Fabrika ağ ayarlarını yeniden yükle (yıkıcı)', configurationUpdateOptionF: 'F — Fabrika ayarlarına sıfırla (yıkıcı)', }, + maintenance: { + maintenanceAlertHeading: 'Maintenance alert', + alertType: 'Alert type', + alertTypeH: 'Head cleaning', + alertTypeR: 'Head replacement', + alertPrint: 'Print warning label', + alertPrintY: 'Print', + alertPrintN: 'Skip', + alertThreshold: 'Threshold', + alertFrequency: 'Repeat frequency', + alertUnits: 'Units', + alertUnitsM: 'Meters', + alertUnitsI: 'Inches', + alertUnitsC: 'Centimeters', + maintenanceMessageHeading: 'Custom message', + messageText: 'Message text', + messageTextHint: 'Characters printed on the alert label.', + headColdWarning: 'Head cold warning', + headColdWarningOn: 'On', + headColdWarningOff: 'Off', + earlyWarningMaintenance: 'Early warning maintenance', + earlyWarningE: 'Enabled', + earlyWarningD: 'Disabled', + headCleaningIntervalMeters: 'Head cleaning interval (m)', + headCleaningIntervalHint: 'Distance after which the printer flags the head for cleaning.', + earlyWarningGateHint: 'Master gate for the maintenance alert system; required for ^MA to fire.', + }, }, app: { diff --git a/src/locales/zh-hans.ts b/src/locales/zh-hans.ts index d723d0a7..50b57627 100644 --- a/src/locales/zh-hans.ts +++ b/src/locales/zh-hans.ts @@ -128,6 +128,7 @@ const zhHans = { clockTime: '日期与时间', encodingLanguage: '编码与语言', identity: '标识', + maintenance: 'Maintenance', }, railGroupPerLabel: '每个标签', railGroupSetupScript: '设置脚本', @@ -244,6 +245,33 @@ const zhHans = { configurationUpdateOptionN: 'N — 重新载入出厂网络设置 (破坏性)', configurationUpdateOptionF: 'F — 恢复出厂设置 (破坏性)', }, + maintenance: { + maintenanceAlertHeading: 'Maintenance alert', + alertType: 'Alert type', + alertTypeH: 'Head cleaning', + alertTypeR: 'Head replacement', + alertPrint: 'Print warning label', + alertPrintY: 'Print', + alertPrintN: 'Skip', + alertThreshold: 'Threshold', + alertFrequency: 'Repeat frequency', + alertUnits: 'Units', + alertUnitsM: 'Meters', + alertUnitsI: 'Inches', + alertUnitsC: 'Centimeters', + maintenanceMessageHeading: 'Custom message', + messageText: 'Message text', + messageTextHint: 'Characters printed on the alert label.', + headColdWarning: 'Head cold warning', + headColdWarningOn: 'On', + headColdWarningOff: 'Off', + earlyWarningMaintenance: 'Early warning maintenance', + earlyWarningE: 'Enabled', + earlyWarningD: 'Disabled', + headCleaningIntervalMeters: 'Head cleaning interval (m)', + headCleaningIntervalHint: 'Distance after which the printer flags the head for cleaning.', + earlyWarningGateHint: 'Master gate for the maintenance alert system; required for ^MA to fire.', + }, }, app: { diff --git a/src/locales/zh-hant.ts b/src/locales/zh-hant.ts index 62280471..e3aff09b 100644 --- a/src/locales/zh-hant.ts +++ b/src/locales/zh-hant.ts @@ -128,6 +128,7 @@ const zhHant = { clockTime: '日期與時間', encodingLanguage: '編碼與語言', identity: '識別', + maintenance: 'Maintenance', }, railGroupPerLabel: '每個標籤', railGroupSetupScript: '設定指令稿', @@ -244,6 +245,33 @@ const zhHant = { configurationUpdateOptionN: 'N — 重新載入出廠網路設定 (破壞性)', configurationUpdateOptionF: 'F — 還原出廠設定 (破壞性)', }, + maintenance: { + maintenanceAlertHeading: 'Maintenance alert', + alertType: 'Alert type', + alertTypeH: 'Head cleaning', + alertTypeR: 'Head replacement', + alertPrint: 'Print warning label', + alertPrintY: 'Print', + alertPrintN: 'Skip', + alertThreshold: 'Threshold', + alertFrequency: 'Repeat frequency', + alertUnits: 'Units', + alertUnitsM: 'Meters', + alertUnitsI: 'Inches', + alertUnitsC: 'Centimeters', + maintenanceMessageHeading: 'Custom message', + messageText: 'Message text', + messageTextHint: 'Characters printed on the alert label.', + headColdWarning: 'Head cold warning', + headColdWarningOn: 'On', + headColdWarningOff: 'Off', + earlyWarningMaintenance: 'Early warning maintenance', + earlyWarningE: 'Enabled', + earlyWarningD: 'Disabled', + headCleaningIntervalMeters: 'Head cleaning interval (m)', + headCleaningIntervalHint: 'Distance after which the printer flags the head for cleaning.', + earlyWarningGateHint: 'Master gate for the maintenance alert system; required for ^MA to fire.', + }, }, app: { diff --git a/src/store/slices/uiSlice.ts b/src/store/slices/uiSlice.ts index c40f827d..4e1ef97c 100644 --- a/src/store/slices/uiSlice.ts +++ b/src/store/slices/uiSlice.ts @@ -35,7 +35,8 @@ export type PrinterSettingsTab = | 'printQuality' | 'clockTime' | 'encodingLanguage' - | 'identity'; + | 'identity' + | 'maintenance'; export type SidebarTab = 'properties' | 'layers' | 'variables' | 'fonts'; diff --git a/src/types/PrinterProfile.ts b/src/types/PrinterProfile.ts index c9caf2f1..ea63384b 100644 --- a/src/types/PrinterProfile.ts +++ b/src/types/PrinterProfile.ts @@ -72,6 +72,46 @@ export const CONFIG_UPDATE_VALUES = ['S', 'R', 'N', 'F'] as const; export type ConfigUpdateAction = (typeof CONFIG_UPDATE_VALUES)[number]; export const isConfigUpdateAction = makeEnumGuard(CONFIG_UPDATE_VALUES); +/** ^MA/^MI alert type. Shared discriminator so one field couples + * alert and message at the wire level. */ +export const MAINTENANCE_ALERT_TYPES = ['H', 'R'] as const; +export type MaintenanceAlertType = (typeof MAINTENANCE_ALERT_TYPES)[number]; +export const isMaintenanceAlertType = makeEnumGuard(MAINTENANCE_ALERT_TYPES); + +/** ^MA print slot: Y=emit warning label, N=skip. */ +export const MAINTENANCE_ALERT_PRINT_VALUES = ['Y', 'N'] as const; +export type MaintenanceAlertPrint = (typeof MAINTENANCE_ALERT_PRINT_VALUES)[number]; +export const isMaintenanceAlertPrint = makeEnumGuard(MAINTENANCE_ALERT_PRINT_VALUES); + +/** ^MA units slot: M=meters, I=inches, C=centimeters. */ +export const MAINTENANCE_ALERT_UNITS = ['M', 'I', 'C'] as const; +export type MaintenanceAlertUnit = (typeof MAINTENANCE_ALERT_UNITS)[number]; +export const isMaintenanceAlertUnit = makeEnumGuard(MAINTENANCE_ALERT_UNITS); + +/** ^MA threshold/frequency cap. Spec is implementation-defined; + * 99999 covers typical ~600m head life. */ +export const MAINTENANCE_DISTANCE_MAX = 99999; +/** ^MI message length cap from Zebra Quick Reference. */ +export const MAINTENANCE_MESSAGE_MAX_LEN = 63; +/** ^JH g-slot (head cleaning interval) range in meters. Min is 1 + * so the unset/disabled state is `undefined` rather than ambiguous + * `0M`. Max 900 keeps PAX4 compatible. + * TODO HW-verify: Zebra docs are ambiguous whether the wire format + * is raw `M` or an indexed table (0..16 mapping to 100M..900M). + * Current implementation uses `M`; verify on real hardware. */ +export const HEAD_CLEANING_INTERVAL_RANGE = { min: 1, max: 900 } as const; + +/** Default seed for a freshly enabled ^MA. Matches the spec example + * `^MAH,Y,5,1,M`. Spread inline so UI placeholder display and + * parser blank-slot fallback stay in sync. */ +export const MAINTENANCE_ALERT_DEFAULTS = { + type: 'H', + print: 'Y', + threshold: 5, + frequency: 1, + units: 'M', +} as const; + /** Printer-installation profile: EEPROM-persistent printer-state * fields separated from `labelConfig` so design files don't leak * per-install values when shared. */ @@ -94,6 +134,29 @@ export const printerProfileSchema = z.object({ printerName: z.string().min(1).max(PRINTER_NAME_MAX_LEN).regex(setupScriptSafeStringRegex).optional(), printerDescription: z.string().min(1).regex(setupScriptSafeStringRegex).optional(), setPassword: z.string().regex(PRINTER_PASSWORD_REGEX).optional(), + /** ^MA alert. Requires earlyWarningMaintenance==='E' to actually + * fire; the two are stored independently. */ + maintenanceAlert: z.object({ + type: z.enum(MAINTENANCE_ALERT_TYPES), + print: z.enum(['Y', 'N']), + threshold: z.number().int().min(0).max(MAINTENANCE_DISTANCE_MAX), + frequency: z.number().int().min(1).max(MAINTENANCE_DISTANCE_MAX), + units: z.enum(MAINTENANCE_ALERT_UNITS), + }).optional(), + /** ^MI custom message printed when the matching `^MA{type}` fires. */ + maintenanceMessage: z.object({ + type: z.enum(MAINTENANCE_ALERT_TYPES), + text: z.string().min(1).max(MAINTENANCE_MESSAGE_MAX_LEN).regex(setupScriptSafeStringRegex), + }).optional(), + /** ^MW head-cold warning LCD/alert toggle. */ + headColdWarning: z.enum(['Y', 'N']).optional(), + /** ^JH f-slot: early-warning-maintenance master gate that turns + * the ^MA alert system on. Without `E` the maintenanceAlert + * config sits dormant on the printer. */ + earlyWarningMaintenance: z.enum(['E', 'D']).optional(), + /** ^JH g-slot head-cleaning interval, stored as a meter count. + * Generator emits `M`; spec accepts an `M` suffix only. */ + headCleaningIntervalMeters: intInRange(HEAD_CLEANING_INTERVAL_RANGE).optional(), /** ^JU action. Generator emits the value as `^JU{action}` last in * the setup-script block so an `S` commit happens after every * other persistent write in the same script. */ @@ -113,6 +176,17 @@ export const printerProfileSchema = z.object({ message: 'clockMode "TOL" requires clockTolerance to be set', }); } + if ( + p.maintenanceAlert && + p.maintenanceMessage && + p.maintenanceAlert.type !== p.maintenanceMessage.type + ) { + ctx.addIssue({ + code: 'custom', + path: ['maintenanceMessage', 'type'], + message: 'maintenanceMessage.type must match maintenanceAlert.type', + }); + } }); export type PrinterProfile = z.infer; From 28bc0eebec67f1df9c1475f936218a696abe79cb Mon Sep 17 00:00:00 2001 From: u8array Date: Thu, 4 Jun 2026 13:48:15 +0200 Subject: [PATCH 2/3] refactor: harden printer-settings UI inputs against silent-rollback Gemini PR review plus four review-iterations surfaced a class of bugs in the printer-settings tabs: typing or pasting a char the schema rejects (^/~/,/control) causes patchPrinterProfile to roll back, and the controlled input visibly freezes. The fix-attempt in the original feat commit only handled the maintenance message field and used a regex without the global flag, so only the first unsafe char was stripped. The same bug class already shipped in IdentityTab (printerName, printerDescription) and EncodingAndLanguageTab (encodingTable). This refactor: - Introduces `SafeStringInput` in `zplFieldPrimitives` that sanitises user input before patching. Default sanitiser is the new `stripUnsafeChars` helper, backed by a module-level g-flagged regex (`setupScriptUnsafeCharGlobalRegex`) so the regex isn't reallocated per keystroke. The primitive routes IME composition through real `compositionstart`/`compositionend` events instead of an `instanceof InputEvent` probe (cross-browser flake) and resets the composition flag on `blur` so a focus-loss mid-composition doesn't leave it stuck. Sanitises on `compositionend` as a safety net for browsers that skip the trailing `input` event (older Safari, some Android IMEs). Trade-off: the caret jumps to end after a mid-string strip (acceptable vs. the caret-math gymnastics the first cut invited). - Hoists the cross-field cascade out of the slice into a pure `normalizeMaintenanceTypes(merged, patch)` helper in `types/PrinterProfile`. Direction follows which side the patch object touches, so a future `^MA`-only-via-import or a message-side edit each get their cascade for free. - Migrates `printerName`, `printerDescription`, `encodingTable`, and the maintenance message field to `SafeStringInput`. Migrates the alert `print`/`units` selects and the message-text row to `ZplSubField` so the label-to-input `htmlFor` link is consistent with the rest of the modal. - Drops the `?? 0` / `?? 1` collapse in the BoundedIntControl onChange handlers. `readBoundedInt` lets sub-min positives through for asymmetric typing; the schema requires `>= min`, so the frequency cell gates the commit. Threshold (`min=0`) needs no gate. 1528 tests green. --- .../EncodingAndLanguageTab.tsx | 11 +- .../PrinterSettings/IdentityTab.tsx | 17 +- .../PrinterSettings/MaintenanceTab.tsx | 240 ++++++++---------- .../PrinterSettings/zplFieldPrimitives.tsx | 78 +++++- src/store/slices/printerProfileSlice.ts | 7 +- src/types/PrinterProfile.ts | 45 +++- 6 files changed, 240 insertions(+), 158 deletions(-) diff --git a/src/components/PrinterSettings/EncodingAndLanguageTab.tsx b/src/components/PrinterSettings/EncodingAndLanguageTab.tsx index 490ba6a3..948d65a7 100644 --- a/src/components/PrinterSettings/EncodingAndLanguageTab.tsx +++ b/src/components/PrinterSettings/EncodingAndLanguageTab.tsx @@ -1,9 +1,9 @@ import { useId } from "react"; import { useT } from "../../lib/useT"; import { useLabelStore } from "../../store/labelStore"; -import { inputCls } from "../ui/formStyles"; import { PRINTER_LOCALE_VALUES, ZPL_MODE_VALUES, isPrinterLocale, isZplMode, type PrinterLocale, type ZplMode } from "../../types/PrinterProfile"; import { + SafeStringInput, ZplCommandLabel, ZplEnumSelect, ZplField, @@ -67,15 +67,12 @@ export function EncodingAndLanguageTab() { conventions vary. */} - - patchPrinterProfile({ encodingTable: e.target.value || undefined }) - } + onChange={(v) => patchPrinterProfile({ encodingTable: v || undefined })} /> {loc.encodingTableHint} diff --git a/src/components/PrinterSettings/IdentityTab.tsx b/src/components/PrinterSettings/IdentityTab.tsx index 8845737d..0ffc8eea 100644 --- a/src/components/PrinterSettings/IdentityTab.tsx +++ b/src/components/PrinterSettings/IdentityTab.tsx @@ -10,6 +10,7 @@ import { isConfigUpdateAction, } from "../../types/PrinterProfile"; import { + SafeStringInput, ZplCommandLabel, ZplEnumSelect, ZplField, @@ -64,15 +65,11 @@ export function IdentityTab() { MediaFeedTab). */} - - patchPrinterProfile({ printerName: e.target.value || undefined }) - } + onChange={(v) => patchPrinterProfile({ printerName: v || undefined })} /> {loc.printerNameHint} @@ -80,14 +77,10 @@ export function IdentityTab() { {(id) => ( - - patchPrinterProfile({ printerDescription: e.target.value || undefined }) - } + onChange={(v) => patchPrinterProfile({ printerDescription: v || undefined })} /> )} diff --git a/src/components/PrinterSettings/MaintenanceTab.tsx b/src/components/PrinterSettings/MaintenanceTab.tsx index a63805cf..1bffd1a9 100644 --- a/src/components/PrinterSettings/MaintenanceTab.tsx +++ b/src/components/PrinterSettings/MaintenanceTab.tsx @@ -17,10 +17,13 @@ import { type MaintenanceAlertUnit, } from "../../types/PrinterProfile"; import { + BoundedIntControl, + SafeStringInput, ZplBoundedIntInput, ZplCommandLabel, ZplEnumSelect, ZplField, + ZplSubField, } from "./zplFieldPrimitives"; type LocMaintenance = ReturnType["printerSettings"]["maintenance"]; @@ -46,18 +49,12 @@ export function MaintenanceTab() { const alert = profile.maintenanceAlert; const message = profile.maintenanceMessage; - // Centralised mutation: any change to maintenanceAlert.type also - // cascades into maintenanceMessage.type so the cross-field invariant - // (superRefine in PrinterProfile.ts) never trips and a silent patch - // rollback never strands the UI. + // Cascade between maintenanceAlert.type and maintenanceMessage.type + // lives in patchPrinterProfile so UI call sites don't carry the + // invariant; this helper only owns the per-field merge. const updateAlert = (key: K, value: MaintenanceAlert[K]) => { if (!alert) return; - const nextAlert = { ...alert, [key]: value }; - const next: Partial = { maintenanceAlert: nextAlert }; - if (key === "type" && message) { - next.maintenanceMessage = { ...message, type: nextAlert.type }; - } - patchPrinterProfile(next); + patchPrinterProfile({ maintenanceAlert: { ...alert, [key]: value } }); }; return ( @@ -78,18 +75,14 @@ export function MaintenanceTab() { return; } if (!isMaintenanceAlertType(raw)) return; - if (alert) { - updateAlert("type", raw); - return; - } - // First-time alert creation: cascade into an - // existing typed-ahead message so the cross-field - // invariant holds atomically. - const next: Partial = { - maintenanceAlert: { ...MAINTENANCE_ALERT_DEFAULTS, type: raw }, - }; - if (message) next.maintenanceMessage = { ...message, type: raw }; - patchPrinterProfile(next); + // First-time creation seeds defaults; subsequent edits + // re-spread the alert. The cross-field type cascade + // is enforced by patchPrinterProfile. + patchPrinterProfile({ + maintenanceAlert: alert + ? { ...alert, type: raw } + : { ...MAINTENANCE_ALERT_DEFAULTS, type: raw }, + }); }} > @@ -98,60 +91,78 @@ export function MaintenanceTab() { ))} -
- - -
+ + {(id) => ( + + )} +
- { - if (threshold !== undefined) updateAlert("threshold", threshold); - }} - disabled={!alert} - /> - { - if (frequency !== undefined) updateAlert("frequency", frequency); - }} - disabled={!alert} - /> -
- - -
+ + {(id) => ( + { + // Drop undefined (mid-typing empty); blur clamps stale state. + if (threshold !== undefined) updateAlert("threshold", threshold); + }} + /> + )} + + + {(id) => ( + { + // readBoundedInt lets sub-min positives through so a + // user typing "1" toward "12" survives a min=2 field; + // schema requires >= min, so gate the commit here. + if (frequency !== undefined && frequency >= 1) { + updateAlert("frequency", frequency); + } + }} + /> + )} + + + {(id) => ( + + )} +
@@ -159,29 +170,28 @@ export function MaintenanceTab() {
-
- - { - const text = e.target.value; - if (!text) { - patchPrinterProfile({ maintenanceMessage: undefined }); - return; - } - // Type sticks to alert.type when an alert exists; otherwise - // falls back to the default so superRefine stays satisfied. - const type = message?.type ?? alert?.type ?? MAINTENANCE_ALERT_DEFAULTS.type; - patchPrinterProfile({ maintenanceMessage: { type, text } }); - }} - /> - - {loc.messageTextHint} ({MAINTENANCE_MESSAGE_MAX_LEN}) - -
+ + {(id) => ( + { + if (!text) { + patchPrinterProfile({ maintenanceMessage: undefined }); + return; + } + // Type sticks to alert.type when an alert exists; otherwise + // falls back to the default so superRefine stays satisfied. + const type = message?.type ?? alert?.type ?? MAINTENANCE_ALERT_DEFAULTS.type; + patchPrinterProfile({ maintenanceMessage: { type, text } }); + }} + /> + )} + + + {loc.messageTextHint} ({MAINTENANCE_MESSAGE_MAX_LEN}) +
@@ -228,39 +238,3 @@ export function MaintenanceTab() { ); } -function BoundedIntCell({ - label, - min, - max, - value, - onChange, - disabled, -}: { - label: string; - min: number; - max: number; - value: number | undefined; - onChange: (next: number | undefined) => void; - disabled?: boolean; -}) { - return ( -
- - { - const raw = e.target.value; - if (raw === "") return; - const n = Number.parseInt(raw, 10); - if (Number.isNaN(n)) return; - onChange(Math.max(min, Math.min(max, n))); - }} - /> -
- ); -} diff --git a/src/components/PrinterSettings/zplFieldPrimitives.tsx b/src/components/PrinterSettings/zplFieldPrimitives.tsx index 0c376d26..667d1a50 100644 --- a/src/components/PrinterSettings/zplFieldPrimitives.tsx +++ b/src/components/PrinterSettings/zplFieldPrimitives.tsx @@ -1,6 +1,7 @@ -import { useId, type ReactNode } from "react"; +import { useId, useRef, type InputHTMLAttributes, type ReactNode } from "react"; import { labelCls, inputCls } from "../ui/formStyles"; import { clampBoundedInt, readBoundedInt } from "../../lib/inputParse"; +import { stripUnsafeChars } from "../../types/PrinterProfile"; /** Shared muted-monospace class for ZPL command tags so the visual * weight stays identical across the label-row, checkbox-row and @@ -92,27 +93,95 @@ export function ZplSubField({ ); } +/** Text input that runs a sanitiser before the value reaches the + * store, so a schema-rejected char never causes a silent rollback + * (frozen-field UX). Default sanitiser strips Setup-Script-unsafe + * chars; pass `sanitize` for other regimes. + * + * IME composition (CJK/Korean) is gated via real `compositionstart`/ + * `compositionend` events so the sanitiser doesn't fire mid-session + * and desync the composition buffer. The caret jumps to end after a + * mid-string strip (accepted trade-off for simplicity over the + * caret-preservation gymnastics that subtle bugs invited). */ +export function SafeStringInput({ + id, + value, + onChange, + sanitize = stripUnsafeChars, + className, + ...rest +}: { + id?: string; + value: string; + onChange: (next: string) => void; + sanitize?: (raw: string) => string; +} & Omit, "id" | "value" | "onChange" | "type">) { + const composing = useRef(false); + return ( + { + composing.current = true; + rest.onCompositionStart?.(e); + }} + onCompositionEnd={(e) => { + composing.current = false; + // Sanitise here as a safety net for browsers that don't dispatch + // the trailing `input` event after `compositionend` (older Safari, + // some Android IMEs). The follow-up `input` (when it fires) runs + // sanitise on the same value, which is idempotent. + onChange(sanitize(e.currentTarget.value)); + rest.onCompositionEnd?.(e); + }} + onBlur={(e) => { + // Blur can fire mid-composition (focus stolen / user clicks + // away) without a preceding `compositionend`. Reset the flag + // so the next focus session starts clean. + composing.current = false; + rest.onBlur?.(e); + }} + onChange={(e) => { + if (composing.current) { + // Raw mid-composition value goes through; schema is permissive + // enough for CJK chars (the unsafe class is ^/~/,/control + // only). A user explicitly typing an unsafe char via IME is + // the rare frozen-field scenario worth a follow-up. + onChange(e.target.value); + return; + } + onChange(sanitize(e.target.value)); + }} + /> + ); +} + /** Bare bounded-int `` plus the asymmetric edit/commit clamp * pair: `readBoundedInt` caps only the upper bound during typing so * the user can transit through non-negative values below `min` * (e.g. type "1" on the way to "12" when min=2). `onBlur` pulls the * committed value back into the full `[min, max]` range. Shared by * `ZplBoundedIntInput` (full ZPL-tag row) and grid-cell wrappers - * that share one parent tag (^PR triple, ^MD pair). Keeping the - * pair colocated avoids the two callers drifting on future fixes - * (e.g. the gemini-review onBlur addition). */ + * that share one parent tag (^PR triple, ^MD pair). */ export function BoundedIntControl({ id, min, max, value, onChange, + disabled, }: { id?: string; min: number; max: number; value: number | undefined; onChange: (next: number | undefined) => void; + disabled?: boolean; }) { return ( onChange(readBoundedInt(e.target.value, min, max))} onBlur={(e) => onChange(clampBoundedInt(e.target.value, min, max))} /> diff --git a/src/store/slices/printerProfileSlice.ts b/src/store/slices/printerProfileSlice.ts index 48cc22b9..0e29a4e7 100644 --- a/src/store/slices/printerProfileSlice.ts +++ b/src/store/slices/printerProfileSlice.ts @@ -1,6 +1,7 @@ import type { StateCreator } from 'zustand'; import { EMPTY_PRINTER_PROFILE, + normalizeMaintenanceTypes, printerProfileSchema, type PrinterProfile, } from '../../types/PrinterProfile'; @@ -40,10 +41,14 @@ export const createPrinterProfileSlice: StateCreator< // undefined". Validate the merged result through the schema so the // cross-field rule (clockMode === 'TOL' ↔ clockTolerance defined) // can't be violated from any caller. - const next = pruneUndefined({ + const merged = pruneUndefined({ ...state.printerProfile, ...patch, }); + // Repair cross-field invariants at the store boundary so any + // caller (UI partial patch, import, undo replay) gets them for + // free. Direction follows patch intent; see normalizeMaintenanceTypes. + const next = normalizeMaintenanceTypes(merged, patch); const parsed = printerProfileSchema.safeParse(next); if (!parsed.success) { const msg = '[printerProfile] rejected invalid patch'; diff --git a/src/types/PrinterProfile.ts b/src/types/PrinterProfile.ts index ea63384b..8b86b3bc 100644 --- a/src/types/PrinterProfile.ts +++ b/src/types/PrinterProfile.ts @@ -15,8 +15,18 @@ export const PRINTER_NAME_MAX_LEN = 16; const SETUP_SCRIPT_UNSAFE_CHARS = '\\^~,\\r\\n\\x00-\\x1f'; /** Anchored, positive form for `z.string().regex(...)` schema checks. */ export const setupScriptSafeStringRegex = new RegExp(`^[^${SETUP_SCRIPT_UNSAFE_CHARS}]+$`); -/** Unanchored, negative form for parser-side dropping. */ +/** Unanchored, negative form for parser-side dropping. No `g` flag so + * `.test()` callers don't carry `lastIndex` state across calls. */ export const setupScriptUnsafeCharRegex = new RegExp(`[${SETUP_SCRIPT_UNSAFE_CHARS}]`); +/** Global form for `.replace()`-based stripping. Hoisted so UI input + * handlers don't allocate a fresh regex per keystroke. */ +const setupScriptUnsafeCharGlobalRegex = new RegExp(`[${SETUP_SCRIPT_UNSAFE_CHARS}]`, 'g'); +/** Strip every occurrence of an unsafe char from `raw`. UI inputs feed + * user keystrokes through this so a single comma in a paste doesn't + * cause the schema regex to reject the whole patch and the store to + * roll back silently (a frozen-field UX). */ +export const stripUnsafeChars = (raw: string): string => + raw.replace(setupScriptUnsafeCharGlobalRegex, ''); /** ^KL printer locale — two-letter ISO 639-1 shorthands per Zebra spec. */ export const PRINTER_LOCALE_VALUES = [ @@ -191,6 +201,39 @@ export const printerProfileSchema = z.object({ export type PrinterProfile = z.infer; +/** Repair the cross-field ^MA/^MI type invariant after a partial patch. + * Direction follows which side the patch object touches: a patch with + * a `maintenanceAlert.type` cascades into message, and vice versa. + * When both sides are touched, no cascade runs and the schema's + * superRefine catches a mismatch. + * + * Caveat: UI callers spread the whole alert (`{ ...alert, [key]: v }`) + * so `patch.maintenanceAlert.type` is "touched" even for non-type + * edits. The cascade then runs but is a no-op as long as the + * pre-patch invariant held (which the slice gates). If the store is + * ever seeded with a mismatched pair, the next single-side sub-field + * edit silently rewrites the unrelated side toward the patched one + * instead of surfacing the seed bug. + * + * Pure function; safe to call before `safeParse` at any boundary. */ +export function normalizeMaintenanceTypes( + merged: PrinterProfile, + patch: Partial, +): PrinterProfile { + const alert = merged.maintenanceAlert; + const message = merged.maintenanceMessage; + if (!alert || !message || alert.type === message.type) return merged; + const alertTouched = patch.maintenanceAlert?.type !== undefined; + const messageTouched = patch.maintenanceMessage?.type !== undefined; + if (alertTouched && !messageTouched) { + return { ...merged, maintenanceMessage: { ...message, type: alert.type } }; + } + if (messageTouched && !alertTouched) { + return { ...merged, maintenanceAlert: { ...alert, type: message.type } }; + } + return merged; +} + /** Derived from the schema so a new optional field is automatically * picked up by the v4→v5 migration and import loader. */ export const PRINTER_PROFILE_FIELDS = Object.keys( From 33af452859121e2c215c5462847ebc9c0a274863 Mon Sep 17 00:00:00 2001 From: u8array Date: Thu, 4 Jun 2026 16:32:22 +0200 Subject: [PATCH 3/3] fix(zpl): correct ^MA type codes + ^JH g-slot wire format per spec MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ^MA type codes were H/R, spec is R (replacement) / C (cleaning). ^MA units default was M, spec default is I (inches). ^MA threshold/frequency caps are type-dependent per spec: R=150000m, C=2000m, enforced via superRefine. ^JH g-slot wire format is an indexed table (0..16 → 100M..900M), not raw M with suffix as previously emitted. UI now exposes the discrete set via a select; the persisted meters value is constrained to the indexed set. A v6→v7 migration remaps maintenanceAlert/Message type 'H' → 'C' ('R' was already correct). The rehydrate fixpoint loop drops any residual schema violations top-key-by-top-key, bailing to {} if no present key can resolve the issue. The MaintenanceTab type-select clamps stale threshold/frequency to the new type's cap before patching, so a switch R→C with an out-of-spec value can't trip the schema and silently snap the dropdown back to R. JH_SLOT_F/G hoisted to PrinterProfile.ts so generator + parser share one definition; JH_SLOT_COUNT replaces the hard-coded 10. --- .../PrinterSettings/ClockAndTimeTab.tsx | 4 + .../PrinterSettings/IdentityTab.tsx | 12 +- .../PrinterSettings/MaintenanceTab.tsx | 262 ++++++++++-------- .../PrinterSettings/zplFieldPrimitives.tsx | 123 +++++--- src/lib/zplParser/handlers/setupScript.ts | 41 ++- src/lib/zplSetupScript.test.ts | 108 +++++--- src/lib/zplSetupScript.ts | 21 +- src/locales/ar.ts | 50 ++-- src/locales/bg.ts | 50 ++-- src/locales/cs.ts | 50 ++-- src/locales/da.ts | 50 ++-- src/locales/de.ts | 50 ++-- src/locales/el.ts | 50 ++-- src/locales/en.ts | 2 +- src/locales/es.ts | 50 ++-- src/locales/et.ts | 50 ++-- src/locales/fa.ts | 50 ++-- src/locales/fi.ts | 50 ++-- src/locales/fr.ts | 50 ++-- src/locales/he.ts | 50 ++-- src/locales/hr.ts | 50 ++-- src/locales/hu.ts | 50 ++-- src/locales/it.ts | 50 ++-- src/locales/ja.ts | 50 ++-- src/locales/ko.ts | 50 ++-- src/locales/lt.ts | 50 ++-- src/locales/lv.ts | 50 ++-- src/locales/nl.ts | 44 +-- src/locales/no.ts | 50 ++-- src/locales/pl.ts | 50 ++-- src/locales/pt.ts | 50 ++-- src/locales/ro.ts | 50 ++-- src/locales/sk.ts | 50 ++-- src/locales/sl.ts | 50 ++-- src/locales/sr.ts | 50 ++-- src/locales/sv.ts | 50 ++-- src/locales/tr.ts | 50 ++-- src/locales/zh-hans.ts | 50 ++-- src/locales/zh-hant.ts | 50 ++-- src/store/labelStore.test.ts | 42 ++- src/store/labelStore.ts | 55 +++- src/types/PrinterProfile.ts | 100 ++++--- 42 files changed, 1285 insertions(+), 1029 deletions(-) diff --git a/src/components/PrinterSettings/ClockAndTimeTab.tsx b/src/components/PrinterSettings/ClockAndTimeTab.tsx index 99e35b7c..762a76a4 100644 --- a/src/components/PrinterSettings/ClockAndTimeTab.tsx +++ b/src/components/PrinterSettings/ClockAndTimeTab.tsx @@ -148,6 +148,10 @@ export function ClockAndTimeTab() { min={CLOCK_TOLERANCE_RANGE.min} max={CLOCK_TOLERANCE_RANGE.max} value={profile.clockTolerance} + // Required while clockMode='TOL': empty blur snaps the + // draft back to the last committed value with no patch + // sent, so the input stays in sync with the store. + required onChange={(v) => patchPrinterProfile({ clockTolerance: v })} /> )} diff --git a/src/components/PrinterSettings/IdentityTab.tsx b/src/components/PrinterSettings/IdentityTab.tsx index 0ffc8eea..ac9f093d 100644 --- a/src/components/PrinterSettings/IdentityTab.tsx +++ b/src/components/PrinterSettings/IdentityTab.tsx @@ -41,13 +41,11 @@ export function IdentityTab() { const loc = t.printerSettings.identity; const nameId = useId(); const passwordId = useId(); - // Local mirror for the 4-digit input: progressive typing ("4", "47") - // is shown immediately but only commits to the store once the regex - // matches. Without this, partial values would always be filtered out - // and the user could never finish entering a password. Re-sync on - // store changes (import, reset) uses the adjust-state-during-render - // pattern so the React compiler doesn't flag a useEffect → setState - // cascade. + // Progressive-commit draft: store only accepts a complete 4-digit + // value, but the user has to type intermediate states. SafeStringInput + // is fully controlled and would clobber the partial draft, so the + // ^KP input stays hand-rolled. Adjust-state-during-render reseeds on + // external changes (import, reset). const storedPassword = profile.setPassword ?? ""; const [passwordDraft, setPasswordDraft] = useState(storedPassword); const [lastStoredPassword, setLastStoredPassword] = useState(storedPassword); diff --git a/src/components/PrinterSettings/MaintenanceTab.tsx b/src/components/PrinterSettings/MaintenanceTab.tsx index 1bffd1a9..3d04d6f1 100644 --- a/src/components/PrinterSettings/MaintenanceTab.tsx +++ b/src/components/PrinterSettings/MaintenanceTab.tsx @@ -1,25 +1,27 @@ +import { useId } from "react"; import { useT } from "../../lib/useT"; import { useLabelStore } from "../../store/labelStore"; import { inputCls, labelCls } from "../ui/formStyles"; import type { PrinterProfile } from "../../types/PrinterProfile"; import { - HEAD_CLEANING_INTERVAL_RANGE, + HEAD_CLEANING_INTERVAL_METERS, MAINTENANCE_ALERT_DEFAULTS, MAINTENANCE_ALERT_PRINT_VALUES, MAINTENANCE_ALERT_TYPES, MAINTENANCE_ALERT_UNITS, - MAINTENANCE_DISTANCE_MAX, + MAINTENANCE_DISTANCE_MAX_BY_TYPE, MAINTENANCE_MESSAGE_MAX_LEN, + isHeadCleaningIntervalMeters, isMaintenanceAlertPrint, isMaintenanceAlertType, isMaintenanceAlertUnit, + type HeadCleaningIntervalMeters, type MaintenanceAlertType, type MaintenanceAlertUnit, } from "../../types/PrinterProfile"; import { BoundedIntControl, SafeStringInput, - ZplBoundedIntInput, ZplCommandLabel, ZplEnumSelect, ZplField, @@ -30,14 +32,14 @@ type LocMaintenance = ReturnType["printerSettings"]["maintenance"]; type MaintenanceAlert = NonNullable; const ALERT_TYPE_LABEL_KEYS = { - H: 'alertTypeH', R: 'alertTypeR', + C: 'alertTypeC', } as const satisfies Record; const ALERT_UNIT_LABEL_KEYS = { - M: 'alertUnitsM', - I: 'alertUnitsI', C: 'alertUnitsC', + I: 'alertUnitsI', + M: 'alertUnitsM', } as const satisfies Record; /** Maintenance tab (^MA/^MI/^MW/^JH). */ @@ -49,9 +51,7 @@ export function MaintenanceTab() { const alert = profile.maintenanceAlert; const message = profile.maintenanceMessage; - // Cascade between maintenanceAlert.type and maintenanceMessage.type - // lives in patchPrinterProfile so UI call sites don't carry the - // invariant; this helper only owns the per-field merge. + // Type-cascade lives in patchPrinterProfile; this helper only owns the merge. const updateAlert = (key: K, value: MaintenanceAlert[K]) => { if (!alert) return; patchPrinterProfile({ maintenanceAlert: { ...alert, [key]: value } }); @@ -61,109 +61,116 @@ export function MaintenanceTab() {
-
-
-
- + + {(id) => ( + + )} + +
+ + {(id) => ( -
- - {(id) => ( - - )} - -
-
- - {(id) => ( - { - // Drop undefined (mid-typing empty); blur clamps stale state. - if (threshold !== undefined) updateAlert("threshold", threshold); - }} - /> - )} - - - {(id) => ( - { - // readBoundedInt lets sub-min positives through so a - // user typing "1" toward "12" survives a min=2 field; - // schema requires >= min, so gate the commit here. - if (frequency !== undefined && frequency >= 1) { - updateAlert("frequency", frequency); - } - }} - /> - )} - - - {(id) => ( - - )} - -
+ )} + + + {(id) => ( + { + if (threshold !== undefined) updateAlert("threshold", threshold); + }} + /> + )} + + + {(id) => ( + { + if (frequency !== undefined) updateAlert("frequency", frequency); + }} + /> + )} + + + {(id) => ( + + )} +
@@ -209,16 +216,14 @@ export function MaintenanceTab() { {loc.earlyWarningGateHint} - patchPrinterProfile({ headCleaningIntervalMeters }) } - unit={loc.alertUnitsM} /> {loc.headCleaningIntervalHint} @@ -238,3 +243,42 @@ export function MaintenanceTab() { ); } +function HeadCleaningIntervalSelect({ + label, + defaultLabel, + unit, + value, + onChange, +}: { + label: string; + defaultLabel: string; + unit: string; + value: HeadCleaningIntervalMeters | undefined; + onChange: (next: HeadCleaningIntervalMeters | undefined) => void; +}) { + const id = useId(); + return ( + + +
+ + {unit} +
+
+ ); +} diff --git a/src/components/PrinterSettings/zplFieldPrimitives.tsx b/src/components/PrinterSettings/zplFieldPrimitives.tsx index 667d1a50..73bc3978 100644 --- a/src/components/PrinterSettings/zplFieldPrimitives.tsx +++ b/src/components/PrinterSettings/zplFieldPrimitives.tsx @@ -1,4 +1,4 @@ -import { useId, useRef, type InputHTMLAttributes, type ReactNode } from "react"; +import { useId, useState, type InputHTMLAttributes, type ReactNode } from "react"; import { labelCls, inputCls } from "../ui/formStyles"; import { clampBoundedInt, readBoundedInt } from "../../lib/inputParse"; import { stripUnsafeChars } from "../../types/PrinterProfile"; @@ -93,16 +93,12 @@ export function ZplSubField({ ); } -/** Text input that runs a sanitiser before the value reaches the - * store, so a schema-rejected char never causes a silent rollback - * (frozen-field UX). Default sanitiser strips Setup-Script-unsafe - * chars; pass `sanitize` for other regimes. - * - * IME composition (CJK/Korean) is gated via real `compositionstart`/ - * `compositionend` events so the sanitiser doesn't fire mid-session - * and desync the composition buffer. The caret jumps to end after a - * mid-string strip (accepted trade-off for simplicity over the - * caret-preservation gymnastics that subtle bugs invited). */ +/** Text input with a local draft so a sanitiser-rejected commit can't + * cause a controlled-input revert (frozen-field UX). Default sanitiser + * strips Setup-Script-unsafe chars. IME-safe: during composition the + * raw value lives in the draft only, sanitise and commit run on + * compositionend. External value changes reseed the draft via + * adjust-state-during-render. */ export function SafeStringInput({ id, value, @@ -115,59 +111,60 @@ export function SafeStringInput({ value: string; onChange: (next: string) => void; sanitize?: (raw: string) => string; -} & Omit, "id" | "value" | "onChange" | "type">) { - const composing = useRef(false); +} & Omit< + InputHTMLAttributes, + "id" | "value" | "onChange" | "onInput" | "defaultValue" | "type" + >) { + const [draft, setDraft] = useState(value); + const [lastExternal, setLastExternal] = useState(value); + const [composing, setComposing] = useState(false); + if (lastExternal !== value) { + setLastExternal(value); + setDraft(value); + } return ( { - composing.current = true; + setComposing(true); rest.onCompositionStart?.(e); }} onCompositionEnd={(e) => { - composing.current = false; - // Sanitise here as a safety net for browsers that don't dispatch - // the trailing `input` event after `compositionend` (older Safari, - // some Android IMEs). The follow-up `input` (when it fires) runs - // sanitise on the same value, which is idempotent. - onChange(sanitize(e.currentTarget.value)); + setComposing(false); + const next = sanitize(e.currentTarget.value); + setDraft(next); + onChange(next); rest.onCompositionEnd?.(e); }} onBlur={(e) => { - // Blur can fire mid-composition (focus stolen / user clicks - // away) without a preceding `compositionend`. Reset the flag - // so the next focus session starts clean. - composing.current = false; + setComposing(false); rest.onBlur?.(e); }} onChange={(e) => { - if (composing.current) { - // Raw mid-composition value goes through; schema is permissive - // enough for CJK chars (the unsafe class is ^/~/,/control - // only). A user explicitly typing an unsafe char via IME is - // the rare frozen-field scenario worth a follow-up. - onChange(e.target.value); + if (composing) { + setDraft(e.target.value); return; } - onChange(sanitize(e.target.value)); + const next = sanitize(e.target.value); + setDraft(next); + onChange(next); }} /> ); } -/** Bare bounded-int `` plus the asymmetric edit/commit clamp - * pair: `readBoundedInt` caps only the upper bound during typing so - * the user can transit through non-negative values below `min` - * (e.g. type "1" on the way to "12" when min=2). `onBlur` pulls the - * committed value back into the full `[min, max]` range. Shared by - * `ZplBoundedIntInput` (full ZPL-tag row) and grid-cell wrappers - * that share one parent tag (^PR triple, ^MD pair). */ +/** Bounded-int `` with a local-draft buffer for intermediate + * states (empty field, sub-min digits) the schema would reject. + * Commits on keystroke when `[min, max]`; on blur, clamps into range. + * External value changes reseed the draft via adjust-state-during-render. + * + * `required`: empty blur on an optional field emits `undefined` (clear); + * on a required field, snaps the draft back to the last committed value + * with no patch, keeping the input in sync with the store. */ export function BoundedIntControl({ id, min, @@ -175,6 +172,7 @@ export function BoundedIntControl({ value, onChange, disabled, + required, }: { id?: string; min: number; @@ -182,7 +180,17 @@ export function BoundedIntControl({ value: number | undefined; onChange: (next: number | undefined) => void; disabled?: boolean; + required?: boolean; }) { + const externalText = value === undefined ? "" : String(value); + const [draft, setDraft] = useState(externalText); + const [lastExternal, setLastExternal] = useState(externalText); + // adjust-state-during-render: external store value (undo, reset, + // sibling edit) reseeds the draft. + if (lastExternal !== externalText) { + setLastExternal(externalText); + setDraft(externalText); + } return ( onChange(readBoundedInt(e.target.value, min, max))} - onBlur={(e) => onChange(clampBoundedInt(e.target.value, min, max))} + // Tells screen-reader users the field can't be cleared. Pairs + // with the snap-back behaviour on empty blur. + aria-required={required || undefined} + onChange={(e) => { + const raw = e.target.value; + setDraft(raw); + const parsed = readBoundedInt(raw, min, max); + // Only commit when the parsed value passes the schema range + // (raw==="" → undefined; sub-min digits stay in the draft). + if (parsed !== undefined && parsed >= min) onChange(parsed); + }} + onBlur={(e) => { + const raw = e.target.value; + if (raw === "") { + if (required) { + // Snap back to the last committed value so the input + // doesn't desync from the store after a rejected clear. + setDraft(lastExternal); + return; + } + onChange(undefined); + return; + } + const clamped = clampBoundedInt(raw, min, max); + if (clamped !== undefined) { + setDraft(String(clamped)); + onChange(clamped); + } + }} /> ); } diff --git a/src/lib/zplParser/handlers/setupScript.ts b/src/lib/zplParser/handlers/setupScript.ts index 1a44a24f..c41637b4 100644 --- a/src/lib/zplParser/handlers/setupScript.ts +++ b/src/lib/zplParser/handlers/setupScript.ts @@ -1,4 +1,4 @@ -import { CLOCK_TOLERANCE_RANGE, HEAD_CLEANING_INTERVAL_RANGE, HEAD_TEST_INTERVAL_RANGE, MAINTENANCE_ALERT_DEFAULTS, MAINTENANCE_DISTANCE_MAX, MAINTENANCE_MESSAGE_MAX_LEN, PRINTER_NAME_MAX_LEN, PRINTER_PASSWORD_REGEX, TEAR_OFF_ADJUST_RANGE, isClockFormat, isClockLanguage, isConfigUpdateAction, isMaintenanceAlertPrint, isMaintenanceAlertType, isMaintenanceAlertUnit, isPrinterLocale, isZplMode, setupScriptUnsafeCharRegex } from "../../../types/PrinterProfile"; +import { CLOCK_TOLERANCE_RANGE, HEAD_CLEANING_INTERVAL_METERS, HEAD_TEST_INTERVAL_RANGE, JH_SLOT_F, JH_SLOT_G, MAINTENANCE_ALERT_DEFAULTS, MAINTENANCE_DISTANCE_MAX_BY_TYPE, MAINTENANCE_MESSAGE_MAX_LEN, PRINTER_NAME_MAX_LEN, PRINTER_PASSWORD_REGEX, TEAR_OFF_ADJUST_RANGE, isClockFormat, isClockLanguage, isConfigUpdateAction, isMaintenanceAlertPrint, isMaintenanceAlertType, isMaintenanceAlertUnit, isPrinterLocale, isZplMode, setupScriptUnsafeCharRegex } from "../../../types/PrinterProfile"; import { parseIntOrUndef } from "../../inputParse"; import { parseRealtimeClock } from "../../realtimeClock"; import type { ParserState } from "../context"; @@ -84,23 +84,42 @@ export function createSetupScriptHandlers(s: ParserState): Record=1; 0 ("alert every 0 labels") is degenerate. - const frequency = inRange(parseIntOrUndef(p[3]), { min: 1, max: MAINTENANCE_DISTANCE_MAX }); + const cap = MAINTENANCE_DISTANCE_MAX_BY_TYPE[type]; + const thresholdRaw = (p[2] ?? "").trim(); + const threshold = thresholdRaw === "" + ? MAINTENANCE_ALERT_DEFAULTS.threshold + : inRange(parseIntOrUndef(thresholdRaw), { min: 0, max: cap }); + const frequencyRaw = (p[3] ?? "").trim(); + const frequency = frequencyRaw === "" + ? MAINTENANCE_ALERT_DEFAULTS.frequency + : inRange(parseIntOrUndef(frequencyRaw), { min: 0, max: cap }); if (threshold === undefined || frequency === undefined) return; const unitsRaw = strParam(p[4]); const units = unitsRaw === "" ? MAINTENANCE_ALERT_DEFAULTS.units : unitsRaw; if (!isMaintenanceAlertUnit(units)) return; printerProfile.maintenanceAlert = { type, print, threshold, frequency, units }; + // Alert wins per the ^MA/^MI pair policy; drop a stale ^MI. + if ( + printerProfile.maintenanceMessage && + printerProfile.maintenanceMessage.type !== type + ) { + delete printerProfile.maintenanceMessage; + } }, MI(p) { const type = strParam(p[0]); if (!isMaintenanceAlertType(type)) return; + // Alert wins: refuse a ^MI that contradicts an existing ^MA. + if ( + printerProfile.maintenanceAlert && + printerProfile.maintenanceAlert.type !== type + ) return; // Rejoin so a message with embedded commas trips the // unsafe-char guard below instead of getting silently // truncated at the first comma. @@ -114,15 +133,13 @@ export function createSetupScriptHandlers(s: ParserState): Record= 0 && idx < HEAD_CLEANING_INTERVAL_METERS.length) { + printerProfile.headCleaningIntervalMeters = HEAD_CLEANING_INTERVAL_METERS[idx]; } }, }; diff --git a/src/lib/zplSetupScript.test.ts b/src/lib/zplSetupScript.test.ts index b7865f63..80c2ec91 100644 --- a/src/lib/zplSetupScript.test.ts +++ b/src/lib/zplSetupScript.test.ts @@ -5,7 +5,12 @@ import { __SETUP_SCRIPT_EMITTERS_FOR_TESTS, } from "./zplSetupScript"; import { parseZPL } from "./zplParser"; -import { printerProfileSchema, type PrinterProfile } from "../types/PrinterProfile"; +import { + MAINTENANCE_ALERT_DEFAULTS, + MAINTENANCE_DISTANCE_MAX_BY_TYPE, + printerProfileSchema, + type PrinterProfile, +} from "../types/PrinterProfile"; const base: PrinterProfile = {}; @@ -456,17 +461,17 @@ describe("generateSetupScript — maintenance commands", () => { it("emits ^MA with the 5 positional params", () => { const script = generateSetupScript({ ...base, - maintenanceAlert: { type: 'H', print: 'Y', threshold: 5, frequency: 1, units: 'M' }, + maintenanceAlert: { type: 'C', print: 'Y', threshold: 5, frequency: 1, units: 'M' }, }); - expect(script).toBe("^XA\n^MAH,Y,5,1,M\n^XZ"); + expect(script).toBe("^XA\n^MAC,Y,5,1,M\n^XZ"); }); it("emits ^MI as type + text", () => { const script = generateSetupScript({ ...base, - maintenanceMessage: { type: 'H', text: 'Clean head soon' }, + maintenanceMessage: { type: 'C', text: 'Clean head soon' }, }); - expect(script).toBe("^XA\n^MIH,Clean head soon\n^XZ"); + expect(script).toBe("^XA\n^MIC,Clean head soon\n^XZ"); }); it("emits ^MW for the head-cold-warning flag", () => { @@ -484,28 +489,28 @@ describe("generateSetupScript — maintenance commands", () => { earlyWarningMaintenance: 'E', headCleaningIntervalMeters: 150, }); - expect(script).toBe("^XA\n^JH,,,,,E,150M,,,\n^XZ"); + expect(script).toBe("^XA\n^JH,,,,,E,1,,,\n^XZ"); }); it("emits ^JH with only g-slot when only the interval is set", () => { const script = generateSetupScript({ ...base, headCleaningIntervalMeters: 200 }); - expect(script).toBe("^XA\n^JH,,,,,,200M,,,\n^XZ"); + expect(script).toBe("^XA\n^JH,,,,,,2,,,\n^XZ"); }); it("parses ^MA / ^MI / ^MW / ^JH back into the profile", () => { const zpl = [ "^XA", - "^MAH,Y,5,1,M", - "^MIH,Clean me", + "^MAC,Y,5,1,M", + "^MIC,Clean me", "^MWN", - "^JH,,,,,E,150M,,,", + "^JH,,,,,E,1,,,", "^XZ", ].join("\n"); const { printerProfile } = parseZPL(zpl); expect(printerProfile.maintenanceAlert).toEqual({ - type: 'H', print: 'Y', threshold: 5, frequency: 1, units: 'M', + type: 'C', print: 'Y', threshold: 5, frequency: 1, units: 'M', }); - expect(printerProfile.maintenanceMessage).toEqual({ type: 'H', text: 'Clean me' }); + expect(printerProfile.maintenanceMessage).toEqual({ type: 'C', text: 'Clean me' }); expect(printerProfile.headColdWarning).toBe('N'); expect(printerProfile.earlyWarningMaintenance).toBe('E'); expect(printerProfile.headCleaningIntervalMeters).toBe(150); @@ -528,19 +533,26 @@ describe("generateSetupScript — maintenance commands", () => { const script = generateSetupScript({ ...base, earlyWarningMaintenance: 'E', - maintenanceAlert: { type: 'H', print: 'Y', threshold: 5, frequency: 1, units: 'M' }, + maintenanceAlert: { type: 'C', print: 'Y', threshold: 5, frequency: 1, units: 'I' }, }); expect(script.indexOf("^JH")).toBeLessThan(script.indexOf("^MA")); }); it("defaults ^MA print to Y when the slot is blank", () => { - const { printerProfile } = parseZPL("^XA^MAH,,5,1,M^XZ"); + const { printerProfile } = parseZPL("^XA^MAC,,5,1,M^XZ"); expect(printerProfile.maintenanceAlert?.print).toBe("Y"); }); - it("defaults ^MA units to M when the slot is blank", () => { - const { printerProfile } = parseZPL("^XA^MAH,Y,5,1^XZ"); - expect(printerProfile.maintenanceAlert?.units).toBe("M"); + it("defaults ^MA units to I when the slot is blank", () => { + const { printerProfile } = parseZPL("^XA^MAC,Y,5,1^XZ"); + expect(printerProfile.maintenanceAlert?.units).toBe("I"); + }); + + it("defaults ^MA threshold and frequency to spec values when slots are blank", () => { + const { printerProfile } = parseZPL("^XA^MAR,Y,,,M^XZ"); + expect(printerProfile.maintenanceAlert).toEqual({ + type: "R", print: "Y", threshold: 5, frequency: 1, units: "M", + }); }); it("drops ^MA with invalid type", () => { @@ -548,47 +560,79 @@ describe("generateSetupScript — maintenance commands", () => { expect(printerProfile.maintenanceAlert).toBeUndefined(); }); - it("drops ^MA with out-of-range threshold", () => { - const { printerProfile } = parseZPL("^XA^MAH,Y,999999,1,M^XZ"); + it("drops ^MA type C with threshold over the 2000 m cap", () => { + const { printerProfile } = parseZPL("^XA^MAC,Y,2001,1,M^XZ"); expect(printerProfile.maintenanceAlert).toBeUndefined(); }); - it("drops ^MA with frequency=0 (out of spec example range)", () => { - // Documents the parser's min=1 stance: older firmware dumps with - // `frequency=0` (intended as a "disabled" idiom) are discarded - // rather than imported as a degenerate "alert every 0 labels". - const { printerProfile } = parseZPL("^XA^MAH,Y,5,0,M^XZ"); - expect(printerProfile.maintenanceAlert).toBeUndefined(); + it("accepts ^MA type R with threshold up to 150000 m", () => { + const { printerProfile } = parseZPL("^XA^MAR,Y,150000,1,M^XZ"); + expect(printerProfile.maintenanceAlert?.threshold).toBe(150000); + }); + + it("accepts ^MA with frequency=0 (disabled-repeat idiom)", () => { + const { printerProfile } = parseZPL("^XA^MAC,Y,5,0,M^XZ"); + expect(printerProfile.maintenanceAlert?.frequency).toBe(0); }); it("drops ^MI with embedded comma", () => { - const { printerProfile } = parseZPL("^XA^MIH,Hi, there^XZ"); + const { printerProfile } = parseZPL("^XA^MIC,Hi, there^XZ"); expect(printerProfile.maintenanceMessage).toBeUndefined(); }); it("drops ^MI exceeding the message length cap", () => { const longText = "A".repeat(64); - const { printerProfile } = parseZPL(`^XA^MIH,${longText}^XZ`); + const { printerProfile } = parseZPL(`^XA^MIC,${longText}^XZ`); expect(printerProfile.maintenanceMessage).toBeUndefined(); }); - it("accepts ^JH g-slot in lowercase m suffix", () => { - const { printerProfile } = parseZPL("^XA^JH,,,,,E,150m,,,^XZ"); + it("parses ^JH g-slot index 1 as 150 m", () => { + const { printerProfile } = parseZPL("^XA^JH,,,,,E,1,,,^XZ"); expect(printerProfile.headCleaningIntervalMeters).toBe(150); }); - it("drops ^JH g-slot without M suffix", () => { - const { printerProfile } = parseZPL("^XA^JH,,,,,E,150,,,^XZ"); + it("drops ^JH g-slot index out of range", () => { + const { printerProfile } = parseZPL("^XA^JH,,,,,E,17,,,^XZ"); expect(printerProfile.headCleaningIntervalMeters).toBeUndefined(); }); it("rejects mismatched ^MA/^MI type via schema", () => { const result = printerProfileSchema.safeParse({ - maintenanceAlert: { type: 'H', print: 'Y', threshold: 5, frequency: 1, units: 'M' }, + maintenanceAlert: { type: 'C', print: 'Y', threshold: 5, frequency: 1, units: 'M' }, maintenanceMessage: { type: 'R', text: 'Mismatched' }, }); expect(result.success).toBe(false); }); + + it("parser drops a mismatched ^MI so the import patch never crashes the schema", () => { + const { printerProfile } = parseZPL("^XA^MAC,Y,5,1,M^MIR,Replace head^XZ"); + expect(printerProfile.maintenanceAlert?.type).toBe("C"); + expect(printerProfile.maintenanceMessage).toBeUndefined(); + }); + + it("parser drops a stale ^MI when a later ^MA flips the type", () => { + const { printerProfile } = parseZPL("^XA^MIC,Clean me^MAR,Y,5,1,M^XZ"); + expect(printerProfile.maintenanceAlert?.type).toBe("R"); + expect(printerProfile.maintenanceMessage).toBeUndefined(); + }); + + it("reverse-order import: ^MI before ^MA with mismatched types, alert wins", () => { + const { printerProfile } = parseZPL("^XA^MIC,Clean me^MAR,Y,5,1,M^XZ"); + expect(printerProfile.maintenanceAlert?.type).toBe("R"); + expect(printerProfile.maintenanceMessage).toBeUndefined(); + }); + + it("MAINTENANCE_ALERT_DEFAULTS fit the tighter (C) cap", () => { + // Parser blank-slot fallbacks are type-independent; if a future + // bump exceeds the C cap the round-trip of a partial ^MAC dump + // would silently corrupt. Pin the invariant. + const minCap = Math.min( + MAINTENANCE_DISTANCE_MAX_BY_TYPE.R, + MAINTENANCE_DISTANCE_MAX_BY_TYPE.C, + ); + expect(MAINTENANCE_ALERT_DEFAULTS.threshold).toBeLessThanOrEqual(minCap); + expect(MAINTENANCE_ALERT_DEFAULTS.frequency).toBeLessThanOrEqual(minCap); + }); }); describe("SETUP_SCRIPT_EMITTERS structural invariants", () => { diff --git a/src/lib/zplSetupScript.ts b/src/lib/zplSetupScript.ts index d08a9432..2f01dd73 100644 --- a/src/lib/zplSetupScript.ts +++ b/src/lib/zplSetupScript.ts @@ -1,4 +1,10 @@ -import type { PrinterProfile } from '../types/PrinterProfile'; +import { + HEAD_CLEANING_INTERVAL_METERS, + JH_SLOT_COUNT, + JH_SLOT_F, + JH_SLOT_G, + type PrinterProfile, +} from '../types/PrinterProfile'; import { formatRealtimeClockForZpl, toLocalIsoString } from './realtimeClock'; /** Generates the one-shot Setup-Script ZPL for a printer profile — @@ -21,10 +27,6 @@ type SetupScriptEntry = target: keyof PrinterProfile; }; -/** Named indices for the two ^JH slots we model (a..j = 0..9). */ -const JH_SLOT_F = 5; -const JH_SLOT_G = 6; - const SETUP_SCRIPT_EMITTERS = { tearOffAdjust: { kind: 'emit', @@ -127,7 +129,7 @@ const SETUP_SCRIPT_EMITTERS = { // ^JH precedes ^MA so the master gate is on when the alert lands; // otherwise the printer discards the alert until ^JUS + reboot. // earlyWarningMaintenance owns the composite emit; - // headCleaningIntervalMeters folds in (cf. ^KN). + // headCleaningIntervalMeters folds in. earlyWarningMaintenance: { kind: 'emit', channel: 'block', @@ -135,10 +137,13 @@ const SETUP_SCRIPT_EMITTERS = { if (p.earlyWarningMaintenance === undefined && p.headCleaningIntervalMeters === undefined) { return null; } - const slots = ['', '', '', '', '', '', '', '', '', '']; + const slots: string[] = Array(JH_SLOT_COUNT).fill(''); slots[JH_SLOT_F] = p.earlyWarningMaintenance ?? ''; if (p.headCleaningIntervalMeters !== undefined) { - slots[JH_SLOT_G] = `${p.headCleaningIntervalMeters}M`; + const idx = HEAD_CLEANING_INTERVAL_METERS.indexOf( + p.headCleaningIntervalMeters as (typeof HEAD_CLEANING_INTERVAL_METERS)[number], + ); + if (idx >= 0) slots[JH_SLOT_G] = String(idx); } return `^JH${slots.join(',')}`; }, diff --git a/src/locales/ar.ts b/src/locales/ar.ts index 54fa0591..8f94c9a3 100644 --- a/src/locales/ar.ts +++ b/src/locales/ar.ts @@ -246,31 +246,31 @@ const ar = { configurationUpdateOptionF: 'F — إعادة الضبط للمصنع (مدمر)', }, maintenance: { - maintenanceAlertHeading: 'Maintenance alert', - alertType: 'Alert type', - alertTypeH: 'Head cleaning', - alertTypeR: 'Head replacement', - alertPrint: 'Print warning label', - alertPrintY: 'Print', - alertPrintN: 'Skip', - alertThreshold: 'Threshold', - alertFrequency: 'Repeat frequency', - alertUnits: 'Units', - alertUnitsM: 'Meters', - alertUnitsI: 'Inches', - alertUnitsC: 'Centimeters', - maintenanceMessageHeading: 'Custom message', - messageText: 'Message text', - messageTextHint: 'Characters printed on the alert label.', - headColdWarning: 'Head cold warning', - headColdWarningOn: 'On', - headColdWarningOff: 'Off', - earlyWarningMaintenance: 'Early warning maintenance', - earlyWarningE: 'Enabled', - earlyWarningD: 'Disabled', - headCleaningIntervalMeters: 'Head cleaning interval (m)', - headCleaningIntervalHint: 'Distance after which the printer flags the head for cleaning.', - earlyWarningGateHint: 'Master gate for the maintenance alert system; required for ^MA to fire.', + maintenanceAlertHeading: 'تنبيه الصيانة', + alertType: 'نوع التنبيه', + alertTypeC: 'تنظيف الرأس', + alertTypeR: 'استبدال الرأس', + alertPrint: 'طباعة ملصق التحذير', + alertPrintY: 'طباعة', + alertPrintN: 'تخطي', + alertThreshold: 'العتبة', + alertFrequency: 'تكرار التنبيه', + alertUnits: 'الوحدات', + alertUnitsM: 'أمتار', + alertUnitsI: 'بوصات', + alertUnitsC: 'سنتيمترات', + maintenanceMessageHeading: 'رسالة مخصصة', + messageText: 'نص الرسالة', + messageTextHint: 'الأحرف المطبوعة على ملصق التنبيه.', + headColdWarning: 'تحذير برودة الرأس', + headColdWarningOn: 'تشغيل', + headColdWarningOff: 'إيقاف', + earlyWarningMaintenance: 'صيانة الإنذار المبكر', + earlyWarningE: 'ممكّن', + earlyWarningD: 'معطّل', + headCleaningIntervalMeters: 'فاصل تنظيف الرأس (م)', + headCleaningIntervalHint: 'المسافة التي يطلب الطابع بعدها تنظيف الرأس.', + earlyWarningGateHint: 'البوابة الرئيسية لنظام تنبيهات الصيانة؛ مطلوبة لتفعيل ^MA.', }, }, diff --git a/src/locales/bg.ts b/src/locales/bg.ts index 4e9565dd..37eb524c 100644 --- a/src/locales/bg.ts +++ b/src/locales/bg.ts @@ -246,31 +246,31 @@ const bg = { configurationUpdateOptionF: 'F — Фабрично нулиране (разрушително)', }, maintenance: { - maintenanceAlertHeading: 'Maintenance alert', - alertType: 'Alert type', - alertTypeH: 'Head cleaning', - alertTypeR: 'Head replacement', - alertPrint: 'Print warning label', - alertPrintY: 'Print', - alertPrintN: 'Skip', - alertThreshold: 'Threshold', - alertFrequency: 'Repeat frequency', - alertUnits: 'Units', - alertUnitsM: 'Meters', - alertUnitsI: 'Inches', - alertUnitsC: 'Centimeters', - maintenanceMessageHeading: 'Custom message', - messageText: 'Message text', - messageTextHint: 'Characters printed on the alert label.', - headColdWarning: 'Head cold warning', - headColdWarningOn: 'On', - headColdWarningOff: 'Off', - earlyWarningMaintenance: 'Early warning maintenance', - earlyWarningE: 'Enabled', - earlyWarningD: 'Disabled', - headCleaningIntervalMeters: 'Head cleaning interval (m)', - headCleaningIntervalHint: 'Distance after which the printer flags the head for cleaning.', - earlyWarningGateHint: 'Master gate for the maintenance alert system; required for ^MA to fire.', + maintenanceAlertHeading: 'Сигнал за поддръжка', + alertType: 'Тип сигнал', + alertTypeC: 'Почистване на главата', + alertTypeR: 'Подмяна на главата', + alertPrint: 'Печат на предупредителен етикет', + alertPrintY: 'Печат', + alertPrintN: 'Пропусни', + alertThreshold: 'Праг', + alertFrequency: 'Честота на повторение', + alertUnits: 'Мерни единици', + alertUnitsM: 'Метри', + alertUnitsI: 'Инчове', + alertUnitsC: 'Сантиметри', + maintenanceMessageHeading: 'Персонализирано съобщение', + messageText: 'Текст на съобщението', + messageTextHint: 'Знаци, отпечатани върху сигналния етикет.', + headColdWarning: 'Предупреждение за студена глава', + headColdWarningOn: 'Вкл.', + headColdWarningOff: 'Изкл.', + earlyWarningMaintenance: 'Поддръжка с ранно предупреждение', + earlyWarningE: 'Активирано', + earlyWarningD: 'Деактивирано', + headCleaningIntervalMeters: 'Интервал на почистване на главата (м)', + headCleaningIntervalHint: 'Разстоянието, след което принтерът сигнализира, че главата трябва да бъде почистена.', + earlyWarningGateHint: 'Главен превключвател за системата за сигнали за поддръжка; необходим, за да се задейства ^MA.', }, }, diff --git a/src/locales/cs.ts b/src/locales/cs.ts index 5b9bee2a..377d6bec 100644 --- a/src/locales/cs.ts +++ b/src/locales/cs.ts @@ -246,31 +246,31 @@ const cs = { configurationUpdateOptionF: 'F — Tovární reset (destruktivní)', }, maintenance: { - maintenanceAlertHeading: 'Maintenance alert', - alertType: 'Alert type', - alertTypeH: 'Head cleaning', - alertTypeR: 'Head replacement', - alertPrint: 'Print warning label', - alertPrintY: 'Print', - alertPrintN: 'Skip', - alertThreshold: 'Threshold', - alertFrequency: 'Repeat frequency', - alertUnits: 'Units', - alertUnitsM: 'Meters', - alertUnitsI: 'Inches', - alertUnitsC: 'Centimeters', - maintenanceMessageHeading: 'Custom message', - messageText: 'Message text', - messageTextHint: 'Characters printed on the alert label.', - headColdWarning: 'Head cold warning', - headColdWarningOn: 'On', - headColdWarningOff: 'Off', - earlyWarningMaintenance: 'Early warning maintenance', - earlyWarningE: 'Enabled', - earlyWarningD: 'Disabled', - headCleaningIntervalMeters: 'Head cleaning interval (m)', - headCleaningIntervalHint: 'Distance after which the printer flags the head for cleaning.', - earlyWarningGateHint: 'Master gate for the maintenance alert system; required for ^MA to fire.', + maintenanceAlertHeading: 'Upozornění na údržbu', + alertType: 'Typ upozornění', + alertTypeC: 'Čištění hlavy', + alertTypeR: 'Výměna hlavy', + alertPrint: 'Tisk varovného štítku', + alertPrintY: 'Tisknout', + alertPrintN: 'Přeskočit', + alertThreshold: 'Práh', + alertFrequency: 'Frekvence opakování', + alertUnits: 'Jednotky', + alertUnitsM: 'Metry', + alertUnitsI: 'Palce', + alertUnitsC: 'Centimetry', + maintenanceMessageHeading: 'Vlastní zpráva', + messageText: 'Text zprávy', + messageTextHint: 'Znaky vytištěné na štítku upozornění.', + headColdWarning: 'Upozornění na studenou hlavu', + headColdWarningOn: 'Zapnuto', + headColdWarningOff: 'Vypnuto', + earlyWarningMaintenance: 'Údržba s včasným varováním', + earlyWarningE: 'Povoleno', + earlyWarningD: 'Zakázáno', + headCleaningIntervalMeters: 'Interval čištění hlavy (m)', + headCleaningIntervalHint: 'Vzdálenost, po jejímž uplynutí tiskárna označí hlavu jako vyžadující čištění.', + earlyWarningGateHint: 'Hlavní spínač systému upozornění na údržbu; vyžadováno pro spuštění ^MA.', }, }, diff --git a/src/locales/da.ts b/src/locales/da.ts index 67bd3433..3997363b 100644 --- a/src/locales/da.ts +++ b/src/locales/da.ts @@ -246,31 +246,31 @@ const da = { configurationUpdateOptionF: 'F — Fabriksnulstilling (destruktiv)', }, maintenance: { - maintenanceAlertHeading: 'Maintenance alert', - alertType: 'Alert type', - alertTypeH: 'Head cleaning', - alertTypeR: 'Head replacement', - alertPrint: 'Print warning label', - alertPrintY: 'Print', - alertPrintN: 'Skip', - alertThreshold: 'Threshold', - alertFrequency: 'Repeat frequency', - alertUnits: 'Units', - alertUnitsM: 'Meters', - alertUnitsI: 'Inches', - alertUnitsC: 'Centimeters', - maintenanceMessageHeading: 'Custom message', - messageText: 'Message text', - messageTextHint: 'Characters printed on the alert label.', - headColdWarning: 'Head cold warning', - headColdWarningOn: 'On', - headColdWarningOff: 'Off', - earlyWarningMaintenance: 'Early warning maintenance', - earlyWarningE: 'Enabled', - earlyWarningD: 'Disabled', - headCleaningIntervalMeters: 'Head cleaning interval (m)', - headCleaningIntervalHint: 'Distance after which the printer flags the head for cleaning.', - earlyWarningGateHint: 'Master gate for the maintenance alert system; required for ^MA to fire.', + maintenanceAlertHeading: 'Vedligeholdelsesalarm', + alertType: 'Alarmtype', + alertTypeC: 'Hovedrensning', + alertTypeR: 'Hovedudskiftning', + alertPrint: 'Udskriv advarselsetiket', + alertPrintY: 'Udskriv', + alertPrintN: 'Spring over', + alertThreshold: 'Tærskel', + alertFrequency: 'Gentagelsesfrekvens', + alertUnits: 'Enheder', + alertUnitsM: 'Meter', + alertUnitsI: 'Tommer', + alertUnitsC: 'Centimeter', + maintenanceMessageHeading: 'Brugerdefineret besked', + messageText: 'Beskedtekst', + messageTextHint: 'Tegn, der udskrives på advarselsetiketten.', + headColdWarning: 'Advarsel om kold hoved', + headColdWarningOn: 'Til', + headColdWarningOff: 'Fra', + earlyWarningMaintenance: 'Tidlig advarselsvedligeholdelse', + earlyWarningE: 'Aktiveret', + earlyWarningD: 'Deaktiveret', + headCleaningIntervalMeters: 'Hovedrensningsinterval (m)', + headCleaningIntervalHint: 'Den afstand, efter hvilken printeren markerer hovedet til rengøring.', + earlyWarningGateHint: 'Hovedport for vedligeholdelsesalarmsystemet; kræves for at ^MA udløses.', }, }, diff --git a/src/locales/de.ts b/src/locales/de.ts index cdccff71..faa52143 100644 --- a/src/locales/de.ts +++ b/src/locales/de.ts @@ -246,31 +246,31 @@ const de = { configurationUpdateOptionF: 'F — Werkseinstellungen (destruktiv)', }, maintenance: { - maintenanceAlertHeading: 'Maintenance alert', - alertType: 'Alert type', - alertTypeH: 'Head cleaning', - alertTypeR: 'Head replacement', - alertPrint: 'Print warning label', - alertPrintY: 'Print', - alertPrintN: 'Skip', - alertThreshold: 'Threshold', - alertFrequency: 'Repeat frequency', - alertUnits: 'Units', - alertUnitsM: 'Meters', - alertUnitsI: 'Inches', - alertUnitsC: 'Centimeters', - maintenanceMessageHeading: 'Custom message', - messageText: 'Message text', - messageTextHint: 'Characters printed on the alert label.', - headColdWarning: 'Head cold warning', - headColdWarningOn: 'On', - headColdWarningOff: 'Off', - earlyWarningMaintenance: 'Early warning maintenance', - earlyWarningE: 'Enabled', - earlyWarningD: 'Disabled', - headCleaningIntervalMeters: 'Head cleaning interval (m)', - headCleaningIntervalHint: 'Distance after which the printer flags the head for cleaning.', - earlyWarningGateHint: 'Master gate for the maintenance alert system; required for ^MA to fire.', + maintenanceAlertHeading: 'Wartungswarnung', + alertType: 'Alarmtyp', + alertTypeC: 'Druckkopfreinigung', + alertTypeR: 'Druckkopfwechsel', + alertPrint: 'Warnetikett drucken', + alertPrintY: 'Drucken', + alertPrintN: 'Überspringen', + alertThreshold: 'Schwellenwert', + alertFrequency: 'Wiederholungsfrequenz', + alertUnits: 'Einheiten', + alertUnitsM: 'Meter', + alertUnitsI: 'Zoll', + alertUnitsC: 'Zentimeter', + maintenanceMessageHeading: 'Benutzerdefinierte Meldung', + messageText: 'Meldungstext', + messageTextHint: 'Zeichen, die auf das Warnetikett gedruckt werden.', + headColdWarning: 'Warnung kalter Druckkopf', + headColdWarningOn: 'Ein', + headColdWarningOff: 'Aus', + earlyWarningMaintenance: 'Frühwarnung Wartung', + earlyWarningE: 'Aktiviert', + earlyWarningD: 'Deaktiviert', + headCleaningIntervalMeters: 'Druckkopfreinigungsintervall (m)', + headCleaningIntervalHint: 'Strecke, nach der der Drucker den Druckkopf zur Reinigung markiert.', + earlyWarningGateHint: 'Hauptschalter für das Wartungswarnsystem; erforderlich, damit ^MA ausgelöst wird.', }, }, diff --git a/src/locales/el.ts b/src/locales/el.ts index d13522d9..e19ca7e4 100644 --- a/src/locales/el.ts +++ b/src/locales/el.ts @@ -246,31 +246,31 @@ const el = { configurationUpdateOptionF: 'F — Επαναφορά εργοστασίου (καταστροφικό)', }, maintenance: { - maintenanceAlertHeading: 'Maintenance alert', - alertType: 'Alert type', - alertTypeH: 'Head cleaning', - alertTypeR: 'Head replacement', - alertPrint: 'Print warning label', - alertPrintY: 'Print', - alertPrintN: 'Skip', - alertThreshold: 'Threshold', - alertFrequency: 'Repeat frequency', - alertUnits: 'Units', - alertUnitsM: 'Meters', - alertUnitsI: 'Inches', - alertUnitsC: 'Centimeters', - maintenanceMessageHeading: 'Custom message', - messageText: 'Message text', - messageTextHint: 'Characters printed on the alert label.', - headColdWarning: 'Head cold warning', - headColdWarningOn: 'On', - headColdWarningOff: 'Off', - earlyWarningMaintenance: 'Early warning maintenance', - earlyWarningE: 'Enabled', - earlyWarningD: 'Disabled', - headCleaningIntervalMeters: 'Head cleaning interval (m)', - headCleaningIntervalHint: 'Distance after which the printer flags the head for cleaning.', - earlyWarningGateHint: 'Master gate for the maintenance alert system; required for ^MA to fire.', + maintenanceAlertHeading: 'Ειδοποίηση συντήρησης', + alertType: 'Τύπος ειδοποίησης', + alertTypeC: 'Καθαρισμός κεφαλής', + alertTypeR: 'Αντικατάσταση κεφαλής', + alertPrint: 'Εκτύπωση ετικέτας προειδοποίησης', + alertPrintY: 'Εκτύπωση', + alertPrintN: 'Παράλειψη', + alertThreshold: 'Όριο', + alertFrequency: 'Συχνότητα επανάληψης', + alertUnits: 'Μονάδες', + alertUnitsM: 'Μέτρα', + alertUnitsI: 'Ίντσες', + alertUnitsC: 'Εκατοστά', + maintenanceMessageHeading: 'Προσαρμοσμένο μήνυμα', + messageText: 'Κείμενο μηνύματος', + messageTextHint: 'Χαρακτήρες που εκτυπώνονται στην ετικέτα ειδοποίησης.', + headColdWarning: 'Προειδοποίηση ψυχρής κεφαλής', + headColdWarningOn: 'Ενεργό', + headColdWarningOff: 'Ανενεργό', + earlyWarningMaintenance: 'Συντήρηση έγκαιρης προειδοποίησης', + earlyWarningE: 'Ενεργοποιημένο', + earlyWarningD: 'Απενεργοποιημένο', + headCleaningIntervalMeters: 'Διάστημα καθαρισμού κεφαλής (m)', + headCleaningIntervalHint: 'Η απόσταση μετά την οποία ο εκτυπωτής σηματοδοτεί την κεφαλή για καθαρισμό.', + earlyWarningGateHint: 'Κύρια πύλη του συστήματος ειδοποιήσεων συντήρησης· απαιτείται για να ενεργοποιηθεί το ^MA.', }, }, diff --git a/src/locales/en.ts b/src/locales/en.ts index a48216a9..f07d7c0c 100644 --- a/src/locales/en.ts +++ b/src/locales/en.ts @@ -248,7 +248,7 @@ const en = { maintenance: { maintenanceAlertHeading: 'Maintenance alert', alertType: 'Alert type', - alertTypeH: 'Head cleaning', + alertTypeC: 'Head cleaning', alertTypeR: 'Head replacement', alertPrint: 'Print warning label', alertPrintY: 'Print', diff --git a/src/locales/es.ts b/src/locales/es.ts index 660ffe41..f70fd1c0 100644 --- a/src/locales/es.ts +++ b/src/locales/es.ts @@ -246,31 +246,31 @@ const es = { configurationUpdateOptionF: 'F — Restablecer de fábrica (destructivo)', }, maintenance: { - maintenanceAlertHeading: 'Maintenance alert', - alertType: 'Alert type', - alertTypeH: 'Head cleaning', - alertTypeR: 'Head replacement', - alertPrint: 'Print warning label', - alertPrintY: 'Print', - alertPrintN: 'Skip', - alertThreshold: 'Threshold', - alertFrequency: 'Repeat frequency', - alertUnits: 'Units', - alertUnitsM: 'Meters', - alertUnitsI: 'Inches', - alertUnitsC: 'Centimeters', - maintenanceMessageHeading: 'Custom message', - messageText: 'Message text', - messageTextHint: 'Characters printed on the alert label.', - headColdWarning: 'Head cold warning', - headColdWarningOn: 'On', - headColdWarningOff: 'Off', - earlyWarningMaintenance: 'Early warning maintenance', - earlyWarningE: 'Enabled', - earlyWarningD: 'Disabled', - headCleaningIntervalMeters: 'Head cleaning interval (m)', - headCleaningIntervalHint: 'Distance after which the printer flags the head for cleaning.', - earlyWarningGateHint: 'Master gate for the maintenance alert system; required for ^MA to fire.', + maintenanceAlertHeading: 'Alerta de mantenimiento', + alertType: 'Tipo de alerta', + alertTypeC: 'Limpieza del cabezal', + alertTypeR: 'Sustitución del cabezal', + alertPrint: 'Imprimir etiqueta de advertencia', + alertPrintY: 'Imprimir', + alertPrintN: 'Omitir', + alertThreshold: 'Umbral', + alertFrequency: 'Frecuencia de repetición', + alertUnits: 'Unidades', + alertUnitsM: 'Metros', + alertUnitsI: 'Pulgadas', + alertUnitsC: 'Centímetros', + maintenanceMessageHeading: 'Mensaje personalizado', + messageText: 'Texto del mensaje', + messageTextHint: 'Caracteres impresos en la etiqueta de alerta.', + headColdWarning: 'Aviso de cabezal frío', + headColdWarningOn: 'Activado', + headColdWarningOff: 'Desactivado', + earlyWarningMaintenance: 'Mantenimiento con aviso anticipado', + earlyWarningE: 'Activado', + earlyWarningD: 'Desactivado', + headCleaningIntervalMeters: 'Intervalo de limpieza del cabezal (m)', + headCleaningIntervalHint: 'Distancia tras la cual la impresora marca el cabezal para limpieza.', + earlyWarningGateHint: 'Interruptor principal del sistema de alertas de mantenimiento; necesario para que se active ^MA.', }, }, diff --git a/src/locales/et.ts b/src/locales/et.ts index 15947b36..24a6b059 100644 --- a/src/locales/et.ts +++ b/src/locales/et.ts @@ -246,31 +246,31 @@ const et = { configurationUpdateOptionF: 'F — Tehase lähtestamine (hävitav)', }, maintenance: { - maintenanceAlertHeading: 'Maintenance alert', - alertType: 'Alert type', - alertTypeH: 'Head cleaning', - alertTypeR: 'Head replacement', - alertPrint: 'Print warning label', - alertPrintY: 'Print', - alertPrintN: 'Skip', - alertThreshold: 'Threshold', - alertFrequency: 'Repeat frequency', - alertUnits: 'Units', - alertUnitsM: 'Meters', - alertUnitsI: 'Inches', - alertUnitsC: 'Centimeters', - maintenanceMessageHeading: 'Custom message', - messageText: 'Message text', - messageTextHint: 'Characters printed on the alert label.', - headColdWarning: 'Head cold warning', - headColdWarningOn: 'On', - headColdWarningOff: 'Off', - earlyWarningMaintenance: 'Early warning maintenance', - earlyWarningE: 'Enabled', - earlyWarningD: 'Disabled', - headCleaningIntervalMeters: 'Head cleaning interval (m)', - headCleaningIntervalHint: 'Distance after which the printer flags the head for cleaning.', - earlyWarningGateHint: 'Master gate for the maintenance alert system; required for ^MA to fire.', + maintenanceAlertHeading: 'Hooldushoiatus', + alertType: 'Hoiatuse tüüp', + alertTypeC: 'Pea puhastamine', + alertTypeR: 'Pea vahetus', + alertPrint: 'Prindi hoiatussilt', + alertPrintY: 'Prindi', + alertPrintN: 'Jäta vahele', + alertThreshold: 'Lävi', + alertFrequency: 'Korduse sagedus', + alertUnits: 'Ühikud', + alertUnitsM: 'Meetrid', + alertUnitsI: 'Tollid', + alertUnitsC: 'Sentimeetrid', + maintenanceMessageHeading: 'Kohandatud sõnum', + messageText: 'Sõnumi tekst', + messageTextHint: 'Hoiatussildile prinditavad märgid.', + headColdWarning: 'Külma pea hoiatus', + headColdWarningOn: 'Sees', + headColdWarningOff: 'Väljas', + earlyWarningMaintenance: 'Varajase hoiatuse hooldus', + earlyWarningE: 'Lubatud', + earlyWarningD: 'Keelatud', + headCleaningIntervalMeters: 'Pea puhastamise intervall (m)', + headCleaningIntervalHint: 'Vahemaa, mille järel printer märgib pea puhastamiseks.', + earlyWarningGateHint: 'Hooldushoiatussüsteemi peavärav; vajalik ^MA käivitamiseks.', }, }, diff --git a/src/locales/fa.ts b/src/locales/fa.ts index bc41d2df..7bfd068a 100644 --- a/src/locales/fa.ts +++ b/src/locales/fa.ts @@ -246,31 +246,31 @@ const fa = { configurationUpdateOptionF: 'F — بازنشانی کارخانه (مخرب)', }, maintenance: { - maintenanceAlertHeading: 'Maintenance alert', - alertType: 'Alert type', - alertTypeH: 'Head cleaning', - alertTypeR: 'Head replacement', - alertPrint: 'Print warning label', - alertPrintY: 'Print', - alertPrintN: 'Skip', - alertThreshold: 'Threshold', - alertFrequency: 'Repeat frequency', - alertUnits: 'Units', - alertUnitsM: 'Meters', - alertUnitsI: 'Inches', - alertUnitsC: 'Centimeters', - maintenanceMessageHeading: 'Custom message', - messageText: 'Message text', - messageTextHint: 'Characters printed on the alert label.', - headColdWarning: 'Head cold warning', - headColdWarningOn: 'On', - headColdWarningOff: 'Off', - earlyWarningMaintenance: 'Early warning maintenance', - earlyWarningE: 'Enabled', - earlyWarningD: 'Disabled', - headCleaningIntervalMeters: 'Head cleaning interval (m)', - headCleaningIntervalHint: 'Distance after which the printer flags the head for cleaning.', - earlyWarningGateHint: 'Master gate for the maintenance alert system; required for ^MA to fire.', + maintenanceAlertHeading: 'هشدار نگهداری', + alertType: 'نوع هشدار', + alertTypeC: 'تمیز کردن هد', + alertTypeR: 'تعویض هد', + alertPrint: 'چاپ برچسب هشدار', + alertPrintY: 'چاپ', + alertPrintN: 'رد کردن', + alertThreshold: 'آستانه', + alertFrequency: 'فرکانس تکرار', + alertUnits: 'واحدها', + alertUnitsM: 'متر', + alertUnitsI: 'اینچ', + alertUnitsC: 'سانتی‌متر', + maintenanceMessageHeading: 'پیام سفارشی', + messageText: 'متن پیام', + messageTextHint: 'نویسه‌های چاپ‌شده روی برچسب هشدار.', + headColdWarning: 'هشدار سرد بودن هد', + headColdWarningOn: 'روشن', + headColdWarningOff: 'خاموش', + earlyWarningMaintenance: 'نگهداری هشدار زودهنگام', + earlyWarningE: 'فعال', + earlyWarningD: 'غیرفعال', + headCleaningIntervalMeters: 'فاصله تمیز کردن هد (متر)', + headCleaningIntervalHint: 'فاصله‌ای که پس از آن چاپگر هد را برای تمیز کردن علامت‌گذاری می‌کند.', + earlyWarningGateHint: 'دروازه اصلی سیستم هشدار نگهداری؛ برای اجرای ^MA لازم است.', }, }, diff --git a/src/locales/fi.ts b/src/locales/fi.ts index 2ba454c9..f56c3f10 100644 --- a/src/locales/fi.ts +++ b/src/locales/fi.ts @@ -246,31 +246,31 @@ const fi = { configurationUpdateOptionF: 'F — Tehdasasetusten palautus (tuhoava)', }, maintenance: { - maintenanceAlertHeading: 'Maintenance alert', - alertType: 'Alert type', - alertTypeH: 'Head cleaning', - alertTypeR: 'Head replacement', - alertPrint: 'Print warning label', - alertPrintY: 'Print', - alertPrintN: 'Skip', - alertThreshold: 'Threshold', - alertFrequency: 'Repeat frequency', - alertUnits: 'Units', - alertUnitsM: 'Meters', - alertUnitsI: 'Inches', - alertUnitsC: 'Centimeters', - maintenanceMessageHeading: 'Custom message', - messageText: 'Message text', - messageTextHint: 'Characters printed on the alert label.', - headColdWarning: 'Head cold warning', - headColdWarningOn: 'On', - headColdWarningOff: 'Off', - earlyWarningMaintenance: 'Early warning maintenance', - earlyWarningE: 'Enabled', - earlyWarningD: 'Disabled', - headCleaningIntervalMeters: 'Head cleaning interval (m)', - headCleaningIntervalHint: 'Distance after which the printer flags the head for cleaning.', - earlyWarningGateHint: 'Master gate for the maintenance alert system; required for ^MA to fire.', + maintenanceAlertHeading: 'Huoltohälytys', + alertType: 'Hälytystyyppi', + alertTypeC: 'Tulostuspään puhdistus', + alertTypeR: 'Tulostuspään vaihto', + alertPrint: 'Tulosta varoitustarra', + alertPrintY: 'Tulosta', + alertPrintN: 'Ohita', + alertThreshold: 'Kynnysarvo', + alertFrequency: 'Toistotaajuus', + alertUnits: 'Yksiköt', + alertUnitsM: 'Metrit', + alertUnitsI: 'Tuumat', + alertUnitsC: 'Senttimetrit', + maintenanceMessageHeading: 'Mukautettu viesti', + messageText: 'Viestin teksti', + messageTextHint: 'Hälytystarraan tulostettavat merkit.', + headColdWarning: 'Kylmän tulostuspään varoitus', + headColdWarningOn: 'Päällä', + headColdWarningOff: 'Pois', + earlyWarningMaintenance: 'Ennakkovaroitushuolto', + earlyWarningE: 'Käytössä', + earlyWarningD: 'Pois käytöstä', + headCleaningIntervalMeters: 'Tulostuspään puhdistusväli (m)', + headCleaningIntervalHint: 'Matka, jonka jälkeen tulostin merkitsee tulostuspään puhdistettavaksi.', + earlyWarningGateHint: 'Huoltohälytysjärjestelmän pääportti; vaaditaan ^MA-laukaisuun.', }, }, diff --git a/src/locales/fr.ts b/src/locales/fr.ts index 1e602710..5ca4ccfd 100644 --- a/src/locales/fr.ts +++ b/src/locales/fr.ts @@ -246,31 +246,31 @@ const fr = { configurationUpdateOptionF: 'F — Réinitialisation usine (destructif)', }, maintenance: { - maintenanceAlertHeading: 'Maintenance alert', - alertType: 'Alert type', - alertTypeH: 'Head cleaning', - alertTypeR: 'Head replacement', - alertPrint: 'Print warning label', - alertPrintY: 'Print', - alertPrintN: 'Skip', - alertThreshold: 'Threshold', - alertFrequency: 'Repeat frequency', - alertUnits: 'Units', - alertUnitsM: 'Meters', - alertUnitsI: 'Inches', - alertUnitsC: 'Centimeters', - maintenanceMessageHeading: 'Custom message', - messageText: 'Message text', - messageTextHint: 'Characters printed on the alert label.', - headColdWarning: 'Head cold warning', - headColdWarningOn: 'On', - headColdWarningOff: 'Off', - earlyWarningMaintenance: 'Early warning maintenance', - earlyWarningE: 'Enabled', - earlyWarningD: 'Disabled', - headCleaningIntervalMeters: 'Head cleaning interval (m)', - headCleaningIntervalHint: 'Distance after which the printer flags the head for cleaning.', - earlyWarningGateHint: 'Master gate for the maintenance alert system; required for ^MA to fire.', + maintenanceAlertHeading: 'Alerte de maintenance', + alertType: "Type d'alerte", + alertTypeC: 'Nettoyage de la tête', + alertTypeR: 'Remplacement de la tête', + alertPrint: "Imprimer l'étiquette d'avertissement", + alertPrintY: 'Imprimer', + alertPrintN: 'Ignorer', + alertThreshold: 'Seuil', + alertFrequency: 'Fréquence de répétition', + alertUnits: 'Unités', + alertUnitsM: 'Mètres', + alertUnitsI: 'Pouces', + alertUnitsC: 'Centimètres', + maintenanceMessageHeading: 'Message personnalisé', + messageText: 'Texte du message', + messageTextHint: "Caractères imprimés sur l'étiquette d'alerte.", + headColdWarning: 'Avertissement tête froide', + headColdWarningOn: 'Activé', + headColdWarningOff: 'Désactivé', + earlyWarningMaintenance: 'Maintenance avec alerte anticipée', + earlyWarningE: 'Activé', + earlyWarningD: 'Désactivé', + headCleaningIntervalMeters: 'Intervalle de nettoyage de la tête (m)', + headCleaningIntervalHint: "Distance après laquelle l'imprimante signale la tête pour nettoyage.", + earlyWarningGateHint: "Interrupteur principal du système d'alerte de maintenance ; requis pour déclencher ^MA.", }, }, diff --git a/src/locales/he.ts b/src/locales/he.ts index fe001b58..d905ef12 100644 --- a/src/locales/he.ts +++ b/src/locales/he.ts @@ -246,31 +246,31 @@ const he = { configurationUpdateOptionF: 'F — איפוס יצרן (הרסני)', }, maintenance: { - maintenanceAlertHeading: 'Maintenance alert', - alertType: 'Alert type', - alertTypeH: 'Head cleaning', - alertTypeR: 'Head replacement', - alertPrint: 'Print warning label', - alertPrintY: 'Print', - alertPrintN: 'Skip', - alertThreshold: 'Threshold', - alertFrequency: 'Repeat frequency', - alertUnits: 'Units', - alertUnitsM: 'Meters', - alertUnitsI: 'Inches', - alertUnitsC: 'Centimeters', - maintenanceMessageHeading: 'Custom message', - messageText: 'Message text', - messageTextHint: 'Characters printed on the alert label.', - headColdWarning: 'Head cold warning', - headColdWarningOn: 'On', - headColdWarningOff: 'Off', - earlyWarningMaintenance: 'Early warning maintenance', - earlyWarningE: 'Enabled', - earlyWarningD: 'Disabled', - headCleaningIntervalMeters: 'Head cleaning interval (m)', - headCleaningIntervalHint: 'Distance after which the printer flags the head for cleaning.', - earlyWarningGateHint: 'Master gate for the maintenance alert system; required for ^MA to fire.', + maintenanceAlertHeading: 'התראת תחזוקה', + alertType: 'סוג התראה', + alertTypeC: 'ניקוי ראש', + alertTypeR: 'החלפת ראש', + alertPrint: 'הדפסת תווית אזהרה', + alertPrintY: 'הדפס', + alertPrintN: 'דלג', + alertThreshold: 'סף', + alertFrequency: 'תדירות חזרה', + alertUnits: 'יחידות', + alertUnitsM: 'מטרים', + alertUnitsI: "אינצ'ים", + alertUnitsC: 'סנטימטרים', + maintenanceMessageHeading: 'הודעה מותאמת אישית', + messageText: 'טקסט ההודעה', + messageTextHint: 'התווים שיודפסו על תווית ההתראה.', + headColdWarning: 'אזהרת ראש קר', + headColdWarningOn: 'פעיל', + headColdWarningOff: 'כבוי', + earlyWarningMaintenance: 'תחזוקת אזהרה מוקדמת', + earlyWarningE: 'מופעל', + earlyWarningD: 'מושבת', + headCleaningIntervalMeters: 'מרווח ניקוי ראש (מ׳)', + headCleaningIntervalHint: 'המרחק שלאחריו המדפסת מסמנת את הראש לניקוי.', + earlyWarningGateHint: 'מתג ראשי של מערכת התראות התחזוקה; נדרש כדי ש-^MA יופעל.', }, }, diff --git a/src/locales/hr.ts b/src/locales/hr.ts index 9e7cae28..302e6c7d 100644 --- a/src/locales/hr.ts +++ b/src/locales/hr.ts @@ -246,31 +246,31 @@ const hr = { configurationUpdateOptionF: 'F — Tvornički reset (destruktivno)', }, maintenance: { - maintenanceAlertHeading: 'Maintenance alert', - alertType: 'Alert type', - alertTypeH: 'Head cleaning', - alertTypeR: 'Head replacement', - alertPrint: 'Print warning label', - alertPrintY: 'Print', - alertPrintN: 'Skip', - alertThreshold: 'Threshold', - alertFrequency: 'Repeat frequency', - alertUnits: 'Units', - alertUnitsM: 'Meters', - alertUnitsI: 'Inches', - alertUnitsC: 'Centimeters', - maintenanceMessageHeading: 'Custom message', - messageText: 'Message text', - messageTextHint: 'Characters printed on the alert label.', - headColdWarning: 'Head cold warning', - headColdWarningOn: 'On', - headColdWarningOff: 'Off', - earlyWarningMaintenance: 'Early warning maintenance', - earlyWarningE: 'Enabled', - earlyWarningD: 'Disabled', - headCleaningIntervalMeters: 'Head cleaning interval (m)', - headCleaningIntervalHint: 'Distance after which the printer flags the head for cleaning.', - earlyWarningGateHint: 'Master gate for the maintenance alert system; required for ^MA to fire.', + maintenanceAlertHeading: 'Upozorenje o održavanju', + alertType: 'Vrsta upozorenja', + alertTypeC: 'Čišćenje glave', + alertTypeR: 'Zamjena glave', + alertPrint: 'Ispiši naljepnicu upozorenja', + alertPrintY: 'Ispiši', + alertPrintN: 'Preskoči', + alertThreshold: 'Prag', + alertFrequency: 'Učestalost ponavljanja', + alertUnits: 'Jedinice', + alertUnitsM: 'Metri', + alertUnitsI: 'Inči', + alertUnitsC: 'Centimetri', + maintenanceMessageHeading: 'Prilagođena poruka', + messageText: 'Tekst poruke', + messageTextHint: 'Znakovi ispisani na naljepnici upozorenja.', + headColdWarning: 'Upozorenje o hladnoj glavi', + headColdWarningOn: 'Uključeno', + headColdWarningOff: 'Isključeno', + earlyWarningMaintenance: 'Održavanje s ranim upozorenjem', + earlyWarningE: 'Omogućeno', + earlyWarningD: 'Onemogućeno', + headCleaningIntervalMeters: 'Interval čišćenja glave (m)', + headCleaningIntervalHint: 'Udaljenost nakon koje pisač označava glavu za čišćenje.', + earlyWarningGateHint: 'Glavni prekidač sustava upozorenja o održavanju; potreban za aktivaciju ^MA.', }, }, diff --git a/src/locales/hu.ts b/src/locales/hu.ts index 1adc5a21..95d2dd3e 100644 --- a/src/locales/hu.ts +++ b/src/locales/hu.ts @@ -246,31 +246,31 @@ const hu = { configurationUpdateOptionF: 'F — Gyári visszaállítás (destruktív)', }, maintenance: { - maintenanceAlertHeading: 'Maintenance alert', - alertType: 'Alert type', - alertTypeH: 'Head cleaning', - alertTypeR: 'Head replacement', - alertPrint: 'Print warning label', - alertPrintY: 'Print', - alertPrintN: 'Skip', - alertThreshold: 'Threshold', - alertFrequency: 'Repeat frequency', - alertUnits: 'Units', - alertUnitsM: 'Meters', - alertUnitsI: 'Inches', - alertUnitsC: 'Centimeters', - maintenanceMessageHeading: 'Custom message', - messageText: 'Message text', - messageTextHint: 'Characters printed on the alert label.', - headColdWarning: 'Head cold warning', - headColdWarningOn: 'On', - headColdWarningOff: 'Off', - earlyWarningMaintenance: 'Early warning maintenance', - earlyWarningE: 'Enabled', - earlyWarningD: 'Disabled', - headCleaningIntervalMeters: 'Head cleaning interval (m)', - headCleaningIntervalHint: 'Distance after which the printer flags the head for cleaning.', - earlyWarningGateHint: 'Master gate for the maintenance alert system; required for ^MA to fire.', + maintenanceAlertHeading: 'Karbantartási riasztás', + alertType: 'Riasztás típusa', + alertTypeC: 'Fejtisztítás', + alertTypeR: 'Fejcsere', + alertPrint: 'Figyelmeztető címke nyomtatása', + alertPrintY: 'Nyomtatás', + alertPrintN: 'Kihagyás', + alertThreshold: 'Küszöbérték', + alertFrequency: 'Ismétlési gyakoriság', + alertUnits: 'Mértékegységek', + alertUnitsM: 'Méter', + alertUnitsI: 'Hüvelyk', + alertUnitsC: 'Centiméter', + maintenanceMessageHeading: 'Egyéni üzenet', + messageText: 'Üzenet szövege', + messageTextHint: 'A riasztási címkére nyomtatott karakterek.', + headColdWarning: 'Hideg fej figyelmeztetés', + headColdWarningOn: 'Be', + headColdWarningOff: 'Ki', + earlyWarningMaintenance: 'Korai figyelmeztetésű karbantartás', + earlyWarningE: 'Engedélyezve', + earlyWarningD: 'Letiltva', + headCleaningIntervalMeters: 'Fejtisztítási intervallum (m)', + headCleaningIntervalHint: 'A távolság, amely után a nyomtató jelzi a fej tisztítását.', + earlyWarningGateHint: 'A karbantartási riasztási rendszer fő kapcsolója; szükséges a ^MA aktiválásához.', }, }, diff --git a/src/locales/it.ts b/src/locales/it.ts index 3fbfb6a3..bfa75526 100644 --- a/src/locales/it.ts +++ b/src/locales/it.ts @@ -246,31 +246,31 @@ const it = { configurationUpdateOptionF: 'F — Ripristino di fabbrica (distruttivo)', }, maintenance: { - maintenanceAlertHeading: 'Maintenance alert', - alertType: 'Alert type', - alertTypeH: 'Head cleaning', - alertTypeR: 'Head replacement', - alertPrint: 'Print warning label', - alertPrintY: 'Print', - alertPrintN: 'Skip', - alertThreshold: 'Threshold', - alertFrequency: 'Repeat frequency', - alertUnits: 'Units', - alertUnitsM: 'Meters', - alertUnitsI: 'Inches', - alertUnitsC: 'Centimeters', - maintenanceMessageHeading: 'Custom message', - messageText: 'Message text', - messageTextHint: 'Characters printed on the alert label.', - headColdWarning: 'Head cold warning', - headColdWarningOn: 'On', - headColdWarningOff: 'Off', - earlyWarningMaintenance: 'Early warning maintenance', - earlyWarningE: 'Enabled', - earlyWarningD: 'Disabled', - headCleaningIntervalMeters: 'Head cleaning interval (m)', - headCleaningIntervalHint: 'Distance after which the printer flags the head for cleaning.', - earlyWarningGateHint: 'Master gate for the maintenance alert system; required for ^MA to fire.', + maintenanceAlertHeading: 'Avviso di manutenzione', + alertType: 'Tipo di avviso', + alertTypeC: 'Pulizia della testina', + alertTypeR: 'Sostituzione della testina', + alertPrint: 'Stampa etichetta di avviso', + alertPrintY: 'Stampa', + alertPrintN: 'Salta', + alertThreshold: 'Soglia', + alertFrequency: 'Frequenza di ripetizione', + alertUnits: 'Unità', + alertUnitsM: 'Metri', + alertUnitsI: 'Pollici', + alertUnitsC: 'Centimetri', + maintenanceMessageHeading: 'Messaggio personalizzato', + messageText: 'Testo del messaggio', + messageTextHint: "Caratteri stampati sull'etichetta di avviso.", + headColdWarning: 'Avviso testina fredda', + headColdWarningOn: 'Attivo', + headColdWarningOff: 'Disattivo', + earlyWarningMaintenance: 'Manutenzione con preavviso', + earlyWarningE: 'Abilitato', + earlyWarningD: 'Disabilitato', + headCleaningIntervalMeters: 'Intervallo di pulizia della testina (m)', + headCleaningIntervalHint: 'Distanza dopo la quale la stampante segnala la testina per la pulizia.', + earlyWarningGateHint: 'Interruttore principale del sistema di avvisi di manutenzione; necessario per attivare ^MA.', }, }, diff --git a/src/locales/ja.ts b/src/locales/ja.ts index 6c5e928f..b8efa75a 100644 --- a/src/locales/ja.ts +++ b/src/locales/ja.ts @@ -246,31 +246,31 @@ const ja = { configurationUpdateOptionF: 'F — 工場リセット (破壊的)', }, maintenance: { - maintenanceAlertHeading: 'Maintenance alert', - alertType: 'Alert type', - alertTypeH: 'Head cleaning', - alertTypeR: 'Head replacement', - alertPrint: 'Print warning label', - alertPrintY: 'Print', - alertPrintN: 'Skip', - alertThreshold: 'Threshold', - alertFrequency: 'Repeat frequency', - alertUnits: 'Units', - alertUnitsM: 'Meters', - alertUnitsI: 'Inches', - alertUnitsC: 'Centimeters', - maintenanceMessageHeading: 'Custom message', - messageText: 'Message text', - messageTextHint: 'Characters printed on the alert label.', - headColdWarning: 'Head cold warning', - headColdWarningOn: 'On', - headColdWarningOff: 'Off', - earlyWarningMaintenance: 'Early warning maintenance', - earlyWarningE: 'Enabled', - earlyWarningD: 'Disabled', - headCleaningIntervalMeters: 'Head cleaning interval (m)', - headCleaningIntervalHint: 'Distance after which the printer flags the head for cleaning.', - earlyWarningGateHint: 'Master gate for the maintenance alert system; required for ^MA to fire.', + maintenanceAlertHeading: 'メンテナンス警告', + alertType: '警告タイプ', + alertTypeC: 'ヘッドクリーニング', + alertTypeR: 'ヘッド交換', + alertPrint: '警告ラベルを印刷', + alertPrintY: '印刷', + alertPrintN: 'スキップ', + alertThreshold: 'しきい値', + alertFrequency: '繰り返し頻度', + alertUnits: '単位', + alertUnitsM: 'メートル', + alertUnitsI: 'インチ', + alertUnitsC: 'センチメートル', + maintenanceMessageHeading: 'カスタムメッセージ', + messageText: 'メッセージテキスト', + messageTextHint: '警告ラベルに印刷される文字。', + headColdWarning: 'ヘッド低温警告', + headColdWarningOn: 'オン', + headColdWarningOff: 'オフ', + earlyWarningMaintenance: '早期警告メンテナンス', + earlyWarningE: '有効', + earlyWarningD: '無効', + headCleaningIntervalMeters: 'ヘッドクリーニング間隔 (m)', + headCleaningIntervalHint: 'プリンターがヘッドのクリーニングを通知するまでの距離。', + earlyWarningGateHint: 'メンテナンス警告システムのマスターゲート。^MA の発火に必要です。', }, }, diff --git a/src/locales/ko.ts b/src/locales/ko.ts index 326060ca..5ec5f86c 100644 --- a/src/locales/ko.ts +++ b/src/locales/ko.ts @@ -246,31 +246,31 @@ const ko = { configurationUpdateOptionF: 'F — 공장 초기화 (파괴적)', }, maintenance: { - maintenanceAlertHeading: 'Maintenance alert', - alertType: 'Alert type', - alertTypeH: 'Head cleaning', - alertTypeR: 'Head replacement', - alertPrint: 'Print warning label', - alertPrintY: 'Print', - alertPrintN: 'Skip', - alertThreshold: 'Threshold', - alertFrequency: 'Repeat frequency', - alertUnits: 'Units', - alertUnitsM: 'Meters', - alertUnitsI: 'Inches', - alertUnitsC: 'Centimeters', - maintenanceMessageHeading: 'Custom message', - messageText: 'Message text', - messageTextHint: 'Characters printed on the alert label.', - headColdWarning: 'Head cold warning', - headColdWarningOn: 'On', - headColdWarningOff: 'Off', - earlyWarningMaintenance: 'Early warning maintenance', - earlyWarningE: 'Enabled', - earlyWarningD: 'Disabled', - headCleaningIntervalMeters: 'Head cleaning interval (m)', - headCleaningIntervalHint: 'Distance after which the printer flags the head for cleaning.', - earlyWarningGateHint: 'Master gate for the maintenance alert system; required for ^MA to fire.', + maintenanceAlertHeading: '유지보수 경고', + alertType: '경고 유형', + alertTypeC: '헤드 청소', + alertTypeR: '헤드 교체', + alertPrint: '경고 라벨 인쇄', + alertPrintY: '인쇄', + alertPrintN: '건너뛰기', + alertThreshold: '임계값', + alertFrequency: '반복 빈도', + alertUnits: '단위', + alertUnitsM: '미터', + alertUnitsI: '인치', + alertUnitsC: '센티미터', + maintenanceMessageHeading: '사용자 지정 메시지', + messageText: '메시지 텍스트', + messageTextHint: '경고 라벨에 인쇄되는 문자입니다.', + headColdWarning: '헤드 저온 경고', + headColdWarningOn: '켜기', + headColdWarningOff: '끄기', + earlyWarningMaintenance: '조기 경고 유지보수', + earlyWarningE: '활성화됨', + earlyWarningD: '비활성화됨', + headCleaningIntervalMeters: '헤드 청소 간격 (m)', + headCleaningIntervalHint: '프린터가 헤드 청소를 알리는 거리입니다.', + earlyWarningGateHint: '유지보수 경고 시스템의 마스터 게이트입니다. ^MA 작동에 필요합니다.', }, }, diff --git a/src/locales/lt.ts b/src/locales/lt.ts index bcd8ad52..0b0fa0ab 100644 --- a/src/locales/lt.ts +++ b/src/locales/lt.ts @@ -246,31 +246,31 @@ const lt = { configurationUpdateOptionF: 'F — Gamyklinis atstatymas (destruktyvus)', }, maintenance: { - maintenanceAlertHeading: 'Maintenance alert', - alertType: 'Alert type', - alertTypeH: 'Head cleaning', - alertTypeR: 'Head replacement', - alertPrint: 'Print warning label', - alertPrintY: 'Print', - alertPrintN: 'Skip', - alertThreshold: 'Threshold', - alertFrequency: 'Repeat frequency', - alertUnits: 'Units', - alertUnitsM: 'Meters', - alertUnitsI: 'Inches', - alertUnitsC: 'Centimeters', - maintenanceMessageHeading: 'Custom message', - messageText: 'Message text', - messageTextHint: 'Characters printed on the alert label.', - headColdWarning: 'Head cold warning', - headColdWarningOn: 'On', - headColdWarningOff: 'Off', - earlyWarningMaintenance: 'Early warning maintenance', - earlyWarningE: 'Enabled', - earlyWarningD: 'Disabled', - headCleaningIntervalMeters: 'Head cleaning interval (m)', - headCleaningIntervalHint: 'Distance after which the printer flags the head for cleaning.', - earlyWarningGateHint: 'Master gate for the maintenance alert system; required for ^MA to fire.', + maintenanceAlertHeading: 'Techninės priežiūros įspėjimas', + alertType: 'Įspėjimo tipas', + alertTypeC: 'Galvutės valymas', + alertTypeR: 'Galvutės keitimas', + alertPrint: 'Spausdinti įspėjimo etiketę', + alertPrintY: 'Spausdinti', + alertPrintN: 'Praleisti', + alertThreshold: 'Slenkstis', + alertFrequency: 'Pakartojimo dažnis', + alertUnits: 'Vienetai', + alertUnitsM: 'Metrai', + alertUnitsI: 'Coliai', + alertUnitsC: 'Centimetrai', + maintenanceMessageHeading: 'Pasirinktinis pranešimas', + messageText: 'Pranešimo tekstas', + messageTextHint: 'Simboliai, spausdinami ant įspėjimo etiketės.', + headColdWarning: 'Šaltos galvutės įspėjimas', + headColdWarningOn: 'Įjungta', + headColdWarningOff: 'Išjungta', + earlyWarningMaintenance: 'Ankstyvojo įspėjimo techninė priežiūra', + earlyWarningE: 'Įjungta', + earlyWarningD: 'Išjungta', + headCleaningIntervalMeters: 'Galvutės valymo intervalas (m)', + headCleaningIntervalHint: 'Atstumas, po kurio spausdintuvas pažymi galvutę valymui.', + earlyWarningGateHint: 'Pagrindinis techninės priežiūros įspėjimo sistemos jungiklis; būtinas ^MA suaktyvinti.', }, }, diff --git a/src/locales/lv.ts b/src/locales/lv.ts index 40a3caf5..7a408795 100644 --- a/src/locales/lv.ts +++ b/src/locales/lv.ts @@ -246,31 +246,31 @@ const lv = { configurationUpdateOptionF: 'F — Rūpnīcas atiestatīšana (destruktīvi)', }, maintenance: { - maintenanceAlertHeading: 'Maintenance alert', - alertType: 'Alert type', - alertTypeH: 'Head cleaning', - alertTypeR: 'Head replacement', - alertPrint: 'Print warning label', - alertPrintY: 'Print', - alertPrintN: 'Skip', - alertThreshold: 'Threshold', - alertFrequency: 'Repeat frequency', - alertUnits: 'Units', - alertUnitsM: 'Meters', - alertUnitsI: 'Inches', - alertUnitsC: 'Centimeters', - maintenanceMessageHeading: 'Custom message', - messageText: 'Message text', - messageTextHint: 'Characters printed on the alert label.', - headColdWarning: 'Head cold warning', - headColdWarningOn: 'On', - headColdWarningOff: 'Off', - earlyWarningMaintenance: 'Early warning maintenance', - earlyWarningE: 'Enabled', - earlyWarningD: 'Disabled', - headCleaningIntervalMeters: 'Head cleaning interval (m)', - headCleaningIntervalHint: 'Distance after which the printer flags the head for cleaning.', - earlyWarningGateHint: 'Master gate for the maintenance alert system; required for ^MA to fire.', + maintenanceAlertHeading: 'Apkopes brīdinājums', + alertType: 'Brīdinājuma veids', + alertTypeC: 'Galviņas tīrīšana', + alertTypeR: 'Galviņas nomaiņa', + alertPrint: 'Drukāt brīdinājuma etiķeti', + alertPrintY: 'Drukāt', + alertPrintN: 'Izlaist', + alertThreshold: 'Slieksnis', + alertFrequency: 'Atkārtošanas biežums', + alertUnits: 'Vienības', + alertUnitsM: 'Metri', + alertUnitsI: 'Collas', + alertUnitsC: 'Centimetri', + maintenanceMessageHeading: 'Pielāgots ziņojums', + messageText: 'Ziņojuma teksts', + messageTextHint: 'Brīdinājuma etiķetē drukājamie rakstzīmes.', + headColdWarning: 'Aukstas galviņas brīdinājums', + headColdWarningOn: 'Ieslēgts', + headColdWarningOff: 'Izslēgts', + earlyWarningMaintenance: 'Agrīnās brīdināšanas apkope', + earlyWarningE: 'Iespējots', + earlyWarningD: 'Atspējots', + headCleaningIntervalMeters: 'Galviņas tīrīšanas intervāls (m)', + headCleaningIntervalHint: 'Attālums, pēc kura printeris atzīmē galviņu tīrīšanai.', + earlyWarningGateHint: 'Apkopes brīdināšanas sistēmas galvenais slēdzis; nepieciešams, lai aktivizētu ^MA.', }, }, diff --git a/src/locales/nl.ts b/src/locales/nl.ts index 12628b44..893baa55 100644 --- a/src/locales/nl.ts +++ b/src/locales/nl.ts @@ -246,31 +246,31 @@ const nl = { configurationUpdateOptionF: 'F — Fabrieksreset (destructief)', }, maintenance: { - maintenanceAlertHeading: 'Maintenance alert', - alertType: 'Alert type', - alertTypeH: 'Head cleaning', - alertTypeR: 'Head replacement', - alertPrint: 'Print warning label', - alertPrintY: 'Print', - alertPrintN: 'Skip', - alertThreshold: 'Threshold', - alertFrequency: 'Repeat frequency', - alertUnits: 'Units', + maintenanceAlertHeading: 'Onderhoudswaarschuwing', + alertType: 'Waarschuwingstype', + alertTypeC: 'Kopreiniging', + alertTypeR: 'Kopvervanging', + alertPrint: 'Waarschuwingsetiket afdrukken', + alertPrintY: 'Afdrukken', + alertPrintN: 'Overslaan', + alertThreshold: 'Drempel', + alertFrequency: 'Herhaalfrequentie', + alertUnits: 'Eenheden', alertUnitsM: 'Meters', alertUnitsI: 'Inches', alertUnitsC: 'Centimeters', - maintenanceMessageHeading: 'Custom message', - messageText: 'Message text', - messageTextHint: 'Characters printed on the alert label.', - headColdWarning: 'Head cold warning', - headColdWarningOn: 'On', - headColdWarningOff: 'Off', - earlyWarningMaintenance: 'Early warning maintenance', - earlyWarningE: 'Enabled', - earlyWarningD: 'Disabled', - headCleaningIntervalMeters: 'Head cleaning interval (m)', - headCleaningIntervalHint: 'Distance after which the printer flags the head for cleaning.', - earlyWarningGateHint: 'Master gate for the maintenance alert system; required for ^MA to fire.', + maintenanceMessageHeading: 'Aangepast bericht', + messageText: 'Berichttekst', + messageTextHint: 'Tekens die op het waarschuwingsetiket worden afgedrukt.', + headColdWarning: 'Waarschuwing koude kop', + headColdWarningOn: 'Aan', + headColdWarningOff: 'Uit', + earlyWarningMaintenance: 'Vroegtijdige waarschuwingsonderhoud', + earlyWarningE: 'Ingeschakeld', + earlyWarningD: 'Uitgeschakeld', + headCleaningIntervalMeters: 'Kopreinigingsinterval (m)', + headCleaningIntervalHint: 'Afstand waarna de printer de kop markeert voor reiniging.', + earlyWarningGateHint: 'Hoofdschakelaar van het onderhoudswaarschuwingssysteem; vereist om ^MA te activeren.', }, }, diff --git a/src/locales/no.ts b/src/locales/no.ts index d448bdff..20150218 100644 --- a/src/locales/no.ts +++ b/src/locales/no.ts @@ -246,31 +246,31 @@ const no = { configurationUpdateOptionF: 'F — Fabrikknullstilling (destruktiv)', }, maintenance: { - maintenanceAlertHeading: 'Maintenance alert', - alertType: 'Alert type', - alertTypeH: 'Head cleaning', - alertTypeR: 'Head replacement', - alertPrint: 'Print warning label', - alertPrintY: 'Print', - alertPrintN: 'Skip', - alertThreshold: 'Threshold', - alertFrequency: 'Repeat frequency', - alertUnits: 'Units', - alertUnitsM: 'Meters', - alertUnitsI: 'Inches', - alertUnitsC: 'Centimeters', - maintenanceMessageHeading: 'Custom message', - messageText: 'Message text', - messageTextHint: 'Characters printed on the alert label.', - headColdWarning: 'Head cold warning', - headColdWarningOn: 'On', - headColdWarningOff: 'Off', - earlyWarningMaintenance: 'Early warning maintenance', - earlyWarningE: 'Enabled', - earlyWarningD: 'Disabled', - headCleaningIntervalMeters: 'Head cleaning interval (m)', - headCleaningIntervalHint: 'Distance after which the printer flags the head for cleaning.', - earlyWarningGateHint: 'Master gate for the maintenance alert system; required for ^MA to fire.', + maintenanceAlertHeading: 'Vedlikeholdsvarsel', + alertType: 'Varseltype', + alertTypeC: 'Hoderengjøring', + alertTypeR: 'Hodeutskifting', + alertPrint: 'Skriv ut advarselsetikett', + alertPrintY: 'Skriv ut', + alertPrintN: 'Hopp over', + alertThreshold: 'Terskel', + alertFrequency: 'Gjentakelsesfrekvens', + alertUnits: 'Enheter', + alertUnitsM: 'Meter', + alertUnitsI: 'Tommer', + alertUnitsC: 'Centimeter', + maintenanceMessageHeading: 'Egendefinert melding', + messageText: 'Meldingstekst', + messageTextHint: 'Tegn som skrives ut på varselsetiketten.', + headColdWarning: 'Advarsel om kaldt hode', + headColdWarningOn: 'På', + headColdWarningOff: 'Av', + earlyWarningMaintenance: 'Tidlig varslingsvedlikehold', + earlyWarningE: 'Aktivert', + earlyWarningD: 'Deaktivert', + headCleaningIntervalMeters: 'Hoderengjøringsintervall (m)', + headCleaningIntervalHint: 'Avstanden som skriveren bruker før den merker hodet for rengjøring.', + earlyWarningGateHint: 'Hovedbryter for vedlikeholdsvarslingssystemet; kreves for at ^MA skal utløses.', }, }, diff --git a/src/locales/pl.ts b/src/locales/pl.ts index c58a1097..19837396 100644 --- a/src/locales/pl.ts +++ b/src/locales/pl.ts @@ -246,31 +246,31 @@ const pl = { configurationUpdateOptionF: 'F — Reset fabryczny (destrukcyjny)', }, maintenance: { - maintenanceAlertHeading: 'Maintenance alert', - alertType: 'Alert type', - alertTypeH: 'Head cleaning', - alertTypeR: 'Head replacement', - alertPrint: 'Print warning label', - alertPrintY: 'Print', - alertPrintN: 'Skip', - alertThreshold: 'Threshold', - alertFrequency: 'Repeat frequency', - alertUnits: 'Units', - alertUnitsM: 'Meters', - alertUnitsI: 'Inches', - alertUnitsC: 'Centimeters', - maintenanceMessageHeading: 'Custom message', - messageText: 'Message text', - messageTextHint: 'Characters printed on the alert label.', - headColdWarning: 'Head cold warning', - headColdWarningOn: 'On', - headColdWarningOff: 'Off', - earlyWarningMaintenance: 'Early warning maintenance', - earlyWarningE: 'Enabled', - earlyWarningD: 'Disabled', - headCleaningIntervalMeters: 'Head cleaning interval (m)', - headCleaningIntervalHint: 'Distance after which the printer flags the head for cleaning.', - earlyWarningGateHint: 'Master gate for the maintenance alert system; required for ^MA to fire.', + maintenanceAlertHeading: 'Alert konserwacji', + alertType: 'Typ alertu', + alertTypeC: 'Czyszczenie głowicy', + alertTypeR: 'Wymiana głowicy', + alertPrint: 'Drukuj etykietę ostrzegawczą', + alertPrintY: 'Drukuj', + alertPrintN: 'Pomiń', + alertThreshold: 'Próg', + alertFrequency: 'Częstotliwość powtarzania', + alertUnits: 'Jednostki', + alertUnitsM: 'Metry', + alertUnitsI: 'Cale', + alertUnitsC: 'Centymetry', + maintenanceMessageHeading: 'Niestandardowa wiadomość', + messageText: 'Treść wiadomości', + messageTextHint: 'Znaki drukowane na etykiecie ostrzegawczej.', + headColdWarning: 'Ostrzeżenie o zimnej głowicy', + headColdWarningOn: 'Włączone', + headColdWarningOff: 'Wyłączone', + earlyWarningMaintenance: 'Konserwacja z wczesnym ostrzeganiem', + earlyWarningE: 'Włączone', + earlyWarningD: 'Wyłączone', + headCleaningIntervalMeters: 'Interwał czyszczenia głowicy (m)', + headCleaningIntervalHint: 'Odległość, po której drukarka zgłasza głowicę do czyszczenia.', + earlyWarningGateHint: 'Główny przełącznik systemu alertów konserwacji; wymagany do uruchomienia ^MA.', }, }, diff --git a/src/locales/pt.ts b/src/locales/pt.ts index a521fcac..0524f83d 100644 --- a/src/locales/pt.ts +++ b/src/locales/pt.ts @@ -246,31 +246,31 @@ const pt = { configurationUpdateOptionF: 'F — Restaurar fábrica (destrutivo)', }, maintenance: { - maintenanceAlertHeading: 'Maintenance alert', - alertType: 'Alert type', - alertTypeH: 'Head cleaning', - alertTypeR: 'Head replacement', - alertPrint: 'Print warning label', - alertPrintY: 'Print', - alertPrintN: 'Skip', - alertThreshold: 'Threshold', - alertFrequency: 'Repeat frequency', - alertUnits: 'Units', - alertUnitsM: 'Meters', - alertUnitsI: 'Inches', - alertUnitsC: 'Centimeters', - maintenanceMessageHeading: 'Custom message', - messageText: 'Message text', - messageTextHint: 'Characters printed on the alert label.', - headColdWarning: 'Head cold warning', - headColdWarningOn: 'On', - headColdWarningOff: 'Off', - earlyWarningMaintenance: 'Early warning maintenance', - earlyWarningE: 'Enabled', - earlyWarningD: 'Disabled', - headCleaningIntervalMeters: 'Head cleaning interval (m)', - headCleaningIntervalHint: 'Distance after which the printer flags the head for cleaning.', - earlyWarningGateHint: 'Master gate for the maintenance alert system; required for ^MA to fire.', + maintenanceAlertHeading: 'Alerta de manutenção', + alertType: 'Tipo de alerta', + alertTypeC: 'Limpeza da cabeça', + alertTypeR: 'Substituição da cabeça', + alertPrint: 'Imprimir etiqueta de aviso', + alertPrintY: 'Imprimir', + alertPrintN: 'Ignorar', + alertThreshold: 'Limite', + alertFrequency: 'Frequência de repetição', + alertUnits: 'Unidades', + alertUnitsM: 'Metros', + alertUnitsI: 'Polegadas', + alertUnitsC: 'Centímetros', + maintenanceMessageHeading: 'Mensagem personalizada', + messageText: 'Texto da mensagem', + messageTextHint: 'Caracteres impressos na etiqueta de alerta.', + headColdWarning: 'Aviso de cabeça fria', + headColdWarningOn: 'Ativado', + headColdWarningOff: 'Desativado', + earlyWarningMaintenance: 'Manutenção com aviso antecipado', + earlyWarningE: 'Ativado', + earlyWarningD: 'Desativado', + headCleaningIntervalMeters: 'Intervalo de limpeza da cabeça (m)', + headCleaningIntervalHint: 'Distância após a qual a impressora marca a cabeça para limpeza.', + earlyWarningGateHint: 'Interruptor principal do sistema de alertas de manutenção; necessário para acionar ^MA.', }, }, diff --git a/src/locales/ro.ts b/src/locales/ro.ts index 72cff53d..d4d9762a 100644 --- a/src/locales/ro.ts +++ b/src/locales/ro.ts @@ -246,31 +246,31 @@ const ro = { configurationUpdateOptionF: 'F — Resetare din fabrică (distructiv)', }, maintenance: { - maintenanceAlertHeading: 'Maintenance alert', - alertType: 'Alert type', - alertTypeH: 'Head cleaning', - alertTypeR: 'Head replacement', - alertPrint: 'Print warning label', - alertPrintY: 'Print', - alertPrintN: 'Skip', - alertThreshold: 'Threshold', - alertFrequency: 'Repeat frequency', - alertUnits: 'Units', - alertUnitsM: 'Meters', - alertUnitsI: 'Inches', - alertUnitsC: 'Centimeters', - maintenanceMessageHeading: 'Custom message', - messageText: 'Message text', - messageTextHint: 'Characters printed on the alert label.', - headColdWarning: 'Head cold warning', - headColdWarningOn: 'On', - headColdWarningOff: 'Off', - earlyWarningMaintenance: 'Early warning maintenance', - earlyWarningE: 'Enabled', - earlyWarningD: 'Disabled', - headCleaningIntervalMeters: 'Head cleaning interval (m)', - headCleaningIntervalHint: 'Distance after which the printer flags the head for cleaning.', - earlyWarningGateHint: 'Master gate for the maintenance alert system; required for ^MA to fire.', + maintenanceAlertHeading: 'Alertă de întreținere', + alertType: 'Tip de alertă', + alertTypeC: 'Curățarea capului', + alertTypeR: 'Înlocuirea capului', + alertPrint: 'Imprimă eticheta de avertizare', + alertPrintY: 'Imprimă', + alertPrintN: 'Omite', + alertThreshold: 'Prag', + alertFrequency: 'Frecvență de repetare', + alertUnits: 'Unități', + alertUnitsM: 'Metri', + alertUnitsI: 'Țoli', + alertUnitsC: 'Centimetri', + maintenanceMessageHeading: 'Mesaj personalizat', + messageText: 'Textul mesajului', + messageTextHint: 'Caractere imprimate pe eticheta de alertă.', + headColdWarning: 'Avertizare cap rece', + headColdWarningOn: 'Activat', + headColdWarningOff: 'Dezactivat', + earlyWarningMaintenance: 'Întreținere cu avertizare timpurie', + earlyWarningE: 'Activat', + earlyWarningD: 'Dezactivat', + headCleaningIntervalMeters: 'Interval de curățare a capului (m)', + headCleaningIntervalHint: 'Distanța după care imprimanta marchează capul pentru curățare.', + earlyWarningGateHint: 'Comutator principal al sistemului de alerte de întreținere; necesar pentru declanșarea ^MA.', }, }, diff --git a/src/locales/sk.ts b/src/locales/sk.ts index 39739ce7..7a283b28 100644 --- a/src/locales/sk.ts +++ b/src/locales/sk.ts @@ -246,31 +246,31 @@ const sk = { configurationUpdateOptionF: 'F — Továrenský reset (deštruktívny)', }, maintenance: { - maintenanceAlertHeading: 'Maintenance alert', - alertType: 'Alert type', - alertTypeH: 'Head cleaning', - alertTypeR: 'Head replacement', - alertPrint: 'Print warning label', - alertPrintY: 'Print', - alertPrintN: 'Skip', - alertThreshold: 'Threshold', - alertFrequency: 'Repeat frequency', - alertUnits: 'Units', - alertUnitsM: 'Meters', - alertUnitsI: 'Inches', - alertUnitsC: 'Centimeters', - maintenanceMessageHeading: 'Custom message', - messageText: 'Message text', - messageTextHint: 'Characters printed on the alert label.', - headColdWarning: 'Head cold warning', - headColdWarningOn: 'On', - headColdWarningOff: 'Off', - earlyWarningMaintenance: 'Early warning maintenance', - earlyWarningE: 'Enabled', - earlyWarningD: 'Disabled', - headCleaningIntervalMeters: 'Head cleaning interval (m)', - headCleaningIntervalHint: 'Distance after which the printer flags the head for cleaning.', - earlyWarningGateHint: 'Master gate for the maintenance alert system; required for ^MA to fire.', + maintenanceAlertHeading: 'Upozornenie na údržbu', + alertType: 'Typ upozornenia', + alertTypeC: 'Čistenie hlavy', + alertTypeR: 'Výmena hlavy', + alertPrint: 'Tlačiť varovný štítok', + alertPrintY: 'Tlačiť', + alertPrintN: 'Preskočiť', + alertThreshold: 'Prah', + alertFrequency: 'Frekvencia opakovania', + alertUnits: 'Jednotky', + alertUnitsM: 'Metre', + alertUnitsI: 'Palce', + alertUnitsC: 'Centimetre', + maintenanceMessageHeading: 'Vlastná správa', + messageText: 'Text správy', + messageTextHint: 'Znaky vytlačené na štítku upozornenia.', + headColdWarning: 'Upozornenie na studenú hlavu', + headColdWarningOn: 'Zapnuté', + headColdWarningOff: 'Vypnuté', + earlyWarningMaintenance: 'Údržba s včasným varovaním', + earlyWarningE: 'Povolené', + earlyWarningD: 'Zakázané', + headCleaningIntervalMeters: 'Interval čistenia hlavy (m)', + headCleaningIntervalHint: 'Vzdialenosť, po ktorej tlačiareň označí hlavu na čistenie.', + earlyWarningGateHint: 'Hlavný spínač systému upozornení na údržbu; vyžaduje sa na spustenie ^MA.', }, }, diff --git a/src/locales/sl.ts b/src/locales/sl.ts index 94f9395b..8ba0c4ca 100644 --- a/src/locales/sl.ts +++ b/src/locales/sl.ts @@ -246,31 +246,31 @@ const sl = { configurationUpdateOptionF: 'F — Tovarniška ponastavitev (destruktivno)', }, maintenance: { - maintenanceAlertHeading: 'Maintenance alert', - alertType: 'Alert type', - alertTypeH: 'Head cleaning', - alertTypeR: 'Head replacement', - alertPrint: 'Print warning label', - alertPrintY: 'Print', - alertPrintN: 'Skip', - alertThreshold: 'Threshold', - alertFrequency: 'Repeat frequency', - alertUnits: 'Units', - alertUnitsM: 'Meters', - alertUnitsI: 'Inches', - alertUnitsC: 'Centimeters', - maintenanceMessageHeading: 'Custom message', - messageText: 'Message text', - messageTextHint: 'Characters printed on the alert label.', - headColdWarning: 'Head cold warning', - headColdWarningOn: 'On', - headColdWarningOff: 'Off', - earlyWarningMaintenance: 'Early warning maintenance', - earlyWarningE: 'Enabled', - earlyWarningD: 'Disabled', - headCleaningIntervalMeters: 'Head cleaning interval (m)', - headCleaningIntervalHint: 'Distance after which the printer flags the head for cleaning.', - earlyWarningGateHint: 'Master gate for the maintenance alert system; required for ^MA to fire.', + maintenanceAlertHeading: 'Opozorilo o vzdrževanju', + alertType: 'Vrsta opozorila', + alertTypeC: 'Čiščenje glave', + alertTypeR: 'Zamenjava glave', + alertPrint: 'Natisni opozorilno nalepko', + alertPrintY: 'Natisni', + alertPrintN: 'Preskoči', + alertThreshold: 'Prag', + alertFrequency: 'Pogostost ponavljanja', + alertUnits: 'Enote', + alertUnitsM: 'Metri', + alertUnitsI: 'Palci', + alertUnitsC: 'Centimetri', + maintenanceMessageHeading: 'Sporočilo po meri', + messageText: 'Besedilo sporočila', + messageTextHint: 'Znaki, natisnjeni na opozorilni nalepki.', + headColdWarning: 'Opozorilo o hladni glavi', + headColdWarningOn: 'Vklopljeno', + headColdWarningOff: 'Izklopljeno', + earlyWarningMaintenance: 'Vzdrževanje z zgodnjim opozorilom', + earlyWarningE: 'Omogočeno', + earlyWarningD: 'Onemogočeno', + headCleaningIntervalMeters: 'Interval čiščenja glave (m)', + headCleaningIntervalHint: 'Razdalja, po kateri tiskalnik označi glavo za čiščenje.', + earlyWarningGateHint: 'Glavno stikalo sistema opozoril za vzdrževanje; potrebno za sprožitev ^MA.', }, }, diff --git a/src/locales/sr.ts b/src/locales/sr.ts index d856ed74..44be5722 100644 --- a/src/locales/sr.ts +++ b/src/locales/sr.ts @@ -246,31 +246,31 @@ const sr = { configurationUpdateOptionF: 'F — Fabričko resetovanje (destruktivno)', }, maintenance: { - maintenanceAlertHeading: 'Maintenance alert', - alertType: 'Alert type', - alertTypeH: 'Head cleaning', - alertTypeR: 'Head replacement', - alertPrint: 'Print warning label', - alertPrintY: 'Print', - alertPrintN: 'Skip', - alertThreshold: 'Threshold', - alertFrequency: 'Repeat frequency', - alertUnits: 'Units', - alertUnitsM: 'Meters', - alertUnitsI: 'Inches', - alertUnitsC: 'Centimeters', - maintenanceMessageHeading: 'Custom message', - messageText: 'Message text', - messageTextHint: 'Characters printed on the alert label.', - headColdWarning: 'Head cold warning', - headColdWarningOn: 'On', - headColdWarningOff: 'Off', - earlyWarningMaintenance: 'Early warning maintenance', - earlyWarningE: 'Enabled', - earlyWarningD: 'Disabled', - headCleaningIntervalMeters: 'Head cleaning interval (m)', - headCleaningIntervalHint: 'Distance after which the printer flags the head for cleaning.', - earlyWarningGateHint: 'Master gate for the maintenance alert system; required for ^MA to fire.', + maintenanceAlertHeading: 'Upozorenje o održavanju', + alertType: 'Vrsta upozorenja', + alertTypeC: 'Čišćenje glave', + alertTypeR: 'Zamena glave', + alertPrint: 'Štampaj nalepnicu upozorenja', + alertPrintY: 'Štampaj', + alertPrintN: 'Preskoči', + alertThreshold: 'Prag', + alertFrequency: 'Učestalost ponavljanja', + alertUnits: 'Jedinice', + alertUnitsM: 'Metri', + alertUnitsI: 'Inči', + alertUnitsC: 'Centimetri', + maintenanceMessageHeading: 'Prilagođena poruka', + messageText: 'Tekst poruke', + messageTextHint: 'Znakovi odštampani na nalepnici upozorenja.', + headColdWarning: 'Upozorenje o hladnoj glavi', + headColdWarningOn: 'Uključeno', + headColdWarningOff: 'Isključeno', + earlyWarningMaintenance: 'Održavanje sa ranim upozorenjem', + earlyWarningE: 'Omogućeno', + earlyWarningD: 'Onemogućeno', + headCleaningIntervalMeters: 'Interval čišćenja glave (m)', + headCleaningIntervalHint: 'Rastojanje nakon kojeg štampač označava glavu za čišćenje.', + earlyWarningGateHint: 'Glavni prekidač sistema upozorenja o održavanju; neophodan za aktivaciju ^MA.', }, }, diff --git a/src/locales/sv.ts b/src/locales/sv.ts index c3dc070a..9bac9114 100644 --- a/src/locales/sv.ts +++ b/src/locales/sv.ts @@ -246,31 +246,31 @@ const sv = { configurationUpdateOptionF: 'F — Återställ till fabrik (destruktiv)', }, maintenance: { - maintenanceAlertHeading: 'Maintenance alert', - alertType: 'Alert type', - alertTypeH: 'Head cleaning', - alertTypeR: 'Head replacement', - alertPrint: 'Print warning label', - alertPrintY: 'Print', - alertPrintN: 'Skip', - alertThreshold: 'Threshold', - alertFrequency: 'Repeat frequency', - alertUnits: 'Units', - alertUnitsM: 'Meters', - alertUnitsI: 'Inches', - alertUnitsC: 'Centimeters', - maintenanceMessageHeading: 'Custom message', - messageText: 'Message text', - messageTextHint: 'Characters printed on the alert label.', - headColdWarning: 'Head cold warning', - headColdWarningOn: 'On', - headColdWarningOff: 'Off', - earlyWarningMaintenance: 'Early warning maintenance', - earlyWarningE: 'Enabled', - earlyWarningD: 'Disabled', - headCleaningIntervalMeters: 'Head cleaning interval (m)', - headCleaningIntervalHint: 'Distance after which the printer flags the head for cleaning.', - earlyWarningGateHint: 'Master gate for the maintenance alert system; required for ^MA to fire.', + maintenanceAlertHeading: 'Underhållsvarning', + alertType: 'Varningstyp', + alertTypeC: 'Huvudrengöring', + alertTypeR: 'Huvudbyte', + alertPrint: 'Skriv ut varningsetikett', + alertPrintY: 'Skriv ut', + alertPrintN: 'Hoppa över', + alertThreshold: 'Tröskel', + alertFrequency: 'Upprepningsfrekvens', + alertUnits: 'Enheter', + alertUnitsM: 'Meter', + alertUnitsI: 'Tum', + alertUnitsC: 'Centimeter', + maintenanceMessageHeading: 'Anpassat meddelande', + messageText: 'Meddelandetext', + messageTextHint: 'Tecken som skrivs ut på varningsetiketten.', + headColdWarning: 'Varning för kallt huvud', + headColdWarningOn: 'På', + headColdWarningOff: 'Av', + earlyWarningMaintenance: 'Underhåll med tidig varning', + earlyWarningE: 'Aktiverad', + earlyWarningD: 'Inaktiverad', + headCleaningIntervalMeters: 'Huvudrengöringsintervall (m)', + headCleaningIntervalHint: 'Avstånd efter vilket skrivaren markerar huvudet för rengöring.', + earlyWarningGateHint: 'Huvudbrytare för underhållsvarningssystemet; krävs för att ^MA ska utlösas.', }, }, diff --git a/src/locales/tr.ts b/src/locales/tr.ts index e93dcef6..9ebf1bcb 100644 --- a/src/locales/tr.ts +++ b/src/locales/tr.ts @@ -246,31 +246,31 @@ const tr = { configurationUpdateOptionF: 'F — Fabrika ayarlarına sıfırla (yıkıcı)', }, maintenance: { - maintenanceAlertHeading: 'Maintenance alert', - alertType: 'Alert type', - alertTypeH: 'Head cleaning', - alertTypeR: 'Head replacement', - alertPrint: 'Print warning label', - alertPrintY: 'Print', - alertPrintN: 'Skip', - alertThreshold: 'Threshold', - alertFrequency: 'Repeat frequency', - alertUnits: 'Units', - alertUnitsM: 'Meters', - alertUnitsI: 'Inches', - alertUnitsC: 'Centimeters', - maintenanceMessageHeading: 'Custom message', - messageText: 'Message text', - messageTextHint: 'Characters printed on the alert label.', - headColdWarning: 'Head cold warning', - headColdWarningOn: 'On', - headColdWarningOff: 'Off', - earlyWarningMaintenance: 'Early warning maintenance', - earlyWarningE: 'Enabled', - earlyWarningD: 'Disabled', - headCleaningIntervalMeters: 'Head cleaning interval (m)', - headCleaningIntervalHint: 'Distance after which the printer flags the head for cleaning.', - earlyWarningGateHint: 'Master gate for the maintenance alert system; required for ^MA to fire.', + maintenanceAlertHeading: 'Bakım uyarısı', + alertType: 'Uyarı türü', + alertTypeC: 'Kafa temizleme', + alertTypeR: 'Kafa değişimi', + alertPrint: 'Uyarı etiketini yazdır', + alertPrintY: 'Yazdır', + alertPrintN: 'Atla', + alertThreshold: 'Eşik', + alertFrequency: 'Tekrar sıklığı', + alertUnits: 'Birimler', + alertUnitsM: 'Metre', + alertUnitsI: 'İnç', + alertUnitsC: 'Santimetre', + maintenanceMessageHeading: 'Özel mesaj', + messageText: 'Mesaj metni', + messageTextHint: 'Uyarı etiketinde yazdırılan karakterler.', + headColdWarning: 'Soğuk kafa uyarısı', + headColdWarningOn: 'Açık', + headColdWarningOff: 'Kapalı', + earlyWarningMaintenance: 'Erken uyarı bakımı', + earlyWarningE: 'Etkin', + earlyWarningD: 'Devre dışı', + headCleaningIntervalMeters: 'Kafa temizleme aralığı (m)', + headCleaningIntervalHint: 'Yazıcının kafayı temizleme için işaretlediği mesafe.', + earlyWarningGateHint: "Bakım uyarı sisteminin ana anahtarı; ^MA'nın tetiklenmesi için gereklidir.", }, }, diff --git a/src/locales/zh-hans.ts b/src/locales/zh-hans.ts index 50b57627..fb0a8290 100644 --- a/src/locales/zh-hans.ts +++ b/src/locales/zh-hans.ts @@ -246,31 +246,31 @@ const zhHans = { configurationUpdateOptionF: 'F — 恢复出厂设置 (破坏性)', }, maintenance: { - maintenanceAlertHeading: 'Maintenance alert', - alertType: 'Alert type', - alertTypeH: 'Head cleaning', - alertTypeR: 'Head replacement', - alertPrint: 'Print warning label', - alertPrintY: 'Print', - alertPrintN: 'Skip', - alertThreshold: 'Threshold', - alertFrequency: 'Repeat frequency', - alertUnits: 'Units', - alertUnitsM: 'Meters', - alertUnitsI: 'Inches', - alertUnitsC: 'Centimeters', - maintenanceMessageHeading: 'Custom message', - messageText: 'Message text', - messageTextHint: 'Characters printed on the alert label.', - headColdWarning: 'Head cold warning', - headColdWarningOn: 'On', - headColdWarningOff: 'Off', - earlyWarningMaintenance: 'Early warning maintenance', - earlyWarningE: 'Enabled', - earlyWarningD: 'Disabled', - headCleaningIntervalMeters: 'Head cleaning interval (m)', - headCleaningIntervalHint: 'Distance after which the printer flags the head for cleaning.', - earlyWarningGateHint: 'Master gate for the maintenance alert system; required for ^MA to fire.', + maintenanceAlertHeading: '维护警报', + alertType: '警报类型', + alertTypeC: '打印头清洁', + alertTypeR: '打印头更换', + alertPrint: '打印警告标签', + alertPrintY: '打印', + alertPrintN: '跳过', + alertThreshold: '阈值', + alertFrequency: '重复频率', + alertUnits: '单位', + alertUnitsM: '米', + alertUnitsI: '英寸', + alertUnitsC: '厘米', + maintenanceMessageHeading: '自定义消息', + messageText: '消息文本', + messageTextHint: '打印在警报标签上的字符。', + headColdWarning: '打印头低温警告', + headColdWarningOn: '开启', + headColdWarningOff: '关闭', + earlyWarningMaintenance: '早期预警维护', + earlyWarningE: '已启用', + earlyWarningD: '已禁用', + headCleaningIntervalMeters: '打印头清洁间隔 (米)', + headCleaningIntervalHint: '打印机标记打印头需要清洁前的距离。', + earlyWarningGateHint: '维护警报系统的主开关;^MA 触发的必要条件。', }, }, diff --git a/src/locales/zh-hant.ts b/src/locales/zh-hant.ts index e3aff09b..8547a322 100644 --- a/src/locales/zh-hant.ts +++ b/src/locales/zh-hant.ts @@ -246,31 +246,31 @@ const zhHant = { configurationUpdateOptionF: 'F — 還原出廠設定 (破壞性)', }, maintenance: { - maintenanceAlertHeading: 'Maintenance alert', - alertType: 'Alert type', - alertTypeH: 'Head cleaning', - alertTypeR: 'Head replacement', - alertPrint: 'Print warning label', - alertPrintY: 'Print', - alertPrintN: 'Skip', - alertThreshold: 'Threshold', - alertFrequency: 'Repeat frequency', - alertUnits: 'Units', - alertUnitsM: 'Meters', - alertUnitsI: 'Inches', - alertUnitsC: 'Centimeters', - maintenanceMessageHeading: 'Custom message', - messageText: 'Message text', - messageTextHint: 'Characters printed on the alert label.', - headColdWarning: 'Head cold warning', - headColdWarningOn: 'On', - headColdWarningOff: 'Off', - earlyWarningMaintenance: 'Early warning maintenance', - earlyWarningE: 'Enabled', - earlyWarningD: 'Disabled', - headCleaningIntervalMeters: 'Head cleaning interval (m)', - headCleaningIntervalHint: 'Distance after which the printer flags the head for cleaning.', - earlyWarningGateHint: 'Master gate for the maintenance alert system; required for ^MA to fire.', + maintenanceAlertHeading: '維護警報', + alertType: '警報類型', + alertTypeC: '列印頭清潔', + alertTypeR: '列印頭更換', + alertPrint: '列印警告標籤', + alertPrintY: '列印', + alertPrintN: '略過', + alertThreshold: '閾值', + alertFrequency: '重複頻率', + alertUnits: '單位', + alertUnitsM: '公尺', + alertUnitsI: '英吋', + alertUnitsC: '公分', + maintenanceMessageHeading: '自訂訊息', + messageText: '訊息文字', + messageTextHint: '列印在警報標籤上的字元。', + headColdWarning: '列印頭低溫警告', + headColdWarningOn: '開啟', + headColdWarningOff: '關閉', + earlyWarningMaintenance: '早期預警維護', + earlyWarningE: '已啟用', + earlyWarningD: '已停用', + headCleaningIntervalMeters: '列印頭清潔間隔 (公尺)', + headCleaningIntervalHint: '印表機標記列印頭需要清潔前的距離。', + earlyWarningGateHint: '維護警報系統的主開關;^MA 觸發的必要條件。', }, }, diff --git a/src/store/labelStore.test.ts b/src/store/labelStore.test.ts index 0bcad256..c8b9f639 100644 --- a/src/store/labelStore.test.ts +++ b/src/store/labelStore.test.ts @@ -1165,7 +1165,7 @@ describe('migrateLegacy — v4→v5 printerProfile extraction', () => { reprintAfterError: 'Y', headTestInterval: 250, tearOffAdjust: 15, - clockFormat: 'd1', + clockFormat: '1', printerName: 'lab-zpl-01', }, pages: [{ objects: [] }], @@ -1178,7 +1178,7 @@ describe('migrateLegacy — v4→v5 printerProfile extraction', () => { reprintAfterError: 'Y', headTestInterval: 250, tearOffAdjust: 15, - clockFormat: 'd1', + clockFormat: '1', printerName: 'lab-zpl-01', }); }); @@ -1205,6 +1205,44 @@ describe('migrateLegacy — v4→v5 printerProfile extraction', () => { }; expect(migrated.printerProfile).toEqual({}); }); + + it('drops the schema-rejected sub-field on rehydrate', () => { + // Legacy persist that survived from before the cross-field + // superRefine landed. The repair walks Zod's issue paths: the + // cross-field rule flags the message side, so the alert stays + // and the (stale) message is dropped instead of silently + // rewriting on the next edit. + const persisted = { + label: { widthMm: 100, heightMm: 50, dpmm: 8 }, + pages: [{ objects: [] }], + printerProfile: { + maintenanceAlert: { type: 'H', print: 'Y', threshold: 5, frequency: 1, units: 'M' }, + maintenanceMessage: { type: 'R', text: 'Replace head' }, + }, + }; + const migrated = migrateLegacy(persisted, 6) as typeof persisted & { + printerProfile: Record; + }; + expect(migrated.printerProfile.maintenanceAlert).toEqual({ + type: 'C', print: 'Y', threshold: 5, frequency: 1, units: 'M', + }); + expect(migrated.printerProfile.maintenanceMessage).toBeUndefined(); + }); + + it('drops clockMode "TOL" when clockTolerance is missing on rehydrate', () => { + // superRefine reports path ['clockTolerance'], which is already + // absent — a single-pass delete would be a no-op and leave the + // invariant violation in place. The fixpoint loop catches this. + const persisted = { + label: { widthMm: 100, heightMm: 50, dpmm: 8 }, + pages: [{ objects: [] }], + printerProfile: { clockMode: 'TOL' }, + }; + const migrated = migrateLegacy(persisted, 6) as typeof persisted & { + printerProfile: Record; + }; + expect(migrated.printerProfile.clockMode).toBeUndefined(); + }); }); describe('variables', () => { diff --git a/src/store/labelStore.ts b/src/store/labelStore.ts index 442bb56f..f73d3559 100644 --- a/src/store/labelStore.ts +++ b/src/store/labelStore.ts @@ -2,7 +2,7 @@ import { create, useStore } from 'zustand'; import { temporal } from 'zundo'; import { persist, createJSONStorage } from 'zustand/middleware'; import type { ObjectChanges } from '../types/LabelObject'; -import { PRINTER_PROFILE_FIELDS } from '../types/PrinterProfile'; +import { PRINTER_PROFILE_FIELDS, printerProfileSchema } from '../types/PrinterProfile'; import { visitLeavesInPages } from '../lib/objectTree'; import type { LabelObject } from '../types/Group'; import { @@ -111,9 +111,60 @@ export function migrateLegacy(persistedState: unknown, version: number): unknown s = { ...s, pages: migrateGs1DatabarInPages(s.pages) }; } + // v6→v7: ^MA/^MI type codes corrected to match spec (R/C instead of + // H/R). The legacy 'H' meant "head cleaning"; the spec letter is 'C'. + // 'R' (replacement) was already correct. + if (version < 7) { + const pp = (s as Record).printerProfile; + if (pp && typeof pp === 'object') { + s = { ...s, printerProfile: migrateMaintenanceTypeCodes(pp as Record) }; + } + } + + // Re-validate the rehydrated profile so a legacy snapshot that + // violates the schema or a cross-field rule can't crash the slice's + // safeParse on the next patch. Cross-field issues report a path + // that may already be absent (clockMode='TOL' without + // clockTolerance: path is ['clockTolerance']), so a single delete + // pass is not enough. Fixpoint-loop until stable. + const profile = (s as Record).printerProfile; + if (profile && typeof profile === 'object') { + let next = { ...(profile as Record) }; + for (let i = 0; i < 8; i++) { + const validation = printerProfileSchema.safeParse(next); + if (validation.success) break; + const drop = new Set(); + for (const issue of validation.error.issues) { + const topKey = issue.path[0]; + if (typeof topKey === 'string' && topKey in next) drop.add(topKey); + } + // No present top-key to drop means the residual violation isn't + // something the loop can resolve; bail to {} rather than spin. + if (drop.size === 0) { + next = {}; + break; + } + next = Object.fromEntries(Object.entries(next).filter(([k]) => !drop.has(k))); + } + s = { ...s, printerProfile: next }; + } + return s; } +function migrateMaintenanceTypeCodes(pp: Record): Record { + const out = { ...pp }; + const remap = (obj: unknown): unknown => { + if (!obj || typeof obj !== 'object') return obj; + const o = obj as Record; + if (o.type === 'H') return { ...o, type: 'C' }; + return obj; + }; + if (out.maintenanceAlert) out.maintenanceAlert = remap(out.maintenanceAlert); + if (out.maintenanceMessage) out.maintenanceMessage = remap(out.maintenanceMessage); + return out; +} + function migrateGs1DatabarInPages(pages: unknown): unknown { // Mutates in place via the shared leaf walker; pages is the persisted // payload not the live store, so mutation is safe. @@ -205,7 +256,7 @@ export const useLabelStore = create()( }), { name: 'zpl-designer-session', - version: 6, + version: 7, migrate: (persistedState, version) => migrateLegacy(persistedState, version) as LabelState, storage: createJSONStorage(() => localStorage), partialize: persistPartialize, diff --git a/src/types/PrinterProfile.ts b/src/types/PrinterProfile.ts index 8b86b3bc..8931ba04 100644 --- a/src/types/PrinterProfile.ts +++ b/src/types/PrinterProfile.ts @@ -82,9 +82,8 @@ export const CONFIG_UPDATE_VALUES = ['S', 'R', 'N', 'F'] as const; export type ConfigUpdateAction = (typeof CONFIG_UPDATE_VALUES)[number]; export const isConfigUpdateAction = makeEnumGuard(CONFIG_UPDATE_VALUES); -/** ^MA/^MI alert type. Shared discriminator so one field couples - * alert and message at the wire level. */ -export const MAINTENANCE_ALERT_TYPES = ['H', 'R'] as const; +/** ^MA/^MI alert type. R=head replacement, C=head cleaning per spec. */ +export const MAINTENANCE_ALERT_TYPES = ['R', 'C'] as const; export type MaintenanceAlertType = (typeof MAINTENANCE_ALERT_TYPES)[number]; export const isMaintenanceAlertType = makeEnumGuard(MAINTENANCE_ALERT_TYPES); @@ -93,33 +92,45 @@ export const MAINTENANCE_ALERT_PRINT_VALUES = ['Y', 'N'] as const; export type MaintenanceAlertPrint = (typeof MAINTENANCE_ALERT_PRINT_VALUES)[number]; export const isMaintenanceAlertPrint = makeEnumGuard(MAINTENANCE_ALERT_PRINT_VALUES); -/** ^MA units slot: M=meters, I=inches, C=centimeters. */ -export const MAINTENANCE_ALERT_UNITS = ['M', 'I', 'C'] as const; +/** ^MA units slot: C=centimeters, I=inches, M=meters. */ +export const MAINTENANCE_ALERT_UNITS = ['C', 'I', 'M'] as const; export type MaintenanceAlertUnit = (typeof MAINTENANCE_ALERT_UNITS)[number]; export const isMaintenanceAlertUnit = makeEnumGuard(MAINTENANCE_ALERT_UNITS); -/** ^MA threshold/frequency cap. Spec is implementation-defined; - * 99999 covers typical ~600m head life. */ -export const MAINTENANCE_DISTANCE_MAX = 99999; +/** ^MA threshold/frequency caps. Type-dependent per spec: + * R (replacement) = 0-150000 m (150 km printhead life), + * C (cleaning) = 0-2000 m. Schema's outer .max uses the larger; + * the per-type cap is enforced in superRefine. */ +export const MAINTENANCE_DISTANCE_MAX_BY_TYPE = { R: 150000, C: 2000 } as const; /** ^MI message length cap from Zebra Quick Reference. */ export const MAINTENANCE_MESSAGE_MAX_LEN = 63; -/** ^JH g-slot (head cleaning interval) range in meters. Min is 1 - * so the unset/disabled state is `undefined` rather than ambiguous - * `0M`. Max 900 keeps PAX4 compatible. - * TODO HW-verify: Zebra docs are ambiguous whether the wire format - * is raw `M` or an indexed table (0..16 mapping to 100M..900M). - * Current implementation uses `M`; verify on real hardware. */ -export const HEAD_CLEANING_INTERVAL_RANGE = { min: 1, max: 900 } as const; + +/** ^JH g-slot head-cleaning interval: indexed table 0..16 mapping to + * meters in steps of 50 starting at 100M. Wire value is the index. */ +export const HEAD_CLEANING_INTERVAL_METERS = [ + 100, 150, 200, 250, 300, 350, 400, 450, + 500, 550, 600, 650, 700, 750, 800, 850, 900, +] as const; +export type HeadCleaningIntervalMeters = (typeof HEAD_CLEANING_INTERVAL_METERS)[number]; +export const isHeadCleaningIntervalMeters = (n: number): n is HeadCleaningIntervalMeters => + (HEAD_CLEANING_INTERVAL_METERS as readonly number[]).includes(n); + +/** ^JH positional slot count and indices: f=early-warning master, + * g=head-cleaning interval. The other 8 slots are runtime reset + * flags we don't model. */ +export const JH_SLOT_COUNT = 10; +export const JH_SLOT_F = 5; +export const JH_SLOT_G = 6; /** Default seed for a freshly enabled ^MA. Matches the spec example - * `^MAH,Y,5,1,M`. Spread inline so UI placeholder display and - * parser blank-slot fallback stay in sync. */ + * `^MAR,Y,5,1,I`. Spread inline so UI placeholder display and parser + * blank-slot fallback stay in sync. */ export const MAINTENANCE_ALERT_DEFAULTS = { - type: 'H', + type: 'R', print: 'Y', threshold: 5, frequency: 1, - units: 'M', + units: 'I', } as const; /** Printer-installation profile: EEPROM-persistent printer-state @@ -145,12 +156,13 @@ export const printerProfileSchema = z.object({ printerDescription: z.string().min(1).regex(setupScriptSafeStringRegex).optional(), setPassword: z.string().regex(PRINTER_PASSWORD_REGEX).optional(), /** ^MA alert. Requires earlyWarningMaintenance==='E' to actually - * fire; the two are stored independently. */ + * fire; the two are stored independently. The type-specific + * threshold/frequency caps are enforced in superRefine. */ maintenanceAlert: z.object({ type: z.enum(MAINTENANCE_ALERT_TYPES), print: z.enum(['Y', 'N']), - threshold: z.number().int().min(0).max(MAINTENANCE_DISTANCE_MAX), - frequency: z.number().int().min(1).max(MAINTENANCE_DISTANCE_MAX), + threshold: z.number().int().min(0).max(MAINTENANCE_DISTANCE_MAX_BY_TYPE.R), + frequency: z.number().int().min(0).max(MAINTENANCE_DISTANCE_MAX_BY_TYPE.R), units: z.enum(MAINTENANCE_ALERT_UNITS), }).optional(), /** ^MI custom message printed when the matching `^MA{type}` fires. */ @@ -164,9 +176,12 @@ export const printerProfileSchema = z.object({ * the ^MA alert system on. Without `E` the maintenanceAlert * config sits dormant on the printer. */ earlyWarningMaintenance: z.enum(['E', 'D']).optional(), - /** ^JH g-slot head-cleaning interval, stored as a meter count. - * Generator emits `M`; spec accepts an `M` suffix only. */ - headCleaningIntervalMeters: intInRange(HEAD_CLEANING_INTERVAL_RANGE).optional(), + /** ^JH g-slot head-cleaning interval. Stored as meters for UX; the + * wire format is an index into HEAD_CLEANING_INTERVAL_METERS. */ + headCleaningIntervalMeters: z.number().int().refine( + isHeadCleaningIntervalMeters, + `must be one of ${HEAD_CLEANING_INTERVAL_METERS.join(', ')}`, + ).optional(), /** ^JU action. Generator emits the value as `^JU{action}` last in * the setup-script block so an `S` commit happens after every * other persistent write in the same script. */ @@ -197,25 +212,30 @@ export const printerProfileSchema = z.object({ message: 'maintenanceMessage.type must match maintenanceAlert.type', }); } + if (p.maintenanceAlert) { + const cap = MAINTENANCE_DISTANCE_MAX_BY_TYPE[p.maintenanceAlert.type]; + if (p.maintenanceAlert.threshold > cap) { + ctx.addIssue({ + code: 'custom', + path: ['maintenanceAlert', 'threshold'], + message: `threshold for type ${p.maintenanceAlert.type} must be ≤ ${cap}`, + }); + } + if (p.maintenanceAlert.frequency > cap) { + ctx.addIssue({ + code: 'custom', + path: ['maintenanceAlert', 'frequency'], + message: `frequency for type ${p.maintenanceAlert.type} must be ≤ ${cap}`, + }); + } + } }); export type PrinterProfile = z.infer; -/** Repair the cross-field ^MA/^MI type invariant after a partial patch. - * Direction follows which side the patch object touches: a patch with - * a `maintenanceAlert.type` cascades into message, and vice versa. - * When both sides are touched, no cascade runs and the schema's - * superRefine catches a mismatch. - * - * Caveat: UI callers spread the whole alert (`{ ...alert, [key]: v }`) - * so `patch.maintenanceAlert.type` is "touched" even for non-type - * edits. The cascade then runs but is a no-op as long as the - * pre-patch invariant held (which the slice gates). If the store is - * ever seeded with a mismatched pair, the next single-side sub-field - * edit silently rewrites the unrelated side toward the patched one - * instead of surfacing the seed bug. - * - * Pure function; safe to call before `safeParse` at any boundary. */ +/** Cascade direction follows whichever side the patch touches: + * alert-touched rewrites message.type, message-touched rewrites + * alert.type. Both-touched leaves the mismatch to superRefine. */ export function normalizeMaintenanceTypes( merged: PrinterProfile, patch: Partial,