From 00c854296c557f14db6a0b966ded1b5ffd48bb7f Mon Sep 17 00:00:00 2001 From: u8array Date: Thu, 7 May 2026 17:14:49 +0200 Subject: [PATCH 01/10] feat(label): add optional printer params to labelConfig schema Adds printSpeed, darkness, mediaType, printOrientation, defaultFont as optional fields. All fields are .optional() so existing persisted designs load unchanged. --- src/types/ObjectType.ts | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/types/ObjectType.ts b/src/types/ObjectType.ts index 6bbec1c3..2efb4a73 100644 --- a/src/types/ObjectType.ts +++ b/src/types/ObjectType.ts @@ -8,6 +8,16 @@ export const labelConfigSchema = z.object({ printQuantity: z.number().optional(), mediaMode: z.enum(['T', 'V', 'D', 'K']).optional(), labelShift: z.number().optional(), + printSpeed: z.number().int().min(2).max(14).optional(), + darkness: z.number().int().min(-30).max(30).optional(), + mediaType: z.enum(['T', 'D']).optional(), + printOrientation: z.enum(['N', 'I']).optional(), + defaultFont: z + .object({ + fontId: z.string().min(1), + height: z.number().int().positive(), + }) + .optional(), }); export type LabelConfig = z.infer; From bbe658e647cbf76f553f0777ffd50ad72ab04ba7 Mon Sep 17 00:00:00 2001 From: u8array Date: Thu, 7 May 2026 17:15:42 +0200 Subject: [PATCH 02/10] feat(zpl): emit printer params in generated header Adds emit logic for printSpeed (^PR), darkness (^MD), mediaType (^MT), printOrientation (^PO) and defaultFont (^CF). Order follows ZPL II header convention; emits only when value is set, with darkness=0 treated as a valid value rather than absence. --- src/lib/zplGenerator.test.ts | 66 ++++++++++++++++++++++++++++++++++++ src/lib/zplGenerator.ts | 8 +++++ 2 files changed, 74 insertions(+) diff --git a/src/lib/zplGenerator.test.ts b/src/lib/zplGenerator.test.ts index 9a703355..629630f9 100644 --- a/src/lib/zplGenerator.test.ts +++ b/src/lib/zplGenerator.test.ts @@ -54,6 +54,72 @@ describe('generateZPL — structure', () => { }); }); +describe('generateZPL — printer params', () => { + it('emits ^PR when printSpeed is set', () => { + const zpl = generateZPL({ ...BASE_LABEL, printSpeed: 6 }, []); + expect(zpl).toContain('^PR6'); + }); + + it('omits ^PR when printSpeed is absent', () => { + expect(generateZPL(BASE_LABEL, [])).not.toContain('^PR'); + }); + + it('emits ^MD for darkness boundaries including 0', () => { + expect(generateZPL({ ...BASE_LABEL, darkness: 0 }, [])).toContain('^MD0'); + expect(generateZPL({ ...BASE_LABEL, darkness: 30 }, [])).toContain('^MD30'); + expect(generateZPL({ ...BASE_LABEL, darkness: -30 }, [])).toContain('^MD-30'); + }); + + it('omits ^MD when darkness is absent', () => { + expect(generateZPL(BASE_LABEL, [])).not.toContain('^MD'); + }); + + it('emits ^MT for thermal transfer and direct thermal', () => { + expect(generateZPL({ ...BASE_LABEL, mediaType: 'T' }, [])).toContain('^MTT'); + expect(generateZPL({ ...BASE_LABEL, mediaType: 'D' }, [])).toContain('^MTD'); + }); + + it('emits ^PO for both orientations when explicitly set', () => { + expect(generateZPL({ ...BASE_LABEL, printOrientation: 'I' }, [])).toContain('^POI'); + expect(generateZPL({ ...BASE_LABEL, printOrientation: 'N' }, [])).toContain('^PON'); + }); + + it('omits ^PO when print orientation is absent', () => { + expect(generateZPL(BASE_LABEL, [])).not.toContain('^PO'); + }); + + it('emits ^CF when defaultFont is set', () => { + const zpl = generateZPL( + { ...BASE_LABEL, defaultFont: { fontId: '0', height: 30 } }, + [], + ); + expect(zpl).toContain('^CF0,30'); + }); + + it('emits printer params in canonical header order before ^LS', () => { + const zpl = generateZPL( + { + ...BASE_LABEL, + mediaMode: 'T', + mediaType: 'T', + printSpeed: 6, + darkness: 10, + printOrientation: 'I', + defaultFont: { fontId: '0', height: 30 }, + labelShift: 5, + }, + [], + ); + const idx = (cmd: string) => zpl.indexOf(cmd); + expect(idx('^MMT')).toBeLessThan(idx('^MTT')); + expect(idx('^MTT')).toBeLessThan(idx('^PR6')); + expect(idx('^PR6')).toBeLessThan(idx('^MD10')); + expect(idx('^MD10')).toBeLessThan(idx('^POI')); + expect(idx('^POI')).toBeLessThan(idx('^CF0,30')); + expect(idx('^CF0,30')).toBeLessThan(idx('^LS5')); + }); +}); + describe('generateZPL — text object', () => { it('emits ^FO, ^A0 and ^FD for a text object', () => { const { objects } = parseZPL('^XA^FO10,20^A0N,30,0^FDHello^FS^XZ', 8); diff --git a/src/lib/zplGenerator.ts b/src/lib/zplGenerator.ts index 1018038b..7d7b8e0f 100644 --- a/src/lib/zplGenerator.ts +++ b/src/lib/zplGenerator.ts @@ -25,6 +25,14 @@ export function generateZPL(label: LabelConfig, objects: LabelObject[]): string ]; if (label.mediaMode) lines.push(`^MM${label.mediaMode}`); + if (label.mediaType) lines.push(`^MT${label.mediaType}`); + if (label.printSpeed !== undefined) lines.push(`^PR${label.printSpeed}`); + // darkness=0 is a valid value (printer baseline), so check undefined explicitly. + if (label.darkness !== undefined) lines.push(`^MD${label.darkness}`); + if (label.printOrientation) lines.push(`^PO${label.printOrientation}`); + if (label.defaultFont) { + lines.push(`^CF${label.defaultFont.fontId},${label.defaultFont.height}`); + } if (label.labelShift) lines.push(`^LS${label.labelShift}`); lines.push(...objects.map((obj) => { From 140192d776272534eec69be9ce844669e67c73be Mon Sep 17 00:00:00 2001 From: u8array Date: Thu, 7 May 2026 17:18:51 +0200 Subject: [PATCH 03/10] feat(zpl): parse printer params and round-trip them Parser now recognises ^PR (print speed), ^MD (darkness), ^MT (media type), ^PO (print orientation) and extends ^CF to populate labelConfig.defaultFont. Removes ^PR/^MT from the noop list. Tests cover each command including the darkness=0 boundary and a full generate-parse round-trip. --- src/lib/zplGenerator.test.ts | 18 +++++++++++++++++ src/lib/zplParser.test.ts | 38 +++++++++++++++++++++++++++++++++++ src/lib/zplParser.ts | 27 +++++++++++++++++++++++-- src/test/zplRoundtrip.test.ts | 5 ++--- 4 files changed, 83 insertions(+), 5 deletions(-) diff --git a/src/lib/zplGenerator.test.ts b/src/lib/zplGenerator.test.ts index 629630f9..47501bc8 100644 --- a/src/lib/zplGenerator.test.ts +++ b/src/lib/zplGenerator.test.ts @@ -207,4 +207,22 @@ describe('generateZPL — parse/generate roundtrip', () => { expect(props(barcode).content).toBe('987654'); expect(props(barcode).height).toBe(150); }); + + it('preserves printer params through generate -> parse', () => { + const label: LabelConfig = { + ...BASE_LABEL, + printSpeed: 8, + darkness: 0, + mediaType: 'D', + printOrientation: 'I', + defaultFont: { fontId: '0', height: 30 }, + }; + const regenerated = generateZPL(label, []); + const { labelConfig } = parseZPL(regenerated, BASE_LABEL.dpmm); + expect(labelConfig.printSpeed).toBe(8); + expect(labelConfig.darkness).toBe(0); + expect(labelConfig.mediaType).toBe('D'); + expect(labelConfig.printOrientation).toBe('I'); + expect(labelConfig.defaultFont).toEqual({ fontId: '0', height: 30 }); + }); }); diff --git a/src/lib/zplParser.test.ts b/src/lib/zplParser.test.ts index 3bd09f60..bd11cfa4 100644 --- a/src/lib/zplParser.test.ts +++ b/src/lib/zplParser.test.ts @@ -514,6 +514,44 @@ describe('parseZPL — ^MM and ^LS', () => { }); }); +describe('parseZPL — printer params', () => { + it('parses ^PR print speed within range', () => { + const { labelConfig } = parseZPL('^XA^PR6^XZ', 8); + expect(labelConfig.printSpeed).toBe(6); + }); + + it('ignores ^PR with out-of-range value', () => { + const { labelConfig } = parseZPL('^XA^PR1^XZ', 8); + expect(labelConfig.printSpeed).toBeUndefined(); + }); + + it('parses ^MD darkness including 0', () => { + expect(parseZPL('^XA^MD0^XZ', 8).labelConfig.darkness).toBe(0); + expect(parseZPL('^XA^MD15^XZ', 8).labelConfig.darkness).toBe(15); + expect(parseZPL('^XA^MD-10^XZ', 8).labelConfig.darkness).toBe(-10); + }); + + it('ignores ^MD outside the supported range', () => { + const { labelConfig } = parseZPL('^XA^MD99^XZ', 8); + expect(labelConfig.darkness).toBeUndefined(); + }); + + it('parses ^MT media type', () => { + expect(parseZPL('^XA^MTT^XZ', 8).labelConfig.mediaType).toBe('T'); + expect(parseZPL('^XA^MTD^XZ', 8).labelConfig.mediaType).toBe('D'); + }); + + it('parses ^PO print orientation', () => { + expect(parseZPL('^XA^PON^XZ', 8).labelConfig.printOrientation).toBe('N'); + expect(parseZPL('^XA^POI^XZ', 8).labelConfig.printOrientation).toBe('I'); + }); + + it('parses ^CF default font with id and height', () => { + const { labelConfig } = parseZPL('^XA^CF0,40^XZ', 8); + expect(labelConfig.defaultFont).toEqual({ fontId: '0', height: 40 }); + }); +}); + // ── edge cases ──────────────────────────────────────────────────────────────── describe('parseZPL — edge cases', () => { diff --git a/src/lib/zplParser.ts b/src/lib/zplParser.ts index 078b6d22..ee611886 100644 --- a/src/lib/zplParser.ts +++ b/src/lib/zplParser.ts @@ -647,6 +647,11 @@ export function parseZPL(zpl: string, dpmm = 8): ParsedZPL { CF(p) { cfHeight = int(p[1], cfHeight); cfWidth = int(p[2], cfWidth); + const fontId = (p[0] ?? "").trim(); + const explicitHeight = parseInt(p[1] ?? "", 10); + if (fontId && !isNaN(explicitHeight) && explicitHeight > 0) { + labelConfig.defaultFont = { fontId, height: explicitHeight }; + } }, // ── Field-wide default rotation ───────────────────────────────────────── @@ -1097,6 +1102,26 @@ export function parseZPL(zpl: string, dpmm = 8): ParsedZPL { const shift = int(rest, 0); if (shift !== 0) labelConfig.labelShift = shift; }, + PR(p) { + const speed = int(p[0], 0); + if (speed >= 2 && speed <= 14) labelConfig.printSpeed = speed; + }, + MD(_, rest) { + // Direct parse: int() falls back to 0 on NaN, which would conflate + // "absent" with the valid darkness value 0. + const parsed = parseInt(rest, 10); + if (!isNaN(parsed) && parsed >= -30 && parsed <= 30) { + labelConfig.darkness = parsed; + } + }, + MT(_, rest) { + const mt = (rest[0] ?? "").toUpperCase(); + if (mt === "T" || mt === "D") labelConfig.mediaType = mt; + }, + PO(_, rest) { + const po = (rest[0] ?? "").toUpperCase(); + if (po === "N" || po === "I") labelConfig.printOrientation = po; + }, // ── Browser-limit: printer-specific features ──────────────────────────── CW: mkBrowserLimit("CW"), // font identifier — assigns alias to printer-resident font @@ -1153,7 +1178,6 @@ export function parseZPL(zpl: string, dpmm = 8): ParsedZPL { FE: noop, // field concatenation — appends data to current field FM: noop, // multiple field origin locations FP: noop, // field parameter — per-character text direction - MT: noop, // media type MN: noop, // media handling / notch tracking JA: noop, // applicator / configuration recall JM: noop, // darkness / print settings @@ -1164,7 +1188,6 @@ export function parseZPL(zpl: string, dpmm = 8): ParsedZPL { JR: noop, // restore factory defaults JS: noop, // change darkness JU: noop, // update firmware - PR: noop, // print rate / speed PM: noop, // part of message PP: noop, // presentation position }; diff --git a/src/test/zplRoundtrip.test.ts b/src/test/zplRoundtrip.test.ts index 82341345..1f344ace 100644 --- a/src/test/zplRoundtrip.test.ts +++ b/src/test/zplRoundtrip.test.ts @@ -307,7 +307,7 @@ describe('parseZPL — real-world structural commands are silently ignored', () '^MNY', // media handling '^MTT', // media type '^JMA', // applicator / configuration recall - '^PON', // this is unknown – will test it IS in unknown + '^XF', // genuinely unknown – will test it IS in unknown '^PW600', '^LL400', '^FO50,50^A0N,30,0^FDReal Label^FS', @@ -331,7 +331,6 @@ describe('parseZPL — real-world structural commands are silently ignored', () it('genuinely unknown commands still appear in importReport.unknown', () => { const { importReport } = parseZPL(ZEBRA_HEADER_ZPL, 8); - // ^PO is not in the structural list — should surface as unknown - expect(importReport.unknown.some((s) => s.startsWith('^PO'))).toBe(true); + expect(importReport.unknown.some((s) => s.startsWith('^XF'))).toBe(true); }); }); From ed4737dbf44cbd18055ce6f552234c2d6823c866 Mon Sep 17 00:00:00 2001 From: u8array Date: Thu, 7 May 2026 17:48:19 +0200 Subject: [PATCH 04/10] feat(i18n): add printer-settings locale keys Adds 15 new keys under the label block for the printer-settings UI in all 32 locales: printerSettingsHeading, printSpeed(+Hint), darkness(+Hint), mediaType (+T/+D), printOrientation (+N/+I/+Indicator), defaultFont (+Id/+Height). Translations generated via scripts/add_locale_key.local.py. --- src/locales/ar.ts | 15 +++++++++++++++ src/locales/bg.ts | 15 +++++++++++++++ src/locales/cs.ts | 15 +++++++++++++++ src/locales/da.ts | 15 +++++++++++++++ src/locales/de.ts | 15 +++++++++++++++ src/locales/el.ts | 15 +++++++++++++++ src/locales/en.ts | 15 +++++++++++++++ src/locales/es.ts | 15 +++++++++++++++ src/locales/et.ts | 15 +++++++++++++++ src/locales/fa.ts | 15 +++++++++++++++ src/locales/fi.ts | 15 +++++++++++++++ src/locales/fr.ts | 15 +++++++++++++++ src/locales/he.ts | 15 +++++++++++++++ src/locales/hr.ts | 15 +++++++++++++++ src/locales/hu.ts | 15 +++++++++++++++ src/locales/it.ts | 15 +++++++++++++++ src/locales/ja.ts | 15 +++++++++++++++ src/locales/ko.ts | 15 +++++++++++++++ src/locales/lt.ts | 15 +++++++++++++++ src/locales/lv.ts | 15 +++++++++++++++ src/locales/nl.ts | 15 +++++++++++++++ src/locales/no.ts | 15 +++++++++++++++ src/locales/pl.ts | 15 +++++++++++++++ src/locales/pt.ts | 15 +++++++++++++++ src/locales/ro.ts | 15 +++++++++++++++ src/locales/sk.ts | 15 +++++++++++++++ src/locales/sl.ts | 15 +++++++++++++++ src/locales/sr.ts | 15 +++++++++++++++ src/locales/sv.ts | 15 +++++++++++++++ src/locales/tr.ts | 15 +++++++++++++++ src/locales/zh-hans.ts | 15 +++++++++++++++ src/locales/zh-hant.ts | 15 +++++++++++++++ 32 files changed, 480 insertions(+) diff --git a/src/locales/ar.ts b/src/locales/ar.ts index f5da316e..baf7e97b 100644 --- a/src/locales/ar.ts +++ b/src/locales/ar.ts @@ -68,6 +68,21 @@ const ar = { mediaModeD: 'D — قاطع', mediaModeK: 'K — كشك', labelShift: 'إزاحة الملصق (dots)', + printerSettingsHeading: 'إعدادات الطابعة (اختياري)', + printSpeed: 'سرعة الطباعة (ips، 2-14)', + printSpeedHint: 'خاص بالطابعة. اتركه فارغًا لاستخدام القيمة الافتراضية.', + darkness: 'الكثافة (-30 إلى +30)', + darknessHint: 'خاص بالطابعة. اتركه فارغًا لاستخدام القيمة الافتراضية.', + mediaType: 'نوع الوسائط', + mediaTypeT: 'نقل حراري', + mediaTypeD: 'حراري مباشر', + printOrientation: 'اتجاه الطباعة', + printOrientationN: 'عادي', + printOrientationI: 'مقلوب (180°)', + printOrientationIndicator: 'مقلوب', + defaultFont: 'نمط النص الافتراضي', + defaultFontId: 'الخط', + defaultFontHeight: 'الارتفاع (نقاط)', }, app: { diff --git a/src/locales/bg.ts b/src/locales/bg.ts index 799b2079..996c16f8 100644 --- a/src/locales/bg.ts +++ b/src/locales/bg.ts @@ -68,6 +68,21 @@ const bg = { mediaModeD: 'D — Резач', mediaModeK: 'K — Киоск', labelShift: 'Отместване на етикета (dots)', + printerSettingsHeading: 'Настройки на принтера (по избор)', + printSpeed: 'Скорост на печат (ips, 2-14)', + printSpeedHint: 'Специфично за принтера. Оставете празно за стойност по подразбиране.', + darkness: 'Плътност (-30 до +30)', + darknessHint: 'Специфично за принтера. Оставете празно за стойност по подразбиране.', + mediaType: 'Тип носител', + mediaTypeT: 'Термотрансферен', + mediaTypeD: 'Директен термичен', + printOrientation: 'Ориентация на печат', + printOrientationN: 'Нормален', + printOrientationI: 'Обърнато (180°)', + printOrientationIndicator: 'Обърнато', + defaultFont: 'Стил на текст по подразбиране', + defaultFontId: 'Шрифт', + defaultFontHeight: 'Височина (точки)', }, app: { diff --git a/src/locales/cs.ts b/src/locales/cs.ts index 1df1d3e9..122be706 100644 --- a/src/locales/cs.ts +++ b/src/locales/cs.ts @@ -68,6 +68,21 @@ const cs = { mediaModeD: 'D — Řezačka', mediaModeK: 'K — Kiosek', labelShift: 'Posun štítku (dots)', + printerSettingsHeading: 'Nastavení tiskárny (volitelné)', + printSpeed: 'Rychlost tisku (ips, 2-14)', + printSpeedHint: 'Specifické pro tiskárnu. Ponechte prázdné pro výchozí hodnotu tiskárny.', + darkness: 'Sytost tisku (-30 až +30)', + darknessHint: 'Specifické pro tiskárnu. Ponechte prázdné pro výchozí hodnotu tiskárny.', + mediaType: 'Typ média', + mediaTypeT: 'Termotransferový', + mediaTypeD: 'Přímý termotisk', + printOrientation: 'Orientace tisku', + printOrientationN: 'Normální', + printOrientationI: 'Vzhůru nohama (180°)', + printOrientationIndicator: 'Vzhůru nohama', + defaultFont: 'Výchozí styl textu', + defaultFontId: 'Písmo', + defaultFontHeight: 'Výška (body)', }, app: { diff --git a/src/locales/da.ts b/src/locales/da.ts index b554b5bc..4f727948 100644 --- a/src/locales/da.ts +++ b/src/locales/da.ts @@ -68,6 +68,21 @@ const da = { mediaModeD: 'D — Skærer', mediaModeK: 'K — Kiosk', labelShift: 'Etiketforskydning (dots)', + printerSettingsHeading: 'Printerindstillinger (valgfrit)', + printSpeed: 'Udskrivningshastighed (ips, 2-14)', + printSpeedHint: 'Printerspecifik. Lad stå tom for printerens standardværdi.', + darkness: 'Svarthed (-30 til +30)', + darknessHint: 'Printerspecifik. Lad stå tom for printerens standardværdi.', + mediaType: 'Materialetype', + mediaTypeT: 'Termotransfer', + mediaTypeD: 'Direkte termisk', + printOrientation: 'Udskriftsretning', + printOrientationN: 'Normal', + printOrientationI: 'På hovedet (180°)', + printOrientationIndicator: 'På hovedet', + defaultFont: 'Standardtekststil', + defaultFontId: 'Skrifttype', + defaultFontHeight: 'Højde (punkter)', }, app: { diff --git a/src/locales/de.ts b/src/locales/de.ts index 0347dded..9bd5dfae 100644 --- a/src/locales/de.ts +++ b/src/locales/de.ts @@ -68,6 +68,21 @@ const de = { mediaModeD: 'D — Cutter', mediaModeK: 'K — Kiosk', labelShift: 'Label-Versatz (Punkte)', + printerSettingsHeading: 'Druckereinstellungen (optional)', + printSpeed: 'Druckgeschwindigkeit (ips, 2-14)', + printSpeedHint: 'Druckerspezifisch. Leer lassen für Drucker-Standard.', + darkness: 'Druckdichte (-30 bis +30)', + darknessHint: 'Druckerspezifisch. Leer lassen für Drucker-Standard.', + mediaType: 'Medientyp', + mediaTypeT: 'Thermotransfer', + mediaTypeD: 'Direkter Thermodruck', + printOrientation: 'Druckorientierung', + printOrientationN: 'Normal', + printOrientationI: 'Kopfüber (180°)', + printOrientationIndicator: 'Kopfüber', + defaultFont: 'Standard-Textstil', + defaultFontId: 'Schriftart', + defaultFontHeight: 'Höhe (Punkte)', }, app: { diff --git a/src/locales/el.ts b/src/locales/el.ts index 5c73c04b..761f2845 100644 --- a/src/locales/el.ts +++ b/src/locales/el.ts @@ -68,6 +68,21 @@ const el = { mediaModeD: 'D — Κοπτικό', mediaModeK: 'K — Κιόσκι', labelShift: 'Μετατόπιση ετικέτας (dots)', + printerSettingsHeading: 'Ρυθμίσεις εκτυπωτή (προαιρετικό)', + printSpeed: 'Ταχύτητα εκτύπωσης (ips, 2-14)', + printSpeedHint: 'Ειδικό για τον εκτυπωτή. Αφήστε κενό για την προεπιλογή.', + darkness: 'Πυκνότητα (-30 έως +30)', + darknessHint: 'Ειδικό για τον εκτυπωτή. Αφήστε κενό για την προεπιλογή.', + mediaType: 'Τύπος μέσου', + mediaTypeT: 'Θερμικής μεταφοράς', + mediaTypeD: 'Άμεση θερμική', + printOrientation: 'Προσανατολισμός εκτύπωσης', + printOrientationN: 'Κανονικός', + printOrientationI: 'Ανάποδα (180°)', + printOrientationIndicator: 'Ανάποδα', + defaultFont: 'Προεπιλεγμένο στυλ κειμένου', + defaultFontId: 'Γραμματοσειρά', + defaultFontHeight: 'Ύψος (κουκκίδες)', }, app: { diff --git a/src/locales/en.ts b/src/locales/en.ts index add2b87a..36516520 100644 --- a/src/locales/en.ts +++ b/src/locales/en.ts @@ -68,6 +68,21 @@ const en = { mediaModeD: 'D — Cutter', mediaModeK: 'K — Kiosk', labelShift: 'Label shift (dots)', + printerSettingsHeading: 'Printer settings (optional)', + printSpeed: 'Print speed (ips, 2-14)', + printSpeedHint: 'Printer-specific. Leave empty to use the printer default.', + darkness: 'Darkness (-30 to +30)', + darknessHint: 'Printer-specific. Leave empty to use the printer default.', + mediaType: 'Media type', + mediaTypeT: 'Thermal transfer', + mediaTypeD: 'Direct thermal', + printOrientation: 'Print orientation', + printOrientationN: 'Normal', + printOrientationI: 'Upside down (180°)', + printOrientationIndicator: 'Upside down', + defaultFont: 'Default text style', + defaultFontId: 'Font', + defaultFontHeight: 'Height (dots)', }, app: { diff --git a/src/locales/es.ts b/src/locales/es.ts index 328dc1b5..e7c3d4af 100644 --- a/src/locales/es.ts +++ b/src/locales/es.ts @@ -68,6 +68,21 @@ const es = { mediaModeD: 'D — Cutter', mediaModeK: 'K — Kiosco', labelShift: 'Desplazamiento de etiqueta (dots)', + printerSettingsHeading: 'Ajustes de impresora (opcional)', + printSpeed: 'Velocidad de impresión (ips, 2-14)', + printSpeedHint: 'Específico de la impresora. Dejar vacío para usar el valor predeterminado.', + darkness: 'Densidad de impresión (-30 a +30)', + darknessHint: 'Específico de la impresora. Dejar vacío para usar el valor predeterminado.', + mediaType: 'Tipo de medio', + mediaTypeT: 'Transferencia térmica', + mediaTypeD: 'Térmica directa', + printOrientation: 'Orientación de impresión', + printOrientationN: 'Normal', + printOrientationI: 'Boca abajo (180°)', + printOrientationIndicator: 'Boca abajo', + defaultFont: 'Estilo de texto predeterminado', + defaultFontId: 'Fuente', + defaultFontHeight: 'Altura (puntos)', }, app: { diff --git a/src/locales/et.ts b/src/locales/et.ts index 9e4fcff0..004c7f6f 100644 --- a/src/locales/et.ts +++ b/src/locales/et.ts @@ -68,6 +68,21 @@ const et = { mediaModeD: 'D — Lõikur', mediaModeK: 'K — Kiosk', labelShift: 'Etiketi nihe (dots)', + printerSettingsHeading: 'Printeri sätted (valikuline)', + printSpeed: 'Trükikiirus (ips, 2-14)', + printSpeedHint: 'Printerispetsiifiline. Vaikeväärtuse kasutamiseks jäta tühjaks.', + darkness: 'Tumedus (-30 kuni +30)', + darknessHint: 'Printerispetsiifiline. Vaikeväärtuse kasutamiseks jäta tühjaks.', + mediaType: 'Meedialiik', + mediaTypeT: 'Termoülekanne', + mediaTypeD: 'Otsetermaal', + printOrientation: 'Trükisuund', + printOrientationN: 'Tavaline', + printOrientationI: 'Tagurpidi (180°)', + printOrientationIndicator: 'Tagurpidi', + defaultFont: 'Vaikimisi teksti stiil', + defaultFontId: 'Font', + defaultFontHeight: 'Kõrgus (punktid)', }, app: { diff --git a/src/locales/fa.ts b/src/locales/fa.ts index cff7a944..bf5c70df 100644 --- a/src/locales/fa.ts +++ b/src/locales/fa.ts @@ -68,6 +68,21 @@ const fa = { mediaModeD: 'D — برش', mediaModeK: 'K — کیوسک', labelShift: 'جابجایی برچسب (dots)', + printerSettingsHeading: 'تنظیمات چاپگر (اختیاری)', + printSpeed: 'سرعت چاپ (ips, 2-14)', + printSpeedHint: 'مختص چاپگر. برای استفاده از پیش‌فرض خالی بگذارید.', + darkness: 'تیرگی (-30 تا +30)', + darknessHint: 'مختص چاپگر. برای استفاده از پیش‌فرض خالی بگذارید.', + mediaType: 'نوع رسانه', + mediaTypeT: 'انتقال حرارتی', + mediaTypeD: 'حرارتی مستقیم', + printOrientation: 'جهت چاپ', + printOrientationN: 'عادی', + printOrientationI: 'وارونه (180°)', + printOrientationIndicator: 'وارونه', + defaultFont: 'سبک متن پیش‌فرض', + defaultFontId: 'قلم', + defaultFontHeight: 'ارتفاع (نقطه)', }, app: { diff --git a/src/locales/fi.ts b/src/locales/fi.ts index d7b76289..14e19360 100644 --- a/src/locales/fi.ts +++ b/src/locales/fi.ts @@ -68,6 +68,21 @@ const fi = { mediaModeD: 'D — Leikkuri', mediaModeK: 'K — Kioski', labelShift: 'Tarrasiirtymä (dots)', + printerSettingsHeading: 'Tulostimen asetukset (valinnainen)', + printSpeed: 'Tulostusnopeus (ips, 2-14)', + printSpeedHint: 'Tulostinkohtainen. Jätä tyhjäksi käyttääksesi oletusarvoa.', + darkness: 'Tummuus (-30 - +30)', + darknessHint: 'Tulostinkohtainen. Jätä tyhjäksi käyttääksesi oletusarvoa.', + mediaType: 'Materiaalityyppi', + mediaTypeT: 'Lämpösiirto', + mediaTypeD: 'Suora lämpö', + printOrientation: 'Tulostussuunta', + printOrientationN: 'Normaali', + printOrientationI: 'Ylösalaisin (180°)', + printOrientationIndicator: 'Ylösalaisin', + defaultFont: 'Oletustekstityyli', + defaultFontId: 'Fontti', + defaultFontHeight: 'Korkeus (pisteet)', }, app: { diff --git a/src/locales/fr.ts b/src/locales/fr.ts index bff24116..1f06a7db 100644 --- a/src/locales/fr.ts +++ b/src/locales/fr.ts @@ -68,6 +68,21 @@ const fr = { mediaModeD: 'D — Cutter', mediaModeK: 'K — Kiosque', labelShift: 'Décalage d\'étiquette (dots)', + printerSettingsHeading: 'Paramètres d\'imprimante (facultatif)', + printSpeed: 'Vitesse d\'impression (ips, 2-14)', + printSpeedHint: 'Spécifique à l\'imprimante. Laisser vide pour la valeur par défaut.', + darkness: 'Densité d\'impression (-30 à +30)', + darknessHint: 'Spécifique à l\'imprimante. Laisser vide pour la valeur par défaut.', + mediaType: 'Type de support', + mediaTypeT: 'Transfert thermique', + mediaTypeD: 'Thermique direct', + printOrientation: 'Orientation d\'impression', + printOrientationN: 'Normal', + printOrientationI: 'Tête en bas (180°)', + printOrientationIndicator: 'Tête en bas', + defaultFont: 'Style de texte par défaut', + defaultFontId: 'Police', + defaultFontHeight: 'Hauteur (points)', }, app: { diff --git a/src/locales/he.ts b/src/locales/he.ts index fbcce6e8..0eb76e86 100644 --- a/src/locales/he.ts +++ b/src/locales/he.ts @@ -68,6 +68,21 @@ const he = { mediaModeD: 'D — חותך', mediaModeK: 'K — קיוסק', labelShift: 'הסטת תווית (dots)', + printerSettingsHeading: 'הגדרות מדפסת (אופציונלי)', + printSpeed: 'מהירות הדפסה (ips, 2-14)', + printSpeedHint: 'ספציפי למדפסת. השאר ריק לערך ברירת המחדל.', + darkness: 'כהות (-30 עד +30)', + darknessHint: 'ספציפי למדפסת. השאר ריק לערך ברירת המחדל.', + mediaType: 'סוג המדיה', + mediaTypeT: 'העברה תרמית', + mediaTypeD: 'תרמי ישיר', + printOrientation: 'כיוון הדפסה', + printOrientationN: 'רגיל', + printOrientationI: 'הפוך (180°)', + printOrientationIndicator: 'הפוך', + defaultFont: 'סגנון טקסט ברירת מחדל', + defaultFontId: 'גופן', + defaultFontHeight: 'גובה (נקודות)', }, app: { diff --git a/src/locales/hr.ts b/src/locales/hr.ts index 9c9b4e7b..419027f8 100644 --- a/src/locales/hr.ts +++ b/src/locales/hr.ts @@ -68,6 +68,21 @@ const hr = { mediaModeD: 'D — Rezač', mediaModeK: 'K — Kiosk', labelShift: 'Pomak naljepnice (dots)', + printerSettingsHeading: 'Postavke pisača (neobavezno)', + printSpeed: 'Brzina ispisa (ips, 2-14)', + printSpeedHint: 'Specifično za pisač. Ostavite prazno za zadanu vrijednost.', + darkness: 'Zacrnjenje (-30 do +30)', + darknessHint: 'Specifično za pisač. Ostavite prazno za zadanu vrijednost.', + mediaType: 'Vrsta medija', + mediaTypeT: 'Toplinski prijenos', + mediaTypeD: 'Izravni termalni', + printOrientation: 'Orijentacija ispisa', + printOrientationN: 'Normalno', + printOrientationI: 'Naopako (180°)', + printOrientationIndicator: 'Naopako', + defaultFont: 'Zadani stil teksta', + defaultFontId: 'Font', + defaultFontHeight: 'Visina (točke)', }, app: { diff --git a/src/locales/hu.ts b/src/locales/hu.ts index 243c1b97..5b72df0f 100644 --- a/src/locales/hu.ts +++ b/src/locales/hu.ts @@ -68,6 +68,21 @@ const hu = { mediaModeD: 'D — Vágó', mediaModeK: 'K — Kioszk', labelShift: 'Címke eltolás (dots)', + printerSettingsHeading: 'Nyomtatóbeállítások (opcionális)', + printSpeed: 'Nyomtatási sebesség (ips, 2-14)', + printSpeedHint: 'Nyomtatófüggő. Hagyja üresen a nyomtató alapértelmezett értékéhez.', + darkness: 'Nyomtatási sűrűség (-30 - +30)', + darknessHint: 'Nyomtatófüggő. Hagyja üresen a nyomtató alapértelmezett értékéhez.', + mediaType: 'Hordozó típusa', + mediaTypeT: 'Hőtranszfer', + mediaTypeD: 'Direkt termál', + printOrientation: 'Nyomtatási tájolás', + printOrientationN: 'Normál', + printOrientationI: 'Fejjel lefelé (180°)', + printOrientationIndicator: 'Fejjel lefelé', + defaultFont: 'Alapértelmezett szövegstílus', + defaultFontId: 'Betűtípus', + defaultFontHeight: 'Magasság (pontok)', }, app: { diff --git a/src/locales/it.ts b/src/locales/it.ts index ffe40947..90f80129 100644 --- a/src/locales/it.ts +++ b/src/locales/it.ts @@ -68,6 +68,21 @@ const it = { mediaModeD: 'D — Cutter', mediaModeK: 'K — Chiosco', labelShift: 'Spostamento etichetta (dots)', + printerSettingsHeading: 'Impostazioni stampante (opzionale)', + printSpeed: 'Velocità di stampa (ips, 2-14)', + printSpeedHint: 'Specifico per stampante. Lascia vuoto per usare il valore predefinito.', + darkness: 'Densità di stampa (-30 a +30)', + darknessHint: 'Specifico per stampante. Lascia vuoto per usare il valore predefinito.', + mediaType: 'Tipo di supporto', + mediaTypeT: 'Trasferimento termico', + mediaTypeD: 'Termica diretta', + printOrientation: 'Orientamento di stampa', + printOrientationN: 'Normale', + printOrientationI: 'Capovolto (180°)', + printOrientationIndicator: 'Capovolto', + defaultFont: 'Stile di testo predefinito', + defaultFontId: 'Carattere', + defaultFontHeight: 'Altezza (punti)', }, app: { diff --git a/src/locales/ja.ts b/src/locales/ja.ts index 5e3dffc1..68f34d18 100644 --- a/src/locales/ja.ts +++ b/src/locales/ja.ts @@ -68,6 +68,21 @@ const ja = { mediaModeD: 'D — カッター', mediaModeK: 'K — キオスク', labelShift: 'ラベルシフト (dots)', + printerSettingsHeading: 'プリンター設定(オプション)', + printSpeed: '印刷速度 (ips, 2-14)', + printSpeedHint: 'プリンター固有。デフォルト値を使用するには空のままにします。', + darkness: '濃度 (-30 ~ +30)', + darknessHint: 'プリンター固有。デフォルト値を使用するには空のままにします。', + mediaType: 'メディアタイプ', + mediaTypeT: '熱転写', + mediaTypeD: 'ダイレクトサーマル', + printOrientation: '印刷方向', + printOrientationN: '通常', + printOrientationI: '上下逆 (180°)', + printOrientationIndicator: '上下逆', + defaultFont: '既定のテキストスタイル', + defaultFontId: 'フォント', + defaultFontHeight: '高さ (ドット)', }, app: { diff --git a/src/locales/ko.ts b/src/locales/ko.ts index eb286deb..dcf868e9 100644 --- a/src/locales/ko.ts +++ b/src/locales/ko.ts @@ -68,6 +68,21 @@ const ko = { mediaModeD: 'D — 커터', mediaModeK: 'K — 키오스크', labelShift: '라벨 이동 (dots)', + printerSettingsHeading: '프린터 설정 (선택 사항)', + printSpeed: '인쇄 속도 (ips, 2-14)', + printSpeedHint: '프린터별로 다릅니다. 기본값을 사용하려면 비워 두세요.', + darkness: '농도 (-30 ~ +30)', + darknessHint: '프린터별로 다릅니다. 기본값을 사용하려면 비워 두세요.', + mediaType: '미디어 유형', + mediaTypeT: '열전사', + mediaTypeD: '감열식', + printOrientation: '인쇄 방향', + printOrientationN: '일반', + printOrientationI: '거꾸로 (180°)', + printOrientationIndicator: '거꾸로', + defaultFont: '기본 텍스트 스타일', + defaultFontId: '글꼴', + defaultFontHeight: '높이 (도트)', }, app: { diff --git a/src/locales/lt.ts b/src/locales/lt.ts index a59b1621..342a82ba 100644 --- a/src/locales/lt.ts +++ b/src/locales/lt.ts @@ -68,6 +68,21 @@ const lt = { mediaModeD: 'D — Pjaustuvas', mediaModeK: 'K — Kioskas', labelShift: 'Etiketės poslinkis (dots)', + printerSettingsHeading: 'Spausdintuvo nustatymai (neprivaloma)', + printSpeed: 'Spausdinimo greitis (ips, 2-14)', + printSpeedHint: 'Priklauso nuo spausdintuvo. Palikite tuščią numatytąjai reikšmei.', + darkness: 'Tamsumas (-30 iki +30)', + darknessHint: 'Priklauso nuo spausdintuvo. Palikite tuščią numatytąjai reikšmei.', + mediaType: 'Laikmenos tipas', + mediaTypeT: 'Šiluminis perdavimas', + mediaTypeD: 'Tiesioginis terminis', + printOrientation: 'Spausdinimo orientacija', + printOrientationN: 'Įprasta', + printOrientationI: 'Apsuktai (180°)', + printOrientationIndicator: 'Apsuktai', + defaultFont: 'Numatytasis teksto stilius', + defaultFontId: 'Šriftas', + defaultFontHeight: 'Aukštis (taškai)', }, app: { diff --git a/src/locales/lv.ts b/src/locales/lv.ts index 845d3627..b763982d 100644 --- a/src/locales/lv.ts +++ b/src/locales/lv.ts @@ -68,6 +68,21 @@ const lv = { mediaModeD: 'D — Griezējs', mediaModeK: 'K — Kiosks', labelShift: 'Etiķetes nobīde (dots)', + printerSettingsHeading: 'Printera iestatījumi (neobligāti)', + printSpeed: 'Drukāšanas ātrums (ips, 2-14)', + printSpeedHint: 'Atkarīgs no printera. Atstājiet tukšu, lai izmantotu noklusējumu.', + darkness: 'Tumšums (-30 līdz +30)', + darknessHint: 'Atkarīgs no printera. Atstājiet tukšu, lai izmantotu noklusējumu.', + mediaType: 'Materiāla veids', + mediaTypeT: 'Termopārnese', + mediaTypeD: 'Tiešais termālais', + printOrientation: 'Drukāšanas orientācija', + printOrientationN: 'Parasta', + printOrientationI: 'Otrādi (180°)', + printOrientationIndicator: 'Otrādi', + defaultFont: 'Noklusējuma teksta stils', + defaultFontId: 'Fonts', + defaultFontHeight: 'Augstums (punkti)', }, app: { diff --git a/src/locales/nl.ts b/src/locales/nl.ts index 586dd2ec..7c833e16 100644 --- a/src/locales/nl.ts +++ b/src/locales/nl.ts @@ -68,6 +68,21 @@ const nl = { mediaModeD: 'D — Snijder', mediaModeK: 'K — Kiosk', labelShift: 'Etiketverschuiving (dots)', + printerSettingsHeading: 'Printerinstellingen (optioneel)', + printSpeed: 'Afdruksnelheid (ips, 2-14)', + printSpeedHint: 'Printerspecifiek. Laat leeg om de standaardwaarde te gebruiken.', + darkness: 'Drukdichtheid (-30 tot +30)', + darknessHint: 'Printerspecifiek. Laat leeg om de standaardwaarde te gebruiken.', + mediaType: 'Mediumtype', + mediaTypeT: 'Thermische transfer', + mediaTypeD: 'Direct thermisch', + printOrientation: 'Afdrukrichting', + printOrientationN: 'Normaal', + printOrientationI: 'Ondersteboven (180°)', + printOrientationIndicator: 'Ondersteboven', + defaultFont: 'Standaard tekststijl', + defaultFontId: 'Lettertype', + defaultFontHeight: 'Hoogte (dots)', }, app: { diff --git a/src/locales/no.ts b/src/locales/no.ts index faf571eb..612cfc76 100644 --- a/src/locales/no.ts +++ b/src/locales/no.ts @@ -68,6 +68,21 @@ const no = { mediaModeD: 'D — Kutter', mediaModeK: 'K — Kiosk', labelShift: 'Etikettforskyvning (dots)', + printerSettingsHeading: 'Skriverinnstillinger (valgfritt)', + printSpeed: 'Utskriftshastighet (ips, 2-14)', + printSpeedHint: 'Skriverspesifikk. La stå tom for skriverens standardverdi.', + darkness: 'Svarthet (-30 til +30)', + darknessHint: 'Skriverspesifikk. La stå tom for skriverens standardverdi.', + mediaType: 'Materialtype', + mediaTypeT: 'Termotransfer', + mediaTypeD: 'Direkte termisk', + printOrientation: 'Utskriftsretning', + printOrientationN: 'Normal', + printOrientationI: 'Opp ned (180°)', + printOrientationIndicator: 'Opp ned', + defaultFont: 'Standardtekststil', + defaultFontId: 'Skrift', + defaultFontHeight: 'Høyde (punkter)', }, app: { diff --git a/src/locales/pl.ts b/src/locales/pl.ts index c6375bac..28377ef7 100644 --- a/src/locales/pl.ts +++ b/src/locales/pl.ts @@ -68,6 +68,21 @@ const pl = { mediaModeD: 'D — Cutter', mediaModeK: 'K — Kiosk', labelShift: 'Przesunięcie etykiety (dots)', + printerSettingsHeading: 'Ustawienia drukarki (opcjonalne)', + printSpeed: 'Prędkość drukowania (ips, 2-14)', + printSpeedHint: 'Specyficzne dla drukarki. Pozostaw puste, aby użyć wartości domyślnej.', + darkness: 'Gęstość druku (-30 do +30)', + darknessHint: 'Specyficzne dla drukarki. Pozostaw puste, aby użyć wartości domyślnej.', + mediaType: 'Typ nośnika', + mediaTypeT: 'Termotransferowy', + mediaTypeD: 'Termiczny bezpośredni', + printOrientation: 'Orientacja druku', + printOrientationN: 'Normalna', + printOrientationI: 'Do góry nogami (180°)', + printOrientationIndicator: 'Do góry nogami', + defaultFont: 'Domyślny styl tekstu', + defaultFontId: 'Czcionka', + defaultFontHeight: 'Wysokość (punkty)', }, app: { diff --git a/src/locales/pt.ts b/src/locales/pt.ts index 764af39b..46ced5f6 100644 --- a/src/locales/pt.ts +++ b/src/locales/pt.ts @@ -68,6 +68,21 @@ const pt = { mediaModeD: 'D — Cutter', mediaModeK: 'K — Quiosque', labelShift: 'Deslocamento da etiqueta (dots)', + printerSettingsHeading: 'Definições da impressora (opcional)', + printSpeed: 'Velocidade de impressão (ips, 2-14)', + printSpeedHint: 'Específico da impressora. Deixe vazio para usar o padrão da impressora.', + darkness: 'Densidade de impressão (-30 a +30)', + darknessHint: 'Específico da impressora. Deixe vazio para usar o padrão da impressora.', + mediaType: 'Tipo de suporte', + mediaTypeT: 'Transferência térmica', + mediaTypeD: 'Térmica direta', + printOrientation: 'Orientação de impressão', + printOrientationN: 'Normal', + printOrientationI: 'De cabeça para baixo (180°)', + printOrientationIndicator: 'De cabeça para baixo', + defaultFont: 'Estilo de texto predefinido', + defaultFontId: 'Fonte', + defaultFontHeight: 'Altura (pontos)', }, app: { diff --git a/src/locales/ro.ts b/src/locales/ro.ts index d8e8a132..4a7a7d95 100644 --- a/src/locales/ro.ts +++ b/src/locales/ro.ts @@ -68,6 +68,21 @@ const ro = { mediaModeD: 'D — Cutter', mediaModeK: 'K — Chioșc', labelShift: 'Deplasare etichetă (dots)', + printerSettingsHeading: 'Setări imprimantă (opțional)', + printSpeed: 'Viteza de imprimare (ips, 2-14)', + printSpeedHint: 'Specific imprimantei. Lăsați gol pentru valoarea implicită.', + darkness: 'Densitate imprimare (-30 până la +30)', + darknessHint: 'Specific imprimantei. Lăsați gol pentru valoarea implicită.', + mediaType: 'Tip mediu', + mediaTypeT: 'Transfer termic', + mediaTypeD: 'Termic direct', + printOrientation: 'Orientare imprimare', + printOrientationN: 'Normal', + printOrientationI: 'Cu susul în jos (180°)', + printOrientationIndicator: 'Cu susul în jos', + defaultFont: 'Stil text implicit', + defaultFontId: 'Font', + defaultFontHeight: 'Înălțime (puncte)', }, app: { diff --git a/src/locales/sk.ts b/src/locales/sk.ts index 1b683dc2..e9528649 100644 --- a/src/locales/sk.ts +++ b/src/locales/sk.ts @@ -68,6 +68,21 @@ const sk = { mediaModeD: 'D — Rezačka', mediaModeK: 'K — Kiosk', labelShift: 'Posun štítka (dots)', + printerSettingsHeading: 'Nastavenia tlačiarne (voliteľné)', + printSpeed: 'Rýchlosť tlače (ips, 2-14)', + printSpeedHint: 'Špecifické pre tlačiareň. Ponechajte prázdne pre predvolenú hodnotu.', + darkness: 'Sýtosť tlače (-30 až +30)', + darknessHint: 'Špecifické pre tlačiareň. Ponechajte prázdne pre predvolenú hodnotu.', + mediaType: 'Typ média', + mediaTypeT: 'Termotransferový', + mediaTypeD: 'Priamy termotisk', + printOrientation: 'Orientácia tlače', + printOrientationN: 'Normálne', + printOrientationI: 'Hore nohami (180°)', + printOrientationIndicator: 'Hore nohami', + defaultFont: 'Predvolený štýl textu', + defaultFontId: 'Písmo', + defaultFontHeight: 'Výška (body)', }, app: { diff --git a/src/locales/sl.ts b/src/locales/sl.ts index e8d7391a..b5910d8d 100644 --- a/src/locales/sl.ts +++ b/src/locales/sl.ts @@ -68,6 +68,21 @@ const sl = { mediaModeD: 'D — Rezalnik', mediaModeK: 'K — Kiosk', labelShift: 'Zamik nalepke (dots)', + printerSettingsHeading: 'Nastavitve tiskalnika (izbirno)', + printSpeed: 'Hitrost tiskanja (ips, 2-14)', + printSpeedHint: 'Specifično za tiskalnik. Pustite prazno za privzeto vrednost.', + darkness: 'Temnost (-30 do +30)', + darknessHint: 'Specifično za tiskalnik. Pustite prazno za privzeto vrednost.', + mediaType: 'Vrsta medija', + mediaTypeT: 'Termalni prenos', + mediaTypeD: 'Neposredno termalno', + printOrientation: 'Usmerjenost tiskanja', + printOrientationN: 'Normalno', + printOrientationI: 'Obrnjeno (180°)', + printOrientationIndicator: 'Obrnjeno', + defaultFont: 'Privzeti slog besedila', + defaultFontId: 'Pisava', + defaultFontHeight: 'Višina (točke)', }, app: { diff --git a/src/locales/sr.ts b/src/locales/sr.ts index ce43c7ab..37125282 100644 --- a/src/locales/sr.ts +++ b/src/locales/sr.ts @@ -68,6 +68,21 @@ const sr = { mediaModeD: 'D — Секач', mediaModeK: 'K — Киоск', labelShift: 'Померај етикете (dots)', + printerSettingsHeading: 'Подешавања штампача (опционално)', + printSpeed: 'Брзина штампе (ips, 2-14)', + printSpeedHint: 'Специфично за штампач. Оставите празно за подразумевану вредност.', + darkness: 'Затамњење (-30 до +30)', + darknessHint: 'Специфично за штампач. Оставите празно за подразумевану вредност.', + mediaType: 'Тип медија', + mediaTypeT: 'Топлотни пренос', + mediaTypeD: 'Директни термални', + printOrientation: 'Оријентација штампе', + printOrientationN: 'Нормално', + printOrientationI: 'Наопако (180°)', + printOrientationIndicator: 'Наопако', + defaultFont: 'Подразумевани стил текста', + defaultFontId: 'Фонт', + defaultFontHeight: 'Висина (тачке)', }, app: { diff --git a/src/locales/sv.ts b/src/locales/sv.ts index 15022c1f..f63d16a8 100644 --- a/src/locales/sv.ts +++ b/src/locales/sv.ts @@ -68,6 +68,21 @@ const sv = { mediaModeD: 'D — Skärare', mediaModeK: 'K — Kiosk', labelShift: 'Etikettförskjutning (dots)', + printerSettingsHeading: 'Skrivarinställningar (valfritt)', + printSpeed: 'Utskriftshastighet (ips, 2-14)', + printSpeedHint: 'Skrivarspecifik. Lämna tom för skrivarens standardvärde.', + darkness: 'Svärta (-30 till +30)', + darknessHint: 'Skrivarspecifik. Lämna tom för skrivarens standardvärde.', + mediaType: 'Materialtyp', + mediaTypeT: 'Termotransfer', + mediaTypeD: 'Direkttermisk', + printOrientation: 'Utskriftsorientering', + printOrientationN: 'Normal', + printOrientationI: 'Upp och ner (180°)', + printOrientationIndicator: 'Upp och ner', + defaultFont: 'Standardtextstil', + defaultFontId: 'Typsnitt', + defaultFontHeight: 'Höjd (punkter)', }, app: { diff --git a/src/locales/tr.ts b/src/locales/tr.ts index 116427f3..2c5df388 100644 --- a/src/locales/tr.ts +++ b/src/locales/tr.ts @@ -68,6 +68,21 @@ const tr = { mediaModeD: 'D — Kesici', mediaModeK: 'K — Kiosk', labelShift: 'Etiket kaydırma (dots)', + printerSettingsHeading: 'Yazıcı ayarları (isteğe bağlı)', + printSpeed: 'Yazdırma hızı (ips, 2-14)', + printSpeedHint: 'Yazıcıya özel. Varsayılan için boş bırakın.', + darkness: 'Koyuluk (-30 ile +30 arası)', + darknessHint: 'Yazıcıya özel. Varsayılan için boş bırakın.', + mediaType: 'Medya türü', + mediaTypeT: 'Termal transfer', + mediaTypeD: 'Doğrudan termal', + printOrientation: 'Yazdırma yönü', + printOrientationN: 'Normal', + printOrientationI: 'Baş aşağı (180°)', + printOrientationIndicator: 'Baş aşağı', + defaultFont: 'Varsayılan metin stili', + defaultFontId: 'Yazı tipi', + defaultFontHeight: 'Yükseklik (nokta)', }, app: { diff --git a/src/locales/zh-hans.ts b/src/locales/zh-hans.ts index a6dee33c..761ef808 100644 --- a/src/locales/zh-hans.ts +++ b/src/locales/zh-hans.ts @@ -68,6 +68,21 @@ const zhHans = { mediaModeD: 'D — 切刀', mediaModeK: 'K — 自助终端', labelShift: '标签偏移 (dots)', + printerSettingsHeading: '打印机设置(可选)', + printSpeed: '打印速度 (ips, 2-14)', + printSpeedHint: '与打印机相关。留空使用打印机默认值。', + darkness: '打印浓度 (-30 至 +30)', + darknessHint: '与打印机相关。留空使用打印机默认值。', + mediaType: '介质类型', + mediaTypeT: '热转印', + mediaTypeD: '直接热敏', + printOrientation: '打印方向', + printOrientationN: '正常', + printOrientationI: '倒置 (180°)', + printOrientationIndicator: '倒置', + defaultFont: '默认文本样式', + defaultFontId: '字体', + defaultFontHeight: '高度 (点)', }, app: { diff --git a/src/locales/zh-hant.ts b/src/locales/zh-hant.ts index d04a2a5f..2f74627b 100644 --- a/src/locales/zh-hant.ts +++ b/src/locales/zh-hant.ts @@ -68,6 +68,21 @@ const zhHant = { mediaModeD: 'D — 裁刀', mediaModeK: 'K — 自助終端', labelShift: '標籤偏移 (dots)', + printerSettingsHeading: '印表機設定(可選)', + printSpeed: '列印速度 (ips, 2-14)', + printSpeedHint: '與印表機相關。留空使用印表機預設值。', + darkness: '列印濃度 (-30 至 +30)', + darknessHint: '與印表機相關。留空使用印表機預設值。', + mediaType: '介質類型', + mediaTypeT: '熱轉印', + mediaTypeD: '直接熱感', + printOrientation: '列印方向', + printOrientationN: '正常', + printOrientationI: '倒置 (180°)', + printOrientationIndicator: '倒置', + defaultFont: '預設文字樣式', + defaultFontId: '字型', + defaultFontHeight: '高度 (點)', }, app: { From 82f3678d8bc7f1ae7dc7ff560396d859f1fee5e1 Mon Sep 17 00:00:00 2001 From: u8array Date: Thu, 7 May 2026 17:50:10 +0200 Subject: [PATCH 05/10] feat(ui): add printer-settings section and inverted-print indicator LabelConfigPanel gains a section for print speed, darkness, media type, print orientation and default font. Empty inputs persist as undefined so absent fields stay absent in the generated output. Darkness preserves 0 as a valid value via parseIntOrUndef. Canvas shows a small badge in the top-right corner when print orientation is set to upside down, signalling the printer-side flip without rotating the design surface. --- src/components/Canvas/LabelCanvas.tsx | 8 ++ src/components/Properties/PropertiesPanel.tsx | 129 ++++++++++++++++++ 2 files changed, 137 insertions(+) diff --git a/src/components/Canvas/LabelCanvas.tsx b/src/components/Canvas/LabelCanvas.tsx index e9f3204d..63dff325 100644 --- a/src/components/Canvas/LabelCanvas.tsx +++ b/src/components/Canvas/LabelCanvas.tsx @@ -21,6 +21,7 @@ import { Ruler, RULER_SIZE } from "./Ruler"; import { ObjectRegistry } from "../../registry"; import type { LabelObject } from "../../registry"; import { useColorScheme } from "../../lib/useColorScheme"; +import { useT } from "../../lib/useT"; import { useCanvasPanZoom } from "./hooks/useCanvasPanZoom"; import { useCanvasLasso } from "./hooks/useCanvasLasso"; import { useKonvaTransformer } from "./hooks/useKonvaTransformer"; @@ -83,6 +84,7 @@ export function LabelCanvas({ }, []); const colors = useColorScheme(); + const t = useT(); const { label, @@ -459,6 +461,12 @@ export function LabelCanvas({ > + {label.printOrientation === "I" && ( +
+ {t.label.printOrientationIndicator} +
+ )} + {/* Bottom-right controls: view options + zoom */}
+ +
+ +

{t.label.printerSettingsHeading}

+ +
+ + + onUpdate({ printSpeed: parseIntOrUndef(e.target.value) }) + } + /> +

{t.label.printSpeedHint}

+
+ +
+ + + onUpdate({ darkness: parseIntOrUndef(e.target.value) }) + } + /> +

{t.label.darknessHint}

+
+ +
+ + +
+ +
+ + +
+ +
+ +
+
+ + + onUpdate({ + defaultFont: mergeDefaultFont(label.defaultFont, { + fontId: e.target.value, + }), + }) + } + /> +
+
+ + + onUpdate({ + defaultFont: mergeDefaultFont(label.defaultFont, { + height: parseIntOrUndef(e.target.value), + }), + }) + } + /> +
+
+
); } + +function parseIntOrUndef(raw: string): number | undefined { + if (raw.trim() === "") return undefined; + const n = parseInt(raw, 10); + return isNaN(n) ? undefined : n; +} + +function mergeDefaultFont( + current: LabelConfig["defaultFont"], + patch: { fontId?: string; height?: number }, +): LabelConfig["defaultFont"] { + const fontId = patch.fontId ?? current?.fontId ?? ""; + const height = patch.height ?? current?.height; + if (!fontId || height === undefined) return undefined; + return { fontId, height }; +} From 1c34b38a45820779c4052bbf72215cb5b14af52a Mon Sep 17 00:00:00 2001 From: u8array Date: Thu, 7 May 2026 17:53:18 +0200 Subject: [PATCH 06/10] refactor(zpl): parse CF height once instead of twice The CF handler parsed p[1] both via int() (for the parser-local cfHeight state) and via parseInt() (to detect explicit height for labelConfig. defaultFont). Both serve distinct purposes but the duplicate parse can be replaced by a single parseInt with NaN-aware fallback. --- src/lib/zplParser.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib/zplParser.ts b/src/lib/zplParser.ts index ee611886..a786d20f 100644 --- a/src/lib/zplParser.ts +++ b/src/lib/zplParser.ts @@ -645,10 +645,10 @@ export function parseZPL(zpl: string, dpmm = 8): ParsedZPL { // ── Change alphanumeric default font ──────────────────────────────────── // ^CF{font},{height},{width} → sets default for fields without ^A CF(p) { - cfHeight = int(p[1], cfHeight); - cfWidth = int(p[2], cfWidth); const fontId = (p[0] ?? "").trim(); const explicitHeight = parseInt(p[1] ?? "", 10); + cfHeight = isNaN(explicitHeight) ? cfHeight : explicitHeight; + cfWidth = int(p[2], cfWidth); if (fontId && !isNaN(explicitHeight) && explicitHeight > 0) { labelConfig.defaultFont = { fontId, height: explicitHeight }; } From bb7a6139a653e5be47ea8ea08113115e6c858631 Mon Sep 17 00:00:00 2001 From: u8array Date: Thu, 7 May 2026 18:21:07 +0200 Subject: [PATCH 07/10] refactor(ui): use printerDefault label for empty media/orientation options The empty option in the mediaMode/mediaType/printOrientation selects previously reused presetCustom ("Custom"), which is the right semantic for the dimension-preset select but not for "unset, use printer's own default". Adds a dedicated label.printerDefault key in all 32 locales and points the three empty-state options at it. --- src/components/Properties/PropertiesPanel.tsx | 6 +++--- src/locales/ar.ts | 1 + src/locales/bg.ts | 1 + src/locales/cs.ts | 1 + src/locales/da.ts | 1 + src/locales/de.ts | 1 + src/locales/el.ts | 1 + src/locales/en.ts | 1 + src/locales/es.ts | 1 + src/locales/et.ts | 1 + src/locales/fa.ts | 1 + src/locales/fi.ts | 1 + src/locales/fr.ts | 1 + src/locales/he.ts | 1 + src/locales/hr.ts | 1 + src/locales/hu.ts | 1 + src/locales/it.ts | 1 + src/locales/ja.ts | 1 + src/locales/ko.ts | 1 + src/locales/lt.ts | 1 + src/locales/lv.ts | 1 + src/locales/nl.ts | 1 + src/locales/no.ts | 1 + src/locales/pl.ts | 1 + src/locales/pt.ts | 1 + src/locales/ro.ts | 1 + src/locales/sk.ts | 1 + src/locales/sl.ts | 1 + src/locales/sr.ts | 1 + src/locales/sv.ts | 1 + src/locales/tr.ts | 1 + src/locales/zh-hans.ts | 1 + src/locales/zh-hant.ts | 1 + 33 files changed, 35 insertions(+), 3 deletions(-) diff --git a/src/components/Properties/PropertiesPanel.tsx b/src/components/Properties/PropertiesPanel.tsx index f649c9fb..eabc5c70 100644 --- a/src/components/Properties/PropertiesPanel.tsx +++ b/src/components/Properties/PropertiesPanel.tsx @@ -316,7 +316,7 @@ function LabelConfigPanel({ }) } > - + @@ -400,7 +400,7 @@ function LabelConfigPanel({ }) } > - + @@ -419,7 +419,7 @@ function LabelConfigPanel({ }) } > - + diff --git a/src/locales/ar.ts b/src/locales/ar.ts index baf7e97b..7c2c52d1 100644 --- a/src/locales/ar.ts +++ b/src/locales/ar.ts @@ -68,6 +68,7 @@ const ar = { mediaModeD: 'D — قاطع', mediaModeK: 'K — كشك', labelShift: 'إزاحة الملصق (dots)', + printerDefault: 'الإعداد الافتراضي للطابعة', printerSettingsHeading: 'إعدادات الطابعة (اختياري)', printSpeed: 'سرعة الطباعة (ips، 2-14)', printSpeedHint: 'خاص بالطابعة. اتركه فارغًا لاستخدام القيمة الافتراضية.', diff --git a/src/locales/bg.ts b/src/locales/bg.ts index 996c16f8..af9ac738 100644 --- a/src/locales/bg.ts +++ b/src/locales/bg.ts @@ -68,6 +68,7 @@ const bg = { mediaModeD: 'D — Резач', mediaModeK: 'K — Киоск', labelShift: 'Отместване на етикета (dots)', + printerDefault: 'По подразбиране на принтера', printerSettingsHeading: 'Настройки на принтера (по избор)', printSpeed: 'Скорост на печат (ips, 2-14)', printSpeedHint: 'Специфично за принтера. Оставете празно за стойност по подразбиране.', diff --git a/src/locales/cs.ts b/src/locales/cs.ts index 122be706..032386b1 100644 --- a/src/locales/cs.ts +++ b/src/locales/cs.ts @@ -68,6 +68,7 @@ const cs = { mediaModeD: 'D — Řezačka', mediaModeK: 'K — Kiosek', labelShift: 'Posun štítku (dots)', + printerDefault: 'Výchozí tiskárny', printerSettingsHeading: 'Nastavení tiskárny (volitelné)', printSpeed: 'Rychlost tisku (ips, 2-14)', printSpeedHint: 'Specifické pro tiskárnu. Ponechte prázdné pro výchozí hodnotu tiskárny.', diff --git a/src/locales/da.ts b/src/locales/da.ts index 4f727948..83b16463 100644 --- a/src/locales/da.ts +++ b/src/locales/da.ts @@ -68,6 +68,7 @@ const da = { mediaModeD: 'D — Skærer', mediaModeK: 'K — Kiosk', labelShift: 'Etiketforskydning (dots)', + printerDefault: 'Printerens standard', printerSettingsHeading: 'Printerindstillinger (valgfrit)', printSpeed: 'Udskrivningshastighed (ips, 2-14)', printSpeedHint: 'Printerspecifik. Lad stå tom for printerens standardværdi.', diff --git a/src/locales/de.ts b/src/locales/de.ts index 9bd5dfae..0313d557 100644 --- a/src/locales/de.ts +++ b/src/locales/de.ts @@ -68,6 +68,7 @@ const de = { mediaModeD: 'D — Cutter', mediaModeK: 'K — Kiosk', labelShift: 'Label-Versatz (Punkte)', + printerDefault: 'Drucker-Standard', printerSettingsHeading: 'Druckereinstellungen (optional)', printSpeed: 'Druckgeschwindigkeit (ips, 2-14)', printSpeedHint: 'Druckerspezifisch. Leer lassen für Drucker-Standard.', diff --git a/src/locales/el.ts b/src/locales/el.ts index 761f2845..94ce4ced 100644 --- a/src/locales/el.ts +++ b/src/locales/el.ts @@ -68,6 +68,7 @@ const el = { mediaModeD: 'D — Κοπτικό', mediaModeK: 'K — Κιόσκι', labelShift: 'Μετατόπιση ετικέτας (dots)', + printerDefault: 'Προεπιλογή εκτυπωτή', printerSettingsHeading: 'Ρυθμίσεις εκτυπωτή (προαιρετικό)', printSpeed: 'Ταχύτητα εκτύπωσης (ips, 2-14)', printSpeedHint: 'Ειδικό για τον εκτυπωτή. Αφήστε κενό για την προεπιλογή.', diff --git a/src/locales/en.ts b/src/locales/en.ts index 36516520..1edb0d32 100644 --- a/src/locales/en.ts +++ b/src/locales/en.ts @@ -68,6 +68,7 @@ const en = { mediaModeD: 'D — Cutter', mediaModeK: 'K — Kiosk', labelShift: 'Label shift (dots)', + printerDefault: 'Printer default', printerSettingsHeading: 'Printer settings (optional)', printSpeed: 'Print speed (ips, 2-14)', printSpeedHint: 'Printer-specific. Leave empty to use the printer default.', diff --git a/src/locales/es.ts b/src/locales/es.ts index e7c3d4af..946509b1 100644 --- a/src/locales/es.ts +++ b/src/locales/es.ts @@ -68,6 +68,7 @@ const es = { mediaModeD: 'D — Cutter', mediaModeK: 'K — Kiosco', labelShift: 'Desplazamiento de etiqueta (dots)', + printerDefault: 'Predeterminado de la impresora', printerSettingsHeading: 'Ajustes de impresora (opcional)', printSpeed: 'Velocidad de impresión (ips, 2-14)', printSpeedHint: 'Específico de la impresora. Dejar vacío para usar el valor predeterminado.', diff --git a/src/locales/et.ts b/src/locales/et.ts index 004c7f6f..57947bfb 100644 --- a/src/locales/et.ts +++ b/src/locales/et.ts @@ -68,6 +68,7 @@ const et = { mediaModeD: 'D — Lõikur', mediaModeK: 'K — Kiosk', labelShift: 'Etiketi nihe (dots)', + printerDefault: 'Printeri vaikeväärtus', printerSettingsHeading: 'Printeri sätted (valikuline)', printSpeed: 'Trükikiirus (ips, 2-14)', printSpeedHint: 'Printerispetsiifiline. Vaikeväärtuse kasutamiseks jäta tühjaks.', diff --git a/src/locales/fa.ts b/src/locales/fa.ts index bf5c70df..b2d1dcce 100644 --- a/src/locales/fa.ts +++ b/src/locales/fa.ts @@ -68,6 +68,7 @@ const fa = { mediaModeD: 'D — برش', mediaModeK: 'K — کیوسک', labelShift: 'جابجایی برچسب (dots)', + printerDefault: 'پیش‌فرض چاپگر', printerSettingsHeading: 'تنظیمات چاپگر (اختیاری)', printSpeed: 'سرعت چاپ (ips, 2-14)', printSpeedHint: 'مختص چاپگر. برای استفاده از پیش‌فرض خالی بگذارید.', diff --git a/src/locales/fi.ts b/src/locales/fi.ts index 14e19360..51fd3101 100644 --- a/src/locales/fi.ts +++ b/src/locales/fi.ts @@ -68,6 +68,7 @@ const fi = { mediaModeD: 'D — Leikkuri', mediaModeK: 'K — Kioski', labelShift: 'Tarrasiirtymä (dots)', + printerDefault: 'Tulostimen oletus', printerSettingsHeading: 'Tulostimen asetukset (valinnainen)', printSpeed: 'Tulostusnopeus (ips, 2-14)', printSpeedHint: 'Tulostinkohtainen. Jätä tyhjäksi käyttääksesi oletusarvoa.', diff --git a/src/locales/fr.ts b/src/locales/fr.ts index 1f06a7db..9e7f086b 100644 --- a/src/locales/fr.ts +++ b/src/locales/fr.ts @@ -68,6 +68,7 @@ const fr = { mediaModeD: 'D — Cutter', mediaModeK: 'K — Kiosque', labelShift: 'Décalage d\'étiquette (dots)', + printerDefault: 'Valeur par défaut de l\'imprimante', printerSettingsHeading: 'Paramètres d\'imprimante (facultatif)', printSpeed: 'Vitesse d\'impression (ips, 2-14)', printSpeedHint: 'Spécifique à l\'imprimante. Laisser vide pour la valeur par défaut.', diff --git a/src/locales/he.ts b/src/locales/he.ts index 0eb76e86..eb7d01d8 100644 --- a/src/locales/he.ts +++ b/src/locales/he.ts @@ -68,6 +68,7 @@ const he = { mediaModeD: 'D — חותך', mediaModeK: 'K — קיוסק', labelShift: 'הסטת תווית (dots)', + printerDefault: 'ברירת מחדל של המדפסת', printerSettingsHeading: 'הגדרות מדפסת (אופציונלי)', printSpeed: 'מהירות הדפסה (ips, 2-14)', printSpeedHint: 'ספציפי למדפסת. השאר ריק לערך ברירת המחדל.', diff --git a/src/locales/hr.ts b/src/locales/hr.ts index 419027f8..be9d2e4c 100644 --- a/src/locales/hr.ts +++ b/src/locales/hr.ts @@ -68,6 +68,7 @@ const hr = { mediaModeD: 'D — Rezač', mediaModeK: 'K — Kiosk', labelShift: 'Pomak naljepnice (dots)', + printerDefault: 'Zadano za pisač', printerSettingsHeading: 'Postavke pisača (neobavezno)', printSpeed: 'Brzina ispisa (ips, 2-14)', printSpeedHint: 'Specifično za pisač. Ostavite prazno za zadanu vrijednost.', diff --git a/src/locales/hu.ts b/src/locales/hu.ts index 5b72df0f..bdcc01ce 100644 --- a/src/locales/hu.ts +++ b/src/locales/hu.ts @@ -68,6 +68,7 @@ const hu = { mediaModeD: 'D — Vágó', mediaModeK: 'K — Kioszk', labelShift: 'Címke eltolás (dots)', + printerDefault: 'Nyomtató alapértelmezett', printerSettingsHeading: 'Nyomtatóbeállítások (opcionális)', printSpeed: 'Nyomtatási sebesség (ips, 2-14)', printSpeedHint: 'Nyomtatófüggő. Hagyja üresen a nyomtató alapértelmezett értékéhez.', diff --git a/src/locales/it.ts b/src/locales/it.ts index 90f80129..d5816a5c 100644 --- a/src/locales/it.ts +++ b/src/locales/it.ts @@ -68,6 +68,7 @@ const it = { mediaModeD: 'D — Cutter', mediaModeK: 'K — Chiosco', labelShift: 'Spostamento etichetta (dots)', + printerDefault: 'Predefinito stampante', printerSettingsHeading: 'Impostazioni stampante (opzionale)', printSpeed: 'Velocità di stampa (ips, 2-14)', printSpeedHint: 'Specifico per stampante. Lascia vuoto per usare il valore predefinito.', diff --git a/src/locales/ja.ts b/src/locales/ja.ts index 68f34d18..d5bcc6e8 100644 --- a/src/locales/ja.ts +++ b/src/locales/ja.ts @@ -68,6 +68,7 @@ const ja = { mediaModeD: 'D — カッター', mediaModeK: 'K — キオスク', labelShift: 'ラベルシフト (dots)', + printerDefault: 'プリンターの既定値', printerSettingsHeading: 'プリンター設定(オプション)', printSpeed: '印刷速度 (ips, 2-14)', printSpeedHint: 'プリンター固有。デフォルト値を使用するには空のままにします。', diff --git a/src/locales/ko.ts b/src/locales/ko.ts index dcf868e9..857446c9 100644 --- a/src/locales/ko.ts +++ b/src/locales/ko.ts @@ -68,6 +68,7 @@ const ko = { mediaModeD: 'D — 커터', mediaModeK: 'K — 키오스크', labelShift: '라벨 이동 (dots)', + printerDefault: '프린터 기본값', printerSettingsHeading: '프린터 설정 (선택 사항)', printSpeed: '인쇄 속도 (ips, 2-14)', printSpeedHint: '프린터별로 다릅니다. 기본값을 사용하려면 비워 두세요.', diff --git a/src/locales/lt.ts b/src/locales/lt.ts index 342a82ba..89ba678a 100644 --- a/src/locales/lt.ts +++ b/src/locales/lt.ts @@ -68,6 +68,7 @@ const lt = { mediaModeD: 'D — Pjaustuvas', mediaModeK: 'K — Kioskas', labelShift: 'Etiketės poslinkis (dots)', + printerDefault: 'Spausdintuvo numatytasis', printerSettingsHeading: 'Spausdintuvo nustatymai (neprivaloma)', printSpeed: 'Spausdinimo greitis (ips, 2-14)', printSpeedHint: 'Priklauso nuo spausdintuvo. Palikite tuščią numatytąjai reikšmei.', diff --git a/src/locales/lv.ts b/src/locales/lv.ts index b763982d..5fe0c76c 100644 --- a/src/locales/lv.ts +++ b/src/locales/lv.ts @@ -68,6 +68,7 @@ const lv = { mediaModeD: 'D — Griezējs', mediaModeK: 'K — Kiosks', labelShift: 'Etiķetes nobīde (dots)', + printerDefault: 'Printera noklusējums', printerSettingsHeading: 'Printera iestatījumi (neobligāti)', printSpeed: 'Drukāšanas ātrums (ips, 2-14)', printSpeedHint: 'Atkarīgs no printera. Atstājiet tukšu, lai izmantotu noklusējumu.', diff --git a/src/locales/nl.ts b/src/locales/nl.ts index 7c833e16..0079d09c 100644 --- a/src/locales/nl.ts +++ b/src/locales/nl.ts @@ -68,6 +68,7 @@ const nl = { mediaModeD: 'D — Snijder', mediaModeK: 'K — Kiosk', labelShift: 'Etiketverschuiving (dots)', + printerDefault: 'Printerstandaard', printerSettingsHeading: 'Printerinstellingen (optioneel)', printSpeed: 'Afdruksnelheid (ips, 2-14)', printSpeedHint: 'Printerspecifiek. Laat leeg om de standaardwaarde te gebruiken.', diff --git a/src/locales/no.ts b/src/locales/no.ts index 612cfc76..0fbceff6 100644 --- a/src/locales/no.ts +++ b/src/locales/no.ts @@ -68,6 +68,7 @@ const no = { mediaModeD: 'D — Kutter', mediaModeK: 'K — Kiosk', labelShift: 'Etikettforskyvning (dots)', + printerDefault: 'Skriverens standard', printerSettingsHeading: 'Skriverinnstillinger (valgfritt)', printSpeed: 'Utskriftshastighet (ips, 2-14)', printSpeedHint: 'Skriverspesifikk. La stå tom for skriverens standardverdi.', diff --git a/src/locales/pl.ts b/src/locales/pl.ts index 28377ef7..955eadc3 100644 --- a/src/locales/pl.ts +++ b/src/locales/pl.ts @@ -68,6 +68,7 @@ const pl = { mediaModeD: 'D — Cutter', mediaModeK: 'K — Kiosk', labelShift: 'Przesunięcie etykiety (dots)', + printerDefault: 'Domyślne drukarki', printerSettingsHeading: 'Ustawienia drukarki (opcjonalne)', printSpeed: 'Prędkość drukowania (ips, 2-14)', printSpeedHint: 'Specyficzne dla drukarki. Pozostaw puste, aby użyć wartości domyślnej.', diff --git a/src/locales/pt.ts b/src/locales/pt.ts index 46ced5f6..60550b23 100644 --- a/src/locales/pt.ts +++ b/src/locales/pt.ts @@ -68,6 +68,7 @@ const pt = { mediaModeD: 'D — Cutter', mediaModeK: 'K — Quiosque', labelShift: 'Deslocamento da etiqueta (dots)', + printerDefault: 'Predefinição da impressora', printerSettingsHeading: 'Definições da impressora (opcional)', printSpeed: 'Velocidade de impressão (ips, 2-14)', printSpeedHint: 'Específico da impressora. Deixe vazio para usar o padrão da impressora.', diff --git a/src/locales/ro.ts b/src/locales/ro.ts index 4a7a7d95..34eee4d9 100644 --- a/src/locales/ro.ts +++ b/src/locales/ro.ts @@ -68,6 +68,7 @@ const ro = { mediaModeD: 'D — Cutter', mediaModeK: 'K — Chioșc', labelShift: 'Deplasare etichetă (dots)', + printerDefault: 'Implicit imprimantă', printerSettingsHeading: 'Setări imprimantă (opțional)', printSpeed: 'Viteza de imprimare (ips, 2-14)', printSpeedHint: 'Specific imprimantei. Lăsați gol pentru valoarea implicită.', diff --git a/src/locales/sk.ts b/src/locales/sk.ts index e9528649..4390cceb 100644 --- a/src/locales/sk.ts +++ b/src/locales/sk.ts @@ -68,6 +68,7 @@ const sk = { mediaModeD: 'D — Rezačka', mediaModeK: 'K — Kiosk', labelShift: 'Posun štítka (dots)', + printerDefault: 'Predvolené tlačiarne', printerSettingsHeading: 'Nastavenia tlačiarne (voliteľné)', printSpeed: 'Rýchlosť tlače (ips, 2-14)', printSpeedHint: 'Špecifické pre tlačiareň. Ponechajte prázdne pre predvolenú hodnotu.', diff --git a/src/locales/sl.ts b/src/locales/sl.ts index b5910d8d..fb7b8866 100644 --- a/src/locales/sl.ts +++ b/src/locales/sl.ts @@ -68,6 +68,7 @@ const sl = { mediaModeD: 'D — Rezalnik', mediaModeK: 'K — Kiosk', labelShift: 'Zamik nalepke (dots)', + printerDefault: 'Privzeto tiskalnika', printerSettingsHeading: 'Nastavitve tiskalnika (izbirno)', printSpeed: 'Hitrost tiskanja (ips, 2-14)', printSpeedHint: 'Specifično za tiskalnik. Pustite prazno za privzeto vrednost.', diff --git a/src/locales/sr.ts b/src/locales/sr.ts index 37125282..1f9e3e1b 100644 --- a/src/locales/sr.ts +++ b/src/locales/sr.ts @@ -68,6 +68,7 @@ const sr = { mediaModeD: 'D — Секач', mediaModeK: 'K — Киоск', labelShift: 'Померај етикете (dots)', + printerDefault: 'Подразумевано за штампач', printerSettingsHeading: 'Подешавања штампача (опционално)', printSpeed: 'Брзина штампе (ips, 2-14)', printSpeedHint: 'Специфично за штампач. Оставите празно за подразумевану вредност.', diff --git a/src/locales/sv.ts b/src/locales/sv.ts index f63d16a8..8a13cac4 100644 --- a/src/locales/sv.ts +++ b/src/locales/sv.ts @@ -68,6 +68,7 @@ const sv = { mediaModeD: 'D — Skärare', mediaModeK: 'K — Kiosk', labelShift: 'Etikettförskjutning (dots)', + printerDefault: 'Skrivarens standard', printerSettingsHeading: 'Skrivarinställningar (valfritt)', printSpeed: 'Utskriftshastighet (ips, 2-14)', printSpeedHint: 'Skrivarspecifik. Lämna tom för skrivarens standardvärde.', diff --git a/src/locales/tr.ts b/src/locales/tr.ts index 2c5df388..d31a24d8 100644 --- a/src/locales/tr.ts +++ b/src/locales/tr.ts @@ -68,6 +68,7 @@ const tr = { mediaModeD: 'D — Kesici', mediaModeK: 'K — Kiosk', labelShift: 'Etiket kaydırma (dots)', + printerDefault: 'Yazıcı varsayılanı', printerSettingsHeading: 'Yazıcı ayarları (isteğe bağlı)', printSpeed: 'Yazdırma hızı (ips, 2-14)', printSpeedHint: 'Yazıcıya özel. Varsayılan için boş bırakın.', diff --git a/src/locales/zh-hans.ts b/src/locales/zh-hans.ts index 761ef808..37fa2b55 100644 --- a/src/locales/zh-hans.ts +++ b/src/locales/zh-hans.ts @@ -68,6 +68,7 @@ const zhHans = { mediaModeD: 'D — 切刀', mediaModeK: 'K — 自助终端', labelShift: '标签偏移 (dots)', + printerDefault: '打印机默认值', printerSettingsHeading: '打印机设置(可选)', printSpeed: '打印速度 (ips, 2-14)', printSpeedHint: '与打印机相关。留空使用打印机默认值。', diff --git a/src/locales/zh-hant.ts b/src/locales/zh-hant.ts index 2f74627b..6cc82cc4 100644 --- a/src/locales/zh-hant.ts +++ b/src/locales/zh-hant.ts @@ -68,6 +68,7 @@ const zhHant = { mediaModeD: 'D — 裁刀', mediaModeK: 'K — 自助終端', labelShift: '標籤偏移 (dots)', + printerDefault: '印表機預設值', printerSettingsHeading: '印表機設定(可選)', printSpeed: '列印速度 (ips, 2-14)', printSpeedHint: '與印表機相關。留空使用印表機預設值。', From 0425a316dd39355cff8e96c3ff83fd7455445628 Mon Sep 17 00:00:00 2001 From: u8array Date: Thu, 7 May 2026 18:26:21 +0200 Subject: [PATCH 08/10] fix(ui): mergeDefaultFont distinguishes cleared input from absent key Using `??` to merge the patch into the current value collapsed two distinct states: "this key was not in the patch" and "the user just cleared this input". The latter passes `{ height: undefined }`, but `undefined ?? current.height` falls through to the old value, so clearing the height field had no effect on stored defaultFont. Replaces `??` with an `in` check so an explicit `undefined` overrides current. --- src/components/Properties/PropertiesPanel.tsx | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/components/Properties/PropertiesPanel.tsx b/src/components/Properties/PropertiesPanel.tsx index eabc5c70..74d96f75 100644 --- a/src/components/Properties/PropertiesPanel.tsx +++ b/src/components/Properties/PropertiesPanel.tsx @@ -481,8 +481,11 @@ function mergeDefaultFont( current: LabelConfig["defaultFont"], patch: { fontId?: string; height?: number }, ): LabelConfig["defaultFont"] { - const fontId = patch.fontId ?? current?.fontId ?? ""; - const height = patch.height ?? current?.height; + // `in` distinguishes "key absent from patch" (keep current value) from + // "key set to undefined" (user just cleared the input). `??` would conflate + // the two and silently keep the old value when the user clears a field. + const fontId = "fontId" in patch ? patch.fontId : current?.fontId; + const height = "height" in patch ? patch.height : current?.height; if (!fontId || height === undefined) return undefined; return { fontId, height }; } From 846441dedd92b1a29728700e0f0385eb314f74bd Mon Sep 17 00:00:00 2001 From: u8array Date: Thu, 7 May 2026 18:33:43 +0200 Subject: [PATCH 09/10] refactor(label): flatten defaultFont into two top-level optional fields Replaces the nested defaultFont: { fontId, height } object with two independent optional fields defaultFontId and defaultFontHeight. The nested shape forced an all-or-nothing UI: typing one field at a time either lost the partial input (deadlock) or required a fragile merge helper that conflated 'absent' and 'cleared' states. Flat fields let each input persist independently; the generator emits ^CF only when both are set. Removes mergeDefaultFont; tests updated including a new pair that asserts ^CF is omitted on partial state. --- src/components/Properties/PropertiesPanel.tsx | 27 +++---------------- src/lib/zplGenerator.test.ts | 23 ++++++++++++---- src/lib/zplGenerator.ts | 4 +-- src/lib/zplParser.test.ts | 5 ++-- src/lib/zplParser.ts | 5 ++-- src/types/ObjectType.ts | 8 ++---- 6 files changed, 32 insertions(+), 40 deletions(-) diff --git a/src/components/Properties/PropertiesPanel.tsx b/src/components/Properties/PropertiesPanel.tsx index 74d96f75..4b6f3592 100644 --- a/src/components/Properties/PropertiesPanel.tsx +++ b/src/components/Properties/PropertiesPanel.tsx @@ -436,13 +436,9 @@ function LabelConfigPanel({ type="text" className={inputCls} maxLength={2} - value={label.defaultFont?.fontId ?? ""} + value={label.defaultFontId ?? ""} onChange={(e) => - onUpdate({ - defaultFont: mergeDefaultFont(label.defaultFont, { - fontId: e.target.value, - }), - }) + onUpdate({ defaultFontId: e.target.value || undefined }) } /> @@ -454,12 +450,10 @@ function LabelConfigPanel({ type="number" className={inputCls} min={1} - value={label.defaultFont?.height ?? ""} + value={label.defaultFontHeight ?? ""} onChange={(e) => onUpdate({ - defaultFont: mergeDefaultFont(label.defaultFont, { - height: parseIntOrUndef(e.target.value), - }), + defaultFontHeight: parseIntOrUndef(e.target.value), }) } /> @@ -476,16 +470,3 @@ function parseIntOrUndef(raw: string): number | undefined { const n = parseInt(raw, 10); return isNaN(n) ? undefined : n; } - -function mergeDefaultFont( - current: LabelConfig["defaultFont"], - patch: { fontId?: string; height?: number }, -): LabelConfig["defaultFont"] { - // `in` distinguishes "key absent from patch" (keep current value) from - // "key set to undefined" (user just cleared the input). `??` would conflate - // the two and silently keep the old value when the user clears a field. - const fontId = "fontId" in patch ? patch.fontId : current?.fontId; - const height = "height" in patch ? patch.height : current?.height; - if (!fontId || height === undefined) return undefined; - return { fontId, height }; -} diff --git a/src/lib/zplGenerator.test.ts b/src/lib/zplGenerator.test.ts index 47501bc8..2025a9d8 100644 --- a/src/lib/zplGenerator.test.ts +++ b/src/lib/zplGenerator.test.ts @@ -88,14 +88,24 @@ describe('generateZPL — printer params', () => { expect(generateZPL(BASE_LABEL, [])).not.toContain('^PO'); }); - it('emits ^CF when defaultFont is set', () => { + it('emits ^CF when both defaultFontId and defaultFontHeight are set', () => { const zpl = generateZPL( - { ...BASE_LABEL, defaultFont: { fontId: '0', height: 30 } }, + { ...BASE_LABEL, defaultFontId: '0', defaultFontHeight: 30 }, [], ); expect(zpl).toContain('^CF0,30'); }); + it('omits ^CF when defaultFontId is set without defaultFontHeight', () => { + expect(generateZPL({ ...BASE_LABEL, defaultFontId: '0' }, [])) + .not.toContain('^CF'); + }); + + it('omits ^CF when defaultFontHeight is set without defaultFontId', () => { + expect(generateZPL({ ...BASE_LABEL, defaultFontHeight: 30 }, [])) + .not.toContain('^CF'); + }); + it('emits printer params in canonical header order before ^LS', () => { const zpl = generateZPL( { @@ -105,7 +115,8 @@ describe('generateZPL — printer params', () => { printSpeed: 6, darkness: 10, printOrientation: 'I', - defaultFont: { fontId: '0', height: 30 }, + defaultFontId: '0', + defaultFontHeight: 30, labelShift: 5, }, [], @@ -215,7 +226,8 @@ describe('generateZPL — parse/generate roundtrip', () => { darkness: 0, mediaType: 'D', printOrientation: 'I', - defaultFont: { fontId: '0', height: 30 }, + defaultFontId: '0', + defaultFontHeight: 30, }; const regenerated = generateZPL(label, []); const { labelConfig } = parseZPL(regenerated, BASE_LABEL.dpmm); @@ -223,6 +235,7 @@ describe('generateZPL — parse/generate roundtrip', () => { expect(labelConfig.darkness).toBe(0); expect(labelConfig.mediaType).toBe('D'); expect(labelConfig.printOrientation).toBe('I'); - expect(labelConfig.defaultFont).toEqual({ fontId: '0', height: 30 }); + expect(labelConfig.defaultFontId).toBe('0'); + expect(labelConfig.defaultFontHeight).toBe(30); }); }); diff --git a/src/lib/zplGenerator.ts b/src/lib/zplGenerator.ts index 7d7b8e0f..2d4ca90a 100644 --- a/src/lib/zplGenerator.ts +++ b/src/lib/zplGenerator.ts @@ -30,8 +30,8 @@ export function generateZPL(label: LabelConfig, objects: LabelObject[]): string // darkness=0 is a valid value (printer baseline), so check undefined explicitly. if (label.darkness !== undefined) lines.push(`^MD${label.darkness}`); if (label.printOrientation) lines.push(`^PO${label.printOrientation}`); - if (label.defaultFont) { - lines.push(`^CF${label.defaultFont.fontId},${label.defaultFont.height}`); + if (label.defaultFontId && label.defaultFontHeight !== undefined) { + lines.push(`^CF${label.defaultFontId},${label.defaultFontHeight}`); } if (label.labelShift) lines.push(`^LS${label.labelShift}`); diff --git a/src/lib/zplParser.test.ts b/src/lib/zplParser.test.ts index bd11cfa4..d54a3ced 100644 --- a/src/lib/zplParser.test.ts +++ b/src/lib/zplParser.test.ts @@ -546,9 +546,10 @@ describe('parseZPL — printer params', () => { expect(parseZPL('^XA^POI^XZ', 8).labelConfig.printOrientation).toBe('I'); }); - it('parses ^CF default font with id and height', () => { + it('parses ^CF into defaultFontId and defaultFontHeight', () => { const { labelConfig } = parseZPL('^XA^CF0,40^XZ', 8); - expect(labelConfig.defaultFont).toEqual({ fontId: '0', height: 40 }); + expect(labelConfig.defaultFontId).toBe('0'); + expect(labelConfig.defaultFontHeight).toBe(40); }); }); diff --git a/src/lib/zplParser.ts b/src/lib/zplParser.ts index a786d20f..d6050562 100644 --- a/src/lib/zplParser.ts +++ b/src/lib/zplParser.ts @@ -649,8 +649,9 @@ export function parseZPL(zpl: string, dpmm = 8): ParsedZPL { const explicitHeight = parseInt(p[1] ?? "", 10); cfHeight = isNaN(explicitHeight) ? cfHeight : explicitHeight; cfWidth = int(p[2], cfWidth); - if (fontId && !isNaN(explicitHeight) && explicitHeight > 0) { - labelConfig.defaultFont = { fontId, height: explicitHeight }; + if (fontId) labelConfig.defaultFontId = fontId; + if (!isNaN(explicitHeight) && explicitHeight > 0) { + labelConfig.defaultFontHeight = explicitHeight; } }, diff --git a/src/types/ObjectType.ts b/src/types/ObjectType.ts index 2efb4a73..929d18ff 100644 --- a/src/types/ObjectType.ts +++ b/src/types/ObjectType.ts @@ -12,12 +12,8 @@ export const labelConfigSchema = z.object({ darkness: z.number().int().min(-30).max(30).optional(), mediaType: z.enum(['T', 'D']).optional(), printOrientation: z.enum(['N', 'I']).optional(), - defaultFont: z - .object({ - fontId: z.string().min(1), - height: z.number().int().positive(), - }) - .optional(), + defaultFontId: z.string().min(1).optional(), + defaultFontHeight: z.number().int().positive().optional(), }); export type LabelConfig = z.infer; From dc1c69c44e4659b428addc90ce49dc3948ff9ade Mon Sep 17 00:00:00 2001 From: u8array Date: Thu, 7 May 2026 18:41:15 +0200 Subject: [PATCH 10/10] feat(zpl): emit partial ^CF for round-trip fidelity The ^CF parameters are individually optional per Zebra spec: ^CF0 sets the font without changing the height, ^CF,30 sets the height without changing the font. Previously the generator required both fields and would silently drop a partial command on round-trip. Now emits whichever fields are set, with explicit round-trip tests for both partial forms. --- src/lib/zplGenerator.test.ts | 29 ++++++++++++++++++++++++----- src/lib/zplGenerator.ts | 10 ++++++++-- 2 files changed, 32 insertions(+), 7 deletions(-) diff --git a/src/lib/zplGenerator.test.ts b/src/lib/zplGenerator.test.ts index 2025a9d8..5f8c2fc3 100644 --- a/src/lib/zplGenerator.test.ts +++ b/src/lib/zplGenerator.test.ts @@ -96,14 +96,19 @@ describe('generateZPL — printer params', () => { expect(zpl).toContain('^CF0,30'); }); - it('omits ^CF when defaultFontId is set without defaultFontHeight', () => { - expect(generateZPL({ ...BASE_LABEL, defaultFontId: '0' }, [])) - .not.toContain('^CF'); + it('emits ^CF{id} when only defaultFontId is set', () => { + const zpl = generateZPL({ ...BASE_LABEL, defaultFontId: '0' }, []); + expect(zpl).toContain('^CF0'); + expect(zpl).not.toContain('^CF0,'); }); - it('omits ^CF when defaultFontHeight is set without defaultFontId', () => { + it('emits ^CF,{height} when only defaultFontHeight is set', () => { expect(generateZPL({ ...BASE_LABEL, defaultFontHeight: 30 }, [])) - .not.toContain('^CF'); + .toContain('^CF,30'); + }); + + it('omits ^CF when neither defaultFont field is set', () => { + expect(generateZPL(BASE_LABEL, [])).not.toContain('^CF'); }); it('emits printer params in canonical header order before ^LS', () => { @@ -238,4 +243,18 @@ describe('generateZPL — parse/generate roundtrip', () => { expect(labelConfig.defaultFontId).toBe('0'); expect(labelConfig.defaultFontHeight).toBe(30); }); + + it('preserves partial ^CF (id only) through generate -> parse', () => { + const regenerated = generateZPL({ ...BASE_LABEL, defaultFontId: 'A' }, []); + const { labelConfig } = parseZPL(regenerated, BASE_LABEL.dpmm); + expect(labelConfig.defaultFontId).toBe('A'); + expect(labelConfig.defaultFontHeight).toBeUndefined(); + }); + + it('preserves partial ^CF (height only) through generate -> parse', () => { + const regenerated = generateZPL({ ...BASE_LABEL, defaultFontHeight: 25 }, []); + const { labelConfig } = parseZPL(regenerated, BASE_LABEL.dpmm); + expect(labelConfig.defaultFontId).toBeUndefined(); + expect(labelConfig.defaultFontHeight).toBe(25); + }); }); diff --git a/src/lib/zplGenerator.ts b/src/lib/zplGenerator.ts index 2d4ca90a..f489d1fd 100644 --- a/src/lib/zplGenerator.ts +++ b/src/lib/zplGenerator.ts @@ -30,8 +30,14 @@ export function generateZPL(label: LabelConfig, objects: LabelObject[]): string // darkness=0 is a valid value (printer baseline), so check undefined explicitly. if (label.darkness !== undefined) lines.push(`^MD${label.darkness}`); if (label.printOrientation) lines.push(`^PO${label.printOrientation}`); - if (label.defaultFontId && label.defaultFontHeight !== undefined) { - lines.push(`^CF${label.defaultFontId},${label.defaultFontHeight}`); + // ^CF parameters are individually optional per Zebra spec: ^CF0 sets the + // font only, ^CF,30 sets the height only. Preserves round-trip fidelity + // when an imported label used a partial command. + if (label.defaultFontId || label.defaultFontHeight !== undefined) { + const id = label.defaultFontId ?? ""; + const height = + label.defaultFontHeight !== undefined ? `,${label.defaultFontHeight}` : ""; + lines.push(`^CF${id}${height}`); } if (label.labelShift) lines.push(`^LS${label.labelShift}`);