From fa1c46d9a5d2cdfc5e70211d6464e85a3d38b779 Mon Sep 17 00:00:00 2001 From: maziggy Date: Tue, 21 Apr 2026 09:46:42 +0200 Subject: [PATCH] feat(printers): show plate name on card for multi-plate active prints (#881) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When two printers were running different plates of the same multi-plate 3MF, the Printers page cards displayed the same file name on both and there was no way to tell them apart. The Queue view already had this information by cross-referencing the archive's plate list; the card didn't have the linkage. Expose `current_archive_id` (resolved by matching the MQTT `subtask_id` against `PrintArchive.subtask_id` — the bridge introduced in #972 for restart-resume) and `current_plate_id` (parsed from `gcode_file` by a new shared `parse_plate_id` helper) on the status endpoint. The helper is also called from the WebSocket push path so plate transitions reflect within 100 ms instead of waiting 30 s for the next REST poll; the archive id itself stays REST-only since it's stable for the life of a print and shouldn't make the push path touch the DB. The card fetches plate metadata via the same `api.getArchivePlates()` call QueuePage uses — shared React Query cache keeps it cheap across polls — and renders the actual plate name (or a "Plate N" fallback) only when `is_multi_plate` is true. Single-plate prints stay clean. Falls back to the previous `plate_N.gcode` regex path when there's no archive linkage (e.g. prints started directly from the printer LCD). Tests cover the plate-id extraction across Bambu Studio path shapes (backend parse_plate_id, printer_state_to_dict wiring) and the label override precedence in formatPrintName (frontend). --- CHANGELOG.md | 3 + backend/app/api/routes/printers.py | 23 ++ backend/app/schemas/printer.py | 8 + backend/app/services/printer_manager.py | 23 ++ .../unit/services/test_printer_manager.py | 58 ++++ .../pages/PrintersPageFormatPrintName.test.ts | 46 +++ frontend/src/api/client.ts | 2 + frontend/src/pages/PrintersPage.tsx | 36 ++- .../{index-DZZn3lqr.js => index-06WvZfP-.js} | 262 +++++++++--------- static/index.html | 2 +- 10 files changed, 327 insertions(+), 136 deletions(-) create mode 100644 frontend/src/__tests__/pages/PrintersPageFormatPrintName.test.ts rename static/assets/{index-DZZn3lqr.js => index-06WvZfP-.js} (87%) diff --git a/CHANGELOG.md b/CHANGELOG.md index e23989629..f9532aea3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,9 @@ All notable changes to Bambuddy will be documented in this file. ## [0.2.4b1] - Unreleased +### Improved +- **Printer Card Shows Plate Name on Multi-Plate Prints** ([#881](https://github.com/maziggy/bambuddy/issues/881)) — When two printers were running different plates of the same multi-plate 3MF, the Printers page cards displayed the same file name on both and gave no visual way to tell them apart. The Queue view already showed the plate name by querying the archive's plate list; the Printers page didn't have that linkage. The `GET /printers/{id}/status` endpoint now returns `current_archive_id` (resolved by matching the MQTT `subtask_id` against `PrintArchive.subtask_id`, the same bridge introduced in #972 for restart-resume) and `current_plate_id` (parsed from the MQTT `gcode_file` path by a new shared `parse_plate_id` helper that's also used by the WebSocket push path, so plate transitions within a running print reflect immediately instead of waiting 30 s for the next REST poll). The card fetches plate metadata via the same `api.getArchivePlates()` call the Queue page uses — shared React Query cache keeps it cheap across polls — and renders the actual plate name (or a "Plate N" fallback) only when the source 3MF is multi-plate, so single-plate prints stay noise-free. Falls back to the previous `plate_(\d+).gcode` regex when there's no archive linkage (e.g. prints started directly from the printer LCD). Regression tests cover the plate-id extraction across Bambu Studio path shapes and the label-override precedence in `formatPrintName`. Thanks to @stringham for the follow-up and screenshot. + ### Fixed - **Spoolman Iframe Blocked by CSP on HTTP Instances** ([#1054](https://github.com/maziggy/bambuddy/issues/1054)) — The Filament tab showed a blank page with a brief Spoolman flash on reload. Browser console reported `Content-Security-Policy: The page's settings blocked the loading of a resource (frame-src) at http://:7912/spool because it violates the following directive: "frame-src 'self' https:"`. Root cause: commit `53a70e37` (#995) tightened the CSP to allow external sidebar iframes but only whitelisted `https:`, overlooking that self-hosted services on LANs — Spoolman, OctoPrint, etc. — almost always run over plain HTTP. The `frame-src` directive now allows `http:` as well (`frame-src 'self' http: https:`), matching the `connect-src 'self' ws: wss:` pattern already used for WebSockets. `frame-ancestors 'none'` still prevents Bambuddy itself from being framed cross-origin. Thanks to @saint-hh for reporting. - **AMS-HT: Custom Filament Preset Reverts to "Generic" in UI After Configure** ([#1053](https://github.com/maziggy/bambuddy/issues/1053)) — After configuring an AMS-HT slot (HT-A/HT-B) with a custom Bambu Cloud preset (e.g. "Devil Design PLA Basic"), the slot card and Configure modal kept showing "Generic PLA" even though the `ams_filament_setting` command succeeded and BambuStudio / the printer's LCD both rendered the correct custom preset. Root cause: the `GET /api/v1/printers/{id}/slot-presets` endpoint keyed its response dict by `ams_id * 4 + tray_id`, which collapses cleanly to the same integer the frontend uses for regular AMS slots (0 through 15) but produces `128 * 4 + 0 = 512` for HT-A — a key nothing looks up. The frontend's PrintersPage HT render path calls `getGlobalTrayId(ams.id, …, false)` which returns the ams_id itself (`128` for HT-A), and SpoolBuddy's AMS page used a third, unrelated formula (`(amsId - 128) * 4 + trayId + 64 = 64`). All three agreed for regular AMS so the mismatch only surfaced on HT, where the saved preset name never reached the UI and the render fell through to `tray.tray_type` → rendered as "Generic PLA". Backend now keys the response via a `_slot_preset_key` helper that mirrors frontend `getGlobalTrayId` (HT → `ams_id`, regular/external → `ams_id * 4 + tray_id`), and SpoolBuddyAmsPage uses the shared `getGlobalTrayId` helper instead of its home-grown formula. Regression test covers the key scheme for regular, HT, and external slots. Thanks to @mrnoisytiger for the detailed reproduction. diff --git a/backend/app/api/routes/printers.py b/backend/app/api/routes/printers.py index 1afaf431e..45f26dbef 100644 --- a/backend/app/api/routes/printers.py +++ b/backend/app/api/routes/printers.py @@ -39,6 +39,7 @@ from backend.app.services.bambu_ftp import ( ) from backend.app.services.printer_manager import ( get_derived_status_name, + parse_plate_id, printer_manager, supports_chamber_temp, supports_drying, @@ -561,6 +562,26 @@ async def get_printer_status( k: v for k, v in temperatures.items() if k not in ("chamber", "chamber_target", "chamber_heating") } + # Resolve the active print's archive + plate (#881 follow-up): lets the + # printer card show the actual plate name for multi-plate 3MFs instead of + # just the 3MF filename. Only attempted for active prints, since subtask_id + # is only meaningful then. + current_archive_id: int | None = None + current_plate_id: int | None = None + if state.state in ("RUNNING", "PAUSE"): + current_plate_id = parse_plate_id(state.gcode_file) + if state.subtask_id: + from backend.app.models.archive import PrintArchive + + archive_row = await db.execute( + select(PrintArchive.id) + .where(PrintArchive.subtask_id == state.subtask_id) + .where(PrintArchive.printer_id == printer_id) + .order_by(PrintArchive.created_at.desc()) + .limit(1) + ) + current_archive_id = archive_row.scalar_one_or_none() + return PrinterStatus( id=printer_id, name=printer.name, @@ -612,6 +633,8 @@ async def get_printer_status( developer_mode=state.developer_mode if state else None, awaiting_plate_clear=printer_manager.is_awaiting_plate_clear(printer_id), supports_drying=supports_drying(printer.model, state.firmware_version), + current_archive_id=current_archive_id, + current_plate_id=current_plate_id, ) diff --git a/backend/app/schemas/printer.py b/backend/app/schemas/printer.py index 220f5598a..dc15d6463 100644 --- a/backend/app/schemas/printer.py +++ b/backend/app/schemas/printer.py @@ -273,3 +273,11 @@ class PrinterStatus(BaseModel): awaiting_plate_clear: bool = False # AMS drying support supports_drying: bool = False + # Linked archive for the active print (resolved via subtask_id). Frontend uses + # this to fetch plate metadata and show the plate name when the source 3MF is + # multi-plate (#881 follow-up). + current_archive_id: int | None = None + # 1-indexed plate number parsed from gcode_file (e.g. /Metadata/plate_2.gcode). + # Set for every active print regardless of plate count; the frontend decides + # whether to render it based on current_archive_id's is_multi_plate flag. + current_plate_id: int | None = None diff --git a/backend/app/services/printer_manager.py b/backend/app/services/printer_manager.py index 8f686984f..4b8445d5b 100644 --- a/backend/app/services/printer_manager.py +++ b/backend/app/services/printer_manager.py @@ -1,5 +1,6 @@ import asyncio import logging +import re import traceback from collections.abc import Callable @@ -622,6 +623,22 @@ def get_derived_status_name(state: PrinterState, model: str | None = None) -> st return None +_PLATE_ID_RE = re.compile(r"plate_(\d+)\.gcode") + + +def parse_plate_id(gcode_file: str | None) -> int | None: + """Extract the 1-indexed plate number from a Bambu gcode_file path. + + Returns None when the path is missing or has no `plate_N.gcode` segment. + Shared by the REST status route and the WebSocket push path so both agree + on the value sent to the frontend (#881 follow-up). + """ + if not gcode_file: + return None + match = _PLATE_ID_RE.search(gcode_file) + return int(match.group(1)) if match else None + + def printer_state_to_dict(state: PrinterState, printer_id: int | None = None, model: str | None = None) -> dict: """Convert PrinterState to a JSON-serializable dict. @@ -838,6 +855,12 @@ def printer_state_to_dict(state: PrinterState, printer_id: int | None = None, mo ], # AMS drying support "supports_drying": supports_drying(model, state.firmware_version), + # 1-indexed plate number parsed from gcode_file (e.g. /Metadata/plate_2.gcode). + # Pushed via WebSocket so the printer card picks up plate transitions within + # a multi-plate 3MF without waiting for the 30 s REST poll (#881 follow-up). + # current_archive_id is intentionally REST-only — it's stable for the life + # of a print and needs a DB lookup the WebSocket path shouldn't pay for. + "current_plate_id": parse_plate_id(state.gcode_file), } # Add cover URL if there's an active print and printer_id is provided # Include PAUSE state so skip objects modal can show cover diff --git a/backend/tests/unit/services/test_printer_manager.py b/backend/tests/unit/services/test_printer_manager.py index a2c407792..4511a5df2 100644 --- a/backend/tests/unit/services/test_printer_manager.py +++ b/backend/tests/unit/services/test_printer_manager.py @@ -13,6 +13,7 @@ from backend.app.services.printer_manager import ( get_derived_status_name, has_stg_cur_idle_bug, init_printer_connections, + parse_plate_id, printer_state_to_dict, supports_chamber_temp, supports_drying, @@ -834,6 +835,22 @@ class TestPrinterStateToDict: assert result["cover_url"] == "/api/v1/printers/1/cover" + def test_current_plate_id_extracted_from_gcode_file(self, mock_state): + """Verify current_plate_id is parsed from a Bambu plate path (#881).""" + mock_state.gcode_file = "/Metadata/plate_3.gcode" + + result = printer_state_to_dict(mock_state) + + assert result["current_plate_id"] == 3 + + def test_current_plate_id_none_when_no_plate_segment(self, mock_state): + """Verify current_plate_id stays None when gcode_file has no plate marker.""" + mock_state.gcode_file = "/sdcard/test.gcode" + + result = printer_state_to_dict(mock_state) + + assert result["current_plate_id"] is None + def test_cover_url_none_when_not_running(self, mock_state): """Verify cover_url is None when not printing.""" mock_state.state = "IDLE" @@ -1295,3 +1312,44 @@ class TestAmsChangeCallback: # This tests the callback signature assert manager._on_ams_change is not None assert callable(manager._on_ams_change) + + +class TestParsePlateId: + """Tests for parse_plate_id() — active-print plate extraction from gcode paths. + + Regression coverage for #881 follow-up: the REST /status endpoint and the + WebSocket push path both use this helper, so they must agree on the plate + number the frontend sees. + """ + + def test_bambu_metadata_path(self): + # Canonical path that Bambu Studio / OrcaSlicer stamp into the 3MF. + assert parse_plate_id("/Metadata/plate_2.gcode") == 2 + + def test_plate_one(self): + assert parse_plate_id("/Metadata/plate_1.gcode") == 1 + + def test_double_digit_plate(self): + assert parse_plate_id("/Metadata/plate_12.gcode") == 12 + + def test_none_input(self): + assert parse_plate_id(None) is None + + def test_empty_string(self): + assert parse_plate_id("") is None + + def test_path_without_plate_segment(self): + # Some firmware / slicers report a bare filename without the plate marker. + assert parse_plate_id("/upload/my-model.gcode") is None + + def test_similar_but_non_matching_names(self): + # "plate.gcode" (no number) and "nameplate_2.gcode" (substring) must not + # be mistaken for real plate markers. The regex anchors on `plate_`. + assert parse_plate_id("/Metadata/plate.gcode") is None + assert parse_plate_id("/plates/3.gcode") is None + + def test_substring_match_still_extracts(self): + # The regex isn't anchored to the start of a segment — any occurrence + # wins. This matches real Bambu paths where the segment is preceded by + # arbitrary directory noise, and matches the equivalent frontend regex. + assert parse_plate_id("/uploads/project/plate_5.gcode.md5") == 5 diff --git a/frontend/src/__tests__/pages/PrintersPageFormatPrintName.test.ts b/frontend/src/__tests__/pages/PrintersPageFormatPrintName.test.ts new file mode 100644 index 000000000..94a3cd8f0 --- /dev/null +++ b/frontend/src/__tests__/pages/PrintersPageFormatPrintName.test.ts @@ -0,0 +1,46 @@ +/** + * Unit tests for the formatPrintName helper on PrintersPage. + * + * Regression coverage for the #881 follow-up: when the printer card has an + * archive-linked plate label (resolved from the backend's current_archive_id + * + the archive's is_multi_plate plate list), the label must take precedence + * over the gcode_file regex fallback, including for plate 1. + */ + +import { describe, it, expect } from 'vitest'; +import { formatPrintName } from '../../pages/PrintersPage'; + +// Minimal translator stub: returns the fallback with the plate number interpolated +// the same way i18next would. Keeps these tests independent of the i18n setup. +const t = (_key: string, fallback: string, opts?: Record) => + fallback.replace('{{number}}', String(opts?.number ?? '')); + +describe('formatPrintName', () => { + it('returns the name unchanged when neither plate source is available', () => { + expect(formatPrintName('Benchy', null, t)).toBe('Benchy'); + }); + + it('appends gcode-file plate number only when > 1 (single-plate noise guard)', () => { + // Plate 1 from gcode_file alone is ambiguous (could be a single-plate 3MF) + // so the legacy fallback path keeps it silent. + expect(formatPrintName('Benchy', '/Metadata/plate_1.gcode', t)).toBe('Benchy'); + expect(formatPrintName('Benchy', '/Metadata/plate_2.gcode', t)).toBe('Benchy — Plate 2'); + }); + + it('uses plateLabel verbatim when provided, overriding the gcode_file fallback', () => { + // plateLabel comes from the archive lookup and is already disambiguated + // (only set when is_multi_plate === true). It must show even for plate 1. + expect(formatPrintName('Benchy', '/Metadata/plate_1.gcode', t, 'Plate 1')).toBe('Benchy — Plate 1'); + expect(formatPrintName('Benchy', '/Metadata/plate_2.gcode', t, 'Small Parts')).toBe('Benchy — Small Parts'); + }); + + it('returns empty string when name is missing, regardless of plate info', () => { + expect(formatPrintName(null, '/Metadata/plate_2.gcode', t)).toBe(''); + expect(formatPrintName(null, null, t, 'Plate 3')).toBe(''); + }); + + it('treats null/empty plateLabel as absent and falls through to gcode_file parsing', () => { + expect(formatPrintName('Benchy', '/Metadata/plate_2.gcode', t, null)).toBe('Benchy — Plate 2'); + expect(formatPrintName('Benchy', '/Metadata/plate_2.gcode', t, '')).toBe('Benchy — Plate 2'); + }); +}); diff --git a/frontend/src/api/client.ts b/frontend/src/api/client.ts index 1c109d405..71098852a 100644 --- a/frontend/src/api/client.ts +++ b/frontend/src/api/client.ts @@ -220,6 +220,8 @@ export interface PrinterStatus { state: string | null; current_print: string | null; subtask_name: string | null; + current_archive_id: number | null; + current_plate_id: number | null; gcode_file: string | null; progress: number | null; remaining_time: number | null; diff --git a/frontend/src/pages/PrintersPage.tsx b/frontend/src/pages/PrintersPage.tsx index fadb69cc3..4f8c18320 100644 --- a/frontend/src/pages/PrintersPage.tsx +++ b/frontend/src/pages/PrintersPage.tsx @@ -90,9 +90,20 @@ import { getColorName, parseFilamentColor, isLightColor } from '../utils/colors' // Color names resolve via getColorName() which reads the backend color_catalog // (loaded once by ColorCatalogProvider). No hardcoded tables here — see #857. -// Extract plate number from gcode_file path and append to print name -function formatPrintName(name: string | null, gcodeFile: string | null | undefined, t: (key: string, fallback: string, opts?: Record) => string): string { +// Append a plate label to the print name. When `plateLabel` is provided (resolved +// by the caller from the linked archive's plate list — see #881 follow-up), it +// is used verbatim, including the explicit "Plate 1" case on multi-plate 3MFs. +// Falls back to parsing `plate_N.gcode` from the MQTT gcode_file path, and in +// that fallback we only show N > 1 because we can't tell from the path alone +// whether the 3MF is multi-plate. +export function formatPrintName( + name: string | null, + gcodeFile: string | null | undefined, + t: (key: string, fallback: string, opts?: Record) => string, + plateLabel?: string | null, +): string { if (!name) return ''; + if (plateLabel) return `${name} — ${plateLabel}`; if (!gcodeFile) return name; const match = gcodeFile.match(/plate_(\d+)\.gcode/); if (match && parseInt(match[1], 10) > 1) { @@ -1528,6 +1539,23 @@ function PrinterCard({ staleTime: 2 * 60 * 1000, // 2 minutes }); + // Fetch plate list for the archive linked to the active print (#881 follow-up). + // Only queried when there's a running print backed by an archive; shared + // React Query cache with the Queue / Archives pages keeps it cheap. + const activeArchiveId = + (status?.state === 'RUNNING' || status?.state === 'PAUSE') ? status?.current_archive_id ?? null : null; + const { data: activeArchivePlates } = useQuery({ + queryKey: ['archive-plates', activeArchiveId], + queryFn: () => api.getArchivePlates(activeArchiveId!), + enabled: activeArchiveId != null, + staleTime: 5 * 60 * 1000, + }); + const activePlateLabel = (() => { + if (!activeArchivePlates?.is_multi_plate || status?.current_plate_id == null) return null; + const plate = activeArchivePlates.plates.find(p => p.index === status.current_plate_id); + return plate?.name || t('printers.plateNumber', 'Plate {{number}}', { number: status.current_plate_id }); + })(); + // Fetch user-defined AMS friendly names from the database const { data: amsLabels, refetch: refetchAmsLabels } = useQuery({ queryKey: ['amsLabels', printer.id], @@ -2718,7 +2746,7 @@ function PrinterCard({ {/* Cover Image */} {/* Print Info */}
@@ -2729,7 +2757,7 @@ function PrinterCard({ {plateStatusPill}

- {formatPrintName(status.subtask_name || status.current_print || null, status.gcode_file, t)} + {formatPrintName(status.subtask_name || status.current_print || null, status.gcode_file, t, activePlateLabel)}

diff --git a/static/assets/index-DZZn3lqr.js b/static/assets/index-06WvZfP-.js similarity index 87% rename from static/assets/index-DZZn3lqr.js rename to static/assets/index-06WvZfP-.js index 71dc11483..b5cee40d0 100644 --- a/static/assets/index-DZZn3lqr.js +++ b/static/assets/index-06WvZfP-.js @@ -1,12 +1,12 @@ -function Fde(t,e){for(var n=0;nr[i]})}}}return Object.freeze(Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}))}(function(){const e=document.createElement("link").relList;if(e&&e.supports&&e.supports("modulepreload"))return;for(const i of document.querySelectorAll('link[rel="modulepreload"]'))r(i);new MutationObserver(i=>{for(const s of i)if(s.type==="childList")for(const o of s.addedNodes)o.tagName==="LINK"&&o.rel==="modulepreload"&&r(o)}).observe(document,{childList:!0,subtree:!0});function n(i){const s={};return i.integrity&&(s.integrity=i.integrity),i.referrerPolicy&&(s.referrerPolicy=i.referrerPolicy),i.crossOrigin==="use-credentials"?s.credentials="include":i.crossOrigin==="anonymous"?s.credentials="omit":s.credentials="same-origin",s}function r(i){if(i.ep)return;i.ep=!0;const s=n(i);fetch(i.href,s)}})();var $_=typeof globalThis<"u"?globalThis:typeof window<"u"?window:typeof global<"u"?global:typeof self<"u"?self:{};function bc(t){return t&&t.__esModule&&Object.prototype.hasOwnProperty.call(t,"default")?t.default:t}var mM={exports:{}},kv={};var T8;function Rde(){if(T8)return kv;T8=1;var t=Symbol.for("react.transitional.element"),e=Symbol.for("react.fragment");function n(r,i,s){var o=null;if(s!==void 0&&(o=""+s),i.key!==void 0&&(o=""+i.key),"key"in i){s={};for(var l in i)l!=="key"&&(s[l]=i[l])}else s=i;return i=s.ref,{$$typeof:t,type:r,key:o,ref:i!==void 0?i:null,props:s}}return kv.Fragment=e,kv.jsx=n,kv.jsxs=n,kv}var A8;function Lde(){return A8||(A8=1,mM.exports=Rde()),mM.exports}var a=Lde(),hM={exports:{}},Xn={};var j8;function Ode(){if(j8)return Xn;j8=1;var t=Symbol.for("react.transitional.element"),e=Symbol.for("react.portal"),n=Symbol.for("react.fragment"),r=Symbol.for("react.strict_mode"),i=Symbol.for("react.profiler"),s=Symbol.for("react.consumer"),o=Symbol.for("react.context"),l=Symbol.for("react.forward_ref"),c=Symbol.for("react.suspense"),d=Symbol.for("react.memo"),u=Symbol.for("react.lazy"),m=Symbol.for("react.activity"),p=Symbol.iterator;function f(W){return W===null||typeof W!="object"?null:(W=p&&W[p]||W["@@iterator"],typeof W=="function"?W:null)}var x={isMounted:function(){return!1},enqueueForceUpdate:function(){},enqueueReplaceState:function(){},enqueueSetState:function(){}},v=Object.assign,b={};function g(W,ne,me){this.props=W,this.context=ne,this.refs=b,this.updater=me||x}g.prototype.isReactComponent={},g.prototype.setState=function(W,ne){if(typeof W!="object"&&typeof W!="function"&&W!=null)throw Error("takes an object of state variables to update or a function which returns an object of state variables.");this.updater.enqueueSetState(this,W,ne,"setState")},g.prototype.forceUpdate=function(W){this.updater.enqueueForceUpdate(this,W,"forceUpdate")};function _(){}_.prototype=g.prototype;function C(W,ne,me){this.props=W,this.context=ne,this.refs=b,this.updater=me||x}var P=C.prototype=new _;P.constructor=C,v(P,g.prototype),P.isPureReactComponent=!0;var N=Array.isArray;function A(){}var T={H:null,A:null,T:null,S:null},F=Object.prototype.hasOwnProperty;function k(W,ne,me){var be=me.ref;return{$$typeof:t,type:W,key:ne,ref:be!==void 0?be:null,props:me}}function D(W,ne){return k(W.type,ne,W.props)}function H(W){return typeof W=="object"&&W!==null&&W.$$typeof===t}function z(W){var ne={"=":"=0",":":"=2"};return"$"+W.replace(/[=:]/g,function(me){return ne[me]})}var X=/\/+/g;function L(W,ne){return typeof W=="object"&&W!==null&&W.key!=null?z(""+W.key):ne.toString(36)}function te(W){switch(W.status){case"fulfilled":return W.value;case"rejected":throw W.reason;default:switch(typeof W.status=="string"?W.then(A,A):(W.status="pending",W.then(function(ne){W.status==="pending"&&(W.status="fulfilled",W.value=ne)},function(ne){W.status==="pending"&&(W.status="rejected",W.reason=ne)})),W.status){case"fulfilled":return W.value;case"rejected":throw W.reason}}throw W}function ie(W,ne,me,be,Pe){var q=typeof W;(q==="undefined"||q==="boolean")&&(W=null);var Q=!1;if(W===null)Q=!0;else switch(q){case"bigint":case"string":case"number":Q=!0;break;case"object":switch(W.$$typeof){case t:case e:Q=!0;break;case u:return Q=W._init,ie(Q(W._payload),ne,me,be,Pe)}}if(Q)return Pe=Pe(W),Q=be===""?"."+L(W,0):be,N(Pe)?(me="",Q!=null&&(me=Q.replace(X,"$&/")+"/"),ie(Pe,ne,me,"",function(O){return O})):Pe!=null&&(H(Pe)&&(Pe=D(Pe,me+(Pe.key==null||W&&W.key===Pe.key?"":(""+Pe.key).replace(X,"$&/")+"/")+Q)),ne.push(Pe)),1;Q=0;var E=be===""?".":be+":";if(N(W))for(var j=0;j>>1,re=ie[fe];if(0>>1;fei(me,oe))bei(Pe,me)?(ie[fe]=Pe,ie[be]=oe,fe=be):(ie[fe]=me,ie[ne]=oe,fe=ne);else if(bei(Pe,oe))ie[fe]=Pe,ie[be]=oe,fe=be;else break e}}return J}function i(ie,J){var oe=ie.sortIndex-J.sortIndex;return oe!==0?oe:ie.id-J.id}if(t.unstable_now=void 0,typeof performance=="object"&&typeof performance.now=="function"){var s=performance;t.unstable_now=function(){return s.now()}}else{var o=Date,l=o.now();t.unstable_now=function(){return o.now()-l}}var c=[],d=[],u=1,m=null,p=3,f=!1,x=!1,v=!1,b=!1,g=typeof setTimeout=="function"?setTimeout:null,_=typeof clearTimeout=="function"?clearTimeout:null,C=typeof setImmediate<"u"?setImmediate:null;function P(ie){for(var J=n(d);J!==null;){if(J.callback===null)r(d);else if(J.startTime<=ie)r(d),J.sortIndex=J.expirationTime,e(c,J);else break;J=n(d)}}function N(ie){if(v=!1,P(ie),!x)if(n(c)!==null)x=!0,A||(A=!0,z());else{var J=n(d);J!==null&&te(N,J.startTime-ie)}}var A=!1,T=-1,F=5,k=-1;function D(){return b?!0:!(t.unstable_now()-kie&&D());){var fe=m.callback;if(typeof fe=="function"){m.callback=null,p=m.priorityLevel;var re=fe(m.expirationTime<=ie);if(ie=t.unstable_now(),typeof re=="function"){m.callback=re,P(ie),J=!0;break t}m===n(c)&&r(c),P(ie)}else r(c);m=n(c)}if(m!==null)J=!0;else{var W=n(d);W!==null&&te(N,W.startTime-ie),J=!1}}break e}finally{m=null,p=oe,f=!1}J=void 0}}finally{J?z():A=!1}}}var z;if(typeof C=="function")z=function(){C(H)};else if(typeof MessageChannel<"u"){var X=new MessageChannel,L=X.port2;X.port1.onmessage=H,z=function(){L.postMessage(null)}}else z=function(){g(H,0)};function te(ie,J){T=g(function(){ie(t.unstable_now())},J)}t.unstable_IdlePriority=5,t.unstable_ImmediatePriority=1,t.unstable_LowPriority=4,t.unstable_NormalPriority=3,t.unstable_Profiling=null,t.unstable_UserBlockingPriority=2,t.unstable_cancelCallback=function(ie){ie.callback=null},t.unstable_forceFrameRate=function(ie){0>ie||125fe?(ie.sortIndex=oe,e(d,ie),n(c)===null&&ie===n(d)&&(v?(_(T),T=-1):v=!0,te(N,oe-fe))):(ie.sortIndex=re,e(c,ie),x||f||(x=!0,A||(A=!0,z()))),ie},t.unstable_shouldYield=D,t.unstable_wrapCallback=function(ie){var J=p;return function(){var oe=p;p=J;try{return ie.apply(this,arguments)}finally{p=oe}}}})(gM)),gM}var D8;function Ude(){return D8||(D8=1,fM.exports=zde()),fM.exports}var bM={exports:{}},Ms={};var F8;function Bde(){if(F8)return Ms;F8=1;var t=Mg();function e(c){var d="https://react.dev/errors/"+c;if(1"u"||typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE!="function"))try{__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(t)}catch(e){console.error(e)}}return t(),bM.exports=Bde(),bM.exports}var L8;function Hde(){if(L8)return Nv;L8=1;var t=Ude(),e=Mg(),n=UY();function r(h){var y="https://react.dev/errors/"+h;if(1re||(h.current=fe[re],fe[re]=null,re--)}function me(h,y){re++,fe[re]=h.current,h.current=y}var be=W(null),Pe=W(null),q=W(null),Q=W(null);function E(h,y){switch(me(q,y),me(Pe,h),me(be,null),y.nodeType){case 9:case 11:h=(h=y.documentElement)&&(h=h.namespaceURI)?QB(h):0;break;default:if(h=y.tagName,y=y.namespaceURI)y=QB(y),h=ZB(y,h);else switch(h){case"svg":h=1;break;case"math":h=2;break;default:h=0}}ne(be),me(be,h)}function j(){ne(be),ne(Pe),ne(q)}function O(h){h.memoizedState!==null&&me(Q,h);var y=be.current,S=ZB(y,h.type);y!==S&&(me(Pe,h),me(be,S))}function K(h){Pe.current===h&&(ne(be),ne(Pe)),Q.current===h&&(ne(Q),vv._currentValue=oe)}var U,ue;function I(h){if(U===void 0)try{throw Error()}catch(S){var y=S.stack.trim().match(/\n( *(at )?)/);U=y&&y[1]||"",ue=-1r[i]})}}}return Object.freeze(Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}))}(function(){const e=document.createElement("link").relList;if(e&&e.supports&&e.supports("modulepreload"))return;for(const i of document.querySelectorAll('link[rel="modulepreload"]'))r(i);new MutationObserver(i=>{for(const s of i)if(s.type==="childList")for(const o of s.addedNodes)o.tagName==="LINK"&&o.rel==="modulepreload"&&r(o)}).observe(document,{childList:!0,subtree:!0});function n(i){const s={};return i.integrity&&(s.integrity=i.integrity),i.referrerPolicy&&(s.referrerPolicy=i.referrerPolicy),i.crossOrigin==="use-credentials"?s.credentials="include":i.crossOrigin==="anonymous"?s.credentials="omit":s.credentials="same-origin",s}function r(i){if(i.ep)return;i.ep=!0;const s=n(i);fetch(i.href,s)}})();var W_=typeof globalThis<"u"?globalThis:typeof window<"u"?window:typeof global<"u"?global:typeof self<"u"?self:{};function xc(t){return t&&t.__esModule&&Object.prototype.hasOwnProperty.call(t,"default")?t.default:t}var gM={exports:{}},Tv={};var T8;function Rde(){if(T8)return Tv;T8=1;var t=Symbol.for("react.transitional.element"),e=Symbol.for("react.fragment");function n(r,i,s){var o=null;if(s!==void 0&&(o=""+s),i.key!==void 0&&(o=""+i.key),"key"in i){s={};for(var l in i)l!=="key"&&(s[l]=i[l])}else s=i;return i=s.ref,{$$typeof:t,type:r,key:o,ref:i!==void 0?i:null,props:s}}return Tv.Fragment=e,Tv.jsx=n,Tv.jsxs=n,Tv}var A8;function Lde(){return A8||(A8=1,gM.exports=Rde()),gM.exports}var a=Lde(),bM={exports:{}},Wn={};var j8;function Ode(){if(j8)return Wn;j8=1;var t=Symbol.for("react.transitional.element"),e=Symbol.for("react.portal"),n=Symbol.for("react.fragment"),r=Symbol.for("react.strict_mode"),i=Symbol.for("react.profiler"),s=Symbol.for("react.consumer"),o=Symbol.for("react.context"),l=Symbol.for("react.forward_ref"),c=Symbol.for("react.suspense"),d=Symbol.for("react.memo"),u=Symbol.for("react.lazy"),m=Symbol.for("react.activity"),p=Symbol.iterator;function f(W){return W===null||typeof W!="object"?null:(W=p&&W[p]||W["@@iterator"],typeof W=="function"?W:null)}var x={isMounted:function(){return!1},enqueueForceUpdate:function(){},enqueueReplaceState:function(){},enqueueSetState:function(){}},v=Object.assign,b={};function g(W,ne,me){this.props=W,this.context=ne,this.refs=b,this.updater=me||x}g.prototype.isReactComponent={},g.prototype.setState=function(W,ne){if(typeof W!="object"&&typeof W!="function"&&W!=null)throw Error("takes an object of state variables to update or a function which returns an object of state variables.");this.updater.enqueueSetState(this,W,ne,"setState")},g.prototype.forceUpdate=function(W){this.updater.enqueueForceUpdate(this,W,"forceUpdate")};function _(){}_.prototype=g.prototype;function C(W,ne,me){this.props=W,this.context=ne,this.refs=b,this.updater=me||x}var P=C.prototype=new _;P.constructor=C,v(P,g.prototype),P.isPureReactComponent=!0;var N=Array.isArray;function A(){}var T={H:null,A:null,T:null,S:null},F=Object.prototype.hasOwnProperty;function k(W,ne,me){var be=me.ref;return{$$typeof:t,type:W,key:ne,ref:be!==void 0?be:null,props:me}}function D(W,ne){return k(W.type,ne,W.props)}function H(W){return typeof W=="object"&&W!==null&&W.$$typeof===t}function z(W){var ne={"=":"=0",":":"=2"};return"$"+W.replace(/[=:]/g,function(me){return ne[me]})}var X=/\/+/g;function L(W,ne){return typeof W=="object"&&W!==null&&W.key!=null?z(""+W.key):ne.toString(36)}function te(W){switch(W.status){case"fulfilled":return W.value;case"rejected":throw W.reason;default:switch(typeof W.status=="string"?W.then(A,A):(W.status="pending",W.then(function(ne){W.status==="pending"&&(W.status="fulfilled",W.value=ne)},function(ne){W.status==="pending"&&(W.status="rejected",W.reason=ne)})),W.status){case"fulfilled":return W.value;case"rejected":throw W.reason}}throw W}function ie(W,ne,me,be,Pe){var q=typeof W;(q==="undefined"||q==="boolean")&&(W=null);var Q=!1;if(W===null)Q=!0;else switch(q){case"bigint":case"string":case"number":Q=!0;break;case"object":switch(W.$$typeof){case t:case e:Q=!0;break;case u:return Q=W._init,ie(Q(W._payload),ne,me,be,Pe)}}if(Q)return Pe=Pe(W),Q=be===""?"."+L(W,0):be,N(Pe)?(me="",Q!=null&&(me=Q.replace(X,"$&/")+"/"),ie(Pe,ne,me,"",function(O){return O})):Pe!=null&&(H(Pe)&&(Pe=D(Pe,me+(Pe.key==null||W&&W.key===Pe.key?"":(""+Pe.key).replace(X,"$&/")+"/")+Q)),ne.push(Pe)),1;Q=0;var E=be===""?".":be+":";if(N(W))for(var j=0;j>>1,re=ie[fe];if(0>>1;fei(me,oe))bei(Pe,me)?(ie[fe]=Pe,ie[be]=oe,fe=be):(ie[fe]=me,ie[ne]=oe,fe=ne);else if(bei(Pe,oe))ie[fe]=Pe,ie[be]=oe,fe=be;else break e}}return J}function i(ie,J){var oe=ie.sortIndex-J.sortIndex;return oe!==0?oe:ie.id-J.id}if(t.unstable_now=void 0,typeof performance=="object"&&typeof performance.now=="function"){var s=performance;t.unstable_now=function(){return s.now()}}else{var o=Date,l=o.now();t.unstable_now=function(){return o.now()-l}}var c=[],d=[],u=1,m=null,p=3,f=!1,x=!1,v=!1,b=!1,g=typeof setTimeout=="function"?setTimeout:null,_=typeof clearTimeout=="function"?clearTimeout:null,C=typeof setImmediate<"u"?setImmediate:null;function P(ie){for(var J=n(d);J!==null;){if(J.callback===null)r(d);else if(J.startTime<=ie)r(d),J.sortIndex=J.expirationTime,e(c,J);else break;J=n(d)}}function N(ie){if(v=!1,P(ie),!x)if(n(c)!==null)x=!0,A||(A=!0,z());else{var J=n(d);J!==null&&te(N,J.startTime-ie)}}var A=!1,T=-1,F=5,k=-1;function D(){return b?!0:!(t.unstable_now()-kie&&D());){var fe=m.callback;if(typeof fe=="function"){m.callback=null,p=m.priorityLevel;var re=fe(m.expirationTime<=ie);if(ie=t.unstable_now(),typeof re=="function"){m.callback=re,P(ie),J=!0;break t}m===n(c)&&r(c),P(ie)}else r(c);m=n(c)}if(m!==null)J=!0;else{var W=n(d);W!==null&&te(N,W.startTime-ie),J=!1}}break e}finally{m=null,p=oe,f=!1}J=void 0}}finally{J?z():A=!1}}}var z;if(typeof C=="function")z=function(){C(H)};else if(typeof MessageChannel<"u"){var X=new MessageChannel,L=X.port2;X.port1.onmessage=H,z=function(){L.postMessage(null)}}else z=function(){g(H,0)};function te(ie,J){T=g(function(){ie(t.unstable_now())},J)}t.unstable_IdlePriority=5,t.unstable_ImmediatePriority=1,t.unstable_LowPriority=4,t.unstable_NormalPriority=3,t.unstable_Profiling=null,t.unstable_UserBlockingPriority=2,t.unstable_cancelCallback=function(ie){ie.callback=null},t.unstable_forceFrameRate=function(ie){0>ie||125fe?(ie.sortIndex=oe,e(d,ie),n(c)===null&&ie===n(d)&&(v?(_(T),T=-1):v=!0,te(N,oe-fe))):(ie.sortIndex=re,e(c,ie),x||f||(x=!0,A||(A=!0,z()))),ie},t.unstable_shouldYield=D,t.unstable_wrapCallback=function(ie){var J=p;return function(){var oe=p;p=J;try{return ie.apply(this,arguments)}finally{p=oe}}}})(vM)),vM}var D8;function Ude(){return D8||(D8=1,yM.exports=zde()),yM.exports}var wM={exports:{}},js={};var F8;function Bde(){if(F8)return js;F8=1;var t=Rg();function e(c){var d="https://react.dev/errors/"+c;if(1"u"||typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE!="function"))try{__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(t)}catch(e){console.error(e)}}return t(),wM.exports=Bde(),wM.exports}var L8;function Hde(){if(L8)return Av;L8=1;var t=Ude(),e=Rg(),n=UY();function r(h){var y="https://react.dev/errors/"+h;if(1re||(h.current=fe[re],fe[re]=null,re--)}function me(h,y){re++,fe[re]=h.current,h.current=y}var be=W(null),Pe=W(null),q=W(null),Q=W(null);function E(h,y){switch(me(q,y),me(Pe,h),me(be,null),y.nodeType){case 9:case 11:h=(h=y.documentElement)&&(h=h.namespaceURI)?QB(h):0;break;default:if(h=y.tagName,y=y.namespaceURI)y=QB(y),h=ZB(y,h);else switch(h){case"svg":h=1;break;case"math":h=2;break;default:h=0}}ne(be),me(be,h)}function j(){ne(be),ne(Pe),ne(q)}function O(h){h.memoizedState!==null&&me(Q,h);var y=be.current,S=ZB(y,h.type);y!==S&&(me(Pe,h),me(be,S))}function K(h){Pe.current===h&&(ne(be),ne(Pe)),Q.current===h&&(ne(Q),kv._currentValue=oe)}var U,ue;function I(h){if(U===void 0)try{throw Error()}catch(S){var y=S.stack.trim().match(/\n( *(at )?)/);U=y&&y[1]||"",ue=-1)":-1$||nt[M]!==Nt[$]){var Lt=` `+nt[M].replace(" at new "," at ");return h.displayName&&Lt.includes("")&&(Lt=Lt.replace("",h.displayName)),Lt}while(1<=M&&0<=$);break}}}finally{G=!1,Error.prepareStackTrace=S}return(S=h?h.displayName||h.name:"")?I(S):""}function V(h,y){switch(h.tag){case 26:case 27:case 5:return I(h.type);case 16:return I("Lazy");case 13:return h.child!==y&&y!==null?I("Suspense Fallback"):I("Suspense");case 19:return I("SuspenseList");case 0:case 15:return Y(h.type,!1);case 11:return Y(h.type.render,!1);case 1:return Y(h.type,!0);case 31:return I("Activity");default:return""}}function ee(h){try{var y="",S=null;do y+=V(h,S),S=h,h=h.return;while(h);return y}catch(M){return` Error generating stack: `+M.message+` -`+M.stack}}var se=Object.prototype.hasOwnProperty,ge=t.unstable_scheduleCallback,pe=t.unstable_cancelCallback,le=t.unstable_shouldYield,B=t.unstable_requestPaint,R=t.unstable_now,ae=t.unstable_getCurrentPriorityLevel,ke=t.unstable_ImmediatePriority,Se=t.unstable_UserBlockingPriority,ye=t.unstable_NormalPriority,Te=t.unstable_LowPriority,ve=t.unstable_IdlePriority,Ae=t.log,Re=t.unstable_setDisableYieldValue,Me=null,Le=null;function Oe(h){if(typeof Ae=="function"&&Re(h),Le&&typeof Le.setStrictMode=="function")try{Le.setStrictMode(Me,h)}catch{}}var qe=Math.clz32?Math.clz32:he,Qe=Math.log,tt=Math.LN2;function he(h){return h>>>=0,h===0?32:31-(Qe(h)/tt|0)|0}var Fe=256,we=262144,Ve=4194304;function je(h){var y=h&42;if(y!==0)return y;switch(h&-h){case 1:return 1;case 2:return 2;case 4:return 4;case 8:return 8;case 16:return 16;case 32:return 32;case 64:return 64;case 128:return 128;case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:return h&261888;case 262144:case 524288:case 1048576:case 2097152:return h&3932160;case 4194304:case 8388608:case 16777216:case 33554432:return h&62914560;case 67108864:return 67108864;case 134217728:return 134217728;case 268435456:return 268435456;case 536870912:return 536870912;case 1073741824:return 0;default:return h}}function ce(h,y,S){var M=h.pendingLanes;if(M===0)return 0;var $=0,Z=h.suspendedLanes,Ne=h.pingedLanes;h=h.warmLanes;var He=M&134217727;return He!==0?(M=He&~Z,M!==0?$=je(M):(Ne&=He,Ne!==0?$=je(Ne):S||(S=He&~h,S!==0&&($=je(S))))):(He=M&~Z,He!==0?$=je(He):Ne!==0?$=je(Ne):S||(S=M&~h,S!==0&&($=je(S)))),$===0?0:y!==0&&y!==$&&(y&Z)===0&&(Z=$&-$,S=y&-y,Z>=S||Z===32&&(S&4194048)!==0)?y:$}function xe(h,y){return(h.pendingLanes&~(h.suspendedLanes&~h.pingedLanes)&y)===0}function Be(h,y){switch(h){case 1:case 2:case 4:case 8:case 64:return y+250;case 16:case 32:case 128:case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:case 262144:case 524288:case 1048576:case 2097152:return y+5e3;case 4194304:case 8388608:case 16777216:case 33554432:return-1;case 67108864:case 134217728:case 268435456:case 536870912:case 1073741824:return-1;default:return-1}}function Ze(){var h=Ve;return Ve<<=1,(Ve&62914560)===0&&(Ve=4194304),h}function pt(h){for(var y=[],S=0;31>S;S++)y.push(h);return y}function xt(h,y){h.pendingLanes|=y,y!==268435456&&(h.suspendedLanes=0,h.pingedLanes=0,h.warmLanes=0)}function yt(h,y,S,M,$,Z){var Ne=h.pendingLanes;h.pendingLanes=S,h.suspendedLanes=0,h.pingedLanes=0,h.warmLanes=0,h.expiredLanes&=S,h.entangledLanes&=S,h.errorRecoveryDisabledLanes&=S,h.shellSuspendCounter=0;var He=h.entanglements,nt=h.expirationTimes,Nt=h.hiddenUpdates;for(S=Ne&~S;0"u")return null;try{return h.activeElement||h.body}catch{return h.body}}var Dr=/[\n"\\]/g;function En(h){return h.replace(Dr,function(y){return"\\"+y.charCodeAt(0).toString(16)+" "})}function Ea(h,y,S,M,$,Z,Ne,He){h.name="",Ne!=null&&typeof Ne!="function"&&typeof Ne!="symbol"&&typeof Ne!="boolean"?h.type=Ne:h.removeAttribute("type"),y!=null?Ne==="number"?(y===0&&h.value===""||h.value!=y)&&(h.value=""+zn(y)):h.value!==""+zn(y)&&(h.value=""+zn(y)):Ne!=="submit"&&Ne!=="reset"||h.removeAttribute("value"),y!=null?na(h,Ne,zn(y)):S!=null?na(h,Ne,zn(S)):M!=null&&h.removeAttribute("value"),$==null&&Z!=null&&(h.defaultChecked=!!Z),$!=null&&(h.checked=$&&typeof $!="function"&&typeof $!="symbol"),He!=null&&typeof He!="function"&&typeof He!="symbol"&&typeof He!="boolean"?h.name=""+zn(He):h.removeAttribute("name")}function Pn(h,y,S,M,$,Z,Ne,He){if(Z!=null&&typeof Z!="function"&&typeof Z!="symbol"&&typeof Z!="boolean"&&(h.type=Z),y!=null||S!=null){if(!(Z!=="submit"&&Z!=="reset"||y!=null)){gn(h);return}S=S!=null?""+zn(S):"",y=y!=null?""+zn(y):S,He||y===h.value||(h.value=y),h.defaultValue=y}M=M??$,M=typeof M!="function"&&typeof M!="symbol"&&!!M,h.checked=He?h.checked:!!M,h.defaultChecked=!!M,Ne!=null&&typeof Ne!="function"&&typeof Ne!="symbol"&&typeof Ne!="boolean"&&(h.name=Ne),gn(h)}function na(h,y,S){y==="number"&&gr(h.ownerDocument)===h||h.defaultValue===""+S||(h.defaultValue=""+S)}function qn(h,y,S,M){if(h=h.options,y){y={};for(var $=0;$"u"||typeof window.document>"u"||typeof window.document.createElement>"u"),kl=!1;if(Ns)try{var ns={};Object.defineProperty(ns,"passive",{get:function(){kl=!0}}),window.addEventListener("test",ns,ns),window.removeEventListener("test",ns,ns)}catch{kl=!1}var $o=null,Nl=null,wc=null;function vo(){if(wc)return wc;var h,y=Nl,S=y.length,M,$="value"in $o?$o.value:$o.textContent,Z=$.length;for(h=0;h=ct),ir=" ",aa=!1;function ka(h,y){switch(h){case"keyup":return Ht.indexOf(y.keyCode)!==-1;case"keydown":return y.keyCode!==229;case"keypress":case"mousedown":case"focusout":return!0;default:return!1}}function an(h){return h=h.detail,typeof h=="object"&&"data"in h?h.data:null}var wn=!1;function Da(h,y){switch(h){case"compositionend":return an(y);case"keypress":return y.which!==32?null:(aa=!0,ir);case"textInput":return h=y.data,h===ir&&aa?null:h;default:return null}}function di(h,y){if(wn)return h==="compositionend"||!Zt&&ka(h,y)?(h=vo(),wc=Nl=$o=null,wn=!1,h):null;switch(h){case"paste":return null;case"keypress":if(!(y.ctrlKey||y.altKey||y.metaKey)||y.ctrlKey&&y.altKey){if(y.char&&1=y)return{node:S,offset:y-h};h=M}e:{for(;S;){if(S.nextSibling){S=S.nextSibling;break e}S=S.parentNode}S=void 0}S=BS(S)}}function b5(h,y){return h&&y?h===y?!0:h&&h.nodeType===3?!1:y&&y.nodeType===3?b5(h,y.parentNode):"contains"in h?h.contains(y):h.compareDocumentPosition?!!(h.compareDocumentPosition(y)&16):!1:!1}function x5(h){h=h!=null&&h.ownerDocument!=null&&h.ownerDocument.defaultView!=null?h.ownerDocument.defaultView:window;for(var y=gr(h.document);y instanceof h.HTMLIFrameElement;){try{var S=typeof y.contentWindow.location.href=="string"}catch{S=!1}if(S)h=y.contentWindow;else break;y=gr(h.document)}return y}function wA(h){var y=h&&h.nodeName&&h.nodeName.toLowerCase();return y&&(y==="input"&&(h.type==="text"||h.type==="search"||h.type==="tel"||h.type==="url"||h.type==="password")||y==="textarea"||h.contentEditable==="true")}var hce=Ns&&"documentMode"in document&&11>=document.documentMode,Wg=null,SA=null,Vy=null,_A=!1;function y5(h,y,S){var M=S.window===S?S.document:S.nodeType===9?S:S.ownerDocument;_A||Wg==null||Wg!==gr(M)||(M=Wg,"selectionStart"in M&&wA(M)?M={start:M.selectionStart,end:M.selectionEnd}:(M=(M.ownerDocument&&M.ownerDocument.defaultView||window).getSelection(),M={anchorNode:M.anchorNode,anchorOffset:M.anchorOffset,focusNode:M.focusNode,focusOffset:M.focusOffset}),Vy&&Um(Vy,M)||(Vy=M,M=A_(SA,"onSelect"),0>=Ne,$-=Ne,md=1<<32-qe(y)+$|S<<$|M,hd=Z+h}else md=1<Zn?(yr=bn,bn=null):yr=bn.sibling;var Ar=Ct(bt,bn,kt[Zn],Ot);if(Ar===null){bn===null&&(bn=yr);break}h&&bn&&Ar.alternate===null&&y(bt,bn),ut=Z(Ar,ut,Zn),Tr===null?Cn=Ar:Tr.sibling=Ar,Tr=Ar,bn=yr}if(Zn===kt.length)return S(bt,bn),wr&&lu(bt,Zn),Cn;if(bn===null){for(;ZnZn?(yr=bn,bn=null):yr=bn.sibling;var lh=Ct(bt,bn,Ar.value,Ot);if(lh===null){bn===null&&(bn=yr);break}h&&bn&&lh.alternate===null&&y(bt,bn),ut=Z(lh,ut,Zn),Tr===null?Cn=lh:Tr.sibling=lh,Tr=lh,bn=yr}if(Ar.done)return S(bt,bn),wr&&lu(bt,Zn),Cn;if(bn===null){for(;!Ar.done;Zn++,Ar=kt.next())Ar=zt(bt,Ar.value,Ot),Ar!==null&&(ut=Z(Ar,ut,Zn),Tr===null?Cn=Ar:Tr.sibling=Ar,Tr=Ar);return wr&&lu(bt,Zn),Cn}for(bn=M(bn);!Ar.done;Zn++,Ar=kt.next())Ar=Et(bn,bt,Zn,Ar.value,Ot),Ar!==null&&(h&&Ar.alternate!==null&&bn.delete(Ar.key===null?Zn:Ar.key),ut=Z(Ar,ut,Zn),Tr===null?Cn=Ar:Tr.sibling=Ar,Tr=Ar);return h&&bn.forEach(function(Dde){return y(bt,Dde)}),wr&&lu(bt,Zn),Cn}function Wr(bt,ut,kt,Ot){if(typeof kt=="object"&&kt!==null&&kt.type===v&&kt.key===null&&(kt=kt.props.children),typeof kt=="object"&&kt!==null){switch(kt.$$typeof){case f:e:{for(var Cn=kt.key;ut!==null;){if(ut.key===Cn){if(Cn=kt.type,Cn===v){if(ut.tag===7){S(bt,ut.sibling),Ot=$(ut,kt.props.children),Ot.return=bt,bt=Ot;break e}}else if(ut.elementType===Cn||typeof Cn=="object"&&Cn!==null&&Cn.$$typeof===F&&Yp(Cn)===ut.type){S(bt,ut.sibling),Ot=$(ut,kt.props),Qy(Ot,kt),Ot.return=bt,bt=Ot;break e}S(bt,ut);break}else y(bt,ut);ut=ut.sibling}kt.type===v?(Ot=Vp(kt.props.children,bt.mode,Ot,kt.key),Ot.return=bt,bt=Ot):(Ot=GS(kt.type,kt.key,kt.props,null,bt.mode,Ot),Qy(Ot,kt),Ot.return=bt,bt=Ot)}return Ne(bt);case x:e:{for(Cn=kt.key;ut!==null;){if(ut.key===Cn)if(ut.tag===4&&ut.stateNode.containerInfo===kt.containerInfo&&ut.stateNode.implementation===kt.implementation){S(bt,ut.sibling),Ot=$(ut,kt.children||[]),Ot.return=bt,bt=Ot;break e}else{S(bt,ut);break}else y(bt,ut);ut=ut.sibling}Ot=jA(kt,bt.mode,Ot),Ot.return=bt,bt=Ot}return Ne(bt);case F:return kt=Yp(kt),Wr(bt,ut,kt,Ot)}if(te(kt))return mn(bt,ut,kt,Ot);if(z(kt)){if(Cn=z(kt),typeof Cn!="function")throw Error(r(150));return kt=Cn.call(kt),Fn(bt,ut,kt,Ot)}if(typeof kt.then=="function")return Wr(bt,ut,JS(kt),Ot);if(kt.$$typeof===C)return Wr(bt,ut,XS(bt,kt),Ot);e_(bt,kt)}return typeof kt=="string"&&kt!==""||typeof kt=="number"||typeof kt=="bigint"?(kt=""+kt,ut!==null&&ut.tag===6?(S(bt,ut.sibling),Ot=$(ut,kt),Ot.return=bt,bt=Ot):(S(bt,ut),Ot=AA(kt,bt.mode,Ot),Ot.return=bt,bt=Ot),Ne(bt)):S(bt,ut)}return function(bt,ut,kt,Ot){try{Yy=0;var Cn=Wr(bt,ut,kt,Ot);return ab=null,Cn}catch(bn){if(bn===rb||bn===QS)throw bn;var Tr=Yo(29,bn,null,bt.mode);return Tr.lanes=Ot,Tr.return=bt,Tr}}}var Zp=H5(!0),q5=H5(!1),Vm=!1;function HA(h){h.updateQueue={baseState:h.memoizedState,firstBaseUpdate:null,lastBaseUpdate:null,shared:{pending:null,lanes:0,hiddenCallbacks:null},callbacks:null}}function qA(h,y){h=h.updateQueue,y.updateQueue===h&&(y.updateQueue={baseState:h.baseState,firstBaseUpdate:h.firstBaseUpdate,lastBaseUpdate:h.lastBaseUpdate,shared:h.shared,callbacks:null})}function Gm(h){return{lane:h,tag:0,payload:null,callback:null,next:null}}function Wm(h,y,S){var M=h.updateQueue;if(M===null)return null;if(M=M.shared,(Fr&2)!==0){var $=M.pending;return $===null?y.next=y:(y.next=$.next,$.next=y),M.pending=y,y=VS(h),C5(h,null,S),y}return $S(h,M,y,S),VS(h)}function Zy(h,y,S){if(y=y.updateQueue,y!==null&&(y=y.shared,(S&4194048)!==0)){var M=y.lanes;M&=h.pendingLanes,S|=M,y.lanes=S,Kt(h,S)}}function $A(h,y){var S=h.updateQueue,M=h.alternate;if(M!==null&&(M=M.updateQueue,S===M)){var $=null,Z=null;if(S=S.firstBaseUpdate,S!==null){do{var Ne={lane:S.lane,tag:S.tag,payload:S.payload,callback:null,next:null};Z===null?$=Z=Ne:Z=Z.next=Ne,S=S.next}while(S!==null);Z===null?$=Z=y:Z=Z.next=y}else $=Z=y;S={baseState:M.baseState,firstBaseUpdate:$,lastBaseUpdate:Z,shared:M.shared,callbacks:M.callbacks},h.updateQueue=S;return}h=S.lastBaseUpdate,h===null?S.firstBaseUpdate=y:h.next=y,S.lastBaseUpdate=y}var VA=!1;function Jy(){if(VA){var h=nb;if(h!==null)throw h}}function ev(h,y,S,M){VA=!1;var $=h.updateQueue;Vm=!1;var Z=$.firstBaseUpdate,Ne=$.lastBaseUpdate,He=$.shared.pending;if(He!==null){$.shared.pending=null;var nt=He,Nt=nt.next;nt.next=null,Ne===null?Z=Nt:Ne.next=Nt,Ne=nt;var Lt=h.alternate;Lt!==null&&(Lt=Lt.updateQueue,He=Lt.lastBaseUpdate,He!==Ne&&(He===null?Lt.firstBaseUpdate=Nt:He.next=Nt,Lt.lastBaseUpdate=nt))}if(Z!==null){var zt=$.baseState;Ne=0,Lt=Nt=nt=null,He=Z;do{var Ct=He.lane&-536870913,Et=Ct!==He.lane;if(Et?(xr&Ct)===Ct:(M&Ct)===Ct){Ct!==0&&Ct===tb&&(VA=!0),Lt!==null&&(Lt=Lt.next={lane:0,tag:He.tag,payload:He.payload,callback:null,next:null});e:{var mn=h,Fn=He;Ct=y;var Wr=S;switch(Fn.tag){case 1:if(mn=Fn.payload,typeof mn=="function"){zt=mn.call(Wr,zt,Ct);break e}zt=mn;break e;case 3:mn.flags=mn.flags&-65537|128;case 0:if(mn=Fn.payload,Ct=typeof mn=="function"?mn.call(Wr,zt,Ct):mn,Ct==null)break e;zt=m({},zt,Ct);break e;case 2:Vm=!0}}Ct=He.callback,Ct!==null&&(h.flags|=64,Et&&(h.flags|=8192),Et=$.callbacks,Et===null?$.callbacks=[Ct]:Et.push(Ct))}else Et={lane:Ct,tag:He.tag,payload:He.payload,callback:He.callback,next:null},Lt===null?(Nt=Lt=Et,nt=zt):Lt=Lt.next=Et,Ne|=Ct;if(He=He.next,He===null){if(He=$.shared.pending,He===null)break;Et=He,He=Et.next,Et.next=null,$.lastBaseUpdate=Et,$.shared.pending=null}}while(!0);Lt===null&&(nt=zt),$.baseState=nt,$.firstBaseUpdate=Nt,$.lastBaseUpdate=Lt,Z===null&&($.shared.lanes=0),Zm|=Ne,h.lanes=Ne,h.memoizedState=zt}}function $5(h,y){if(typeof h!="function")throw Error(r(191,h));h.call(y)}function V5(h,y){var S=h.callbacks;if(S!==null)for(h.callbacks=null,h=0;hZ?Z:8;var Ne=ie.T,He={};ie.T=He,dj(h,!1,y,S);try{var nt=$(),Nt=ie.S;if(Nt!==null&&Nt(He,nt),nt!==null&&typeof nt=="object"&&typeof nt.then=="function"){var Lt=Sce(nt,M);rv(h,y,Lt,tl(h))}else rv(h,y,M,tl(h))}catch(zt){rv(h,y,{then:function(){},status:"rejected",reason:zt},tl())}finally{J.p=Z,Ne!==null&&He.types!==null&&(Ne.types=He.types),ie.T=Ne}}function Tce(){}function lj(h,y,S,M){if(h.tag!==5)throw Error(r(476));var $=_U(h).queue;SU(h,$,y,oe,S===null?Tce:function(){return kU(h),S(M)})}function _U(h){var y=h.memoizedState;if(y!==null)return y;y={memoizedState:oe,baseState:oe,baseQueue:null,queue:{pending:null,lanes:0,dispatch:null,lastRenderedReducer:mu,lastRenderedState:oe},next:null};var S={};return y.next={memoizedState:S,baseState:S,baseQueue:null,queue:{pending:null,lanes:0,dispatch:null,lastRenderedReducer:mu,lastRenderedState:S},next:null},h.memoizedState=y,h=h.alternate,h!==null&&(h.memoizedState=y),y}function kU(h){var y=_U(h);y.next===null&&(y=h.alternate.memoizedState),rv(h,y.next.queue,{},tl())}function cj(){return as(vv)}function NU(){return ai().memoizedState}function CU(){return ai().memoizedState}function Ace(h){for(var y=h.return;y!==null;){switch(y.tag){case 24:case 3:var S=tl();h=Gm(S);var M=Wm(y,h,S);M!==null&&(Co(M,y,S),Zy(M,y,S)),y={cache:IA()},h.payload=y;return}y=y.return}}function jce(h,y,S){var M=tl();S={lane:M,revertLane:0,gesture:null,action:S,hasEagerState:!1,eagerState:null,next:null},d_(h)?TU(y,S):(S=PA(h,y,S,M),S!==null&&(Co(S,h,M),AU(S,y,M)))}function PU(h,y,S){var M=tl();rv(h,y,S,M)}function rv(h,y,S,M){var $={lane:M,revertLane:0,gesture:null,action:S,hasEagerState:!1,eagerState:null,next:null};if(d_(h))TU(y,$);else{var Z=h.alternate;if(h.lanes===0&&(Z===null||Z.lanes===0)&&(Z=y.lastRenderedReducer,Z!==null))try{var Ne=y.lastRenderedState,He=Z(Ne,S);if($.hasEagerState=!0,$.eagerState=He,js(He,Ne))return $S(h,y,$,0),oa===null&&qS(),!1}catch{}if(S=PA(h,y,$,M),S!==null)return Co(S,h,M),AU(S,y,M),!0}return!1}function dj(h,y,S,M){if(M={lane:2,revertLane:Hj(),gesture:null,action:M,hasEagerState:!1,eagerState:null,next:null},d_(h)){if(y)throw Error(r(479))}else y=PA(h,S,M,2),y!==null&&Co(y,h,2)}function d_(h){var y=h.alternate;return h===Yn||y!==null&&y===Yn}function TU(h,y){sb=r_=!0;var S=h.pending;S===null?y.next=y:(y.next=S.next,S.next=y),h.pending=y}function AU(h,y,S){if((S&4194048)!==0){var M=y.lanes;M&=h.pendingLanes,S|=M,y.lanes=S,Kt(h,S)}}var av={readContext:as,use:s_,useCallback:Ba,useContext:Ba,useEffect:Ba,useImperativeHandle:Ba,useLayoutEffect:Ba,useInsertionEffect:Ba,useMemo:Ba,useReducer:Ba,useRef:Ba,useState:Ba,useDebugValue:Ba,useDeferredValue:Ba,useTransition:Ba,useSyncExternalStore:Ba,useId:Ba,useHostTransitionStatus:Ba,useFormState:Ba,useActionState:Ba,useOptimistic:Ba,useMemoCache:Ba,useCacheRefresh:Ba};av.useEffectEvent=Ba;var jU={readContext:as,use:s_,useCallback:function(h,y){return Qs().memoizedState=[h,y===void 0?null:y],h},useContext:as,useEffect:hU,useImperativeHandle:function(h,y,S){S=S!=null?S.concat([h]):null,l_(4194308,4,bU.bind(null,y,h),S)},useLayoutEffect:function(h,y){return l_(4194308,4,h,y)},useInsertionEffect:function(h,y){l_(4,2,h,y)},useMemo:function(h,y){var S=Qs();y=y===void 0?null:y;var M=h();if(Jp){Oe(!0);try{h()}finally{Oe(!1)}}return S.memoizedState=[M,y],M},useReducer:function(h,y,S){var M=Qs();if(S!==void 0){var $=S(y);if(Jp){Oe(!0);try{S(y)}finally{Oe(!1)}}}else $=y;return M.memoizedState=M.baseState=$,h={pending:null,lanes:0,dispatch:null,lastRenderedReducer:h,lastRenderedState:$},M.queue=h,h=h.dispatch=jce.bind(null,Yn,h),[M.memoizedState,h]},useRef:function(h){var y=Qs();return h={current:h},y.memoizedState=h},useState:function(h){h=rj(h);var y=h.queue,S=PU.bind(null,Yn,y);return y.dispatch=S,[h.memoizedState,S]},useDebugValue:sj,useDeferredValue:function(h,y){var S=Qs();return oj(S,h,y)},useTransition:function(){var h=rj(!1);return h=SU.bind(null,Yn,h.queue,!0,!1),Qs().memoizedState=h,[!1,h]},useSyncExternalStore:function(h,y,S){var M=Yn,$=Qs();if(wr){if(S===void 0)throw Error(r(407));S=S()}else{if(S=y(),oa===null)throw Error(r(349));(xr&127)!==0||Q5(M,y,S)}$.memoizedState=S;var Z={value:S,getSnapshot:y};return $.queue=Z,hU(J5.bind(null,M,Z,h),[h]),M.flags|=2048,lb(9,{destroy:void 0},Z5.bind(null,M,Z,S,y),null),S},useId:function(){var h=Qs(),y=oa.identifierPrefix;if(wr){var S=hd,M=md;S=(M&~(1<<32-qe(M)-1)).toString(32)+S,y="_"+y+"R_"+S,S=a_++,0<\/script>",Z=Z.removeChild(Z.firstChild);break;case"select":Z=typeof M.is=="string"?Ne.createElement("select",{is:M.is}):Ne.createElement("select"),M.multiple?Z.multiple=!0:M.size&&(Z.size=M.size);break;default:Z=typeof M.is=="string"?Ne.createElement($,{is:M.is}):Ne.createElement($)}}Z[at]=y,Z[dt]=M;e:for(Ne=y.child;Ne!==null;){if(Ne.tag===5||Ne.tag===6)Z.appendChild(Ne.stateNode);else if(Ne.tag!==4&&Ne.tag!==27&&Ne.child!==null){Ne.child.return=Ne,Ne=Ne.child;continue}if(Ne===y)break e;for(;Ne.sibling===null;){if(Ne.return===null||Ne.return===y)break e;Ne=Ne.return}Ne.sibling.return=Ne.return,Ne=Ne.sibling}y.stateNode=Z;e:switch(ss(Z,$,M),$){case"button":case"input":case"select":case"textarea":M=!!M.autoFocus;break e;case"img":M=!0;break e;default:M=!1}M&&pu(y)}}return va(y),kj(y,y.type,h===null?null:h.memoizedProps,y.pendingProps,S),null;case 6:if(h&&y.stateNode!=null)h.memoizedProps!==M&&pu(y);else{if(typeof M!="string"&&y.stateNode===null)throw Error(r(166));if(h=q.current,Jg(y)){if(h=y.stateNode,S=y.memoizedProps,M=null,$=rs,$!==null)switch($.tag){case 27:case 5:M=$.memoizedProps}h[at]=y,h=!!(h.nodeValue===S||M!==null&&M.suppressHydrationWarning===!0||XB(h.nodeValue,S)),h||qm(y,!0)}else h=j_(h).createTextNode(M),h[at]=y,y.stateNode=h}return va(y),null;case 31:if(S=y.memoizedState,h===null||h.memoizedState!==null){if(M=Jg(y),S!==null){if(h===null){if(!M)throw Error(r(318));if(h=y.memoizedState,h=h!==null?h.dehydrated:null,!h)throw Error(r(557));h[at]=y}else Gp(),(y.flags&128)===0&&(y.memoizedState=null),y.flags|=4;va(y),h=!1}else S=FA(),h!==null&&h.memoizedState!==null&&(h.memoizedState.hydrationErrors=S),h=!0;if(!h)return y.flags&256?(Zo(y),y):(Zo(y),null);if((y.flags&128)!==0)throw Error(r(558))}return va(y),null;case 13:if(M=y.memoizedState,h===null||h.memoizedState!==null&&h.memoizedState.dehydrated!==null){if($=Jg(y),M!==null&&M.dehydrated!==null){if(h===null){if(!$)throw Error(r(318));if($=y.memoizedState,$=$!==null?$.dehydrated:null,!$)throw Error(r(317));$[at]=y}else Gp(),(y.flags&128)===0&&(y.memoizedState=null),y.flags|=4;va(y),$=!1}else $=FA(),h!==null&&h.memoizedState!==null&&(h.memoizedState.hydrationErrors=$),$=!0;if(!$)return y.flags&256?(Zo(y),y):(Zo(y),null)}return Zo(y),(y.flags&128)!==0?(y.lanes=S,y):(S=M!==null,h=h!==null&&h.memoizedState!==null,S&&(M=y.child,$=null,M.alternate!==null&&M.alternate.memoizedState!==null&&M.alternate.memoizedState.cachePool!==null&&($=M.alternate.memoizedState.cachePool.pool),Z=null,M.memoizedState!==null&&M.memoizedState.cachePool!==null&&(Z=M.memoizedState.cachePool.pool),Z!==$&&(M.flags|=2048)),S!==h&&S&&(y.child.flags|=8192),f_(y,y.updateQueue),va(y),null);case 4:return j(),h===null&&Gj(y.stateNode.containerInfo),va(y),null;case 10:return du(y.type),va(y),null;case 19:if(ne(ri),M=y.memoizedState,M===null)return va(y),null;if($=(y.flags&128)!==0,Z=M.rendering,Z===null)if($)sv(M,!1);else{if(Ha!==0||h!==null&&(h.flags&128)!==0)for(h=y.child;h!==null;){if(Z=n_(h),Z!==null){for(y.flags|=128,sv(M,!1),h=Z.updateQueue,y.updateQueue=h,f_(y,h),y.subtreeFlags=0,h=S,S=y.child;S!==null;)P5(S,h),S=S.sibling;return me(ri,ri.current&1|2),wr&&lu(y,M.treeForkCount),y.child}h=h.sibling}M.tail!==null&&R()>v_&&(y.flags|=128,$=!0,sv(M,!1),y.lanes=4194304)}else{if(!$)if(h=n_(Z),h!==null){if(y.flags|=128,$=!0,h=h.updateQueue,y.updateQueue=h,f_(y,h),sv(M,!0),M.tail===null&&M.tailMode==="hidden"&&!Z.alternate&&!wr)return va(y),null}else 2*R()-M.renderingStartTime>v_&&S!==536870912&&(y.flags|=128,$=!0,sv(M,!1),y.lanes=4194304);M.isBackwards?(Z.sibling=y.child,y.child=Z):(h=M.last,h!==null?h.sibling=Z:y.child=Z,M.last=Z)}return M.tail!==null?(h=M.tail,M.rendering=h,M.tail=h.sibling,M.renderingStartTime=R(),h.sibling=null,S=ri.current,me(ri,$?S&1|2:S&1),wr&&lu(y,M.treeForkCount),h):(va(y),null);case 22:case 23:return Zo(y),WA(),M=y.memoizedState!==null,h!==null?h.memoizedState!==null!==M&&(y.flags|=8192):M&&(y.flags|=8192),M?(S&536870912)!==0&&(y.flags&128)===0&&(va(y),y.subtreeFlags&6&&(y.flags|=8192)):va(y),S=y.updateQueue,S!==null&&f_(y,S.retryQueue),S=null,h!==null&&h.memoizedState!==null&&h.memoizedState.cachePool!==null&&(S=h.memoizedState.cachePool.pool),M=null,y.memoizedState!==null&&y.memoizedState.cachePool!==null&&(M=y.memoizedState.cachePool.pool),M!==S&&(y.flags|=2048),h!==null&&ne(Xp),null;case 24:return S=null,h!==null&&(S=h.memoizedState.cache),y.memoizedState.cache!==S&&(y.flags|=2048),du(ui),va(y),null;case 25:return null;case 30:return null}throw Error(r(156,y.tag))}function Rce(h,y){switch(EA(y),y.tag){case 1:return h=y.flags,h&65536?(y.flags=h&-65537|128,y):null;case 3:return du(ui),j(),h=y.flags,(h&65536)!==0&&(h&128)===0?(y.flags=h&-65537|128,y):null;case 26:case 27:case 5:return K(y),null;case 31:if(y.memoizedState!==null){if(Zo(y),y.alternate===null)throw Error(r(340));Gp()}return h=y.flags,h&65536?(y.flags=h&-65537|128,y):null;case 13:if(Zo(y),h=y.memoizedState,h!==null&&h.dehydrated!==null){if(y.alternate===null)throw Error(r(340));Gp()}return h=y.flags,h&65536?(y.flags=h&-65537|128,y):null;case 19:return ne(ri),null;case 4:return j(),null;case 10:return du(y.type),null;case 22:case 23:return Zo(y),WA(),h!==null&&ne(Xp),h=y.flags,h&65536?(y.flags=h&-65537|128,y):null;case 24:return du(ui),null;case 25:return null;default:return null}}function eB(h,y){switch(EA(y),y.tag){case 3:du(ui),j();break;case 26:case 27:case 5:K(y);break;case 4:j();break;case 31:y.memoizedState!==null&&Zo(y);break;case 13:Zo(y);break;case 19:ne(ri);break;case 10:du(y.type);break;case 22:case 23:Zo(y),WA(),h!==null&&ne(Xp);break;case 24:du(ui)}}function ov(h,y){try{var S=y.updateQueue,M=S!==null?S.lastEffect:null;if(M!==null){var $=M.next;S=$;do{if((S.tag&h)===h){M=void 0;var Z=S.create,Ne=S.inst;M=Z(),Ne.destroy=M}S=S.next}while(S!==$)}}catch(He){Br(y,y.return,He)}}function Ym(h,y,S){try{var M=y.updateQueue,$=M!==null?M.lastEffect:null;if($!==null){var Z=$.next;M=Z;do{if((M.tag&h)===h){var Ne=M.inst,He=Ne.destroy;if(He!==void 0){Ne.destroy=void 0,$=y;var nt=S,Nt=He;try{Nt()}catch(Lt){Br($,nt,Lt)}}}M=M.next}while(M!==Z)}}catch(Lt){Br(y,y.return,Lt)}}function tB(h){var y=h.updateQueue;if(y!==null){var S=h.stateNode;try{V5(y,S)}catch(M){Br(h,h.return,M)}}}function nB(h,y,S){S.props=ef(h.type,h.memoizedProps),S.state=h.memoizedState;try{S.componentWillUnmount()}catch(M){Br(h,y,M)}}function lv(h,y){try{var S=h.ref;if(S!==null){switch(h.tag){case 26:case 27:case 5:var M=h.stateNode;break;case 30:M=h.stateNode;break;default:M=h.stateNode}typeof S=="function"?h.refCleanup=S(M):S.current=M}}catch($){Br(h,y,$)}}function pd(h,y){var S=h.ref,M=h.refCleanup;if(S!==null)if(typeof M=="function")try{M()}catch($){Br(h,y,$)}finally{h.refCleanup=null,h=h.alternate,h!=null&&(h.refCleanup=null)}else if(typeof S=="function")try{S(null)}catch($){Br(h,y,$)}else S.current=null}function rB(h){var y=h.type,S=h.memoizedProps,M=h.stateNode;try{e:switch(y){case"button":case"input":case"select":case"textarea":S.autoFocus&&M.focus();break e;case"img":S.src?M.src=S.src:S.srcSet&&(M.srcset=S.srcSet)}}catch($){Br(h,h.return,$)}}function Nj(h,y,S){try{var M=h.stateNode;rde(M,h.type,S,y),M[dt]=y}catch($){Br(h,h.return,$)}}function aB(h){return h.tag===5||h.tag===3||h.tag===26||h.tag===27&&rh(h.type)||h.tag===4}function Cj(h){e:for(;;){for(;h.sibling===null;){if(h.return===null||aB(h.return))return null;h=h.return}for(h.sibling.return=h.return,h=h.sibling;h.tag!==5&&h.tag!==6&&h.tag!==18;){if(h.tag===27&&rh(h.type)||h.flags&2||h.child===null||h.tag===4)continue e;h.child.return=h,h=h.child}if(!(h.flags&2))return h.stateNode}}function Pj(h,y,S){var M=h.tag;if(M===5||M===6)h=h.stateNode,y?(S.nodeType===9?S.body:S.nodeName==="HTML"?S.ownerDocument.body:S).insertBefore(h,y):(y=S.nodeType===9?S.body:S.nodeName==="HTML"?S.ownerDocument.body:S,y.appendChild(h),S=S._reactRootContainer,S!=null||y.onclick!==null||(y.onclick=qt));else if(M!==4&&(M===27&&rh(h.type)&&(S=h.stateNode,y=null),h=h.child,h!==null))for(Pj(h,y,S),h=h.sibling;h!==null;)Pj(h,y,S),h=h.sibling}function g_(h,y,S){var M=h.tag;if(M===5||M===6)h=h.stateNode,y?S.insertBefore(h,y):S.appendChild(h);else if(M!==4&&(M===27&&rh(h.type)&&(S=h.stateNode),h=h.child,h!==null))for(g_(h,y,S),h=h.sibling;h!==null;)g_(h,y,S),h=h.sibling}function iB(h){var y=h.stateNode,S=h.memoizedProps;try{for(var M=h.type,$=y.attributes;$.length;)y.removeAttributeNode($[0]);ss(y,M,S),y[at]=h,y[dt]=S}catch(Z){Br(h,h.return,Z)}}var fu=!1,pi=!1,Tj=!1,sB=typeof WeakSet=="function"?WeakSet:Set,Hi=null;function Lce(h,y){if(h=h.containerInfo,Xj=O_,h=x5(h),wA(h)){if("selectionStart"in h)var S={start:h.selectionStart,end:h.selectionEnd};else e:{S=(S=h.ownerDocument)&&S.defaultView||window;var M=S.getSelection&&S.getSelection();if(M&&M.rangeCount!==0){S=M.anchorNode;var $=M.anchorOffset,Z=M.focusNode;M=M.focusOffset;try{S.nodeType,Z.nodeType}catch{S=null;break e}var Ne=0,He=-1,nt=-1,Nt=0,Lt=0,zt=h,Ct=null;t:for(;;){for(var Et;zt!==S||$!==0&&zt.nodeType!==3||(He=Ne+$),zt!==Z||M!==0&&zt.nodeType!==3||(nt=Ne+M),zt.nodeType===3&&(Ne+=zt.nodeValue.length),(Et=zt.firstChild)!==null;)Ct=zt,zt=Et;for(;;){if(zt===h)break t;if(Ct===S&&++Nt===$&&(He=Ne),Ct===Z&&++Lt===M&&(nt=Ne),(Et=zt.nextSibling)!==null)break;zt=Ct,Ct=zt.parentNode}zt=Et}S=He===-1||nt===-1?null:{start:He,end:nt}}else S=null}S=S||{start:0,end:0}}else S=null;for(Yj={focusedElem:h,selectionRange:S},O_=!1,Hi=y;Hi!==null;)if(y=Hi,h=y.child,(y.subtreeFlags&1028)!==0&&h!==null)h.return=y,Hi=h;else for(;Hi!==null;){switch(y=Hi,Z=y.alternate,h=y.flags,y.tag){case 0:if((h&4)!==0&&(h=y.updateQueue,h=h!==null?h.events:null,h!==null))for(S=0;S title"))),ss(Z,M,S),Z[at]=h,Ke(Z),M=Z;break e;case"link":var Ne=m8("link","href",$).get(M+(S.href||""));if(Ne){for(var He=0;HeWr&&(Ne=Wr,Wr=Fn,Fn=Ne);var bt=g5(He,Fn),ut=g5(He,Wr);if(bt&&ut&&(Et.rangeCount!==1||Et.anchorNode!==bt.node||Et.anchorOffset!==bt.offset||Et.focusNode!==ut.node||Et.focusOffset!==ut.offset)){var kt=zt.createRange();kt.setStart(bt.node,bt.offset),Et.removeAllRanges(),Fn>Wr?(Et.addRange(kt),Et.extend(ut.node,ut.offset)):(kt.setEnd(ut.node,ut.offset),Et.addRange(kt))}}}}for(zt=[],Et=He;Et=Et.parentNode;)Et.nodeType===1&&zt.push({element:Et,left:Et.scrollLeft,top:Et.scrollTop});for(typeof He.focus=="function"&&He.focus(),He=0;HeS?32:S,ie.T=null,S=Rj,Rj=null;var Z=eh,Ne=vu;if(Ti=0,hb=eh=null,vu=0,(Fr&6)!==0)throw Error(r(331));var He=Fr;if(Fr|=4,bB(Z.current),pB(Z,Z.current,Ne,S),Fr=He,pv(0,!1),Le&&typeof Le.onPostCommitFiberRoot=="function")try{Le.onPostCommitFiberRoot(Me,Z)}catch{}return!0}finally{J.p=$,ie.T=M,RB(h,y)}}function OB(h,y,S){y=Al(S,y),y=pj(h.stateNode,y,2),h=Wm(h,y,2),h!==null&&(xt(h,2),fd(h))}function Br(h,y,S){if(h.tag===3)OB(h,h,S);else for(;y!==null;){if(y.tag===3){OB(y,h,S);break}else if(y.tag===1){var M=y.stateNode;if(typeof y.type.getDerivedStateFromError=="function"||typeof M.componentDidCatch=="function"&&(Jm===null||!Jm.has(M))){h=Al(S,h),S=IU(2),M=Wm(y,S,2),M!==null&&(zU(S,M,y,h),xt(M,2),fd(M));break}}y=y.return}}function zj(h,y,S){var M=h.pingCache;if(M===null){M=h.pingCache=new zce;var $=new Set;M.set(y,$)}else $=M.get(y),$===void 0&&($=new Set,M.set(y,$));$.has(S)||(Mj=!0,$.add(S),h=$ce.bind(null,h,y,S),y.then(h,h))}function $ce(h,y,S){var M=h.pingCache;M!==null&&M.delete(y),h.pingedLanes|=h.suspendedLanes&S,h.warmLanes&=~S,oa===h&&(xr&S)===S&&(Ha===4||Ha===3&&(xr&62914560)===xr&&300>R()-y_?(Fr&2)===0&&pb(h,0):Ej|=S,mb===xr&&(mb=0)),fd(h)}function IB(h,y){y===0&&(y=Ze()),h=$p(h,y),h!==null&&(xt(h,y),fd(h))}function Vce(h){var y=h.memoizedState,S=0;y!==null&&(S=y.retryLane),IB(h,S)}function Gce(h,y){var S=0;switch(h.tag){case 31:case 13:var M=h.stateNode,$=h.memoizedState;$!==null&&(S=$.retryLane);break;case 19:M=h.stateNode;break;case 22:M=h.stateNode._retryCache;break;default:throw Error(r(314))}M!==null&&M.delete(y),IB(h,S)}function Wce(h,y){return ge(h,y)}var C_=null,gb=null,Uj=!1,P_=!1,Bj=!1,nh=0;function fd(h){h!==gb&&h.next===null&&(gb===null?C_=gb=h:gb=gb.next=h),P_=!0,Uj||(Uj=!0,Xce())}function pv(h,y){if(!Bj&&P_){Bj=!0;do for(var S=!1,M=C_;M!==null;){if(h!==0){var $=M.pendingLanes;if($===0)var Z=0;else{var Ne=M.suspendedLanes,He=M.pingedLanes;Z=(1<<31-qe(42|h)+1)-1,Z&=$&~(Ne&~He),Z=Z&201326741?Z&201326741|1:Z?Z|2:0}Z!==0&&(S=!0,HB(M,Z))}else Z=xr,Z=ce(M,M===oa?Z:0,M.cancelPendingCommit!==null||M.timeoutHandle!==-1),(Z&3)===0||xe(M,Z)||(S=!0,HB(M,Z));M=M.next}while(S);Bj=!1}}function Kce(){zB()}function zB(){P_=Uj=!1;var h=0;nh!==0&&ide()&&(h=nh);for(var y=R(),S=null,M=C_;M!==null;){var $=M.next,Z=UB(M,y);Z===0?(M.next=null,S===null?C_=$:S.next=$,$===null&&(gb=S)):(S=M,(h!==0||(Z&3)!==0)&&(P_=!0)),M=$}Ti!==0&&Ti!==5||pv(h),nh!==0&&(nh=0)}function UB(h,y){for(var S=h.suspendedLanes,M=h.pingedLanes,$=h.expirationTimes,Z=h.pendingLanes&-62914561;0He)break;var Lt=nt.transferSize,zt=nt.initiatorType;Lt&&YB(zt)&&(nt=nt.responseEnd,Ne+=Lt*(nt"u"?null:document;function l8(h,y,S){var M=bb;if(M&&typeof y=="string"&&y){var $=En(y);$='link[rel="'+h+'"][href="'+$+'"]',typeof S=="string"&&($+='[crossorigin="'+S+'"]'),o8.has($)||(o8.add($),h={rel:h,crossOrigin:S,href:y},M.querySelector($)===null&&(y=M.createElement("link"),ss(y,"link",h),Ke(y),M.head.appendChild(y)))}}function pde(h){wu.D(h),l8("dns-prefetch",h,null)}function fde(h,y){wu.C(h,y),l8("preconnect",h,y)}function gde(h,y,S){wu.L(h,y,S);var M=bb;if(M&&h&&y){var $='link[rel="preload"][as="'+En(y)+'"]';y==="image"&&S&&S.imageSrcSet?($+='[imagesrcset="'+En(S.imageSrcSet)+'"]',typeof S.imageSizes=="string"&&($+='[imagesizes="'+En(S.imageSizes)+'"]')):$+='[href="'+En(h)+'"]';var Z=$;switch(y){case"style":Z=xb(h);break;case"script":Z=yb(h)}Rl.has(Z)||(h=m({rel:"preload",href:y==="image"&&S&&S.imageSrcSet?void 0:h,as:y},S),Rl.set(Z,h),M.querySelector($)!==null||y==="style"&&M.querySelector(xv(Z))||y==="script"&&M.querySelector(yv(Z))||(y=M.createElement("link"),ss(y,"link",h),Ke(y),M.head.appendChild(y)))}}function bde(h,y){wu.m(h,y);var S=bb;if(S&&h){var M=y&&typeof y.as=="string"?y.as:"script",$='link[rel="modulepreload"][as="'+En(M)+'"][href="'+En(h)+'"]',Z=$;switch(M){case"audioworklet":case"paintworklet":case"serviceworker":case"sharedworker":case"worker":case"script":Z=yb(h)}if(!Rl.has(Z)&&(h=m({rel:"modulepreload",href:h},y),Rl.set(Z,h),S.querySelector($)===null)){switch(M){case"audioworklet":case"paintworklet":case"serviceworker":case"sharedworker":case"worker":case"script":if(S.querySelector(yv(Z)))return}M=S.createElement("link"),ss(M,"link",h),Ke(M),S.head.appendChild(M)}}}function xde(h,y,S){wu.S(h,y,S);var M=bb;if(M&&h){var $=et(M).hoistableStyles,Z=xb(h);y=y||"default";var Ne=$.get(Z);if(!Ne){var He={loading:0,preload:null};if(Ne=M.querySelector(xv(Z)))He.loading=5;else{h=m({rel:"stylesheet",href:h,"data-precedence":y},S),(S=Rl.get(Z))&&rM(h,S);var nt=Ne=M.createElement("link");Ke(nt),ss(nt,"link",h),nt._p=new Promise(function(Nt,Lt){nt.onload=Nt,nt.onerror=Lt}),nt.addEventListener("load",function(){He.loading|=1}),nt.addEventListener("error",function(){He.loading|=2}),He.loading|=4,E_(Ne,y,M)}Ne={type:"stylesheet",instance:Ne,count:1,state:He},$.set(Z,Ne)}}}function yde(h,y){wu.X(h,y);var S=bb;if(S&&h){var M=et(S).hoistableScripts,$=yb(h),Z=M.get($);Z||(Z=S.querySelector(yv($)),Z||(h=m({src:h,async:!0},y),(y=Rl.get($))&&aM(h,y),Z=S.createElement("script"),Ke(Z),ss(Z,"link",h),S.head.appendChild(Z)),Z={type:"script",instance:Z,count:1,state:null},M.set($,Z))}}function vde(h,y){wu.M(h,y);var S=bb;if(S&&h){var M=et(S).hoistableScripts,$=yb(h),Z=M.get($);Z||(Z=S.querySelector(yv($)),Z||(h=m({src:h,async:!0,type:"module"},y),(y=Rl.get($))&&aM(h,y),Z=S.createElement("script"),Ke(Z),ss(Z,"link",h),S.head.appendChild(Z)),Z={type:"script",instance:Z,count:1,state:null},M.set($,Z))}}function c8(h,y,S,M){var $=($=q.current)?M_($):null;if(!$)throw Error(r(446));switch(h){case"meta":case"title":return null;case"style":return typeof S.precedence=="string"&&typeof S.href=="string"?(y=xb(S.href),S=et($).hoistableStyles,M=S.get(y),M||(M={type:"style",instance:null,count:0,state:null},S.set(y,M)),M):{type:"void",instance:null,count:0,state:null};case"link":if(S.rel==="stylesheet"&&typeof S.href=="string"&&typeof S.precedence=="string"){h=xb(S.href);var Z=et($).hoistableStyles,Ne=Z.get(h);if(Ne||($=$.ownerDocument||$,Ne={type:"stylesheet",instance:null,count:0,state:{loading:0,preload:null}},Z.set(h,Ne),(Z=$.querySelector(xv(h)))&&!Z._p&&(Ne.instance=Z,Ne.state.loading=5),Rl.has(h)||(S={rel:"preload",as:"style",href:S.href,crossOrigin:S.crossOrigin,integrity:S.integrity,media:S.media,hrefLang:S.hrefLang,referrerPolicy:S.referrerPolicy},Rl.set(h,S),Z||wde($,h,S,Ne.state))),y&&M===null)throw Error(r(528,""));return Ne}if(y&&M!==null)throw Error(r(529,""));return null;case"script":return y=S.async,S=S.src,typeof S=="string"&&y&&typeof y!="function"&&typeof y!="symbol"?(y=yb(S),S=et($).hoistableScripts,M=S.get(y),M||(M={type:"script",instance:null,count:0,state:null},S.set(y,M)),M):{type:"void",instance:null,count:0,state:null};default:throw Error(r(444,h))}}function xb(h){return'href="'+En(h)+'"'}function xv(h){return'link[rel="stylesheet"]['+h+"]"}function d8(h){return m({},h,{"data-precedence":h.precedence,precedence:null})}function wde(h,y,S,M){h.querySelector('link[rel="preload"][as="style"]['+y+"]")?M.loading=1:(y=h.createElement("link"),M.preload=y,y.addEventListener("load",function(){return M.loading|=1}),y.addEventListener("error",function(){return M.loading|=2}),ss(y,"link",S),Ke(y),h.head.appendChild(y))}function yb(h){return'[src="'+En(h)+'"]'}function yv(h){return"script[async]"+h}function u8(h,y,S){if(y.count++,y.instance===null)switch(y.type){case"style":var M=h.querySelector('style[data-href~="'+En(S.href)+'"]');if(M)return y.instance=M,Ke(M),M;var $=m({},S,{"data-href":S.href,"data-precedence":S.precedence,href:null,precedence:null});return M=(h.ownerDocument||h).createElement("style"),Ke(M),ss(M,"style",$),E_(M,S.precedence,h),y.instance=M;case"stylesheet":$=xb(S.href);var Z=h.querySelector(xv($));if(Z)return y.state.loading|=4,y.instance=Z,Ke(Z),Z;M=d8(S),($=Rl.get($))&&rM(M,$),Z=(h.ownerDocument||h).createElement("link"),Ke(Z);var Ne=Z;return Ne._p=new Promise(function(He,nt){Ne.onload=He,Ne.onerror=nt}),ss(Z,"link",M),y.state.loading|=4,E_(Z,S.precedence,h),y.instance=Z;case"script":return Z=yb(S.src),($=h.querySelector(yv(Z)))?(y.instance=$,Ke($),$):(M=S,($=Rl.get(Z))&&(M=m({},S),aM(M,$)),h=h.ownerDocument||h,$=h.createElement("script"),Ke($),ss($,"link",M),h.head.appendChild($),y.instance=$);case"void":return null;default:throw Error(r(443,y.type))}else y.type==="stylesheet"&&(y.state.loading&4)===0&&(M=y.instance,y.state.loading|=4,E_(M,S.precedence,h));return y.instance}function E_(h,y,S){for(var M=S.querySelectorAll('link[rel="stylesheet"][data-precedence],style[data-precedence]'),$=M.length?M[M.length-1]:null,Z=$,Ne=0;Ne title"):null)}function Sde(h,y,S){if(S===1||y.itemProp!=null)return!1;switch(h){case"meta":case"title":return!0;case"style":if(typeof y.precedence!="string"||typeof y.href!="string"||y.href==="")break;return!0;case"link":if(typeof y.rel!="string"||typeof y.href!="string"||y.href===""||y.onLoad||y.onError)break;return y.rel==="stylesheet"?(h=y.disabled,typeof y.precedence=="string"&&h==null):!0;case"script":if(y.async&&typeof y.async!="function"&&typeof y.async!="symbol"&&!y.onLoad&&!y.onError&&y.src&&typeof y.src=="string")return!0}return!1}function p8(h){return!(h.type==="stylesheet"&&(h.state.loading&3)===0)}function _de(h,y,S,M){if(S.type==="stylesheet"&&(typeof M.media!="string"||matchMedia(M.media).matches!==!1)&&(S.state.loading&4)===0){if(S.instance===null){var $=xb(M.href),Z=y.querySelector(xv($));if(Z){y=Z._p,y!==null&&typeof y=="object"&&typeof y.then=="function"&&(h.count++,h=F_.bind(h),y.then(h,h)),S.state.loading|=4,S.instance=Z,Ke(Z);return}Z=y.ownerDocument||y,M=d8(M),($=Rl.get($))&&rM(M,$),Z=Z.createElement("link"),Ke(Z);var Ne=Z;Ne._p=new Promise(function(He,nt){Ne.onload=He,Ne.onerror=nt}),ss(Z,"link",M),S.instance=Z}h.stylesheets===null&&(h.stylesheets=new Map),h.stylesheets.set(S,y),(y=S.state.preload)&&(S.state.loading&3)===0&&(h.count++,S=F_.bind(h),y.addEventListener("load",S),y.addEventListener("error",S))}}var iM=0;function kde(h,y){return h.stylesheets&&h.count===0&&L_(h,h.stylesheets),0iM?50:800)+y);return h.unsuspend=S,function(){h.unsuspend=null,clearTimeout(M),clearTimeout($)}}:null}function F_(){if(this.count--,this.count===0&&(this.imgCount===0||!this.waitingForImages)){if(this.stylesheets)L_(this,this.stylesheets);else if(this.unsuspend){var h=this.unsuspend;this.unsuspend=null,h()}}}var R_=null;function L_(h,y){h.stylesheets=null,h.unsuspend!==null&&(h.count++,R_=new Map,y.forEach(Nde,h),R_=null,F_.call(h))}function Nde(h,y){if(!(y.state.loading&4)){var S=R_.get(h);if(S)var M=S.get(null);else{S=new Map,R_.set(h,S);for(var $=h.querySelectorAll("link[data-precedence],style[data-precedence]"),Z=0;Z<$.length;Z++){var Ne=$[Z];(Ne.nodeName==="LINK"||Ne.getAttribute("media")!=="not all")&&(S.set(Ne.dataset.precedence,Ne),M=Ne)}M&&S.set(null,M)}$=y.instance,Ne=$.getAttribute("data-precedence"),Z=S.get(Ne)||M,Z===M&&S.set(null,$),S.set(Ne,$),this.count++,M=F_.bind(this),$.addEventListener("load",M),$.addEventListener("error",M),Z?Z.parentNode.insertBefore($,Z.nextSibling):(h=h.nodeType===9?h.head:h,h.insertBefore($,h.firstChild)),y.state.loading|=4}}var vv={$$typeof:C,Provider:null,Consumer:null,_currentValue:oe,_currentValue2:oe,_threadCount:0};function Cde(h,y,S,M,$,Z,Ne,He,nt){this.tag=1,this.containerInfo=h,this.pingCache=this.current=this.pendingChildren=null,this.timeoutHandle=-1,this.callbackNode=this.next=this.pendingContext=this.context=this.cancelPendingCommit=null,this.callbackPriority=0,this.expirationTimes=pt(-1),this.entangledLanes=this.shellSuspendCounter=this.errorRecoveryDisabledLanes=this.expiredLanes=this.warmLanes=this.pingedLanes=this.suspendedLanes=this.pendingLanes=0,this.entanglements=pt(0),this.hiddenUpdates=pt(null),this.identifierPrefix=M,this.onUncaughtError=$,this.onCaughtError=Z,this.onRecoverableError=Ne,this.pooledCache=null,this.pooledCacheLanes=0,this.formState=nt,this.incompleteTransitions=new Map}function f8(h,y,S,M,$,Z,Ne,He,nt,Nt,Lt,zt){return h=new Cde(h,y,S,Ne,nt,Nt,Lt,zt,He),y=1,Z===!0&&(y|=24),Z=Yo(3,null,null,y),h.current=Z,Z.stateNode=h,y=IA(),y.refCount++,h.pooledCache=y,y.refCount++,Z.memoizedState={element:M,isDehydrated:S,cache:y},HA(Z),h}function g8(h){return h?(h=Yg,h):Yg}function b8(h,y,S,M,$,Z){$=g8($),M.context===null?M.context=$:M.pendingContext=$,M=Gm(y),M.payload={element:S},Z=Z===void 0?null:Z,Z!==null&&(M.callback=Z),S=Wm(h,M,y),S!==null&&(Co(S,h,y),Zy(S,h,y))}function x8(h,y){if(h=h.memoizedState,h!==null&&h.dehydrated!==null){var S=h.retryLane;h.retryLane=S!==0&&S"u"||typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE!="function"))try{__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(t)}catch(e){console.error(e)}}return t(),pM.exports=Hde(),pM.exports}var $de=qde();const $n=t=>typeof t=="string",Cv=()=>{let t,e;const n=new Promise((r,i)=>{t=r,e=i});return n.resolve=t,n.reject=e,n},I8=t=>t==null?"":""+t,Vde=(t,e,n)=>{t.forEach(r=>{e[r]&&(n[r]=e[r])})},Gde=/###/g,z8=t=>t&&t.indexOf("###")>-1?t.replace(Gde,"."):t,U8=t=>!t||$n(t),_0=(t,e,n)=>{const r=$n(e)?e.split("."):e;let i=0;for(;i{const{obj:r,k:i}=_0(t,e,Object);if(r!==void 0||e.length===1){r[i]=n;return}let s=e[e.length-1],o=e.slice(0,e.length-1),l=_0(t,o,Object);for(;l.obj===void 0&&o.length;)s=`${o[o.length-1]}.${s}`,o=o.slice(0,o.length-1),l=_0(t,o,Object),l?.obj&&typeof l.obj[`${l.k}.${s}`]<"u"&&(l.obj=void 0);l.obj[`${l.k}.${s}`]=n},Wde=(t,e,n,r)=>{const{obj:i,k:s}=_0(t,e,Object);i[s]=i[s]||[],i[s].push(n)},dN=(t,e)=>{const{obj:n,k:r}=_0(t,e);if(n&&Object.prototype.hasOwnProperty.call(n,r))return n[r]},Kde=(t,e,n)=>{const r=dN(t,n);return r!==void 0?r:dN(e,n)},BY=(t,e,n)=>{for(const r in e)r!=="__proto__"&&r!=="constructor"&&(r in t?$n(t[r])||t[r]instanceof String||$n(e[r])||e[r]instanceof String?n&&(t[r]=e[r]):BY(t[r],e[r],n):t[r]=e[r]);return t},wb=t=>t.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g,"\\$&");var Xde={"&":"&","<":"<",">":">",'"':""","'":"'","/":"/"};const Yde=t=>$n(t)?t.replace(/[&<>"'\/]/g,e=>Xde[e]):t;class Qde{constructor(e){this.capacity=e,this.regExpMap=new Map,this.regExpQueue=[]}getRegExp(e){const n=this.regExpMap.get(e);if(n!==void 0)return n;const r=new RegExp(e);return this.regExpQueue.length===this.capacity&&this.regExpMap.delete(this.regExpQueue.shift()),this.regExpMap.set(e,r),this.regExpQueue.push(e),r}}const Zde=[" ",",","?","!",";"],Jde=new Qde(20),eue=(t,e,n)=>{e=e||"",n=n||"";const r=Zde.filter(o=>e.indexOf(o)<0&&n.indexOf(o)<0);if(r.length===0)return!0;const i=Jde.getRegExp(`(${r.map(o=>o==="?"?"\\?":o).join("|")})`);let s=!i.test(t);if(!s){const o=t.indexOf(n);o>0&&!i.test(t.substring(0,o))&&(s=!0)}return s},I3=(t,e,n=".")=>{if(!t)return;if(t[e])return Object.prototype.hasOwnProperty.call(t,e)?t[e]:void 0;const r=e.split(n);let i=t;for(let s=0;s-1&&ct?.replace("_","-"),tue={type:"logger",log(t){this.output("log",t)},warn(t){this.output("warn",t)},error(t){this.output("error",t)},output(t,e){console?.[t]?.apply?.(console,e)}};class uN{constructor(e,n={}){this.init(e,n)}init(e,n={}){this.prefix=n.prefix||"i18next:",this.logger=e||tue,this.options=n,this.debug=n.debug}log(...e){return this.forward(e,"log","",!0)}warn(...e){return this.forward(e,"warn","",!0)}error(...e){return this.forward(e,"error","")}deprecate(...e){return this.forward(e,"warn","WARNING DEPRECATED: ",!0)}forward(e,n,r,i){return i&&!this.debug?null:($n(e[0])&&(e[0]=`${r}${this.prefix} ${e[0]}`),this.logger[n](e))}create(e){return new uN(this.logger,{prefix:`${this.prefix}:${e}:`,...this.options})}clone(e){return e=e||this.options,e.prefix=e.prefix||this.prefix,new uN(this.logger,e)}}var Ed=new uN;let LP=class{constructor(){this.observers={}}on(e,n){return e.split(" ").forEach(r=>{this.observers[r]||(this.observers[r]=new Map);const i=this.observers[r].get(n)||0;this.observers[r].set(n,i+1)}),this}off(e,n){if(this.observers[e]){if(!n){delete this.observers[e];return}this.observers[e].delete(n)}}emit(e,...n){this.observers[e]&&Array.from(this.observers[e].entries()).forEach(([i,s])=>{for(let o=0;o{for(let o=0;o-1&&this.options.ns.splice(n,1)}getResource(e,n,r,i={}){const s=i.keySeparator!==void 0?i.keySeparator:this.options.keySeparator,o=i.ignoreJSONStructure!==void 0?i.ignoreJSONStructure:this.options.ignoreJSONStructure;let l;e.indexOf(".")>-1?l=e.split("."):(l=[e,n],r&&(Array.isArray(r)?l.push(...r):$n(r)&&s?l.push(...r.split(s)):l.push(r)));const c=dN(this.data,l);return!c&&!n&&!r&&e.indexOf(".")>-1&&(e=l[0],n=l[1],r=l.slice(2).join(".")),c||!o||!$n(r)?c:I3(this.data?.[e]?.[n],r,s)}addResource(e,n,r,i,s={silent:!1}){const o=s.keySeparator!==void 0?s.keySeparator:this.options.keySeparator;let l=[e,n];r&&(l=l.concat(o?r.split(o):r)),e.indexOf(".")>-1&&(l=e.split("."),i=n,n=l[1]),this.addNamespaces(n),B8(this.data,l,i),s.silent||this.emit("added",e,n,r,i)}addResources(e,n,r,i={silent:!1}){for(const s in r)($n(r[s])||Array.isArray(r[s]))&&this.addResource(e,n,s,r[s],{silent:!0});i.silent||this.emit("added",e,n,r)}addResourceBundle(e,n,r,i,s,o={silent:!1,skipCopy:!1}){let l=[e,n];e.indexOf(".")>-1&&(l=e.split("."),i=r,r=n,n=l[1]),this.addNamespaces(n);let c=dN(this.data,l)||{};o.skipCopy||(r=JSON.parse(JSON.stringify(r))),i?BY(c,r,s):c={...c,...r},B8(this.data,l,c),o.silent||this.emit("added",e,n,r)}removeResourceBundle(e,n){this.hasResourceBundle(e,n)&&delete this.data[e][n],this.removeNamespaces(n),this.emit("removed",e,n)}hasResourceBundle(e,n){return this.getResource(e,n)!==void 0}getResourceBundle(e,n){return n||(n=this.options.defaultNS),this.getResource(e,n)}getDataByLanguage(e){return this.data[e]}hasLanguageSomeTranslations(e){const n=this.getDataByLanguage(e);return!!(n&&Object.keys(n)||[]).find(i=>n[i]&&Object.keys(n[i]).length>0)}toJSON(){return this.data}}var HY={processors:{},addPostProcessor(t){this.processors[t.name]=t},handle(t,e,n,r,i){return t.forEach(s=>{e=this.processors[s]?.process(e,n,r,i)??e}),e}};const qY=Symbol("i18next/PATH_KEY");function nue(){const t=[],e=Object.create(null);let n;return e.get=(r,i)=>(n?.revoke?.(),i===qY?t:(t.push(i),n=Proxy.revocable(r,e),n.proxy)),Proxy.revocable(Object.create(null),e).proxy}function z3(t,e){const{[qY]:n}=t(nue());return n.join(e?.keySeparator??".")}const q8={},xM=t=>!$n(t)&&typeof t!="boolean"&&typeof t!="number";class mN extends LP{constructor(e,n={}){super(),Vde(["resourceStore","languageUtils","pluralResolver","interpolator","backendConnector","i18nFormat","utils"],e,this),this.options=n,this.options.keySeparator===void 0&&(this.options.keySeparator="."),this.logger=Ed.create("translator")}changeLanguage(e){e&&(this.language=e)}exists(e,n={interpolation:{}}){const r={...n};if(e==null)return!1;const i=this.resolve(e,r);if(i?.res===void 0)return!1;const s=xM(i.res);return!(r.returnObjects===!1&&s)}extractFromKey(e,n){let r=n.nsSeparator!==void 0?n.nsSeparator:this.options.nsSeparator;r===void 0&&(r=":");const i=n.keySeparator!==void 0?n.keySeparator:this.options.keySeparator;let s=n.ns||this.options.defaultNS||[];const o=r&&e.indexOf(r)>-1,l=!this.options.userDefinedKeySeparator&&!n.keySeparator&&!this.options.userDefinedNsSeparator&&!n.nsSeparator&&!eue(e,r,i);if(o&&!l){const c=e.match(this.interpolator.nestingRegexp);if(c&&c.length>0)return{key:e,namespaces:$n(s)?[s]:s};const d=e.split(r);(r!==i||r===i&&this.options.ns.indexOf(d[0])>-1)&&(s=d.shift()),e=d.join(i)}return{key:e,namespaces:$n(s)?[s]:s}}translate(e,n,r){let i=typeof n=="object"?{...n}:n;if(typeof i!="object"&&this.options.overloadTranslationOptionHandler&&(i=this.options.overloadTranslationOptionHandler(arguments)),typeof i=="object"&&(i={...i}),i||(i={}),e==null)return"";typeof e=="function"&&(e=z3(e,{...this.options,...i})),Array.isArray(e)||(e=[String(e)]);const s=i.returnDetails!==void 0?i.returnDetails:this.options.returnDetails,o=i.keySeparator!==void 0?i.keySeparator:this.options.keySeparator,{key:l,namespaces:c}=this.extractFromKey(e[e.length-1],i),d=c[c.length-1];let u=i.nsSeparator!==void 0?i.nsSeparator:this.options.nsSeparator;u===void 0&&(u=":");const m=i.lng||this.language,p=i.appendNamespaceToCIMode||this.options.appendNamespaceToCIMode;if(m?.toLowerCase()==="cimode")return p?s?{res:`${d}${u}${l}`,usedKey:l,exactUsedKey:l,usedLng:m,usedNS:d,usedParams:this.getUsedParamsDetails(i)}:`${d}${u}${l}`:s?{res:l,usedKey:l,exactUsedKey:l,usedLng:m,usedNS:d,usedParams:this.getUsedParamsDetails(i)}:l;const f=this.resolve(e,i);let x=f?.res;const v=f?.usedKey||l,b=f?.exactUsedKey||l,g=["[object Number]","[object Function]","[object RegExp]"],_=i.joinArrays!==void 0?i.joinArrays:this.options.joinArrays,C=!this.i18nFormat||this.i18nFormat.handleAsObject,P=i.count!==void 0&&!$n(i.count),N=mN.hasDefaultValue(i),A=P?this.pluralResolver.getSuffix(m,i.count,i):"",T=i.ordinal&&P?this.pluralResolver.getSuffix(m,i.count,{ordinal:!1}):"",F=P&&!i.ordinal&&i.count===0,k=F&&i[`defaultValue${this.options.pluralSeparator}zero`]||i[`defaultValue${A}`]||i[`defaultValue${T}`]||i.defaultValue;let D=x;C&&!x&&N&&(D=k);const H=xM(D),z=Object.prototype.toString.apply(D);if(C&&D&&H&&g.indexOf(z)<0&&!($n(_)&&Array.isArray(D))){if(!i.returnObjects&&!this.options.returnObjects){this.options.returnedObjectHandler||this.logger.warn("accessing an object - but returnObjects options is not enabled!");const X=this.options.returnedObjectHandler?this.options.returnedObjectHandler(v,D,{...i,ns:c}):`key '${l} (${this.language})' returned an object instead of string.`;return s?(f.res=X,f.usedParams=this.getUsedParamsDetails(i),f):X}if(o){const X=Array.isArray(D),L=X?[]:{},te=X?b:v;for(const ie in D)if(Object.prototype.hasOwnProperty.call(D,ie)){const J=`${te}${o}${ie}`;N&&!x?L[ie]=this.translate(J,{...i,defaultValue:xM(k)?k[ie]:void 0,joinArrays:!1,ns:c}):L[ie]=this.translate(J,{...i,joinArrays:!1,ns:c}),L[ie]===J&&(L[ie]=D[ie])}x=L}}else if(C&&$n(_)&&Array.isArray(x))x=x.join(_),x&&(x=this.extendTranslation(x,e,i,r));else{let X=!1,L=!1;!this.isValidLookup(x)&&N&&(X=!0,x=k),this.isValidLookup(x)||(L=!0,x=l);const ie=(i.missingKeyNoValueFallbackToKey||this.options.missingKeyNoValueFallbackToKey)&&L?void 0:x,J=N&&k!==x&&this.options.updateMissing;if(L||X||J){if(this.logger.log(J?"updateKey":"missingKey",m,d,l,J?k:x),o){const W=this.resolve(l,{...i,keySeparator:!1});W&&W.res&&this.logger.warn("Seems the loaded translations were in flat JSON format instead of nested. Either set keySeparator: false on init or make sure your translations are published in nested format.")}let oe=[];const fe=this.languageUtils.getFallbackCodes(this.options.fallbackLng,i.lng||this.language);if(this.options.saveMissingTo==="fallback"&&fe&&fe[0])for(let W=0;W{const be=N&&me!==x?me:ie;this.options.missingKeyHandler?this.options.missingKeyHandler(W,d,ne,be,J,i):this.backendConnector?.saveMissing&&this.backendConnector.saveMissing(W,d,ne,be,J,i),this.emit("missingKey",W,d,ne,x)};this.options.saveMissing&&(this.options.saveMissingPlurals&&P?oe.forEach(W=>{const ne=this.pluralResolver.getSuffixes(W,i);F&&i[`defaultValue${this.options.pluralSeparator}zero`]&&ne.indexOf(`${this.options.pluralSeparator}zero`)<0&&ne.push(`${this.options.pluralSeparator}zero`),ne.forEach(me=>{re([W],l+me,i[`defaultValue${me}`]||k)})}):re(oe,l,k))}x=this.extendTranslation(x,e,i,f,r),L&&x===l&&this.options.appendNamespaceToMissingKey&&(x=`${d}${u}${l}`),(L||X)&&this.options.parseMissingKeyHandler&&(x=this.options.parseMissingKeyHandler(this.options.appendNamespaceToMissingKey?`${d}${u}${l}`:l,X?x:void 0,i))}return s?(f.res=x,f.usedParams=this.getUsedParamsDetails(i),f):x}extendTranslation(e,n,r,i,s){if(this.i18nFormat?.parse)e=this.i18nFormat.parse(e,{...this.options.interpolation.defaultVariables,...r},r.lng||this.language||i.usedLng,i.usedNS,i.usedKey,{resolved:i});else if(!r.skipInterpolation){r.interpolation&&this.interpolator.init({...r,interpolation:{...this.options.interpolation,...r.interpolation}});const c=$n(e)&&(r?.interpolation?.skipOnVariables!==void 0?r.interpolation.skipOnVariables:this.options.interpolation.skipOnVariables);let d;if(c){const m=e.match(this.interpolator.nestingRegexp);d=m&&m.length}let u=r.replace&&!$n(r.replace)?r.replace:r;if(this.options.interpolation.defaultVariables&&(u={...this.options.interpolation.defaultVariables,...u}),e=this.interpolator.interpolate(e,u,r.lng||this.language||i.usedLng,r),c){const m=e.match(this.interpolator.nestingRegexp),p=m&&m.length;ds?.[0]===m[0]&&!r.context?(this.logger.warn(`It seems you are nesting recursively key: ${m[0]} in key: ${n[0]}`),null):this.translate(...m,n),r)),r.interpolation&&this.interpolator.reset()}const o=r.postProcess||this.options.postProcess,l=$n(o)?[o]:o;return e!=null&&l?.length&&r.applyPostProcessor!==!1&&(e=HY.handle(l,e,n,this.options&&this.options.postProcessPassResolved?{i18nResolved:{...i,usedParams:this.getUsedParamsDetails(r)},...r}:r,this)),e}resolve(e,n={}){let r,i,s,o,l;return $n(e)&&(e=[e]),e.forEach(c=>{if(this.isValidLookup(r))return;const d=this.extractFromKey(c,n),u=d.key;i=u;let m=d.namespaces;this.options.fallbackNS&&(m=m.concat(this.options.fallbackNS));const p=n.count!==void 0&&!$n(n.count),f=p&&!n.ordinal&&n.count===0,x=n.context!==void 0&&($n(n.context)||typeof n.context=="number")&&n.context!=="",v=n.lngs?n.lngs:this.languageUtils.toResolveHierarchy(n.lng||this.language,n.fallbackLng);m.forEach(b=>{this.isValidLookup(r)||(l=b,!q8[`${v[0]}-${b}`]&&this.utils?.hasLoadedNamespace&&!this.utils?.hasLoadedNamespace(l)&&(q8[`${v[0]}-${b}`]=!0,this.logger.warn(`key "${i}" for languages "${v.join(", ")}" won't get resolved as namespace "${l}" was not yet loaded`,"This means something IS WRONG in your setup. You access the t function before i18next.init / i18next.loadNamespace / i18next.changeLanguage was done. Wait for the callback or Promise to resolve before accessing it!!!")),v.forEach(g=>{if(this.isValidLookup(r))return;o=g;const _=[u];if(this.i18nFormat?.addLookupKeys)this.i18nFormat.addLookupKeys(_,u,g,b,n);else{let P;p&&(P=this.pluralResolver.getSuffix(g,n.count,n));const N=`${this.options.pluralSeparator}zero`,A=`${this.options.pluralSeparator}ordinal${this.options.pluralSeparator}`;if(p&&(n.ordinal&&P.indexOf(A)===0&&_.push(u+P.replace(A,this.options.pluralSeparator)),_.push(u+P),f&&_.push(u+N)),x){const T=`${u}${this.options.contextSeparator||"_"}${n.context}`;_.push(T),p&&(n.ordinal&&P.indexOf(A)===0&&_.push(T+P.replace(A,this.options.pluralSeparator)),_.push(T+P),f&&_.push(T+N))}}let C;for(;C=_.pop();)this.isValidLookup(r)||(s=C,r=this.getResource(g,b,C,n))}))})}),{res:r,usedKey:i,exactUsedKey:s,usedLng:o,usedNS:l}}isValidLookup(e){return e!==void 0&&!(!this.options.returnNull&&e===null)&&!(!this.options.returnEmptyString&&e==="")}getResource(e,n,r,i={}){return this.i18nFormat?.getResource?this.i18nFormat.getResource(e,n,r,i):this.resourceStore.getResource(e,n,r,i)}getUsedParamsDetails(e={}){const n=["defaultValue","ordinal","context","replace","lng","lngs","fallbackLng","ns","keySeparator","nsSeparator","returnObjects","returnDetails","joinArrays","postProcess","interpolation"],r=e.replace&&!$n(e.replace);let i=r?e.replace:e;if(r&&typeof e.count<"u"&&(i.count=e.count),this.options.interpolation.defaultVariables&&(i={...this.options.interpolation.defaultVariables,...i}),!r){i={...i};for(const s of n)delete i[s]}return i}static hasDefaultValue(e){const n="defaultValue";for(const r in e)if(Object.prototype.hasOwnProperty.call(e,r)&&n===r.substring(0,n.length)&&e[r]!==void 0)return!0;return!1}}class $8{constructor(e){this.options=e,this.supportedLngs=this.options.supportedLngs||!1,this.logger=Ed.create("languageUtils")}getScriptPartFromCode(e){if(e=Y0(e),!e||e.indexOf("-")<0)return null;const n=e.split("-");return n.length===2||(n.pop(),n[n.length-1].toLowerCase()==="x")?null:this.formatLanguageCode(n.join("-"))}getLanguagePartFromCode(e){if(e=Y0(e),!e||e.indexOf("-")<0)return e;const n=e.split("-");return this.formatLanguageCode(n[0])}formatLanguageCode(e){if($n(e)&&e.indexOf("-")>-1){let n;try{n=Intl.getCanonicalLocales(e)[0]}catch{}return n&&this.options.lowerCaseLng&&(n=n.toLowerCase()),n||(this.options.lowerCaseLng?e.toLowerCase():e)}return this.options.cleanCode||this.options.lowerCaseLng?e.toLowerCase():e}isSupportedCode(e){return(this.options.load==="languageOnly"||this.options.nonExplicitSupportedLngs)&&(e=this.getLanguagePartFromCode(e)),!this.supportedLngs||!this.supportedLngs.length||this.supportedLngs.indexOf(e)>-1}getBestMatchFromCodes(e){if(!e)return null;let n;return e.forEach(r=>{if(n)return;const i=this.formatLanguageCode(r);(!this.options.supportedLngs||this.isSupportedCode(i))&&(n=i)}),!n&&this.options.supportedLngs&&e.forEach(r=>{if(n)return;const i=this.getScriptPartFromCode(r);if(this.isSupportedCode(i))return n=i;const s=this.getLanguagePartFromCode(r);if(this.isSupportedCode(s))return n=s;n=this.options.supportedLngs.find(o=>{if(o===s)return o;if(!(o.indexOf("-")<0&&s.indexOf("-")<0)&&(o.indexOf("-")>0&&s.indexOf("-")<0&&o.substring(0,o.indexOf("-"))===s||o.indexOf(s)===0&&s.length>1))return o})}),n||(n=this.getFallbackCodes(this.options.fallbackLng)[0]),n}getFallbackCodes(e,n){if(!e)return[];if(typeof e=="function"&&(e=e(n)),$n(e)&&(e=[e]),Array.isArray(e))return e;if(!n)return e.default||[];let r=e[n];return r||(r=e[this.getScriptPartFromCode(n)]),r||(r=e[this.formatLanguageCode(n)]),r||(r=e[this.getLanguagePartFromCode(n)]),r||(r=e.default),r||[]}toResolveHierarchy(e,n){const r=this.getFallbackCodes((n===!1?[]:n)||this.options.fallbackLng||[],e),i=[],s=o=>{o&&(this.isSupportedCode(o)?i.push(o):this.logger.warn(`rejecting language code not found in supportedLngs: ${o}`))};return $n(e)&&(e.indexOf("-")>-1||e.indexOf("_")>-1)?(this.options.load!=="languageOnly"&&s(this.formatLanguageCode(e)),this.options.load!=="languageOnly"&&this.options.load!=="currentOnly"&&s(this.getScriptPartFromCode(e)),this.options.load!=="currentOnly"&&s(this.getLanguagePartFromCode(e))):$n(e)&&s(this.formatLanguageCode(e)),r.forEach(o=>{i.indexOf(o)<0&&s(this.formatLanguageCode(o))}),i}}const V8={zero:0,one:1,two:2,few:3,many:4,other:5},G8={select:t=>t===1?"one":"other",resolvedOptions:()=>({pluralCategories:["one","other"]})};class rue{constructor(e,n={}){this.languageUtils=e,this.options=n,this.logger=Ed.create("pluralResolver"),this.pluralRulesCache={}}addRule(e,n){this.rules[e]=n}clearCache(){this.pluralRulesCache={}}getRule(e,n={}){const r=Y0(e==="dev"?"en":e),i=n.ordinal?"ordinal":"cardinal",s=JSON.stringify({cleanedCode:r,type:i});if(s in this.pluralRulesCache)return this.pluralRulesCache[s];let o;try{o=new Intl.PluralRules(r,{type:i})}catch{if(!Intl)return this.logger.error("No Intl support, please use an Intl polyfill!"),G8;if(!e.match(/-|_/))return G8;const c=this.languageUtils.getLanguagePartFromCode(e);o=this.getRule(c,n)}return this.pluralRulesCache[s]=o,o}needsPlural(e,n={}){let r=this.getRule(e,n);return r||(r=this.getRule("dev",n)),r?.resolvedOptions().pluralCategories.length>1}getPluralFormsOfKey(e,n,r={}){return this.getSuffixes(e,r).map(i=>`${n}${i}`)}getSuffixes(e,n={}){let r=this.getRule(e,n);return r||(r=this.getRule("dev",n)),r?r.resolvedOptions().pluralCategories.sort((i,s)=>V8[i]-V8[s]).map(i=>`${this.options.prepend}${n.ordinal?`ordinal${this.options.prepend}`:""}${i}`):[]}getSuffix(e,n,r={}){const i=this.getRule(e,r);return i?`${this.options.prepend}${r.ordinal?`ordinal${this.options.prepend}`:""}${i.select(n)}`:(this.logger.warn(`no plural rule found for: ${e}`),this.getSuffix("dev",n,r))}}const W8=(t,e,n,r=".",i=!0)=>{let s=Kde(t,e,n);return!s&&i&&$n(n)&&(s=I3(t,n,r),s===void 0&&(s=I3(e,n,r))),s},yM=t=>t.replace(/\$/g,"$$$$");class aue{constructor(e={}){this.logger=Ed.create("interpolator"),this.options=e,this.format=e?.interpolation?.format||(n=>n),this.init(e)}init(e={}){e.interpolation||(e.interpolation={escapeValue:!0});const{escape:n,escapeValue:r,useRawValueToEscape:i,prefix:s,prefixEscaped:o,suffix:l,suffixEscaped:c,formatSeparator:d,unescapeSuffix:u,unescapePrefix:m,nestingPrefix:p,nestingPrefixEscaped:f,nestingSuffix:x,nestingSuffixEscaped:v,nestingOptionsSeparator:b,maxReplaces:g,alwaysFormat:_}=e.interpolation;this.escape=n!==void 0?n:Yde,this.escapeValue=r!==void 0?r:!0,this.useRawValueToEscape=i!==void 0?i:!1,this.prefix=s?wb(s):o||"{{",this.suffix=l?wb(l):c||"}}",this.formatSeparator=d||",",this.unescapePrefix=u?"":m||"-",this.unescapeSuffix=this.unescapePrefix?"":u||"",this.nestingPrefix=p?wb(p):f||wb("$t("),this.nestingSuffix=x?wb(x):v||wb(")"),this.nestingOptionsSeparator=b||",",this.maxReplaces=g||1e3,this.alwaysFormat=_!==void 0?_:!1,this.resetRegExp()}reset(){this.options&&this.init(this.options)}resetRegExp(){const e=(n,r)=>n?.source===r?(n.lastIndex=0,n):new RegExp(r,"g");this.regexp=e(this.regexp,`${this.prefix}(.+?)${this.suffix}`),this.regexpUnescape=e(this.regexpUnescape,`${this.prefix}${this.unescapePrefix}(.+?)${this.unescapeSuffix}${this.suffix}`),this.nestingRegexp=e(this.nestingRegexp,`${this.nestingPrefix}((?:[^()"']+|"[^"]*"|'[^']*'|\\((?:[^()]|"[^"]*"|'[^']*')*\\))*?)${this.nestingSuffix}`)}interpolate(e,n,r,i){let s,o,l;const c=this.options&&this.options.interpolation&&this.options.interpolation.defaultVariables||{},d=f=>{if(f.indexOf(this.formatSeparator)<0){const g=W8(n,c,f,this.options.keySeparator,this.options.ignoreJSONStructure);return this.alwaysFormat?this.format(g,void 0,r,{...i,...n,interpolationkey:f}):g}const x=f.split(this.formatSeparator),v=x.shift().trim(),b=x.join(this.formatSeparator).trim();return this.format(W8(n,c,v,this.options.keySeparator,this.options.ignoreJSONStructure),b,r,{...i,...n,interpolationkey:v})};this.resetRegExp();const u=i?.missingInterpolationHandler||this.options.missingInterpolationHandler,m=i?.interpolation?.skipOnVariables!==void 0?i.interpolation.skipOnVariables:this.options.interpolation.skipOnVariables;return[{regex:this.regexpUnescape,safeValue:f=>yM(f)},{regex:this.regexp,safeValue:f=>this.escapeValue?yM(this.escape(f)):yM(f)}].forEach(f=>{for(l=0;s=f.regex.exec(e);){const x=s[1].trim();if(o=d(x),o===void 0)if(typeof u=="function"){const b=u(e,s,i);o=$n(b)?b:""}else if(i&&Object.prototype.hasOwnProperty.call(i,x))o="";else if(m){o=s[0];continue}else this.logger.warn(`missed to pass in variable ${x} for interpolating ${e}`),o="";else!$n(o)&&!this.useRawValueToEscape&&(o=I8(o));const v=f.safeValue(o);if(e=e.replace(s[0],v),m?(f.regex.lastIndex+=o.length,f.regex.lastIndex-=s[0].length):f.regex.lastIndex=0,l++,l>=this.maxReplaces)break}}),e}nest(e,n,r={}){let i,s,o;const l=(c,d)=>{const u=this.nestingOptionsSeparator;if(c.indexOf(u)<0)return c;const m=c.split(new RegExp(`${u}[ ]*{`));let p=`{${m[1]}`;c=m[0],p=this.interpolate(p,o);const f=p.match(/'/g),x=p.match(/"/g);((f?.length??0)%2===0&&!x||x.length%2!==0)&&(p=p.replace(/'/g,'"'));try{o=JSON.parse(p),d&&(o={...d,...o})}catch(v){return this.logger.warn(`failed parsing options string in nesting for key ${c}`,v),`${c}${u}${p}`}return o.defaultValue&&o.defaultValue.indexOf(this.prefix)>-1&&delete o.defaultValue,c};for(;i=this.nestingRegexp.exec(e);){let c=[];o={...r},o=o.replace&&!$n(o.replace)?o.replace:o,o.applyPostProcessor=!1,delete o.defaultValue;const d=/{.*}/.test(i[1])?i[1].lastIndexOf("}")+1:i[1].indexOf(this.formatSeparator);if(d!==-1&&(c=i[1].slice(d).split(this.formatSeparator).map(u=>u.trim()).filter(Boolean),i[1]=i[1].slice(0,d)),s=n(l.call(this,i[1].trim(),o),o),s&&i[0]===e&&!$n(s))return s;$n(s)||(s=I8(s)),s||(this.logger.warn(`missed to resolve ${i[1]} for nesting ${e}`),s=""),c.length&&(s=c.reduce((u,m)=>this.format(u,m,r.lng,{...r,interpolationkey:i[1].trim()}),s.trim())),e=e.replace(i[0],s),this.regexp.lastIndex=0}return e}}const iue=t=>{let e=t.toLowerCase().trim();const n={};if(t.indexOf("(")>-1){const r=t.split("(");e=r[0].toLowerCase().trim();const i=r[1].substring(0,r[1].length-1);e==="currency"&&i.indexOf(":")<0?n.currency||(n.currency=i.trim()):e==="relativetime"&&i.indexOf(":")<0?n.range||(n.range=i.trim()):i.split(";").forEach(o=>{if(o){const[l,...c]=o.split(":"),d=c.join(":").trim().replace(/^'+|'+$/g,""),u=l.trim();n[u]||(n[u]=d),d==="false"&&(n[u]=!1),d==="true"&&(n[u]=!0),isNaN(d)||(n[u]=parseInt(d,10))}})}return{formatName:e,formatOptions:n}},K8=t=>{const e={};return(n,r,i)=>{let s=i;i&&i.interpolationkey&&i.formatParams&&i.formatParams[i.interpolationkey]&&i[i.interpolationkey]&&(s={...s,[i.interpolationkey]:void 0});const o=r+JSON.stringify(s);let l=e[o];return l||(l=t(Y0(r),i),e[o]=l),l(n)}},sue=t=>(e,n,r)=>t(Y0(n),r)(e);class oue{constructor(e={}){this.logger=Ed.create("formatter"),this.options=e,this.init(e)}init(e,n={interpolation:{}}){this.formatSeparator=n.interpolation.formatSeparator||",";const r=n.cacheInBuiltFormats?K8:sue;this.formats={number:r((i,s)=>{const o=new Intl.NumberFormat(i,{...s});return l=>o.format(l)}),currency:r((i,s)=>{const o=new Intl.NumberFormat(i,{...s,style:"currency"});return l=>o.format(l)}),datetime:r((i,s)=>{const o=new Intl.DateTimeFormat(i,{...s});return l=>o.format(l)}),relativetime:r((i,s)=>{const o=new Intl.RelativeTimeFormat(i,{...s});return l=>o.format(l,s.range||"day")}),list:r((i,s)=>{const o=new Intl.ListFormat(i,{...s});return l=>o.format(l)})}}add(e,n){this.formats[e.toLowerCase().trim()]=n}addCached(e,n){this.formats[e.toLowerCase().trim()]=K8(n)}format(e,n,r,i={}){const s=n.split(this.formatSeparator);if(s.length>1&&s[0].indexOf("(")>1&&s[0].indexOf(")")<0&&s.find(l=>l.indexOf(")")>-1)){const l=s.findIndex(c=>c.indexOf(")")>-1);s[0]=[s[0],...s.splice(1,l)].join(this.formatSeparator)}return s.reduce((l,c)=>{const{formatName:d,formatOptions:u}=iue(c);if(this.formats[d]){let m=l;try{const p=i?.formatParams?.[i.interpolationkey]||{},f=p.locale||p.lng||i.locale||i.lng||r;m=this.formats[d](l,f,{...u,...i,...p})}catch(p){this.logger.warn(p)}return m}else this.logger.warn(`there was no format function for ${d}`);return l},e)}}const lue=(t,e)=>{t.pending[e]!==void 0&&(delete t.pending[e],t.pendingCount--)};class cue extends LP{constructor(e,n,r,i={}){super(),this.backend=e,this.store=n,this.services=r,this.languageUtils=r.languageUtils,this.options=i,this.logger=Ed.create("backendConnector"),this.waitingReads=[],this.maxParallelReads=i.maxParallelReads||10,this.readingCalls=0,this.maxRetries=i.maxRetries>=0?i.maxRetries:5,this.retryTimeout=i.retryTimeout>=1?i.retryTimeout:350,this.state={},this.queue=[],this.backend?.init?.(r,i.backend,i)}queueLoad(e,n,r,i){const s={},o={},l={},c={};return e.forEach(d=>{let u=!0;n.forEach(m=>{const p=`${d}|${m}`;!r.reload&&this.store.hasResourceBundle(d,m)?this.state[p]=2:this.state[p]<0||(this.state[p]===1?o[p]===void 0&&(o[p]=!0):(this.state[p]=1,u=!1,o[p]===void 0&&(o[p]=!0),s[p]===void 0&&(s[p]=!0),c[m]===void 0&&(c[m]=!0)))}),u||(l[d]=!0)}),(Object.keys(s).length||Object.keys(o).length)&&this.queue.push({pending:o,pendingCount:Object.keys(o).length,loaded:{},errors:[],callback:i}),{toLoad:Object.keys(s),pending:Object.keys(o),toLoadLanguages:Object.keys(l),toLoadNamespaces:Object.keys(c)}}loaded(e,n,r){const i=e.split("|"),s=i[0],o=i[1];n&&this.emit("failedLoading",s,o,n),!n&&r&&this.store.addResourceBundle(s,o,r,void 0,void 0,{skipCopy:!0}),this.state[e]=n?-1:2,n&&r&&(this.state[e]=0);const l={};this.queue.forEach(c=>{Wde(c.loaded,[s],o),lue(c,e),n&&c.errors.push(n),c.pendingCount===0&&!c.done&&(Object.keys(c.loaded).forEach(d=>{l[d]||(l[d]={});const u=c.loaded[d];u.length&&u.forEach(m=>{l[d][m]===void 0&&(l[d][m]=!0)})}),c.done=!0,c.errors.length?c.callback(c.errors):c.callback())}),this.emit("loaded",l),this.queue=this.queue.filter(c=>!c.done)}read(e,n,r,i=0,s=this.retryTimeout,o){if(!e.length)return o(null,{});if(this.readingCalls>=this.maxParallelReads){this.waitingReads.push({lng:e,ns:n,fcName:r,tried:i,wait:s,callback:o});return}this.readingCalls++;const l=(d,u)=>{if(this.readingCalls--,this.waitingReads.length>0){const m=this.waitingReads.shift();this.read(m.lng,m.ns,m.fcName,m.tried,m.wait,m.callback)}if(d&&u&&i{this.read.call(this,e,n,r,i+1,s*2,o)},s);return}o(d,u)},c=this.backend[r].bind(this.backend);if(c.length===2){try{const d=c(e,n);d&&typeof d.then=="function"?d.then(u=>l(null,u)).catch(l):l(null,d)}catch(d){l(d)}return}return c(e,n,l)}prepareLoading(e,n,r={},i){if(!this.backend)return this.logger.warn("No backend was added via i18next.use. Will not load resources."),i&&i();$n(e)&&(e=this.languageUtils.toResolveHierarchy(e)),$n(n)&&(n=[n]);const s=this.queueLoad(e,n,r,i);if(!s.toLoad.length)return s.pending.length||i(),null;s.toLoad.forEach(o=>{this.loadOne(o)})}load(e,n,r){this.prepareLoading(e,n,{},r)}reload(e,n,r){this.prepareLoading(e,n,{reload:!0},r)}loadOne(e,n=""){const r=e.split("|"),i=r[0],s=r[1];this.read(i,s,"read",void 0,void 0,(o,l)=>{o&&this.logger.warn(`${n}loading namespace ${s} for language ${i} failed`,o),!o&&l&&this.logger.log(`${n}loaded namespace ${s} for language ${i}`,l),this.loaded(e,o,l)})}saveMissing(e,n,r,i,s,o={},l=()=>{}){if(this.services?.utils?.hasLoadedNamespace&&!this.services?.utils?.hasLoadedNamespace(n)){this.logger.warn(`did not save key "${r}" as the namespace "${n}" was not yet loaded`,"This means something IS WRONG in your setup. You access the t function before i18next.init / i18next.loadNamespace / i18next.changeLanguage was done. Wait for the callback or Promise to resolve before accessing it!!!");return}if(!(r==null||r==="")){if(this.backend?.create){const c={...o,isUpdate:s},d=this.backend.create.bind(this.backend);if(d.length<6)try{let u;d.length===5?u=d(e,n,r,i,c):u=d(e,n,r,i),u&&typeof u.then=="function"?u.then(m=>l(null,m)).catch(l):l(null,u)}catch(u){l(u)}else d(e,n,r,i,l,c)}!e||!e[0]||this.store.addResource(e[0],n,r,i)}}}const X8=()=>({debug:!1,initAsync:!0,ns:["translation"],defaultNS:["translation"],fallbackLng:["dev"],fallbackNS:!1,supportedLngs:!1,nonExplicitSupportedLngs:!1,load:"all",preload:!1,simplifyPluralSuffix:!0,keySeparator:".",nsSeparator:":",pluralSeparator:"_",contextSeparator:"_",partialBundledLanguages:!1,saveMissing:!1,updateMissing:!1,saveMissingTo:"fallback",saveMissingPlurals:!0,missingKeyHandler:!1,missingInterpolationHandler:!1,postProcess:!1,postProcessPassResolved:!1,returnNull:!1,returnEmptyString:!0,returnObjects:!1,joinArrays:!1,returnedObjectHandler:!1,parseMissingKeyHandler:!1,appendNamespaceToMissingKey:!1,appendNamespaceToCIMode:!1,overloadTranslationOptionHandler:t=>{let e={};if(typeof t[1]=="object"&&(e=t[1]),$n(t[1])&&(e.defaultValue=t[1]),$n(t[2])&&(e.tDescription=t[2]),typeof t[2]=="object"||typeof t[3]=="object"){const n=t[3]||t[2];Object.keys(n).forEach(r=>{e[r]=n[r]})}return e},interpolation:{escapeValue:!0,format:t=>t,prefix:"{{",suffix:"}}",formatSeparator:",",unescapePrefix:"-",nestingPrefix:"$t(",nestingSuffix:")",nestingOptionsSeparator:",",maxReplaces:1e3,skipOnVariables:!0},cacheInBuiltFormats:!0}),Y8=t=>($n(t.ns)&&(t.ns=[t.ns]),$n(t.fallbackLng)&&(t.fallbackLng=[t.fallbackLng]),$n(t.fallbackNS)&&(t.fallbackNS=[t.fallbackNS]),t.supportedLngs?.indexOf?.("cimode")<0&&(t.supportedLngs=t.supportedLngs.concat(["cimode"])),typeof t.initImmediate=="boolean"&&(t.initAsync=t.initImmediate),t),V_=()=>{},due=t=>{Object.getOwnPropertyNames(Object.getPrototypeOf(t)).forEach(n=>{typeof t[n]=="function"&&(t[n]=t[n].bind(t))})};class k0 extends LP{constructor(e={},n){if(super(),this.options=Y8(e),this.services={},this.logger=Ed,this.modules={external:[]},due(this),n&&!this.isInitialized&&!e.isClone){if(!this.options.initAsync)return this.init(e,n),this;setTimeout(()=>{this.init(e,n)},0)}}init(e={},n){this.isInitializing=!0,typeof e=="function"&&(n=e,e={}),e.defaultNS==null&&e.ns&&($n(e.ns)?e.defaultNS=e.ns:e.ns.indexOf("translation")<0&&(e.defaultNS=e.ns[0]));const r=X8();this.options={...r,...this.options,...Y8(e)},this.options.interpolation={...r.interpolation,...this.options.interpolation},e.keySeparator!==void 0&&(this.options.userDefinedKeySeparator=e.keySeparator),e.nsSeparator!==void 0&&(this.options.userDefinedNsSeparator=e.nsSeparator);const i=d=>d?typeof d=="function"?new d:d:null;if(!this.options.isClone){this.modules.logger?Ed.init(i(this.modules.logger),this.options):Ed.init(null,this.options);let d;this.modules.formatter?d=this.modules.formatter:d=oue;const u=new $8(this.options);this.store=new H8(this.options.resources,this.options);const m=this.services;m.logger=Ed,m.resourceStore=this.store,m.languageUtils=u,m.pluralResolver=new rue(u,{prepend:this.options.pluralSeparator,simplifyPluralSuffix:this.options.simplifyPluralSuffix}),this.options.interpolation.format&&this.options.interpolation.format!==r.interpolation.format&&this.logger.deprecate("init: you are still using the legacy format function, please use the new approach: https://www.i18next.com/translation-function/formatting"),d&&(!this.options.interpolation.format||this.options.interpolation.format===r.interpolation.format)&&(m.formatter=i(d),m.formatter.init&&m.formatter.init(m,this.options),this.options.interpolation.format=m.formatter.format.bind(m.formatter)),m.interpolator=new aue(this.options),m.utils={hasLoadedNamespace:this.hasLoadedNamespace.bind(this)},m.backendConnector=new cue(i(this.modules.backend),m.resourceStore,m,this.options),m.backendConnector.on("*",(f,...x)=>{this.emit(f,...x)}),this.modules.languageDetector&&(m.languageDetector=i(this.modules.languageDetector),m.languageDetector.init&&m.languageDetector.init(m,this.options.detection,this.options)),this.modules.i18nFormat&&(m.i18nFormat=i(this.modules.i18nFormat),m.i18nFormat.init&&m.i18nFormat.init(this)),this.translator=new mN(this.services,this.options),this.translator.on("*",(f,...x)=>{this.emit(f,...x)}),this.modules.external.forEach(f=>{f.init&&f.init(this)})}if(this.format=this.options.interpolation.format,n||(n=V_),this.options.fallbackLng&&!this.services.languageDetector&&!this.options.lng){const d=this.services.languageUtils.getFallbackCodes(this.options.fallbackLng);d.length>0&&d[0]!=="dev"&&(this.options.lng=d[0])}!this.services.languageDetector&&!this.options.lng&&this.logger.warn("init: no languageDetector is used and no lng is defined"),["getResource","hasResourceBundle","getResourceBundle","getDataByLanguage"].forEach(d=>{this[d]=(...u)=>this.store[d](...u)}),["addResource","addResources","addResourceBundle","removeResourceBundle"].forEach(d=>{this[d]=(...u)=>(this.store[d](...u),this)});const l=Cv(),c=()=>{const d=(u,m)=>{this.isInitializing=!1,this.isInitialized&&!this.initializedStoreOnce&&this.logger.warn("init: i18next is already initialized. You should call init just once!"),this.isInitialized=!0,this.options.isClone||this.logger.log("initialized",this.options),this.emit("initialized",this.options),l.resolve(m),n(u,m)};if(this.languages&&!this.isInitialized)return d(null,this.t.bind(this));this.changeLanguage(this.options.lng,d)};return this.options.resources||!this.options.initAsync?c():setTimeout(c,0),l}loadResources(e,n=V_){let r=n;const i=$n(e)?e:this.language;if(typeof e=="function"&&(r=e),!this.options.resources||this.options.partialBundledLanguages){if(i?.toLowerCase()==="cimode"&&(!this.options.preload||this.options.preload.length===0))return r();const s=[],o=l=>{if(!l||l==="cimode")return;this.services.languageUtils.toResolveHierarchy(l).forEach(d=>{d!=="cimode"&&s.indexOf(d)<0&&s.push(d)})};i?o(i):this.services.languageUtils.getFallbackCodes(this.options.fallbackLng).forEach(c=>o(c)),this.options.preload?.forEach?.(l=>o(l)),this.services.backendConnector.load(s,this.options.ns,l=>{!l&&!this.resolvedLanguage&&this.language&&this.setResolvedLanguage(this.language),r(l)})}else r(null)}reloadResources(e,n,r){const i=Cv();return typeof e=="function"&&(r=e,e=void 0),typeof n=="function"&&(r=n,n=void 0),e||(e=this.languages),n||(n=this.options.ns),r||(r=V_),this.services.backendConnector.reload(e,n,s=>{i.resolve(),r(s)}),i}use(e){if(!e)throw new Error("You are passing an undefined module! Please check the object you are passing to i18next.use()");if(!e.type)throw new Error("You are passing a wrong module! Please check the object you are passing to i18next.use()");return e.type==="backend"&&(this.modules.backend=e),(e.type==="logger"||e.log&&e.warn&&e.error)&&(this.modules.logger=e),e.type==="languageDetector"&&(this.modules.languageDetector=e),e.type==="i18nFormat"&&(this.modules.i18nFormat=e),e.type==="postProcessor"&&HY.addPostProcessor(e),e.type==="formatter"&&(this.modules.formatter=e),e.type==="3rdParty"&&this.modules.external.push(e),this}setResolvedLanguage(e){if(!(!e||!this.languages)&&!(["cimode","dev"].indexOf(e)>-1)){for(let n=0;n-1)&&this.store.hasLanguageSomeTranslations(r)){this.resolvedLanguage=r;break}}!this.resolvedLanguage&&this.languages.indexOf(e)<0&&this.store.hasLanguageSomeTranslations(e)&&(this.resolvedLanguage=e,this.languages.unshift(e))}}changeLanguage(e,n){this.isLanguageChangingTo=e;const r=Cv();this.emit("languageChanging",e);const i=l=>{this.language=l,this.languages=this.services.languageUtils.toResolveHierarchy(l),this.resolvedLanguage=void 0,this.setResolvedLanguage(l)},s=(l,c)=>{c?this.isLanguageChangingTo===e&&(i(c),this.translator.changeLanguage(c),this.isLanguageChangingTo=void 0,this.emit("languageChanged",c),this.logger.log("languageChanged",c)):this.isLanguageChangingTo=void 0,r.resolve((...d)=>this.t(...d)),n&&n(l,(...d)=>this.t(...d))},o=l=>{!e&&!l&&this.services.languageDetector&&(l=[]);const c=$n(l)?l:l&&l[0],d=this.store.hasLanguageSomeTranslations(c)?c:this.services.languageUtils.getBestMatchFromCodes($n(l)?[l]:l);d&&(this.language||i(d),this.translator.language||this.translator.changeLanguage(d),this.services.languageDetector?.cacheUserLanguage?.(d)),this.loadResources(d,u=>{s(u,d)})};return!e&&this.services.languageDetector&&!this.services.languageDetector.async?o(this.services.languageDetector.detect()):!e&&this.services.languageDetector&&this.services.languageDetector.async?this.services.languageDetector.detect.length===0?this.services.languageDetector.detect().then(o):this.services.languageDetector.detect(o):o(e),r}getFixedT(e,n,r){const i=(s,o,...l)=>{let c;typeof o!="object"?c=this.options.overloadTranslationOptionHandler([s,o].concat(l)):c={...o},c.lng=c.lng||i.lng,c.lngs=c.lngs||i.lngs,c.ns=c.ns||i.ns,c.keyPrefix!==""&&(c.keyPrefix=c.keyPrefix||r||i.keyPrefix);const d=this.options.keySeparator||".";let u;return c.keyPrefix&&Array.isArray(s)?u=s.map(m=>(typeof m=="function"&&(m=z3(m,{...this.options,...o})),`${c.keyPrefix}${d}${m}`)):(typeof s=="function"&&(s=z3(s,{...this.options,...o})),u=c.keyPrefix?`${c.keyPrefix}${d}${s}`:s),this.t(u,c)};return $n(e)?i.lng=e:i.lngs=e,i.ns=n,i.keyPrefix=r,i}t(...e){return this.translator?.translate(...e)}exists(...e){return this.translator?.exists(...e)}setDefaultNamespace(e){this.options.defaultNS=e}hasLoadedNamespace(e,n={}){if(!this.isInitialized)return this.logger.warn("hasLoadedNamespace: i18next was not initialized",this.languages),!1;if(!this.languages||!this.languages.length)return this.logger.warn("hasLoadedNamespace: i18n.languages were undefined or empty",this.languages),!1;const r=n.lng||this.resolvedLanguage||this.languages[0],i=this.options?this.options.fallbackLng:!1,s=this.languages[this.languages.length-1];if(r.toLowerCase()==="cimode")return!0;const o=(l,c)=>{const d=this.services.backendConnector.state[`${l}|${c}`];return d===-1||d===0||d===2};if(n.precheck){const l=n.precheck(this,o);if(l!==void 0)return l}return!!(this.hasResourceBundle(r,e)||!this.services.backendConnector.backend||this.options.resources&&!this.options.partialBundledLanguages||o(r,e)&&(!i||o(s,e)))}loadNamespaces(e,n){const r=Cv();return this.options.ns?($n(e)&&(e=[e]),e.forEach(i=>{this.options.ns.indexOf(i)<0&&this.options.ns.push(i)}),this.loadResources(i=>{r.resolve(),n&&n(i)}),r):(n&&n(),Promise.resolve())}loadLanguages(e,n){const r=Cv();$n(e)&&(e=[e]);const i=this.options.preload||[],s=e.filter(o=>i.indexOf(o)<0&&this.services.languageUtils.isSupportedCode(o));return s.length?(this.options.preload=i.concat(s),this.loadResources(o=>{r.resolve(),n&&n(o)}),r):(n&&n(),Promise.resolve())}dir(e){if(e||(e=this.resolvedLanguage||(this.languages?.length>0?this.languages[0]:this.language)),!e)return"rtl";try{const i=new Intl.Locale(e);if(i&&i.getTextInfo){const s=i.getTextInfo();if(s&&s.direction)return s.direction}}catch{}const n=["ar","shu","sqr","ssh","xaa","yhd","yud","aao","abh","abv","acm","acq","acw","acx","acy","adf","ads","aeb","aec","afb","ajp","apc","apd","arb","arq","ars","ary","arz","auz","avl","ayh","ayl","ayn","ayp","bbz","pga","he","iw","ps","pbt","pbu","pst","prp","prd","ug","ur","ydd","yds","yih","ji","yi","hbo","men","xmn","fa","jpr","peo","pes","prs","dv","sam","ckb"],r=this.services?.languageUtils||new $8(X8());return e.toLowerCase().indexOf("-latn")>1?"ltr":n.indexOf(r.getLanguagePartFromCode(e))>-1||e.toLowerCase().indexOf("-arab")>1?"rtl":"ltr"}static createInstance(e={},n){const r=new k0(e,n);return r.createInstance=k0.createInstance,r}cloneInstance(e={},n=V_){const r=e.forkResourceStore;r&&delete e.forkResourceStore;const i={...this.options,...e,isClone:!0},s=new k0(i);if((e.debug!==void 0||e.prefix!==void 0)&&(s.logger=s.logger.clone(e)),["store","services","language"].forEach(l=>{s[l]=this[l]}),s.services={...this.services},s.services.utils={hasLoadedNamespace:s.hasLoadedNamespace.bind(s)},r){const l=Object.keys(this.store.data).reduce((c,d)=>(c[d]={...this.store.data[d]},c[d]=Object.keys(c[d]).reduce((u,m)=>(u[m]={...c[d][m]},u),c[d]),c),{});s.store=new H8(l,i),s.services.resourceStore=s.store}return s.translator=new mN(s.services,i),s.translator.on("*",(l,...c)=>{s.emit(l,...c)}),s.init(i,n),s.translator.options=i,s.translator.backendConnector.services.utils={hasLoadedNamespace:s.hasLoadedNamespace.bind(s)},s}toJSON(){return{options:this.options,store:this.store,language:this.language,languages:this.languages,resolvedLanguage:this.resolvedLanguage}}}const xo=k0.createInstance();xo.createInstance;xo.dir;xo.init;xo.loadResources;xo.reloadResources;xo.use;xo.changeLanguage;xo.getFixedT;xo.t;xo.exists;xo.setDefaultNamespace;xo.hasLoadedNamespace;xo.loadNamespaces;xo.loadLanguages;const uue=(t,e,n,r)=>{const i=[n,{code:e,...r||{}}];if(t?.services?.logger?.forward)return t.services.logger.forward(i,"warn","react-i18next::",!0);ng(i[0])&&(i[0]=`react-i18next:: ${i[0]}`),t?.services?.logger?.warn?t.services.logger.warn(...i):console?.warn&&console.warn(...i)},Q8={},$Y=(t,e,n,r)=>{ng(n)&&Q8[n]||(ng(n)&&(Q8[n]=new Date),uue(t,e,n,r))},VY=(t,e)=>()=>{if(t.isInitialized)e();else{const n=()=>{setTimeout(()=>{t.off("initialized",n)},0),e()};t.on("initialized",n)}},U3=(t,e,n)=>{t.loadNamespaces(e,VY(t,n))},Z8=(t,e,n,r)=>{if(ng(n)&&(n=[n]),t.options.preload&&t.options.preload.indexOf(e)>-1)return U3(t,n,r);n.forEach(i=>{t.options.ns.indexOf(i)<0&&t.options.ns.push(i)}),t.loadLanguages(e,VY(t,r))},mue=(t,e,n={})=>!e.languages||!e.languages.length?($Y(e,"NO_LANGUAGES","i18n.languages were undefined or empty",{languages:e.languages}),!0):e.hasLoadedNamespace(t,{lng:n.lng,precheck:(r,i)=>{if(n.bindI18n&&n.bindI18n.indexOf("languageChanging")>-1&&r.services.backendConnector.backend&&r.isLanguageChangingTo&&!i(r.isLanguageChangingTo,t))return!1}}),ng=t=>typeof t=="string",hue=t=>typeof t=="object"&&t!==null,pue=/&(?:amp|#38|lt|#60|gt|#62|apos|#39|quot|#34|nbsp|#160|copy|#169|reg|#174|hellip|#8230|#x2F|#47);/g,fue={"&":"&","&":"&","<":"<","<":"<",">":">",">":">","'":"'","'":"'",""":'"',""":'"'," ":" "," ":" ","©":"©","©":"©","®":"®","®":"®","…":"…","…":"…","/":"/","/":"/"},gue=t=>fue[t],bue=t=>t.replace(pue,gue);let B3={bindI18n:"languageChanged",bindI18nStore:"",transEmptyNodeValue:"",transSupportBasicHtmlNodes:!0,transWrapTextNodes:"",transKeepBasicHtmlNodesFor:["br","strong","i","p"],useSuspense:!0,unescape:bue,transDefaultProps:void 0};const xue=(t={})=>{B3={...B3,...t}},yue=()=>B3;let GY;const vue=t=>{GY=t},wue=()=>GY,Sue={type:"3rdParty",init(t){xue(t.options.react),vue(t)}},_ue=w.createContext();class kue{constructor(){this.usedNamespaces={}}addUsedNamespaces(e){e.forEach(n=>{this.usedNamespaces[n]||(this.usedNamespaces[n]=!0)})}getUsedNamespaces(){return Object.keys(this.usedNamespaces)}}var vM={exports:{}},wM={};var J8;function Nue(){if(J8)return wM;J8=1;var t=Mg();function e(m,p){return m===p&&(m!==0||1/m===1/p)||m!==m&&p!==p}var n=typeof Object.is=="function"?Object.is:e,r=t.useState,i=t.useEffect,s=t.useLayoutEffect,o=t.useDebugValue;function l(m,p){var f=p(),x=r({inst:{value:f,getSnapshot:p}}),v=x[0].inst,b=x[1];return s(function(){v.value=f,v.getSnapshot=p,c(v)&&b({inst:v})},[m,f,p]),i(function(){return c(v)&&b({inst:v}),m(function(){c(v)&&b({inst:v})})},[m]),o(f),f}function c(m){var p=m.getSnapshot;m=m.value;try{var f=p();return!n(m,f)}catch{return!0}}function d(m,p){return p()}var u=typeof window>"u"||typeof window.document>"u"||typeof window.document.createElement>"u"?d:l;return wM.useSyncExternalStore=t.useSyncExternalStore!==void 0?t.useSyncExternalStore:u,wM}var eH;function WY(){return eH||(eH=1,vM.exports=Nue()),vM.exports}var cO=WY();const Cue=(t,e)=>ng(e)?e:hue(e)&&ng(e.defaultValue)?e.defaultValue:Array.isArray(t)?t[t.length-1]:t,Pue={t:Cue,ready:!1},Tue=()=>()=>{},Dt=(t,e={})=>{const{i18n:n}=e,{i18n:r,defaultNS:i}=w.useContext(_ue)||{},s=n||r||wue();s&&!s.reportNamespaces&&(s.reportNamespaces=new kue),s||$Y(s,"NO_I18NEXT_INSTANCE","useTranslation: You will need to pass in an i18next instance by using initReactI18next");const o=w.useMemo(()=>({...yue(),...s?.options?.react,...e}),[s,e]),{useSuspense:l,keyPrefix:c}=o,d=i||s?.options?.defaultNS,u=ng(d)?[d]:d||["translation"],m=w.useMemo(()=>u,u);s?.reportNamespaces?.addUsedNamespaces?.(m);const p=w.useRef(0),f=w.useCallback(k=>{if(!s)return Tue;const{bindI18n:D,bindI18nStore:H}=o,z=()=>{p.current+=1,k()};return D&&s.on(D,z),H&&s.store.on(H,z),()=>{D&&D.split(" ").forEach(X=>s.off(X,z)),H&&H.split(" ").forEach(X=>s.store.off(X,z))}},[s,o]),x=w.useRef(),v=w.useCallback(()=>{if(!s)return Pue;const k=!!(s.isInitialized||s.initializedStoreOnce)&&m.every(te=>mue(te,s,o)),D=e.lng||s.language,H=p.current,z=x.current;if(z&&z.ready===k&&z.lng===D&&z.keyPrefix===c&&z.revision===H)return z;const L={t:s.getFixedT(D,o.nsMode==="fallback"?m:m[0],c),ready:k,lng:D,keyPrefix:c,revision:H};return x.current=L,L},[s,m,c,o,e.lng]),[b,g]=w.useState(0),{t:_,ready:C}=cO.useSyncExternalStore(f,v,v);w.useEffect(()=>{if(s&&!C&&!l){const k=()=>g(D=>D+1);e.lng?Z8(s,e.lng,m,k):U3(s,m,k)}},[s,e.lng,m,C,l,b]);const P=s||{},N=w.useRef(null),A=w.useRef(),T=k=>{const D=Object.getOwnPropertyDescriptors(k);D.__original&&delete D.__original;const H=Object.create(Object.getPrototypeOf(k),D);if(!Object.prototype.hasOwnProperty.call(H,"__original"))try{Object.defineProperty(H,"__original",{value:k,writable:!1,enumerable:!1,configurable:!1})}catch{}return H},F=w.useMemo(()=>{const k=P,D=k?.language;let H=k;k&&(N.current&&N.current.__original===k?A.current!==D?(H=T(k),N.current=H,A.current=D):H=N.current:(H=T(k),N.current=H,A.current=D));const z=[_,H,C];return z.t=_,z.i18n=H,z.ready=C,z},[_,P,C,P.resolvedLanguage,P.language,P.languages]);if(s&&l&&!C)throw new Promise(k=>{const D=()=>k();e.lng?Z8(s,e.lng,m,D):U3(s,m,D)});return F},{slice:Aue,forEach:jue}=[];function Mue(t){return jue.call(Aue.call(arguments,1),e=>{if(e)for(const n in e)t[n]===void 0&&(t[n]=e[n])}),t}function Eue(t){return typeof t!="string"?!1:[/<\s*script.*?>/i,/<\s*\/\s*script\s*>/i,/<\s*img.*?on\w+\s*=/i,/<\s*\w+\s*on\w+\s*=.*?>/i,/javascript\s*:/i,/vbscript\s*:/i,/expression\s*\(/i,/eval\s*\(/i,/alert\s*\(/i,/document\.cookie/i,/document\.write\s*\(/i,/window\.location/i,/innerHTML/i].some(n=>n.test(t))}const tH=/^[\u0009\u0020-\u007e\u0080-\u00ff]+$/,Due=function(t,e){const r=arguments.length>2&&arguments[2]!==void 0?arguments[2]:{path:"/"},i=encodeURIComponent(e);let s=`${t}=${i}`;if(r.maxAge>0){const o=r.maxAge-0;if(Number.isNaN(o))throw new Error("maxAge should be a Number");s+=`; Max-Age=${Math.floor(o)}`}if(r.domain){if(!tH.test(r.domain))throw new TypeError("option domain is invalid");s+=`; Domain=${r.domain}`}if(r.path){if(!tH.test(r.path))throw new TypeError("option path is invalid");s+=`; Path=${r.path}`}if(r.expires){if(typeof r.expires.toUTCString!="function")throw new TypeError("option expires is invalid");s+=`; Expires=${r.expires.toUTCString()}`}if(r.httpOnly&&(s+="; HttpOnly"),r.secure&&(s+="; Secure"),r.sameSite)switch(typeof r.sameSite=="string"?r.sameSite.toLowerCase():r.sameSite){case!0:s+="; SameSite=Strict";break;case"lax":s+="; SameSite=Lax";break;case"strict":s+="; SameSite=Strict";break;case"none":s+="; SameSite=None";break;default:throw new TypeError("option sameSite is invalid")}return r.partitioned&&(s+="; Partitioned"),s},nH={create(t,e,n,r){let i=arguments.length>4&&arguments[4]!==void 0?arguments[4]:{path:"/",sameSite:"strict"};n&&(i.expires=new Date,i.expires.setTime(i.expires.getTime()+n*60*1e3)),r&&(i.domain=r),document.cookie=Due(t,e,i)},read(t){const e=`${t}=`,n=document.cookie.split(";");for(let r=0;r-1&&(r=window.location.hash.substring(window.location.hash.indexOf("?")));const s=r.substring(1).split("&");for(let o=0;o0&&s[o].substring(0,l)===e&&(n=s[o].substring(l+1))}}return n}},Lue={name:"hash",lookup(t){let{lookupHash:e,lookupFromHashIndex:n}=t,r;if(typeof window<"u"){const{hash:i}=window.location;if(i&&i.length>2){const s=i.substring(1);if(e){const o=s.split("&");for(let l=0;l0&&o[l].substring(0,c)===e&&(r=o[l].substring(c+1))}}if(r)return r;if(!r&&n>-1){const o=i.match(/\/([a-zA-Z-]*)/g);return Array.isArray(o)?o[typeof n=="number"?n:0]?.replace("/",""):void 0}}}return r}};let Sb=null;const rH=()=>{if(Sb!==null)return Sb;try{if(Sb=typeof window<"u"&&window.localStorage!==null,!Sb)return!1;const t="i18next.translate.boo";window.localStorage.setItem(t,"foo"),window.localStorage.removeItem(t)}catch{Sb=!1}return Sb};var Oue={name:"localStorage",lookup(t){let{lookupLocalStorage:e}=t;if(e&&rH())return window.localStorage.getItem(e)||void 0},cacheUserLanguage(t,e){let{lookupLocalStorage:n}=e;n&&rH()&&window.localStorage.setItem(n,t)}};let _b=null;const aH=()=>{if(_b!==null)return _b;try{if(_b=typeof window<"u"&&window.sessionStorage!==null,!_b)return!1;const t="i18next.translate.boo";window.sessionStorage.setItem(t,"foo"),window.sessionStorage.removeItem(t)}catch{_b=!1}return _b};var Iue={name:"sessionStorage",lookup(t){let{lookupSessionStorage:e}=t;if(e&&aH())return window.sessionStorage.getItem(e)||void 0},cacheUserLanguage(t,e){let{lookupSessionStorage:n}=e;n&&aH()&&window.sessionStorage.setItem(n,t)}},zue={name:"navigator",lookup(t){const e=[];if(typeof navigator<"u"){const{languages:n,userLanguage:r,language:i}=navigator;if(n)for(let s=0;s0?e:void 0}},Uue={name:"htmlTag",lookup(t){let{htmlTag:e}=t,n;const r=e||(typeof document<"u"?document.documentElement:null);return r&&typeof r.getAttribute=="function"&&(n=r.getAttribute("lang")),n}},Bue={name:"path",lookup(t){let{lookupFromPathIndex:e}=t;if(typeof window>"u")return;const n=window.location.pathname.match(/\/([a-zA-Z-]*)/g);return Array.isArray(n)?n[typeof e=="number"?e:0]?.replace("/",""):void 0}},Hue={name:"subdomain",lookup(t){let{lookupFromSubdomainIndex:e}=t;const n=typeof e=="number"?e+1:1,r=typeof window<"u"&&window.location?.hostname?.match(/^(\w{2,5})\.(([a-z0-9-]{1,63}\.[a-z]{2,6})|localhost)/i);if(r)return r[n]}};let KY=!1;try{document.cookie,KY=!0}catch{}const XY=["querystring","cookie","localStorage","sessionStorage","navigator","htmlTag"];KY||XY.splice(1,1);const que=()=>({order:XY,lookupQuerystring:"lng",lookupCookie:"i18next",lookupLocalStorage:"i18nextLng",lookupSessionStorage:"i18nextLng",caches:["localStorage"],excludeCacheFor:["cimode"],convertDetectedLanguage:t=>t});class YY{constructor(e){let n=arguments.length>1&&arguments[1]!==void 0?arguments[1]:{};this.type="languageDetector",this.detectors={},this.init(e,n)}init(){let e=arguments.length>0&&arguments[0]!==void 0?arguments[0]:{languageUtils:{}},n=arguments.length>1&&arguments[1]!==void 0?arguments[1]:{},r=arguments.length>2&&arguments[2]!==void 0?arguments[2]:{};this.services=e,this.options=Mue(n,this.options||{},que()),typeof this.options.convertDetectedLanguage=="string"&&this.options.convertDetectedLanguage.indexOf("15897")>-1&&(this.options.convertDetectedLanguage=i=>i.replace("-","_")),this.options.lookupFromUrlIndex&&(this.options.lookupFromPathIndex=this.options.lookupFromUrlIndex),this.i18nOptions=r,this.addDetector(Fue),this.addDetector(Rue),this.addDetector(Oue),this.addDetector(Iue),this.addDetector(zue),this.addDetector(Uue),this.addDetector(Bue),this.addDetector(Hue),this.addDetector(Lue)}addDetector(e){return this.detectors[e.name]=e,this}detect(){let e=arguments.length>0&&arguments[0]!==void 0?arguments[0]:this.options.order,n=[];return e.forEach(r=>{if(this.detectors[r]){let i=this.detectors[r].lookup(this.options);i&&typeof i=="string"&&(i=[i]),i&&(n=n.concat(i))}}),n=n.filter(r=>r!=null&&!Eue(r)).map(r=>this.options.convertDetectedLanguage(r)),this.services&&this.services.languageUtils&&this.services.languageUtils.getBestMatchFromCodes?n:n.length>0?n[0]:null}cacheUserLanguage(e){let n=arguments.length>1&&arguments[1]!==void 0?arguments[1]:this.options.caches;n&&(this.options.excludeCacheFor&&this.options.excludeCacheFor.indexOf(e)>-1||n.forEach(r=>{this.detectors[r]&&this.detectors[r].cacheUserLanguage(e,this.options)}))}}YY.type="languageDetector";const $ue={nav:{printers:"Printers",archives:"Archives",queue:"Queue",stats:"Statistics",profiles:"Profiles",maintenance:"Maintenance",projects:"Projects",inventory:"Filament",files:"File Manager",notifications:"Notifications",settings:"Settings",system:"System",collapseSidebar:"Collapse sidebar",expandSidebar:"Expand sidebar",update:"Update",updateAvailable:"Update available: v{{version}}",updateAvailableBanner:"Version {{version}} is available!",viewUpdate:"View update",viewOnGithub:"View on GitHub",keyboardShortcuts:"Keyboard shortcuts (?)",switchToLight:"Switch to light mode",switchToDark:"Switch to dark mode",smartSwitches:"Smart Switches",logout:"Logout"},common:{save:"Save",saving:"Saving...",cancel:"Cancel",delete:"Delete",edit:"Edit",add:"Add",close:"Close",confirm:"Confirm",loading:"Loading...",error:"Error",success:"Success",warning:"Warning",enabled:"Enabled",disabled:"Disabled",yes:"Yes",no:"No",on:"On",off:"Off",all:"All",none:"None",search:"Search",filter:"Filter",sort:"Sort",refresh:"Refresh",download:"Download",upload:"Upload",uploading:"Uploading...",uploadFailed:"Upload failed",actions:"Actions",status:"Status",name:"Name",description:"Description",date:"Date",time:"Time",hours:"hours",minutes:"minutes",seconds:"seconds",days:"days",enable:"Enable",disable:"Disable",permissions:"Permissions",noPrinters:"No printers configured",noData:"No data available",linkNotFound:"Link not found",required:"Required",optional:"Optional",dismiss:"Dismiss",apply:"Apply",reset:"Reset",export:"Export",import:"Import",clear:"Clear",selectAll:"Select All",deselectAll:"Deselect All",noChange:"— No change —",unchanged:"Unchanged",unassigned:"Unassigned",unknown:"Unknown",unknownError:"Unknown error",today:"Today",tomorrow:"Tomorrow",asap:"ASAP",overdue:"Overdue",now:"Now",collapse:"Collapse",expand:"Expand",viewArchive:"View archive",viewInFileManager:"View in File Manager",addedBy:"Added by {{username}}",prints:"prints",more:"+{{count}} more",ascending:"Ascending",descending:"Descending",back:"Back",copy:"Copy",copied:"Copied!",printer:"Printer",remove:"Remove",type:"Type",print:"Print",rename:"Rename",move:"Move",create:"Create",duplicate:"Duplicate",left:"Left",right:"Right"},printers:{title:"Printers",addPrinter:"Add Printer",editPrinter:"Edit Printer",deletePrinter:"Delete Printer",printerName:"Printer Name",serialNumber:"Serial Number",ipAddress:"IP Address / Hostname",accessCode:"Access Code",model:"Model",nozzleCount:"Nozzle Count",autoArchive:"Auto Archive",status:{available:"Available",idle:"Idle",printing:"Printing",paused:"Paused",offline:"Offline",problem:"Problem",error:"Error",finished:"Finished",unknown:"Unknown"},temperatures:{nozzle:"Nozzle",bed:"Bed",chamber:"Chamber"},progress:"{{percent}}% complete",timeRemaining:"{{time}} remaining",deleteConfirm:'Are you sure you want to delete "{{name}}"?',maintenanceOk:"Maintenance OK",maintenanceWarning:"{{count}} warning",maintenanceWarning_plural:"{{count}} warnings",maintenanceDue:"{{count}} due",maintenanceDue_plural:"{{count}} due",sort:{name:"Name",status:"Status",model:"Model",location:"Location",ascending:"Sort ascending",descending:"Sort descending"},cardSize:{small:"Small cards",medium:"Medium cards",large:"Large cards",extraLarge:"Extra large cards"},hideOffline:"Hide offline",nextAvailable:"Next available",powerOn:"Power On",offlinePrintersWithPlugs:"Offline printers with smart plugs",noPrintersConfigured:"No printers configured yet",search:"Search printers...",noSearchResults:"No printers match your search or filters",filter:{allStatuses:"All statuses",allLocations:"All locations"},readyToPrint:"Ready to print",external:"External",extL:"Ext-L",extR:"Ext-R",deleteArchives:"Delete print archives",noLabel:"No label",printPreview:"Print preview",width:"Width",height:"Height",noObjectsFound:"No objects found",objectsLoadedOnPrintStart:"Objects are loaded when a print starts",willBeSkipped:"Will be skipped",name:"Name",serialCannotBeChanged:"Serial number cannot be changed",locationHelp:"Used to group printers and filter queue jobs",wifiSignal:{veryWeak:"Very weak",weak:"Weak",fair:"Fair",good:"Good",excellent:"Excellent"},maintenanceUpToDate:"All maintenance up to date - Click to view",chamberLightOn:"Turn on chamber light",chamberLightOff:"Turn off chamber light",files:"Files",browseFiles:"Browse printer files",autoOffAfterPrint:"Auto power-off after print",autoOffExecuted:"Auto-off was executed - turn printer on to reset",hmsErrors:"HMS Errors",viewHmsErrors:"View {{count}} HMS error(s)",resume:"Resume",pause:"Pause",stop:"Stop",camera:"Camera",skipObject:"Skip Object",reconnect:"Reconnect",forceRefresh:"Force Refresh",forceRefreshSuccess:"Refresh requested",mqttDebug:"MQTT Debug",printerInformation:"Printer Information",copyToClipboard:"Copy",copied:"Copied!",state:"State",wifiSignalLabel:"WiFi Signal",developerMode:"Developer Mode",enabled:"Enabled",disabled:"Disabled",addedOn:"Added",sdCard:"SD Card",inserted:"Inserted",notInserted:"Not inserted",totalPrintHours:"Print Hours",activeNozzle:"Active: {{nozzle}} nozzle",nozzleRack:"Nozzle Rack",nozzleDocked:"Docked",nozzleMounted:"Mounted",nozzleActive:"Active",nozzleIdle:"Idle",nozzleDiameter:"Diameter",nozzleType:"Type",nozzleStatus:"Status",nozzleFilament:"Filament",nozzleWear:"Wear",nozzleMaxTemp:"Max Temp",nozzleSerial:"Serial",nozzleHardenedSteel:"Hardened Steel",nozzleStainlessSteel:"Stainless Steel",nozzleTungstenCarbide:"Tungsten Carbide",nozzleFlow:"Flow",nozzleHighFlow:"High Flow",nozzleStandardFlow:"Standard",firmwareUpdate:"Firmware Update",firmwareInstructions:"On the printer's touchscreen, go to",firmwareNav:"Navigate to",settings:"Settings",firmware:"Firmware",discoverPrinters:"Discover Printers",searching:"Searching...",manualEntry:"Manual Entry",addFromCloud:"Add from Cloud",toast:{printerDeleted:"Printer deleted",missingSpoolAssignment:"Print started on {{printer}}. Missing spool assignment for: {{slots}}",printerAdded:"Printer added",printerUpdated:"Printer updated",failedToDelete:"Failed to delete printer",failedToAdd:"Failed to add printer",failedToUpdate:"Failed to update printer",commandSent:"Command sent",failedToSendCommand:"Failed to send command",turnedOn:"{{name}} turned on",failedToPowerOn:"Failed to power on {{name}}",scriptTriggered:"Script triggered",printStopped:"Print stopped",printPaused:"Print paused",printResumed:"Print resumed",referenceDeleted:"Reference deleted",detectionAreaSaved:"Detection area saved",failedToRunScript:"Failed to run script",failedToStopPrint:"Failed to stop print",failedToPausePrint:"Failed to pause print",failedToResumePrint:"Failed to resume print",failedToControlChamberLight:"Failed to control chamber light",failedToSetSpeed:"Failed to set print speed",failedToUpdateSetting:"Failed to update setting",failedToSkipObjects:"Failed to skip objects",failedToRereadRfid:"Failed to re-read RFID",failedToCheckPlate:"Failed to check plate",failedToUpdateLabel:"Failed to update label",failedToDeleteReference:"Failed to delete reference",failedToSaveDetectionArea:"Failed to save detection area",plateCheckEnabled:"Plate check enabled",plateCheckDisabled:"Plate check disabled",calibrationSaved:"Calibration saved!",calibrationFailed:"Calibration failed",rfidRereadInitiated:"RFID re-read initiated"},connection:{connected:"Connected",offline:"Offline"},plateStatus:{markCleared:"Mark plate as cleared",cleared:"Plate Clear",notCleared:"Plate not Clear",inUse:"Plate in Use"},queue:{inQueue:"{{count}} print in queue",inQueue_plural:"{{count}} prints in queue"},controls:"Controls",rfid:{reread:"Re-read RFID"},bedJog:{title:"Move build plate",bed:"Bed",step:"Step (mm)",up:"Move plate up",down:"Move plate down",disabledWhilePrinting:"Disabled while printing",notHomedTitle:"Printer is not homed",notHomedMessage:"The printer has not been homed since the last print. Run auto-home first for safe positioning (parks the toolhead, then homes X, Y, and Z), or move anyway — soft endstops will be bypassed.",homeZ:"Auto Home",moveAnyway:"Move anyway",homingStarted:"Auto-homing printer…"},permission:{noAdd:"You do not have permission to add printers",noEdit:"You do not have permission to edit printers",noDelete:"You do not have permission to delete printers",noControl:"You do not have permission to control printers",noFiles:"You do not have permission to access printer files",noAmsRfid:"You do not have permission to re-read AMS RFID",noSmartPlugControl:"You do not have permission to control smart plugs",noCamera:"You do not have permission to view cameras"},modal:{addTitle:"Add Printer",editTitle:"Edit Printer",myPrinter:"My Printer",selectModel:"Select model...",locationGroup:"Location / Group (optional)",locationPlaceholder:"e.g., Workshop, Office, Basement",autoArchiveLabel:"Auto-archive completed prints",fromPrinterSettings:"From printer settings",modelOptional:"Model (optional)",saveChanges:"Save Changes"},skipObjects:{tooltip:"Skip objects",onlyWhilePrinting:"Skip objects (only while printing)",requiresMultiple:"Skip objects (requires 2+ objects)",title:"Skip Objects",matchIdsInfo:"Match IDs with your printer display",printerShowsIds:"The printer screen shows object IDs on the build plate",skipSelected:"Skip Selected",skipping:"Skipping...",noObjectsSelected:"No objects selected",selectObjectsToSkip:"Select objects you want to skip from the current print",skipped:"skipped",objectsSkipped:"Objects skipped",activeCount:"{{count}} active",waitForLayer:"Wait for layer 2+ to skip objects (currently layer {{layer}})",skip:"Skip",confirmTitle:"Skip Object?",confirmMessage:'Are you sure you want to skip "{{name}}"? This cannot be undone.'},confirm:{deleteTitle:"Delete Printer",deleteMessage:'Are you sure you want to delete "{{name}}"? This will remove all connection settings.',deleteArchivesNote:"All print history for this printer will be permanently deleted.",keepArchivesNote:"Print history will be kept but no longer associated with this printer.",stopTitle:"Stop Print",stopMessage:'Are you sure you want to stop the current print on "{{name}}"? This will cancel the print job.',stopButton:"Stop Print",pauseTitle:"Pause Print",pauseMessage:'Are you sure you want to pause the current print on "{{name}}"?',pauseButton:"Pause Print",resumeTitle:"Resume Print",resumeMessage:'Are you sure you want to resume the print on "{{name}}"?',resumeButton:"Resume Print",powerOnTitle:"Power On Printer",powerOnMessage:'Are you sure you want to turn ON the power for "{{name}}"?',powerOnButton:"Power On",powerOffTitle:"Power Off Printer",powerOffMessage:'Are you sure you want to turn OFF the power for "{{name}}"?',powerOffWarning:'WARNING: "{{name}}" is currently printing! Are you sure you want to turn OFF the power? This will interrupt the print and may damage the printer.',powerOffButton:"Power Off"},bulk:{select:"Select",selectAll:"Select All",selectByLocation:"Select by Location",selected:"{{count}} selected",actions:{stop:"Stop",pause:"Pause",resume:"Resume",clearPlate:"Clear Bed",clearHMS:"Clear Notifications"},confirm:{stopTitle:"Stop {{count}} Prints",stopMessage:"This will cancel active prints on {{count}} printer(s). This action cannot be undone.",stopButton:"Stop All",pauseTitle:"Pause {{count}} Prints",pauseMessage:"This will pause active prints on {{count}} printer(s).",pauseButton:"Pause All",clearPlateTitle:"Clear {{count}} Print Beds",clearPlateMessage:"This will clear the print bed on {{count}} printer(s) and may trigger queued jobs.",clearPlateButton:"Clear All"},success:"{{action}} completed on {{count}} printer(s)",partial:"{{succeeded}} succeeded, {{failed}} failed",noneApplicable:"No selected printers are in the right state for this action",selectByState:"Select by State"},discovery:{title:"Discover Printers",searching:"Searching...",scanning:"Scanning...",scanProgress:"Scanning... {{scanned}}/{{total}}",foundPrinters:"Found {{count}} printer(s)",noPrintersFound:"No printers found",noPrintersFoundSubnet:"No printers found in the specified subnet.",noPrintersFoundNetwork:"No printers found on the network.",allConfigured:"All discovered printers are already configured.",alreadyAdded:"Already added",select:"Select",manualEntry:"Manual Entry",addFromCloud:"Add from Cloud",subnetToScan:"Subnet to scan",dockerNote:"Docker detected. Enter your printer's subnet in CIDR notation. Requires network_mode: host in docker-compose.yml.",scanSubnet:"Scan Subnet for Printers",discoverNetwork:"Discover Printers on Network",scanningSubnet:"Scanning subnet for Bambu printers...",scanningNetwork:"Scanning network...",serialRequired:"Serial required",unknown:"Unknown",failedToStart:"Failed to start discovery"},drying:{start:"Start Drying",stop:"Stop Drying",temperature:"Temperature",duration:"Duration",hours:"hours",timeRemaining:"{{time}} left",active:"Drying",notSupported:"Drying not supported",powerRequired:"Connect AMS power adapter to enable drying",startingDrying:"Starting drying...",stoppingDrying:"Stopping drying...",rotateTray:"Rotate spool during drying"},filaments:"Filaments",openCameraOverlay:"Open camera overlay",openCameraWindow:"Open camera in new window",firmwareUpdateAvailable:"Firmware update available: {{current}} → {{latest}}",firmwareUpToDate:"Firmware {{version}} — Up to date",firmwareUpdateButton:"Update",plateDetection:{noPermission:"You do not have permission to update printers",enabledClick:"Plate check enabled - Click to disable",disabledClick:"Plate check disabled - Click to enable",manageCalibration:"Manage plate detection calibration",calibrationRequired:"Calibration Required",calibrationInstructions:"Please ensure the build plate is completely empty, then click Calibrate.",calibrationDescription:"Calibration captures a reference image of the empty plate. Future checks will compare against this reference to detect objects.",calibrationTip:"Tip: You can store up to 5 calibrations for different plates. The system automatically uses the best match when checking.",plateEmpty:"Plate appears empty",objectsDetected:"Objects detected on plate",confidence:"Confidence",difference:"Difference",analysisPreview:"Analysis preview:",analysisLegend:"Green box = detection area, Red overlay = differences from calibration",savedReferences:"Saved References ({{count}}/{{max}})",deleteReference:"Delete reference",labelPlaceholder:"Label...",clickToEdit:"{{label}} - Click to edit",clickToAddLabel:"Click to add label"},speed:{title:"Print Speed",silent:"Silent (50%)",standard:"Standard (100%)",sport:"Sport (124%)",ludicrous:"Ludicrous (166%)"},airduct:{title:"Airduct Mode",cooling:"Cooling",heating:"Heating"},noSdCard:"No SD",door:{open:"Open",closed:"Closed"},fans:{partCooling:"Part Cooling Fan",auxiliary:"Auxiliary Fan",chamber:"Chamber Fan"},clickToViewHmsErrors:"Click to view HMS errors",estimatedCompletion:"Estimated completion time",plateNumber:"Plate {{number}}",slotOptions:"Slot options",amsPopup:{friendlyName:"AMS Name",friendlyNamePlaceholder:"e.g. AMS Friendly Name",serialNumber:"Serial Number",firmwareVersion:"Firmware",save:"Save",clear:"Clear",noEditPermission:"You do not have permission to rename AMS units"},firmwareModal:{title:"Firmware Update",titleUpToDate:"Firmware Info",currentVersion:"Current:",latestVersion:"Latest:",releaseNotes:"Release Notes",checkingPrereqs:"Checking prerequisites...",sdCardReady:"SD card ready. Click below to upload firmware.",uploadedSuccess:"Firmware uploaded to SD card!",applyInstructions:"To apply the update on your printer:",step1:"On the printer's touchscreen, go to Settings",step2:"Navigate to Firmware",step3:"Select Update from SD card",step4:"The update will take 10-20 minutes",done:"Done",starting:"Starting...",uploadFirmware:"Upload Firmware",uploadFailed:"Failed to start upload: {{error}}",uploadedToast:"Firmware uploaded! Trigger update from printer screen.",availableVersions:"Available versions",usable:"Usable",unavailable:"Unavailable",installed:"Installed",newerBadge:"newer",olderBadge:"older",currentBadge:"current"},accessCodePlaceholder:"Leave empty to keep current",roi:{title:"Detection Area (ROI)",xStart:"X Start",yStart:"Y Start",width:"Width",height:"Height",instruction:"Adjust the detection area to focus on the build plate. The green box in the preview shows the current area."},developerModeWarning:"Developer LAN mode is not enabled on: {{names}}. Some features may not work.",howToEnable:"How to enable",incompatibleFile:"This file was sliced for {{slicedFor}}, but this printer is a {{printerModel}}",dropNotPrintable:"Only .gcode and .gcode.3mf files can be printed",dropToPrint:"Drop to print",cannotPrint:"Printer busy"},archives:{title:"Print Archives",searchPlaceholder:"Search archives...",filterByPrinter:"Filter by printer",filterByStatus:"Filter by status",sortBy:"Sort by",sortNewest:"Newest first",sortOldest:"Oldest first",sortName:"Name",sortDuration:"Duration",sortLargest:"Largest first",sortSmallest:"Smallest first",sortSize:"Size",noArchives:"No archives found",noArchivesSearch:"No archives match your search",originalPrintNotVisible:"Original print not visible - try clearing filters",noArchivesYet:"No archives yet",prints:"prints",pagination:{showing:"Showing",to:"to",of:"of",show:"Show",page:"Page",all:"All"},loadingArchives:"Loading archives...",releaseToUpload:"Release to upload",showAll:"Show all",showFavoritesOnly:"Show favorites only",gridView:"Grid view",listView:"List view",calendarView:"Calendar view",logView:"Print Log",manageTags:"Manage Tags",showFailedPrints:"Show failed prints",hideFailedPrints:"Hide failed prints",hideDuplicates:"Hide Duplicates",viewOriginalPrint:"Click to view original print (#{{id}})",printTime:"Print Time",filamentUsed:"Filament Used",cost:"Cost",reprint:"Reprint",preview:"Preview",deleteArchive:"Delete Archive",deleteConfirm:"Are you sure you want to delete this archive?",favorite:"Favorite",unfavorite:"Remove from favorites",viewDetails:"View Details",status:{completed:"Completed",failed:"Failed",stopped:"Stopped"},toast:{source3mfAttached:"Source 3MF attached: {{filename}}",failedUploadSource3mf:"Failed to upload source 3MF",source3mfRemoved:"Source 3MF removed",failedRemoveSource3mf:"Failed to remove source 3MF",f3dAttached:"F3D attached: {{filename}}",failedUploadF3d:"Failed to upload F3D",f3dRemoved:"F3D removed",failedRemoveF3d:"Failed to remove F3D",timelapseAttached:"Timelapse attached: {{filename}}",timelapseAlreadyAttached:"Timelapse already attached",noMatchingTimelapse:"No matching timelapse found",failedScanTimelapse:"Failed to scan for timelapse",failedAttachTimelapse:"Failed to attach timelapse",timelapseRemoved:"Timelapse removed",failedRemoveTimelapse:"Failed to remove timelapse",timelapseUploaded:"Timelapse uploaded: {{filename}}",failedUploadTimelapse:"Failed to upload timelapse",archiveDeleted:"Archive deleted",failedDeleteArchive:"Failed to delete archive",addedToFavorites:"Added to favorites",removedFromFavorites:"Removed from favorites",projectUpdated:"Project updated",failedUpdateProject:"Failed to update project",linkCopied:"Link copied to clipboard",failedCopyLink:"Failed to copy link",photoDeleted:"Photo deleted",failedDeletePhoto:"Failed to delete photo",failedDeleteArchives:"Failed to delete archives",failedUpdateFavorites:"Failed to update favorites",exportDownloaded:"Export downloaded",exportFailed:"Export failed"},menu:{print:"Print",schedule:"Schedule",openInBambuStudio:"Open in Slicer",slice:"Slice",externalLink:"External Link",viewOnMakerWorld:"View on MakerWorld",preview3d:"3D Preview",viewTimelapse:"View Timelapse",scanForTimelapse:"Scan for Timelapse",uploadTimelapse:"Upload Timelapse",removeTimelapse:"Remove Timelapse",downloadSource3mf:"Download Source 3MF",uploadSource3mf:"Upload Source 3MF",replaceSource3mf:"Replace Source 3MF",removeSource3mf:"Remove Source 3MF",uploadF3d:"Upload F3D",replaceF3d:"Replace F3D",downloadF3d:"Download F3D",removeF3d:"Remove F3D",download:"Download",copyDownloadLink:"Copy Download Link",qrCode:"QR Code",viewPhotos:"View Photos",viewPhotosCount:"View Photos ({{count}})",projectPage:"Project Page",addToFavorites:"Add to Favorites",removeFromFavorites:"Remove from Favorites",edit:"Edit",goToProject:"Go to Project: {{name}}",addToProject:"Add to Project",removeFromProject:"Remove from Project",loading:"Loading...",noProjectsAvailable:"No projects available",select:"Select",deselect:"Deselect",delete:"Delete"},permission:{noReprint:"You do not have permission to reprint this archive",noAddToQueue:"You do not have permission to add to queue",noUpdateArchives:"You do not have permission to update archives",noUploadFiles:"You do not have permission to upload files",noDownload:"You do not have permission to download archives",noCopyLink:"You do not have permission to copy download links",noDelete:"You do not have permission to delete this archive",noCreate:"You do not have permission to create archives"},card:{previousPlate:"Previous plate",nextPlate:"Next plate",plateNumber:"Plate {{index}}",moreOptions:"Right-click for more options",addToFavorites:"Add to favorites",removeFromFavorites:"Remove from favorites",cancelled:"cancelled",failed:"failed",duplicate:"duplicate",duplicateTitle:"This model has been printed before",openSource3mf:"Open source 3MF in Bambu Studio (right-click for more options)",downloadF3d:"Download Fusion 360 design file",viewTimelapse:"View timelapse",viewPhoto:"View 1 photo",viewPhotos:"View {{count}} photos",openFolder:"Open folder: {{name}}",slicedFile:"Sliced file - ready to print",sourceFile:"Source file only - no AMS mapping available",gcode:"GCODE",source:"SOURCE",project:"Project: {{name}}",estimated:"Estimated: {{time}}",actual:"Actual: {{time}}",accuracy:"Accuracy: {{percent}}%",filament:"{{weight}}g",layer:"{{count}} layer",layers:"{{count}} layers",object:"{{count}} object",objects:"{{count}} objects",slicedFor:"Sliced for {{model}}",uploadedBy:"Uploaded By",noPermissionReprint:"You do not have permission to reprint",noFileForReprint:"No 3MF file available — the file could not be downloaded from the printer when the print was recorded",noPermissionEdit:"You do not have permission to edit archives",noPermissionDelete:"You do not have permission to delete archives",reprint:"Reprint",schedulePrint:"Schedule Print",schedule:"Schedule",openInBambuStudio:"Open in Slicer",openInBambuStudioToSlice:"Open in Slicer to slice",slice:"Slice",externalLink:"External Link",makerWorld:"MakerWorld: {{designer}}",viewProject:"View project",noExternalLink:"No external link",preview3d:"3D Preview",download:"Download",edit:"Edit",delete:"Delete"},modal:{deleteArchive:"Delete Archive",deleteConfirm:'Are you sure you want to delete "{{name}}"? This action cannot be undone.',deleteButton:"Delete",removeSource3mf:"Remove Source 3MF",removeSource3mfConfirm:'Are you sure you want to remove the source 3MF file from "{{name}}"? This will delete the original slicer project file.',removeButton:"Remove",removeF3d:"Remove F3D",removeF3dConfirm:'Are you sure you want to remove the Fusion 360 design file from "{{name}}"?',removeTimelapse:"Remove Timelapse",removeTimelapseConfirm:'Are you sure you want to remove the timelapse video from "{{name}}"?',timelapse:"{{name}} - Timelapse",selectTimelapse:"Select Timelapse",selectTimelapseDesc:"No auto-match found. Select the timelapse for this print:",deleteArchives:"Delete Archives",deleteArchivesConfirm:"Are you sure you want to delete {{count}} archive(s)? This action cannot be undone.",deleteCount:"Delete {{count}}"},page:{title:"Archives",printsCount:"{{filtered}} of {{total}} prints",dropFilesHere:"Drop .3mf files here",releaseToUpload:"Release to upload",only3mfSupported:"Only .3mf files are supported",close:"Close",selected:"{{count}} selected",selectAll:"Select All",tags:"Tags",project:"Project",favorite:"Favorite",delete:"Delete",toggledFavorites:"Toggled favorites for {{count}} archive(s)",failedUpdateFavorites:"Failed to update favorites",archivesDeleted:"{{count}} archive(s) deleted",failedDeleteArchives:"Failed to delete archives",photoDeleted:"Photo deleted",failedDeletePhoto:"Failed to delete photo"},list:{name:"Name",printer:"Printer",date:"Date",size:"Size",actions:"Actions",hasTimelapse:"Has timelapse"},log:{date:"Date",printName:"Print Name",printer:"Printer",user:"User",status:"Status",duration:"Duration",filament:"Filament",allPrinters:"All Printers",allUsers:"All Users",allStatuses:"All Statuses",cancelled:"Cancelled",skipped:"Skipped",dateFrom:"From",dateTo:"To",noEntries:"No print log entries found",showing:"Showing {{count}} of {{total}} entries",rowsPerPage:"Rows",page:"Page",prev:"Prev",next:"Next",clearLog:"Clear Log",clearLogTitle:"Clear Print Log",clearLogConfirm:"All print log entries will be permanently deleted. Archives and queue items are not affected. This action cannot be undone. Are you sure?",clearLogButton:"Clear All",cleared:"{{count}} log entries cleared",clearFailed:"Failed to clear print log"}},queue:{title:"Print Queue",subtitle:"Schedule and manage your print jobs",addToQueue:"Add to Queue",print:"Print",reprint:"Re-print",schedulePrint:"Schedule Print",editQueueItem:"Edit Queue Item",printToPrinters:"Print to {{count}} Printers",queueToPrinters:"Queue to {{count}} Printers",queueSelectedPlates:"Queue {{count}} Plates",selectAllPlates:"Select All {{count}} Plates",deselectAll:"Deselect All",printQueued:"Print queued",itemsQueued:"{{count}} items queued",sending:"Sending...",sendingProgress:"Sending {{current}}/{{total}}...",adding:"Adding...",addingProgress:"Adding {{current}}/{{total}}...",savingProgress:"Saving {{current}}/{{total}}...",clearQueue:"Clear Queue",clearHistory:"Clear History",emptyQueue:"Queue is empty",position:"Position",scheduledTime:"Scheduled Time",moveUp:"Move Up",moveDown:"Move Down",startNow:"Start Now",printingInProgress:"Printing in progress...",viewArchive:"View archive",viewInFileManager:"View in File Manager",itemCount:"{{count}} item",itemCount_plural:"{{count}} items",dragToReorder:"Drag to reorder (ASAP only)",reorderHint:"Position only affects ASAP items. Scheduled items run at their set time.",sjf:{label:"SJF",tooltip:"Shortest Job First — scheduler prioritizes shorter prints"},addedBy:"Added by {{name}}",nextInQueue:"Next in queue",clearPlate:"Clear Plate & Start Next",clearPlateSuccess:"Plate cleared — ready for next print",plateReady:"Plate cleared — ready for next print",plateNumber:"Plate {{index}}",quantity:"Quantity",quantityHint:"Creates {{count}} queue items",activeBatches:"Active Batches",batchProgress:"{{completed}} of {{total}} completed",cancelBatch:"Cancel Remaining",batchCancelled:"Remaining batch items cancelled",cancelBatchConfirmTitle:"Cancel Batch",cancelBatchConfirmMessage:"Cancel all remaining pending items in this batch?",batch:"Batch",sections:{currentlyPrinting:"Currently Printing",queued:"Queued",history:"History"},status:{pending:"Pending",waiting:"Waiting",printing:"Printing",paused:"Paused",completed:"Completed",failed:"Failed",skipped:"Skipped",cancelled:"Cancelled"},summary:{printing:"Printing",queued:"Queued",totalTime:"Total Queue Time",totalWeight:"Total Queue Weight",history:"History"},filter:{allPrinters:"All Printers",unassigned:"Unassigned",allStatus:"All Status",allLocations:"All Locations",any:"Any"},sort:{byPosition:"Sort by Position",byName:"Sort by Name",byPrinter:"Sort by Printer",bySchedule:"Sort by Schedule",byDate:"Sort by Date",ascendingOldest:"Ascending (oldest first)",descendingNewest:"Descending (newest first)"},badges:{staged:"Staged",requiresPrevious:"Requires previous success",autoPowerOff:"Auto power off",gcodeInjection:"G-code"},empty:{title:"No prints scheduled",description:'Schedule a print from the Archives page using the "Schedule" option in the context menu, or drag and drop files to get started.'},time:{asap:"ASAP",overdue:"Overdue",now:"Now",lessThanMinute:"In less than a minute",inMinutes:"In {{count}} min",inHours:"In {{count}} hours"},actions:{stopPrint:"Stop Print",startPrint:"Start Print",requeue:"Re-queue"},bulkEdit:{title:"Edit {{count}} Item",title_plural:"Edit {{count}} Items",description:"Only changed settings will be applied to selected items.",printer:"Printer",noChange:"— No change —",queueOptions:"Queue Options",staged:"Staged (manual start)",autoPowerOff:"Auto power off after print",requirePrevious:"Require previous success",printOptions:"Print Options",bedLevelling:"Bed levelling",flowCalibration:"Flow calibration",vibrationCalibration:"Vibration calibration",layerInspection:"First layer inspection",timelapse:"Timelapse",useAms:"Use AMS",applyChanges:"Apply Changes",selectAll:"Select All",deselectAll:"Deselect All",selected:"{{count}} selected",editSelected:"Edit Selected",cancelSelected:"Cancel Selected"},confirm:{cancelTitle:"Cancel Scheduled Print",cancelMessage:'Are you sure you want to cancel "{{name}}"?',stopTitle:"Stop Print",stopMessage:'Are you sure you want to stop the current print "{{name}}"? This will cancel the print job on the printer.',removeTitle:"Remove from History",removeMessage:'Are you sure you want to remove "{{name}}" from the queue history?',clearHistoryTitle:"Clear History",clearHistoryMessage:"Are you sure you want to remove all {{count}} item(s) from the history?",cancelButton:"Cancel Print",stopButton:"Stop Print",thisPrint:"this print",thisItem:"this item"},toast:{cancelled:"Queue item cancelled",cancelFailed:"Failed to cancel item",removed:"Queue item removed",removeFailed:"Failed to remove item",stopped:"Print stopped",stopFailed:"Failed to stop print",released:"Print released to queue",startFailed:"Failed to start print",reorderFailed:"Failed to reorder queue",historyCleared:"Cleared {{count}} history item(s)",clearHistoryFailed:"Failed to clear history",updateFailed:"Failed to update items",bulkCancelled:"Cancelled {{count}} item(s)",bulkCancelFailed:"Failed to cancel items"},timeline:{listView:"List",timelineView:"Timeline",unassigned:"Unassigned",noData:"No scheduled prints for this day",allDoneBy:"All prints estimated done by {{time}}",staged:"Staged",filterAll:"Show All",filterPrinting:"Printing",filterQueued:"Queued",time:{anyMoment:"any moment",minutesLeft:"{{minutes}}m left",hoursLeft:"{{hours}}h left",hoursMinutesLeft:"{{hours}}h {{minutes}}m left"},day:{previous:"Previous day",next:"Next day",today:"Today"}},permissions:{noStopPrint:"You do not have permission to stop prints",noStartPrint:"You do not have permission to start prints",noEdit:"You do not have permission to edit this queue item",noCancel:"You do not have permission to cancel this queue item",noRequeue:"You do not have permission to re-queue items",noRemove:"You do not have permission to remove this queue item",noClearHistory:"You do not have permission to clear all history",noEditItems:"You do not have permission to edit queue items",noCancelItems:"You do not have permission to cancel queue items"}},backgroundDispatch:{unknownFile:"Unknown file",unknownPrinter:"Unknown printer",startingPrints:"Starting prints",progressSummary:"{{complete}}/{{total}} complete • Dispatched: {{dispatched}} • Processing: {{processing}}",expandDetails:"Expand dispatch details",collapseDetails:"Collapse dispatch details",dismissToast:"Dismiss dispatch toast",cancelDispatchJob:"Cancel dispatch job",cancel:"Cancel",cancelling:"Cancelling…",status:{dispatched:"Dispatched",processing:"Processing",completed:"Completed",failed:"Failed",cancelled:"Cancelled"},toast:{cancellingUpload:"Cancelling upload...",cancelled:"Dispatch cancelled",cancelFailed:"Failed to cancel dispatch",completeWithFailures:"Background dispatch complete: {{completed}} succeeded, {{failed}} failed",completeSuccess:"Background dispatch complete: {{completed}} succeeded",printStartedRemaining:"{{completed}} print(s) started, {{remaining}} more sending..."}},stats:{title:"Dashboard",subtitle:"Drag widgets to rearrange. Click the eye icon to hide.",overview:"Overview",totalPrints:"Total Prints",successRate:"Success Rate",totalPrintTime:"Total Print Time",printTime:"Print Time",totalFilament:"Total Filament Used",filamentUsed:"Filament Used",filamentCost:"Filament Cost",totalCost:"Total Cost",energyUsed:"Energy Used",energyCost:"Energy Cost",energyWarmingUpTooltip:"Energy tracking is still collecting hourly snapshots. Date-range totals will become accurate once at least one snapshot exists before the selected range. Early values may undercount.",averagePrintTime:"Average Print Time",printsPerDay:"Prints per Day",byPrinter:"By Printer",printsByPrinter:"Prints by Printer",byMaterial:"By Material",byMonth:"By Month",last7Days:"Last 7 Days",last30Days:"Last 30 Days",last90Days:"Last 90 Days",allTime:"All Time",quickStats:"Quick Stats",printActivity:"Print Activity",filamentTypes:"Filament Types",filamentTrends:"Filament Trends",failureAnalysis:"Failure Analysis",timeAccuracy:"Time Accuracy",successful:"Successful:",failed:"Failed:",perfectEstimate:"100% = perfect estimate",noTimeAccuracyData:"No time accuracy data yet",noFilamentData:"No filament data available",noPrinterData:"No printer data available",noPrintData:"No print data available",noPrintDataLast30Days:"No print data in the last 30 days",failureReasons:"Failure Reasons",topFailureReasons:"Top Failure Reasons",failedPrintsCount:"{{failed}} / {{total}} prints failed",lastWeekRate:"Last week: {{rate}}%",resetLayout:"Reset Layout",recalculateCosts:"Recalculate Costs",recalculateCostsHint:"Recalculate all archive costs using current filament prices",exportStats:"Export Stats",exportAsCsv:"Export as CSV",exportAsExcel:"Export as Excel",hiddenCount:"{{count}} Hidden",exportDownloaded:"Export downloaded",exportFailed:"Export failed",layoutReset:"Layout reset",recalculatedCosts:"Recalculated costs for {{count}} archives",recalculateFailed:"Failed to recalculate costs",loadingStats:"Loading statistics...",noPermissionResetLayout:"You do not have permission to reset layout",noPermissionRecalculate:"You do not have permission to recalculate costs",noPrintDataInRange:"No print data in selected range",periodFilament:"Period Filament",periodCost:"Period Cost",avgPerPrint:"Avg per Print",usageOverTime:"Usage Over Time",filamentByWeight:"Weight",printDuration:"Print Duration",printerUtilization:"Printer Utilization",filamentSuccess:"Success by Material",printHabits:"Print Habits",printTimeOfDay:"Print Time of Day",colorDistribution:"Color Distribution",noColorData:"No color data available",records:"Records",longestPrint:"Longest Print",heaviestPrint:"Heaviest Print",mostExpensivePrint:"Most Expensive",busiestDay:"Busiest Day",successStreak:"Success Streak",streakPrint:"consecutive print",streakPrints:"{{count}} consecutive prints",printerStats:"Printer Stats",hours:"hours",avgPrints:"Avg. prints",noArchiveData:"No print data available",filamentByTime:"Time",avgWeight:"Avg. weight",avgTime:"Avg. time",filamentByPrints:"Prints",timeframe:{today:"Today","this-week":"This Week","this-month":"This Month","last-7":"Last 7 Days","last-30":"Last 30 Days","last-90":"Last 90 Days","this-year":"This Year","all-time":"All Time",custom:"Custom Range",from:"From",to:"To"},allUsers:"All Users",noUser:"No User (System)",filterByUser:"Filter by User"},maintenance:{title:"Maintenance",overview:"Overview",allOk:"All maintenance up to date",dueCount:"{{count}} item due",dueCount_plural:"{{count}} items due",warningCount:"{{count}} warning",warningCount_plural:"{{count}} warnings",totalPrintTime:"Total Print Time",nextMaintenance:"Next Maintenance",nothingDue:"Nothing due",tasks:"Tasks",lastPerformed:"Last performed",interval:"Interval",hoursRemaining:"{{hours}}h remaining",hoursOverdue:"{{hours}}h overdue",markDone:"Mark as Done",performMaintenance:"Perform Maintenance",history:"History",noHistory:"No maintenance history",editPrintHours:"Edit Print Hours",currentHours:"Current Hours",statusTab:"Status",settingsTab:"Settings",overdueCount:"{{count}} overdue",dueSoonCount:"{{count}} due soon",dueSoon:"Due soon",allGood:"All good",overdueBy:"Overdue by {{duration}}",dueIn:"Due in {{duration}}",timeLeft:"{{duration}} left",day:"1 day",days:"{{count}} days",week:"1 week",weeks:"{{count}} weeks",month:"1 month",months:"{{count}} months",year:"1 year",maintenanceTypes:"Maintenance Types",maintenanceTypesDescription:"System types and your custom maintenance tasks",addCustomType:"Add Custom Type",restoreDefaults:"Restore Default Tasks",intervalType:"Interval Type",intervalValue:"Interval ({{type}})",icon:"Icon",documentationLink:"Documentation Link (optional)",assignToPrinters:"Assign to Printers",selectAtLeastOnePrinter:"Select at least one printer",addType:"Add Type",custom:"Custom",printHours:"Print Hours",calendarDays:"Calendar Days",exampleName:"e.g., Replace HEPA Filter",viewDocumentation:"View documentation",timeBasedInterval:"Time-based interval",intervalOverrides:"Interval Overrides",intervalOverridesDescription:"Customize intervals for specific printers",assignedToPrinters:"Assigned to printers:",noPrintersAssigned:"No printers assigned",addPrinterShort:"Add:",printersAssignedClick:"{{count}} printer(s) assigned - click to manage",removeFromPrinter:"Remove from this printer",types:{lubricateCarbonRods:"Lubricate Carbon Rods",lubricateRails:"Lubricate Linear Rails",cleanNozzle:"Clean Nozzle/Hotend",checkBelts:"Check Belt Tension",cleanBuildPlate:"Clean Build Plate",checkExtruder:"Check Extruder Gears",checkCooling:"Check Cooling Fans",generalInspection:"General Inspection",cleanCarbonRods:"Clean Carbon Rods",lubricateSteelRods:"Lubricate Steel Rods",cleanSteelRods:"Clean Steel Rods",cleanLinearRails:"Clean Linear Rails",checkPtfeTube:"Check PTFE Tube",replaceHepaFilter:"Replace HEPA Filter",replaceCarbonFilter:"Replace Carbon Filter",lubricateLeftNozzleRail:"Lubricate Left Nozzle Rail"},maintenanceComplete:"Maintenance marked as complete",typeUpdated:"Maintenance type updated",typeDeleted:"Maintenance type deleted",defaultsRestored:"Restored {{count}} default task(s)",printHoursUpdated:"Print hours updated",printerAssigned:"Printer assigned",printerRemoved:"Printer removed",deleteTypeConfirm:'Delete "{{name}}"?',deleteSystemTypeTitle:"Delete default maintenance task?",deleteSystemTypeMessage:'Are you sure you want to delete the default maintenance task "{{name}}"?',noPermissionUpdate:"You do not have permission to update maintenance items",noPermissionPerform:"You do not have permission to perform maintenance",noPermissionEditTypes:"You do not have permission to edit maintenance types",noPermissionDeleteTypes:"You do not have permission to delete maintenance types",noPermissionEditHours:"You do not have permission to edit print hours",noPermissionRemovePrinter:"You do not have permission to remove printer assignments",noPermissionAssignPrinter:"You do not have permission to assign printers",noPermissionEditIntervals:"You do not have permission to edit intervals",configureSettings:"Configure maintenance types and intervals"},settings:{title:"Settings",general:"General",tabs:{general:"General",smartPlugs:"Smart Plugs",notifications:"Notifications",queue:"Workflow",filament:"Filament",network:"Network",apiKeys:"API Keys",virtualPrinter:"Virtual Printer",spoolbuddy:"SpoolBuddy",failureDetection:"Failure Detection",users:"Authentication",backup:"Backup",emailAuth:"Email Authentication",ldap:"LDAP",twoFa:"Two-Factor Auth",oidc:"SSO / OIDC"},spoolbuddy:{infoTitle:"SpoolBuddy devices",infoBody:"SpoolBuddy kiosks register themselves automatically via heartbeat. Unregister a device here if it is no longer in use or if a stale duplicate was left behind by a daemon crash.",duplicatesTitle:"{{count}} devices registered",duplicatesBody:"Only the first registered device is used by the kiosk UI. If one of these is a stale duplicate from a crash, unregister it — an online device will re-register itself on its next heartbeat.",empty:"No SpoolBuddy devices registered yet.",online:"Online",offline:"Offline",unregister:"Unregister",unregisterSuccess:"Device unregistered",unregisterError:"Failed to unregister device",confirmTitle:"Unregister SpoolBuddy device?",confirmBody:'This will remove "{{hostname}}" ({{deviceId}}) from the database. If the device is online, it will re-register itself on its next heartbeat.',ipAddress:"IP address",firmware:"Firmware",lastSeen:"Last seen",daemonUptime:"Daemon uptime",systemUptime:"System uptime",never:"never",nfc:"NFC",scale:"Scale",cpuTemp:"CPU temp",memory:"Memory",disk:"Disk",update:"Update",updateConfirmTitle:"Update Spoolbuddy daemon?",updateConfirmBody:'Trigger a software update on "{{hostname}}"? The daemon will restart once the update is applied.',restartBrowser:"Restart Browser",restartBrowserConfirmTitle:"Restart kiosk browser?",restartBrowserConfirmBody:'Restart the kiosk browser on "{{hostname}}"? The display will blank briefly.',restartDaemon:"Restart Daemon",restartDaemonConfirmTitle:"Restart Spoolbuddy daemon?",restartDaemonConfirmBody:'Restart the Spoolbuddy daemon on "{{hostname}}"? The device will go offline for a few seconds.',reboot:"Reboot",rebootConfirmTitle:"Reboot device?",rebootConfirmBody:'Reboot "{{hostname}}"? The device will be offline for around a minute.',shutdown:"Shutdown",shutdownConfirmTitle:"Shutdown device?",shutdownConfirmBody:'Shutdown "{{hostname}}"? You will need physical access to power it back on.',commandConfirm:"Confirm",commandQueued:"Command queued",commandError:"Failed to send command"},ldap:{title:"LDAP Authentication",enabledDesc:"LDAP authentication is enabled",disabledDesc:"LDAP authentication is disabled",disabledHint:"Configure and save LDAP settings below, then enable.",enabled:"LDAP authentication enabled",disabled:"LDAP authentication disabled",feature1:"Users can login with LDAP credentials",feature2:"Local admin account remains as fallback",feature3:"LDAP groups are mapped to BamBuddy groups on login",serverConfig:"LDAP Server Configuration",serverUrl:"Server URL",serverUrlHint:"Use ldaps:// for SSL or ldap:// with StartTLS",security:"Security",securityHint:"StartTLS upgrades a plain connection to TLS. LDAPS uses TLS from the start.",bindDn:"Bind DN (Service Account)",bindPassword:"Bind Password",searchBase:"Search Base DN",userFilter:"User Search Filter",userFilterHint:"{username} is replaced with the login username. Use (uid={username}) for OpenLDAP.",autoProvision:"Auto-provision users",autoProvisionHint:"Automatically create a BamBuddy account on first LDAP login",defaultGroup:"Default group",defaultGroupNone:"— None (no fallback) —",defaultGroupHint:"Fallback group assigned when an LDAP user authenticates but is not listed in any mapped LDAP group. Leave empty to leave unmapped users without permissions.",groupMapping:"Group Mapping (JSON)",groupMappingHint:"Map LDAP group DNs to BamBuddy groups. Available groups: ",testConnection:"Test Connection",settingsSaved:"LDAP settings saved",errors:{serverRequired:"LDAP server URL is required",searchBaseRequired:"Search base DN is required",enableAuthFirst:"Enable authentication first",configureLdapFirst:"Save LDAP settings first"}},email:{smtpSettings:"SMTP Configuration",smtpHost:"SMTP Server",smtpPort:"SMTP Port",security:"Security",authentication:"Authentication",username:"Username",password:"Password",fromEmail:"From Email",fromName:"From Name",testConnection:"Test SMTP Connection",testRecipient:"Test Recipient Email",sendTest:"Send Test Email",sending:"Sending...",save:"Save Settings",saving:"Saving...",advancedAuth:"Advanced Authentication",advancedAuthEnabled:"Advanced Authentication is enabled",advancedAuthEnabledDesc:"Email-based user management features are active. New users will receive auto-generated passwords via email, and users can reset their passwords through the forgot password feature.",advancedAuthDisabled:"Advanced Authentication is disabled",advancedAuthDisabledDesc:"Enable advanced authentication to activate email-based features for user management.",enable:"Enable",disable:"Disable",feature1:"Passwords are auto-generated and emailed to new users",feature2:"Users can login with username or email",feature3:"Forgot password feature is available",feature4:"Admins can reset user passwords via email",errors:{requiredFields:"Please fill in all required fields",usernameRequired:"Username is required when authentication is enabled",enterTestEmail:"Please enter a test email address",smtpServerAndEmail:"Please fill in SMTP Server and From Email before testing",usernamePasswordRequired:"Username and Password are required when authentication is enabled",configureSmtpFirst:"Please configure and test SMTP settings first",enableAuthFirst:"Please enable authentication first to use email-based features."},success:{settingsSaved:"SMTP settings saved successfully"},securityOptions:{starttls:"STARTTLS (Port 587)",ssl:"SSL/TLS (Port 465)",none:"None (Port 25)"},authOptions:{enabled:"Enabled",disabled:"Disabled"}},appearance:"Appearance",notifications:"Notifications",smartPlugs:"Smart Plugs",spoolman:"Spoolman",updates:"Updates",language:"Language",languageDescription:"Select your preferred language",theme:"Theme",themeLight:"Light",themeDark:"Dark",themeSystem:"System",defaultView:"Default View",defaultViewDescription:"Page to show when opening the app",checkForUpdates:"Check for Updates",autoUpdate:"Auto Update",currentVersion:"Current Version",latestVersion:"Latest Version",upToDate:"You are up to date",updateAvailable:"Update available",notificationLanguage:"Notification Language",notificationLanguageDescription:"Language for push notifications",bedCooledThreshold:"Bed Cooled Threshold",bedCooledThresholdDescription:"Temperature below which the bed is considered cooled after a print",userNotificationsEnabled:"User Notifications",userNotificationsEnabledDescription:"Enable the user notifications menu and email notifications for print job events. Requires Advanced Authentication.",userNotificationsDisabledHint:"Enable Advanced Authentication to use user notifications.",notificationProviders:"Notification Providers",addProvider:"Add Provider",editProvider:"Edit Provider",providerType:"Provider Type",testNotification:"Test Notification",testSuccess:"Test notification sent successfully",testFailed:"Failed to send test notification",quietHours:"Quiet Hours",quietHoursDescription:"Do not disturb during these hours",quietHoursStart:"Start",quietHoursEnd:"End",events:{title:"Notification Events",printStart:"Print Started",printComplete:"Print Completed",printFailed:"Print Failed",printStopped:"Print Stopped",printProgress:"Progress Milestones",printProgressDescription:"Notify at 25%, 50%, 75%",printerOffline:"Printer Offline",printerError:"Printer Error",filamentLow:"Low Filament",maintenanceDue:"Maintenance Due",maintenanceDueDescription:"Notify when maintenance is needed"},smartPlug:{title:"Smart Plugs",add:"Add Smart Plug",edit:"Edit Smart Plug",name:"Name",ipAddress:"IP Address",linkedPrinter:"Linked Printer",autoOn:"Auto Power On",autoOnDescription:"Turn on when print starts",autoOff:"Auto Power Off",autoOffDescription:"Turn off after print completes",offDelay:"Off Delay",offDelayMinutes:"Minutes after print",offDelayTemp:"When nozzle below temperature",currentState:"Current State",turnOn:"Turn On",turnOff:"Turn Off"},filamentTracking:"Filament Tracking",filamentTrackingDesc:"Choose how to track your filament spools. You can use the built-in inventory or connect an external Spoolman server.",filamentChecks:"Filament checks",disableFilamentWarnings:"Disable filament warnings",disableFilamentWarningsDesc:"Don't show warnings about insufficient filament when printing or queueing",preferLowestFilament:"Prefer lowest remaining filament",preferLowestFilamentDesc:"When multiple spools match, use the one with the least filament remaining",trackingModeBuiltIn:"Built-in Inventory",trackingModeBuiltInDesc:"RFID auto-matching and usage tracking included",trackingModeSpoolmanDesc:"External filament management server",builtInFeatureRfid:"Automatically detects Bambu Lab RFID spools in AMS",builtInFeatureUsage:"Tracks filament consumption per print",builtInFeatureCatalog:"Manage spools, colors, and K-factor profiles",builtInFeatureThirdParty:"Third-party spools can be assigned to inventory spools",amsSyncButton:"Sync Weights from AMS",amsSyncTitle:"Sync Spool Weights from AMS",amsSyncMessage:"This will overwrite all inventory spool weights with the current AMS remain% values from connected printers. Use this to recover from corrupted weight data. Printers must be online.",amsSyncing:"Syncing...",amsSyncSuccess:"{{synced}} spool(s) synced, {{skipped}} skipped",amsSyncError:"Failed to sync weights from AMS",spoolmanUrl:"Spoolman URL",spoolmanUrlHint:"URL of your Spoolman server (e.g., http://localhost:7912)",spoolmanConnected:"Connected",spoolmanDisconnected:"Disconnected",status:"Status",connect:"Connect",disconnect:"Disconnect",howSyncWorks:"How Sync Works",syncInfoRfidOnly:"Only official Bambu Lab spools with RFID are synced",syncInfoAutoCreate:"New spools are auto-created in Spoolman on first sync",syncInfoThirdPartySkipped:"Non-Bambu Lab spools (third-party, refilled) are skipped",linkingExistingSpools:"Linking Existing Spools",linkingExistingSpoolsDesc:'To link existing Spoolman spools to your AMS, hover over an AMS slot and click "Link to Spoolman".',syncMode:"Sync Mode",syncModeAuto:"Automatic",syncModeManual:"Manual Only",syncModeAutoDesc:"AMS data syncs automatically when changes are detected",syncModeManualDesc:"Only sync when manually triggered",syncAmsData:"Sync AMS Data",syncAmsDataDesc:"Manually sync printer AMS data to Spoolman",allPrinters:"All Printers",noDefaultPrinter:"No default (ask each time)",sidebarOrder:"Sidebar order",saveThumbnails:"Save thumbnails",captureFinishPhoto:"Capture finish photo",noPrintersConfigured:"No printers configured",archiveMode:{always:"Always create archive entry",never:"Never create archive entry",ask:"Ask each time"},checkForUpdatesLabel:"Check for updates",checkPrinterFirmware:"Check printer firmware",includeBetaUpdates:"Include beta versions",includeBetaUpdatesDesc:"Notify about beta and prerelease versions when checking for updates",enableRetry:"Enable retry",homeAssistantDescription:"Control smart plugs via Home Assistant",environmentManagedLabel:"(Environment Managed)",autoEnabledViaEnv:"Automatically enabled via environment variables",urlFromEnvReadOnly:"Value set by HA_URL environment variable (read-only)",tokenFromEnvReadOnly:"Value set by HA_TOKEN environment variable (read-only)",mqttConnectedTo:"Connected to",prometheusDescription:"Expose printer data in Prometheus format",noSmartPlugsTitle:"No smart plugs configured",noSmartPlugsDescription:"Add a Tasmota-based smart plug to track energy usage and automate power control.",noProvidersTitle:"No providers configured",noProvidersDescription:"Add a provider to receive alerts.",noTemplatesAvailable:"No templates available. Restart the backend to seed default templates.",apiPermissionView:"View printer status and queue",apiPermissionEdit:"Add and remove items from print queue",apiKeysEmptyTitle:"No API keys",apiKeysEmptyDescription:"Create an API key to integrate with external services.",noUsersFound:"No users found",noGroupsFound:"No groups found",noGroupsAvailable:"No groups available",passwordsDoNotMatch:"Passwords do not match",systemGroupWarning:"System group names cannot be changed",authDisabledTitle:"Authentication is Disabled",authDisabledFeature1:"Require login to access the system",authDisabledFeature2:"Create multiple users with group-based permissions",authDisabledFeature3:"Control access with 50+ granular permissions",userHasCreated:"This user has created:",userItemsQuestion:"What would you like to do with these items?",deleteUserConfirm:"Are you sure you want to delete this user?",actionCannotBeUndone:"This action cannot be undone.",addFirstSmartPlug:"Add Your First Smart Plug",providers:"Providers",log:"Log",testAll:"Test All",testResults:"Test Results",testPassedCount:"{{count}} passed",testFailedCount:"{{count}} failed",messageTemplates:"Message Templates",messageTemplatesDescription:"Customize notification messages for each event.",apiKeys:"API Keys",apiKeysDescription:"Create API keys for external integrations and webhooks.",createKey:"Create Key",apiKeyCreated:"API Key Created Successfully",apiKeyCopyWarning:"Copy this key now - it won't be shown again!",useInApiBrowser:"Use in API Browser",createNewApiKey:"Create New API Key",keyName:"Key Name",keyNamePlaceholder:"e.g., Home Assistant, OctoPrint",readStatus:"Read Status",readStatusDescription:"View printer status and queue",manageQueue:"Manage Queue",manageQueueDescription:"Add and remove items from print queue",controlPrinter:"Control Printer",controlPrinterDescription:"Pause, resume, and stop prints",unnamedKey:"Unnamed Key",lastUsed:"Last used",read:"Read",control:"Control",createFirstKey:"Create Your First Key",webhookEndpoints:"Webhook Endpoints",webhookApiKeyHint:"Use your API key in the X-API-Key header.",webhook:{getAllStatus:"Get all printer status",getSpecificStatus:"Get specific printer status",addToQueue:"Add to print queue",pausePrint:"Pause print",resumePrint:"Resume print",stopPrint:"Stop print"},apiBrowser:"API Browser",apiBrowserDescription:"Explore and test all available API endpoints.",apiKeyForTesting:"API Key for Testing",apiKeyPlaceholder:"Paste your API key here to test authenticated endpoints...",apiKeyHint:"This key will be sent as X-API-Key header with requests.",deleteApiKeyTitle:"Delete API Key",deleteApiKeyMessage:"Are you sure you want to delete this API key? Any integrations using this key will stop working.",deleteKey:"Delete Key",amsDisplayThresholds:"AMS Display Thresholds",amsThresholdsDescription:"Configure color thresholds for AMS humidity and temperature indicators.",humidity:"Humidity",goodGreen:"Good (green)",fairOrange:"Fair (orange)",aboveFairBad:"Above fair threshold shows as red (bad)",fairAlsoDryingThreshold:"This threshold is also used to trigger auto-drying when enabled",temperature:"Temperature",goodBlue:"Good (blue)",aboveFairHot:"Above fair threshold shows as red (hot)",historyRetention:"History Retention",keepSensorHistory:"Keep sensor history for",historyRetentionDescription:"Older humidity and temperature data will be automatically deleted",defaultPrintOptions:"Default Print Options",defaultPrintOptionsDescription:"Set default values for print options when starting new prints. These can be overridden per print in the print dialog.",defaultBedLevelling:"Bed Levelling",defaultBedLevellingDesc:"Auto-level bed before print",defaultFlowCali:"Flow Calibration",defaultFlowCaliDesc:"Calibrate extrusion flow",defaultVibrationCali:"Vibration Calibration",defaultVibrationCaliDesc:"Reduce ringing artifacts",defaultLayerInspect:"First Layer Inspection",defaultLayerInspectDesc:"AI inspection of first layer",defaultTimelapse:"Timelapse",defaultTimelapseDesc:"Record timelapse video",staggeredStart:"Staggered Start",staggeredStartDescription:"Default group size and interval when staggering multi-printer batch starts. Can be overridden per batch in the print modal.",plateClear:"Plate-Clear Confirmation",requirePlateClear:"Require plate-clear confirmation",requirePlateClearDescription:'When enabled, the scheduler waits for per-printer plate-clear confirmation before starting queued prints on printers with finished jobs. Disabling this also hides the plate status badge and the "Mark plate as cleared" button on printer cards.',gcodeInjection:"G-code Injection",gcodeInjectionDescription:'Configure custom G-code to inject at the start and/or end of prints for auto-print systems like Farmloop, SwapMod, AutoClear, and Printflow 3D. Snippets are configured per printer model and applied when "Inject G-code" is enabled on a queue item.',gcodeInjectionNoPrinters:"No printers found. Add printers to configure G-code snippets.",gcodeStartLabel:"Start G-code",gcodeEndLabel:"End G-code",gcodeStartPlaceholder:"G-code prepended before the print starts...",gcodeEndPlaceholder:"G-code appended after the print ends...",staggerGroupSize:"Group size",staggerGroupSizeHelp:"Printers to start simultaneously per group",staggerInterval:"Interval (minutes)",staggerIntervalHelp:"Delay between each group starting",queueDrying:"Queue Auto-Drying",queueDryingDescription:"Automatically dry AMS filament when printer is idle between queued prints. Uses humidity threshold above to trigger drying.",queueDryingEnabled:"Enable auto-drying",queueDryingEnabledDescription:"Start AMS drying automatically when printer is idle and humidity is above threshold",queueDryingBlock:"Wait for drying to complete",queueDryingBlockDescription:"Block the print queue until drying finishes. When off, prints take priority over drying.",ambientDryingEnabled:"Ambient drying",ambientDryingEnabledDescription:"Automatically dry filament on idle printers when humidity exceeds threshold, even without queued prints.",dryingPresets:"Drying Presets",dryingPresetsDescription:"Temperature and duration per filament type. AMS 2 Pro uses lower temps, AMS-HT supports higher temps.",dryingFilament:"Filament",printModal:"Print Modal",expandCustomMapping:"Expand custom mapping by default",expandCustomMappingDescription:"When printing to multiple printers, show per-printer AMS mapping expanded",authentication:"Authentication",authEnabledDescription:"Your instance is secured with user authentication",authDisabledDescription:"Enable to require login and manage user access",authDisabledMessage:"Enable authentication to create user accounts, manage permissions, and secure your Bambuddy instance.",enableAuthentication:"Enable Authentication",currentUser:"Current User",changePassword:"Change Password",admin:"Admin",users:"Users",addUser:"Add User",groups:"Groups",addGroup:"Add Group",system:"System",noDescription:"No description",userCount:"{{count}} users",permissionCount:"{{count}} permissions",createUser:"Create User",username:"Username",enterUsername:"Enter username",password:"Password",enterPassword:"Enter password (min 6 characters)",confirmPassword:"Confirm Password",confirmPasswordPlaceholder:"Confirm password",viewReleaseOnGitHub:"View release on GitHub",turnAllPlugsOn:"Turn all plugs on",turnAllPlugsOff:"Turn all plugs off",clearNotificationLogs:"Clear Notification Logs",clearLogsMessage:"This will permanently delete all notification logs older than 30 days. This action cannot be undone.",clearLogs:"Clear Logs",resetUiPreferences:"Reset UI Preferences",resetUiPreferencesMessage:"This will reset all UI preferences to defaults: sidebar order, theme, dashboard layout, view modes, and sorting preferences. Your printers, archives, and server settings will NOT be affected. The page will reload after clearing.",resetPreferences:"Reset Preferences",deleteGroupTitle:"Delete Group",deleteGroupMessage:"Are you sure you want to delete this group? Users in this group will lose these permissions.",deleteGroup:"Delete Group",disableAuthenticationTitle:"Disable Authentication",disableAuthenticationMessage:"Are you sure you want to disable authentication? This will make your Bambuddy instance accessible without login. All users will remain in the database but authentication will be disabled.",disableAuthentication:"Disable Authentication",configureBambuddy:"Configure Bambuddy",systemDefault:"System Default",archiveSettings:"Archive Settings",newWindow:"New Window",embeddedOverlay:"Embedded Overlay",preferredSlicer:"Preferred Slicer",preferredSlicerDescription:"Choose which slicer application to open files with",externalCameras:"External Cameras",costTracking:"Cost Tracking",printsOnly:"Prints Only",totalConsumption:"Total Consumption",dataManagement:"Data Management",storageUsage:"Storage Usage",storageUsageDescription:"Breakdown of data usage by category",storageUsageTotal:"Total",storageUsageErrors:"Errors",storageUsageOtherBreakdown:"Other (includes static assets, scripts, and configuration files)",storageUsageSystem:"System",storageUsageData:"Data",storageUsageUnavailable:"Storage usage information unavailable",clearNotificationLogsDescription:"Delete notification logs older than 30 days",resetUiPreferencesDescription:"Reset sidebar order, theme, view modes, and layout preferences. Printers, archives, and settings are not affected.",enableHomeAssistant:"Enable Home Assistant",enableMqtt:"Enable MQTT",useTls:"Use TLS",enableMetricsEndpoint:"Enable Metrics Endpoint",availableMetrics:"Available Metrics",editUser:"Edit User",deleteUserTitle:"Delete User",groupName:"Group Name",leaveEmptyForAnonymous:"Leave empty for anonymous",leaveEmptyForNoAuth:"Leave empty for no authentication",enterNewPassword:"Enter new password",confirmNewPassword:"Confirm new password",enterGroupName:"Enter group name",enterDescriptionOptional:"Enter description (optional)",enterCurrentPassword:"Enter current password",enterNewPasswordMin6:"Enter new password (min 6 characters)",toast:{keyCopied:"Key copied to clipboard",copyFailed:"Failed to copy key",keyAddedToBrowser:"Key added to API Browser",clearLogsFailed:"Failed to clear logs",uiPreferencesReset:"UI preferences reset. Refreshing...",authDisabled:"Authentication disabled successfully",authDisableFailed:"Failed to disable authentication",apiKeyCreated:"API key created",apiKeyDeleted:"API key deleted",userCreated:"User created successfully",userUpdated:"User updated successfully",userDeleted:"User deleted successfully",groupCreated:"Group created successfully",groupUpdated:"Group updated successfully",groupDeleted:"Group deleted successfully",fillRequiredFields:"Please fill in all required fields",passwordsDoNotMatch:"Passwords do not match",passwordTooShort:"Password must be at least 6 characters",enterGroupName:"Please enter a group name",settingsSaved:"Settings saved",cameraSettingsSaved:"Camera settings saved",enterCameraUrl:"Please enter a camera URL",passwordChanged:"Password changed successfully",connectionFailed:"Connection failed",testFailed:"Test failed",cameraConnected:"Camera connected{{resolution}}"},testConnection:"Test Connection",catalog:{spoolCatalog:"Spool Catalog",spoolCatalogDescription:"Empty spool weights by brand/type. Used for automatic weight lookup when adding spools.",searchCatalog:"Search catalog...",addNewEntry:"Add New Entry",namePlaceholder:"Name (e.g., Bambu Lab - Plastic)",weight:"Weight",type:"Type",default:"Default",custom:"Custom",noMatch:"No entries match your search",empty:"No entries in catalog",deleteEntry:"Delete Entry",deleteConfirm:'Are you sure you want to delete "{{name}}"?',resetCatalog:"Reset Catalog",resetConfirm:"Reset catalog to defaults? This will remove all custom entries.",loadFailed:"Failed to load spool catalog",nameWeightRequired:"Name and weight are required",entryAdded:"Entry added",addFailed:"Failed to add entry",entryUpdated:"Entry updated",updateFailed:"Failed to update entry",entryDeleted:"Entry deleted",deleteFailed:"Failed to delete entry",resetSuccess:"Catalog reset to defaults",resetFailed:"Failed to reset catalog",exported:"Exported {{count}} entries",imported:"Imported {{added}} entries ({{skipped}} skipped)",importFailed:"Failed to import: invalid JSON format",exportTooltip:"Export catalog to JSON",importTooltip:"Import catalog from JSON",resetTooltip:"Reset to defaults",selectedCount:"{{count}} selected",deleteSelected:"Delete Selected",bulkDeleteConfirm:"Are you sure you want to delete {{count}} entries?",bulkDeleted:"Deleted {{count}} entries",bulkDeleteFailed:"Failed to delete entries"},colorCatalog:{title:"Color Catalog",description:"Filament colors by manufacturer/material. Used for automatic color lookup when adding spools.",searchColors:"Search colors...",allManufacturers:"All manufacturers",addNewColor:"Add New Color",manufacturer:"Manufacturer",colorName:"Color Name",hex:"Hex",materialOptional:"Material (optional)",showing:"Showing {{filtered}} of {{total}} colors",noMatch:"No colors match your search",empty:"No colors in catalog",deleteColor:"Delete Color",deleteConfirm:'Are you sure you want to delete "{{name}}"?',resetCatalog:"Reset Color Catalog",resetConfirm:"Reset catalog to defaults? This will remove all custom colors.",sync:"Sync",starting:"Starting...",syncTooltip:"Sync from FilamentColors.xyz (2000+ colors, may take a minute)",loadFailed:"Failed to load color catalog",fieldsRequired:"Manufacturer, color name, and hex color are required",colorAdded:"Color added",addFailed:"Failed to add color",colorUpdated:"Color updated",updateFailed:"Failed to update color",colorDeleted:"Color deleted",deleteFailed:"Failed to delete color",resetSuccess:"Color catalog reset to defaults",resetFailed:"Failed to reset catalog",syncUpToDate:"Already up to date ({{count}} colors checked)",syncComplete:"Added {{added}} new colors ({{skipped}} already existed)",syncError:"Sync error",syncFailed:"Failed to sync from FilamentColors.xyz",exported:"Exported {{count}} colors",imported:"Imported {{added}} colors ({{skipped}} skipped)",importFailed:"Failed to import: invalid JSON format",selectedCount:"{{count}} selected",deleteSelected:"Delete Selected",bulkDeleteConfirm:"Are you sure you want to delete {{count}} colors?",bulkDeleted:"Deleted {{count}} colors",bulkDeleteFailed:"Failed to delete colors"},dateFormat:"Date Format",dateFormatUs:"US (MM/DD/YYYY)",dateFormatEu:"EU (DD/MM/YYYY)",dateFormatIso:"ISO (YYYY-MM-DD)",timeFormat:"Time Format",timeFormat12:"12-hour (3:30 PM)",timeFormat24:"24-hour (15:30)",defaultPrinter:"Default Printer",defaultPrinterDescription:"Pre-select this printer for uploads, reprints, and other operations.",slicerBambuStudio:"Bambu Studio",slicerOrcaSlicer:"OrcaSlicer",sidebarOrderDescription:"Drag items in the sidebar to reorder. Reset to default order here.",setDefault:"Set Default",sidebarOrderSetDefaultHint:"Set default applies the current menu order to users who haven't customized theirs.",sidebarDefaultSet:"Default menu order has been set.",sidebarDefaultCleared:"Default menu order cleared.",sidebarDefaultFailed:"Failed to set default menu order.",reset:"Reset",darkMode:"Dark Mode",lightMode:"Light Mode",active:"(active)",background:"Background",accent:"Accent",style:"Style",bgNeutral:"Neutral",bgWarm:"Warm",bgCool:"Cool",bgOled:"OLED Black",bgSlate:"Slate Blue",bgForest:"Forest Green",accentGreen:"Green",accentTeal:"Teal",accentBlue:"Blue",accentOrange:"Orange",accentPurple:"Purple",accentRed:"Red",styleClassic:"Classic",styleGlow:"Glow",styleVibrant:"Vibrant",themeToggleHint:"Toggle between dark and light mode using the sun/moon icon in the sidebar.",autoArchivePrints:"Auto-archive prints",autoArchiveDescription:"Automatically save 3MF files when prints complete",saveThumbnailsDescription:"Extract and save preview images from 3MF files",captureFinishPhotoDescription:"Take a photo from printer camera when print completes",ffmpegNotInstalled:"ffmpeg not installed",ffmpegRequired:"Camera capture requires ffmpeg. Install it via brew install ffmpeg (macOS) or apt install ffmpeg (Linux).",camera:"Camera",cameraViewMode:"Camera View Mode",cameraOverlayDescription:"Camera opens in a resizable overlay on the main screen",cameraWindowDescription:"Camera opens in a separate browser window",externalCamerasDescription:"Configure external cameras to replace the built-in printer camera. Supports MJPEG streams, RTSP, HTTP snapshots, and USB cameras (V4L2). When enabled, the external camera is used for live view and finish photos.",cameraPlaceholderUsb:"Device path (/dev/video0)",cameraPlaceholderUrl:"Camera URL (rtsp://... or http://...)",cameraTypeMjpeg:"MJPEG Stream",cameraTypeRtsp:"RTSP Stream",cameraTypeSnapshot:"HTTP Snapshot",cameraTypeUsb:"USB Camera (V4L2)",cameraRotation:"Rotation",test:"Test",connected:"Connected",disconnected:"Disconnected",currency:"Currency",defaultFilamentCost:"Default filament cost (per kg)",electricityCost:"Electricity cost per kWh",energyDisplayMode:"Energy display mode",energyModePrintDescription:"Dashboard shows sum of energy used during prints",energyModeTotalDescription:"Dashboard shows lifetime energy from smart plugs",fileManager:"File Manager",createArchiveEntry:"Create Archive Entry When Printing",createArchiveEntryDescription:"When printing from File Manager, optionally create an archive entry",lowDiskSpaceWarning:"Low Disk Space Warning",lowDiskSpaceDescription:"Show warning when free disk space falls below this threshold",printerFirmware:"Printer Firmware",checkFirmwareDescription:"Check for printer firmware updates from Bambu Lab",bambuddySoftware:"Bambuddy Software",autoCheckDescription:"Automatically check for new versions on startup",checkNow:"Check now",updateAvailableVersion:"Update available: v{{version}}",releaseNotes:"Release Notes",updateViaDocker:"Update via Docker Compose:",installUpdate:"Install Update",latestVersionRunning:"You're running the latest version",failedToCheckUpdates:"Failed to check for updates: {{error}}",backupRestore:"Backup & Restore",backupRestoreDescription:"Export/import settings and configure GitHub backup",goToBackup:"Go to Backup",externalUrl:"External URL",externalUrlDescription:"The external URL where Bambuddy is accessible. Used for notification images and external integrations.",bambuddyUrl:"Bambuddy URL",externalUrlHint:"Include protocol and port (e.g., http://192.168.1.100:8000)",ftpRetry:"FTP Retry",ftpRetryDescription:"Retry FTP operations when printer WiFi is unreliable. Applies to 3MF downloads, print uploads, timelapse downloads, and firmware updates.",autoRetryDescription:"Automatically retry failed FTP operations",retryAttempts:"Retry attempts",retryDelay:"Retry delay",connectionTimeout:"Connection timeout",time_one:"{{count}} time",time_other:"{{count}} times",second_one:"{{count}} second",second_other:"{{count}} seconds",nSeconds:"{{count}} seconds",increaseForWeakWifi:"Increase for printers with weak WiFi",homeAssistant:"Home Assistant",homeAssistantFullDescription:"Connect to Home Assistant to control smart plugs via HA's REST API. Supports switch, light, input_boolean, and script entities.",homeAssistantUrl:"Home Assistant URL",longLivedAccessToken:"Long-Lived Access Token",haTokenHint:"Create a token in HA: Profile → Long-Lived Access Tokens → Create Token",connectionSuccessful:"Connection Successful",connectionFailed:"Connection Failed",haConnectionSuccess:"Successfully connected to Home Assistant.",haConnectionFailed:"Failed to connect to Home Assistant.",mqttPublishing:"MQTT Publishing",mqttDescription:"Publish BamBuddy events to an external MQTT broker for integration with Node-RED, Home Assistant, and other automation systems.",mqttEnableDescription:"Publish events to external MQTT broker",brokerHostname:"Broker hostname",port:"Port",usernameOptional:"Username (optional)",passwordOptional:"Password (optional)",topicPrefix:"Topic prefix",topicPrefixHint:"Topics will be: {{prefix}}/printers//status, etc.",prometheusMetrics:"Prometheus Metrics",prometheusEndpointDescription:"Expose printer metrics at /api/v1/metrics for Prometheus/Grafana monitoring.",bearerTokenOptional:"Bearer Token (optional)",bearerTokenHint:"If set, requests must include Authorization: Bearer ",metricsConnectionStatus:"Connection status",metricsPrinterState:"Printer state (idle/printing/etc)",metricsPrintProgress:"Print progress 0-100%",metricsBedTemp:"Bed temperature",metricsNozzleTemp:"Nozzle temperature",metricsPrintsTotal:"Total prints by result",metricsMore:"...and more (layers, fans, queue, filament usage)",smartPlugsDescription:"Connect smart plugs (Tasmota or Home Assistant) to automate power control and track energy usage for your printers.",allOn:"All On",allOff:"All Off",addSmartPlug:"Add Smart Plug",energySummary:"Energy Summary",currentPower:"Current Power",plugsOnline:"{{reachable}}/{{total}} plugs online",today:"Today",yesterday:"Yesterday",total:"Total",enablePlugsForSummary:"Enable plugs to see energy summary",addNotificationProvider:"Add",systemBadge:"(System)",creating:"Creating...",changing:"Changing...",deleteUserAndItems:"Delete user AND their items",deleteUserKeepItems:"Delete user, keep items (become ownerless)",ok:"OK",twoFa:{totpTitle:"Authenticator App (TOTP)",totpDesc:"Use an authenticator app like Google Authenticator, Aegis or Authy.",emailOtpTitle:"Email OTP",emailOtpDesc:"Send a one-time code to {{email}} when you log in.",emailOtpNoEmail:"Add an email address to your account to enable this method.",addEmailFirst:"Your account has no email address. Ask an admin to add one before enabling Email OTP.",setupTotp:"Set up Authenticator App",setupAuthApp:"Set up Authenticator App",setupInstructions:"Scan the QR code below with your authenticator app, then confirm with a code.",manualEntry:"Can't scan? Enter this secret manually:",scannedContinue:"I've scanned the code — continue",enterCodeToConfirm:"Enter the 6-digit code from your authenticator app to confirm setup.",activate:"Activate",disableTotp:"Disable Authenticator",disableConfirmHint:"Enter a valid TOTP code or a backup code to disable the authenticator.",totpDisabled:"Authenticator app disabled.",emailOtpEnabled:"Email OTP enabled.",emailOtpDisabled:"Email OTP disabled.",smtpRequired:"Please configure and test SMTP settings first.",invalidCode:"Invalid code. Please try again.",enableEmailOtp:"Enable Email OTP",disableEmailOtp:"Disable Email OTP",emailSetupEnterCode:"A verification code has been sent to your email address. Enter it below to confirm you own this inbox.",verifyAndEnable:"Verify & Enable",emailDisablePasswordHint:"Enter your account password to confirm disabling email OTP.",passwordPlaceholder:"Enter your password",backupCodesTitle:"Save your backup codes",backupCodesWarning:"Save these codes somewhere safe. Each code can only be used once and they will not be shown again.",backupCodesRemaining:"{{count}} backup codes remaining",savedCodes:"I've saved my codes",regenBackup:"Regenerate Backup Codes",regenBackupHint:"Enter your current TOTP code to generate 10 new backup codes. All existing backup codes will be invalidated.",newBackupCodes:"New backup codes",linkedAccounts:"Linked SSO Accounts",linkedAccountsDesc:"These external identity providers are linked to your account.",oidcUnlinked:"Account unlinked."},oidc:{title:"SSO / OIDC Providers",desc:"Configure OpenID Connect providers to allow single sign-on via external identity providers.",addProvider:"Add Provider",newProvider:"New Provider",empty:"No OIDC providers configured yet.",created:"Provider created.",updated:"Provider updated.",deleted:"Provider deleted.",deleteTitle:"Delete Provider",deleteMessage:'Delete "{{name}}"? All linked user accounts will be disconnected.',form:{name:"Display Name",issuerUrl:"Issuer URL",clientId:"Client ID",clientSecret:"Client Secret",scopes:"Scopes",iconUrl:"Icon URL (optional)",enabled:"Enabled",autoCreate:"Auto-create users",autoCreateDesc:"Automatically create a local account on first login.",autoLink:"Auto-link existing accounts",autoLinkDesc:"Link existing local accounts by matching email on first login.",secretHint:"leave blank to keep current",secretPlaceholder:"new secret"}}},notification:{printStarted:{title:"Print Started",body:"{{printer}}: {{filename}} has started printing"},printCompleted:{title:"Print Completed",body:"{{printer}}: {{filename}} completed successfully"},printFailed:{title:"Print Failed",body:"{{printer}}: {{filename}} has failed"},printStopped:{title:"Print Stopped",body:"{{printer}}: {{filename}} was stopped"},printProgress:{title:"Print Progress",body:"{{printer}}: {{filename}} is {{percent}}% complete"},printerOffline:{title:"Printer Offline",body:"{{printer}} is offline"},printerError:{title:"Printer Error",body:"{{printer}}: {{error}}"},filamentLow:{title:"Low Filament",body:"{{printer}}: Filament is running low"},maintenanceDue:{title:"Maintenance Due",body:"{{printer}}: {{items}} need attention"}},errors:{generic:"Something went wrong",networkError:"Network error. Please check your connection.",notFound:"Not found",unauthorized:"Unauthorized",serverError:"Server error",validationError:"Please check your input",printerConnectionFailed:"Failed to connect to printer",saveFailed:"Failed to save changes",deleteFailed:"Failed to delete",loadFailed:"Failed to load data"},hmsErrors:{title:"Errors - {{name}}",noErrors:"No errors",viewOnWiki:"View on Bambu Lab Wiki",clearInstructions:"Clear errors on the printer to dismiss them here.",clearErrors:"Clear Errors",clearSuccess:"HMS errors cleared",clearFailed:"Failed to clear HMS errors"},mqttDebug:{title:"MQTT Debug Log",searchPlaceholder:"Search topic or payload...",noMessages:"No messages logged yet",startLoggingHint:'Click "Start Logging" to begin capturing MQTT messages',noMessagesMatch:"No messages match your filter",adjustFilterHint:"Try adjusting your search or filter criteria",incoming:"Incoming",outgoing:"Outgoing",loggingStopped:"Logging stopped",loggingActive:"Logging active - messages will auto-refresh",startLogging:"Start Logging",stopLogging:"Stop Logging",clearLog:"Clear Log",topic:"Topic",timestamp:"Timestamp",direction:"Direction",all:"All"},printerFiles:{title:"File Manager",storageUsed:"Used:",storageFree:"Free:",filterPlaceholder:"Filter files...",deleteButton:"Delete",deleteFiles:"Delete {{count}} Files",deleteFileConfirm:'Delete "{{name}}"? This cannot be undone.',deleteFilesConfirm:"Delete {{count}} selected files? This cannot be undone.",noFiles:"No files on printer",loadingFiles:"Loading files...",failedToLoad:"Failed to load files",toast:{filesDeleted:"Deleted {{count}} file(s)",deleteFailed:"Delete failed: {{error}}"}},confirm:{delete:"Are you sure you want to delete this?",unsavedChanges:"You have unsaved changes. Are you sure you want to leave?",clearQueue:"Are you sure you want to clear the queue?"},login:{title:"Bambuddy Login",subtitle:"Sign in to your account",username:"Username",usernamePlaceholder:"Enter your username",usernameOrEmail:"Username or Email",usernameOrEmailPlaceholder:"Username or @ Email",password:"Password",passwordPlaceholder:"Enter your password",signIn:"Sign in",signingIn:"Logging in...",forgotPassword:"Forgot your password?",loginSuccess:"Logged in successfully",loginFailed:"Login failed",enterCredentials:"Please enter username and password",enterEmail:"Please enter your email address",oidcLoginFailed:"OIDC login failed",oidcErrors:{providerError:"The identity provider returned an error",missingParameters:"OIDC callback is missing required parameters",invalidState:"OIDC state is invalid or has already been used",stateExpired:"OIDC login session expired — please try again",providerNotFound:"OIDC provider not found",discoveryFailed:"Failed to fetch OIDC discovery document",invalidDiscovery:"OIDC discovery document is invalid",networkError:"Network error during OIDC token exchange",badResponse:"Unexpected response during OIDC token exchange",noIdToken:"OIDC provider did not return an ID token",validationFailed:"OIDC token validation failed",nonceMismatch:"OIDC nonce mismatch — possible replay attack",missingSubClaim:"OIDC token is missing the sub claim",noLinkedAccount:"No local account is linked to this OIDC identity",accountInactive:"Your account is inactive",userResolutionFailed:"Failed to resolve your account",internalError:"An internal error occurred during OIDC login",tokenExchangeFailed:"OIDC token exchange failed"},forgotPasswordTitle:"Forgot Password",forgotPasswordMessage:"If you've forgotten your password, please contact your system administrator to reset it.",forgotPasswordEmailMessage:"Enter your email address and we'll send you a new password.",emailAddress:"Email Address",emailPlaceholder:"your.email@example.com",cancel:"Cancel",sending:"Sending...",sendResetEmail:"Send Reset Email",howToReset:"How to reset your password:",resetStep1:"Contact your Bambuddy administrator",resetStep2:"Ask them to reset your password in User Management",resetStep3:"They can set a new temporary password for you",resetStep4:"Log in with the new password and change it in Settings",gotIt:"Got it",resetPassword:{title:"Set New Password",subtitle:"Enter and confirm your new password below.",newPassword:"New Password",newPasswordPlaceholder:"At least 8 characters",confirmPassword:"Confirm Password",confirmPasswordPlaceholder:"Repeat new password",saving:"Saving…",submit:"Set New Password",backToLogin:"Back to login",passwordsDoNotMatch:"Passwords do not match",passwordTooShort:"Password must be at least 8 characters",resetFailed:"Password reset failed. The link may have expired."},twoFA:{title:"Two-Factor Authentication",subtitle:"Your account is protected with 2FA. Enter the verification code below.",methodAuthenticator:"Authenticator App",methodEmail:"Email Code",methodBackup:"Backup Code",instructionsTotp:"Open your authenticator app and enter the 6-digit code for Bambuddy.",instructionsEmail:"A 6-digit code has been sent to your email address. It expires in 10 minutes.",instructionsEmailNotSent:"Click the button below to receive a verification code via email.",instructionsBackup:"Enter one of your 8-character backup recovery codes. Each code can only be used once.",sendCodeButton:"Send Code via Email",sendingCode:"Sending...",resendCode:"Resend code",codeLabel:"Verification Code",backupCodeLabel:"Backup Code",codePlaceholder:"000000",backupCodePlaceholder:"XXXXXXXX",verifyButton:"Verify",verifyingButton:"Verifying...",backToLogin:"← Back to login",orContinueWith:"or continue with",signInWith:"Sign in with {{provider}}",enterCode:"Please enter the verification code",sendCodeFailed:"Failed to send verification code",invalidCode:"Invalid code. Please try again."}},setup:{title:"Bambuddy Setup",subtitle:"Configure authentication for your Bambuddy instance",enableAuth:"Enable Authentication",adminAccount:"Admin Account",adminAccountDesc:"If admin users already exist, authentication will be enabled using the existing admin accounts. Leave the fields below empty to use existing admins, or enter new credentials to create a new admin user.",adminUsername:"Admin Username",adminPassword:"Admin Password",optionalIfAdminExists:"(optional if admin users exist)",adminUsernamePlaceholder:"Enter admin username (optional)",adminPasswordPlaceholder:"Enter admin password (optional)",confirmPassword:"Confirm Password",confirmPasswordPlaceholder:"Confirm admin password",settingUp:"Setting up...",completeSetup:"Complete Setup",toast:{authEnabledAdminCreated:"Authentication enabled and admin user created",authEnabledExistingAdmins:"Authentication enabled using existing admin users",setupCompleted:"Setup completed",enterBothCredentials:"Please enter both admin username and password, or leave both empty to use existing admin users",passwordsDoNotMatch:"Passwords do not match",passwordTooShort:"Password must be at least 6 characters"}},changePassword:{title:"Change Password",currentPassword:"Current Password",currentPasswordPlaceholder:"Enter current password",newPassword:"New Password",newPasswordPlaceholder:"Enter new password (min 6 characters)",confirmPassword:"Confirm New Password",confirmPasswordPlaceholder:"Confirm new password",passwordsDoNotMatch:"Passwords do not match",passwordTooShort:"Password must be at least 6 characters",changing:"Changing...",success:"Password changed successfully",failed:"Failed to change password"},plateAlert:{title:"Print Paused!",message:"Objects detected on build plate. The print has been automatically paused. Please clear the plate and resume the print.",understand:"I Understand"},camera:{title:"Camera View",invalidPrinterId:"Invalid printer ID",live:"Live",snapshot:"Snapshot",restartStream:"Restart stream",refreshSnapshot:"Refresh snapshot",fullscreen:"Fullscreen",exitFullscreen:"Exit fullscreen",connectingToCamera:"Connecting to camera...",capturingSnapshot:"Capturing snapshot...",connectionLost:"Connection lost",connectionFailed:"Camera connection failed",reconnecting:"Reconnecting in {{countdown}}s... (attempt {{attempt}}/{{max}})",reconnectNow:"Reconnect now",cameraUnavailable:"Camera unavailable",cameraUnavailableDesc:"Make sure the printer is powered on and connected.",noCamera:"No camera available",retry:"Retry",cameraStream:"Camera stream",zoomOut:"Zoom out",zoomIn:"Zoom in",resetZoom:"Reset zoom",recording:"Recording",startRecording:"Start Recording",stopRecording:"Stop Recording",chamberLight:"Toggle chamber light"},groups:{title:"Group Management",subtitle:"Manage permission groups for access control",backToSettings:"Back to Settings",createGroup:"Create Group",noPermission:"You do not have permission to access this page.",system:"System",noDescription:"No description",usersCount:"{{count}} users",permissionsCount:"{{count}} permissions",edit:"Edit",delete:"Delete",toast:{created:"Group created successfully",updated:"Group updated successfully",deleted:"Group deleted successfully",enterGroupName:"Please enter a group name"},modal:{editGroup:"Edit Group",createGroup:"Create Group",cancel:"Cancel",saving:"Saving...",creating:"Creating...",saveChanges:"Save Changes"},form:{groupName:"Group Name",groupNamePlaceholder:"Enter group name",systemGroupWarning:"System group names cannot be changed",description:"Description",descriptionPlaceholder:"Enter description (optional)",permissions:"Permissions ({{count}} selected)"},deleteModal:{title:"Delete Group",message:"Are you sure you want to delete this group? Users in this group will lose these permissions.",confirm:"Delete Group"},editor:{title:"Edit Group",createTitle:"Create Group",search:"Search permissions...",selectAll:"Select All",clearAll:"Clear All",permissionsSelected:"{{count}} selected",noResults:"No permissions match your search"}},users:{title:"User Management",subtitle:"Manage users and their access to your Bambuddy instance",backToSettings:"Back to Settings",createUser:"Create User",noPermission:"You do not have permission to access this page.",admin:"Admin",noGroups:"No groups",active:"Active",inactive:"Inactive",edit:"Edit",delete:"Delete",system:"System",noGroupsAvailable:"No groups available",table:{username:"Username",groups:"Groups",status:"Status",actions:"Actions"},toast:{created:"User created successfully",updated:"User updated successfully",deleted:"User deleted successfully",fillRequired:"Please fill in all required fields",passwordsDoNotMatch:"Passwords do not match",passwordTooShort:"Password must be at least 6 characters"},modal:{createUser:"Create User",editUser:"Edit User",cancel:"Cancel",creating:"Creating...",saving:"Saving...",saveChanges:"Save Changes",advancedAuthSubtitle:"with Advanced Authentication"},form:{username:"Username",usernamePlaceholder:"Enter username",email:"Email",emailPlaceholder:"user@example.com",password:"Password",passwordPlaceholder:"Enter password",confirmPassword:"Confirm Password",confirmPasswordPlaceholder:"Confirm password",newPasswordPlaceholder:"Enter new password",confirmNewPasswordPlaceholder:"Confirm new password",leaveBlankToKeep:"leave blank to keep current",groups:"Groups",optional:"optional",autoGeneratedPassword:"A secure password will be automatically generated and emailed to the user.",passwordManagedByAdvancedAuth:'Password is managed by Advanced Authentication. Use "Reset Password" to send a new password to the user via email.',resetPassword:"Reset Password",resettingPassword:"Resetting Password..."},deleteModal:{title:"Delete User",message:"Are you sure you want to delete this user? This action cannot be undone.",confirm:"Delete User"}},streamOverlay:{title:"Stream Overlay",invalidPrinterId:"Invalid printer ID",cameraStream:"Camera stream",progress:"Progress",eta:"ETA",printerIdle:"Printer is idle",printerOffline:"Printer offline",status:{printing:"Printing",paused:"Paused",finished:"Finished",failed:"Failed",idle:"Idle",unknown:"Unknown"}},profiles:{title:"Profiles",subtitle:"Manage your slicer presets and pressure advance calibrations",tabs:{cloud:"Cloud Profiles",local:"Local Profiles",kprofiles:"K-Profiles"},localProfiles:{title:"Local Profiles",subtitle:"Import and manage slicer presets from OrcaSlicer",import:"Import Profiles",importDesc:"Drop .bbscfg, .bbsflmt, .orca_filament, .zip, or .json files here",importing:"Importing...",search:"Search local presets...",noPresets:"No local presets yet",badge:"Local",edit:"Edit",delete:"Delete",cancel:"Cancel",deleteConfirmTitle:"Delete Preset",deleteConfirm:"Are you sure you want to delete this preset? This cannot be undone.",source:"Source",inheritsFrom:"Inherits",filamentType:"Type",vendor:"Vendor",compatiblePrinters:"Printers",nozzleTemp:"Nozzle Temp",cost:"Cost",density:"Density",pressureAdvance:"Pressure Advance",filament:"Filament",process:"Process",printer:"Printer",toast:{importSuccess:"{{count}} preset(s) imported",importSkipped:"{{count}} preset(s) skipped (duplicates)",importError:"{{count}} error(s) during import",deleted:"Preset deleted",updated:"Preset updated"}},connectedAs:"Connected as",logout:"Logout",noLogoutPermission:"You do not have permission to logout",failedToLoad:"Failed to load profiles",retry:"Retry",time:{justNow:"Just now",minsAgo:"{{count}}m ago",hoursAgo:"{{count}}h ago",daysAgo:"{{count}}d ago"},toast:{loggedOut:"Logged out"},login:{title:"Connect to Bambu Cloud",subtitle:"Sync your slicer presets across devices",email:"Email",password:"Password",region:"Region",regionGlobal:"Global",regionChina:"China",verificationCode:"Verification Code",totpCode:"Authenticator Code",checkEmail:"Check your email ({{email}}) for a 6-digit code",enterTotpHint:"Enter the 6-digit code from your authenticator app",accessToken:"Access Token",accessTokenHint:"Paste your Bambu Lab access token (from Bambu Studio)",back:"Back",loginButton:"Login",verifyButton:"Verify",setTokenButton:"Set Token",useToken:"Use access token instead",useEmail:"Login with email instead",toast:{loggedIn:"Logged in successfully",codeSent:"Verification code sent to your email",enterTotp:"Enter code from your authenticator app",tokenSet:"Token set successfully"}},presets:{myPreset:"My preset (editable)",duplicate:"Duplicate",editable:"Editable",failedToLoadDetails:"Failed to load preset details",deleteConfirm:"Delete this preset?",deleteWarning:'This will permanently delete "{{name}}" from Bambu Cloud. This cannot be undone.',noDuplicatePermission:"You do not have permission to duplicate presets",noEditPermission:"You do not have permission to edit presets",noDeletePermission:"You do not have permission to delete presets",types:{filament:"Filament preset",printer:"Printer preset",process:"Process preset"},toast:{deleted:"Preset deleted",created:"Preset created",updated:"Preset updated",duplicated:"Preset duplicated",fieldAdded:'Field "{{key}}" added',exported:"Preset exported"},baseLabel:"Base: {{name}}",currentLabel:"Current: {{name}}",newPreset:"New Preset",editPreset:"Edit Preset",duplicatePreset:"Duplicate Preset",createNewPreset:"Create New Preset",customizeSettings:"Customize settings for your new preset",compareWithBase:"Compare with base preset",compare:"Compare",basePreset:"Base Preset",selectBasePreset:"Select base preset...",presetName:"Preset Name",myCustomPreset:"My custom preset",inheritsFrom:"Inherits from",dropJsonToImport:"Drop JSON to import",tabs:{common:"Common",allFields:"All Fields"},availableFields:"Available Fields",searchFieldsPlaceholder:"Search fields...",noMatchingFields:"No matching fields",allFieldsAdded:"All fields added",addCustomField:"Add custom field",yourOverrides:"Your Overrides",noOverridesYet:"No overrides yet",clickFieldsToAdd:"Click fields on the left to add them",saveAsTemplate:"Save as template",jsonTip:"Tip: Drag & drop a .json file anywhere on this modal to import settings"},cloudView:{searchPlaceholder:"Search presets...",templates:"Templates",refresh:"Refresh",newPreset:"New Preset",clearFilters:"Clear filters",compareMode:"Compare Mode",selectAnotherPreset:"Select another {{type}} preset",clickTwoPresets:"Click two presets of the same type to compare",selectFirst:"1. Select first",selectSecond:"2. Select second",compareNow:"Compare Now",lastSynced:"Last synced:",showingCount:"Showing {{showing}} of {{total}} presets",noPresetsFound:"No presets found",columns:{filament:"Filament",process:"Process",printer:"Printer"},noFilamentPresets:"No filament presets",noProcessPresets:"No process presets",noPrinterPresets:"No printer presets",filters:{type:"Type",owner:"Owner",printer:"Printer",nozzle:"Nozzle",filament:"Filament",layer:"Layer",all:"All",myPresets:"My Presets",builtIn:"Built-in",process:"Process"},noTemplatesPermission:"You do not have permission to manage templates",noRefreshPermission:"You do not have permission to refresh profiles",noCreatePermission:"You do not have permission to create presets"},templates:{title:"Quick Templates",noTemplates:"No templates yet",createFirst:"Create templates from the preset editor",typeFilter:"Type:",deleteTitle:"Delete Template",deleteWarning:"This action cannot be undone",deleteConfirm:'Are you sure you want to delete "{{name}}"?',namePlaceholder:"Template name",descriptionPlaceholder:"Description",settingsJson:"Settings (JSON)",fieldsCount:"{{count}} fields",shownInModals:"Shown in modals",hiddenInModals:"Hidden in modals",apply:"Apply",toast:{deleted:"Template deleted",updated:"Template updated",created:"Template created",applied:"Template applied"}}},support:{debugLoggingActive:"Debug logging is active",manageLogs:"Manage",collectItem7:"Printer connectivity and firmware versions",collectItem8:"Integration status (Spoolman, MQTT, HA)",collectItem9:"Network interfaces (subnets only)",collectItem10:"Python package versions",collectItem11:"Database health checks",collectItem12:"Docker environment details"},fileManager:{title:"File Manager",subtitle:"Organize and manage your print files",uploadFiles:"Upload Files",newFolder:"New Folder",folderName:"Folder Name",folderNamePlaceholder:"e.g., Functional Parts",renameFile:"Rename File",renameFolder:"Rename Folder",moveFiles:"Move {{count}} File(s)",rootNoFolder:"Root (No Folder)",current:"current",linkFolder:"Link Folder",linkFolderDescription:'Link "{{name}}" to a project or archive for quick access.',project:"Project",archive:"Archive",noProjectsFound:"No projects found",noArchivesFound:"No archives found",unlink:"Unlink",link:"Link",dragDropFiles:"Drag & drop files here",dropFilesHere:"Drop files here",orClickToBrowse:"or click to browse",allFileTypesSupported:"All file types supported. ZIP files will be extracted.",zipFilesDetected:"ZIP files detected",zipExtractOptions:"ZIP files will be extracted. Choose how to handle folder structure:",preserveZipStructure:"Preserve folder structure from ZIP",createFolderFromZip:"Create folder from ZIP filename",stlThumbnailGeneration:"STL thumbnail generation",zipMayContainStl:"ZIP files may contain STL files. Thumbnails can be generated during extraction.",thumbnailsCanBeGenerated:"Thumbnails can be generated for STL files. Large models may take longer to process.",generateThumbnailsForStl:"Generate thumbnails for STL files",threemfDetected:"3MF files detected",threemfExtractionInfo:"Printer model, material, color, and print settings will be automatically extracted from 3MF files.",willBeExtracted:"Will be extracted",filesExtracted:"{{count}} files extracted",uploadComplete:"Upload complete: {{succeeded}} succeeded",uploadFailed:"Upload failed",zipFilesFailed:"{{count}} files failed",uploading:"Uploading...",changeLink:"Change Link...",linkTo:"Link to...",linkToProjectOrArchive:"Link to project or archive",addToQueue:"Add to Queue",schedulePrint:"Schedule",generateThumbnail:"Generate Thumbnail",generateThumbnails:"Generate Thumbnails",generateThumbnailsForMissing:"Generate thumbnails for STL files missing them",gridView:"Grid view",listView:"List view",lowDiskSpaceWarning:"Low disk space warning",lowDiskSpaceDetails:"Only {{free}} free of {{total}} total. Threshold is set to {{threshold}} GB in settings.",files:"Files",folders:"Folders",size:"Size",free:"Free",allFiles:"All Files",wrap:"Wrap",enableTextWrapping:"Enable text wrapping",disableTextWrapping:"Disable text wrapping",collapse:"Collapse",collapseFoldersByDefault:"Collapse folders by default",expandFoldersByDefault:"Expand folders by default",dragToResizeTooltip:"Drag to resize, double-click to reset",searchFiles:"Search files...",allTypes:"All types",prints:"Prints",ascending:"Ascending",descending:"Descending",resultsCount:"{{showing}} of {{total}} files",selectAll:"Select All",deselectAll:"Deselect All",selected:"{{count}} selected",adding:"Adding...",loadingFiles:"Loading files...",folderIsEmpty:"Folder is empty",noFilesYet:"No files yet",folderEmptyDescription:"Upload files or move files into this folder to get started.",noFilesDescription:"Upload files to start organizing your print-related files.",noMatchingFiles:"No matching files",noMatchingFilesDescription:"No files match your current search or filter criteria.",clearFilters:"Clear filters",printedCount:"Printed {{count}}x",uploadedBy:"Uploaded By",deleteFolder:"Delete Folder",deleteFile:"Delete File",deleteFilesCount:"Delete {{count}} Files",deleteFolderConfirm:"Are you sure you want to delete this folder? All files inside will also be deleted.",deleteFileConfirm:"Are you sure you want to delete this file?",deleteFilesConfirm:"Are you sure you want to delete {{count}} selected files? This action cannot be undone.",deleting:"Deleting...",noPermissionRenameFolder:"You do not have permission to rename folders",noPermissionLinkFolder:"You do not have permission to link folders",noPermissionDeleteFolder:"You do not have permission to delete folders",noPermissionPrint:"You do not have permission to print",noPermissionAddToQueue:"You do not have permission to add to queue",noPermissionDownload:"You do not have permission to download files",noPermissionRenameFile:"You do not have permission to rename this file",noPermissionGenerateThumbnail:"You do not have permission to generate thumbnails",noPermissionDeleteFile:"You do not have permission to delete this file",noPermissionCreateFolder:"You do not have permission to create folders",noPermissionUpload:"You do not have permission to upload files",noPermissionMoveFiles:"You do not have permission to move files",noPermissionDeleteFiles:"You do not have permission to delete files",linkExternal:"Link External",linkExternalFolder:"Link External Folder",linkExternalFolderDescription:"Mount a host directory (NAS, USB, network share) into the File Manager. Files are not copied — they are accessed directly from the original path.",externalFolderNamePlaceholder:"e.g., NAS Prints",externalPath:"Host Path",externalPathHelp:"Absolute path to the directory on the Docker host. Must be bind-mounted into the container.",readOnly:"Read Only",readOnlyHelp:"prevents uploads and deletions",showHiddenFiles:"Show hidden files (dotfiles)",externalFolder:"External Folder",scanFolder:"Scan",toast:{folderCreated:"Folder created",folderDeleted:"Folder deleted",fileDeleted:"File deleted",filesDeleted:"Deleted {{count}} files",filesMoved:"Files moved",folderLinked:"Folder linked",folderUnlinked:"Folder unlinked",externalFolderLinked:"External folder linked and scanned",folderScanned:"Scan complete: {{added}} added, {{removed}} removed",addedToQueue:"Added {{count}} file(s) to queue",addedToQueuePartial:"Added {{added}} file(s), {{failed}} failed",failedToAddToQueue:"Failed to add files: {{error}}",fileRenamed:"File renamed",folderRenamed:"Folder renamed",thumbnailsGenerated:"Generated {{count}} thumbnail(s)",thumbnailsGeneratedPartial:"Generated {{succeeded}} thumbnail(s), {{failed}} failed",noStlMissingThumbnails:"No STL files missing thumbnails",failedToGenerateThumbnails:"Failed to generate thumbnails: {{error}}",thumbnailGenerated:"Thumbnail generated",failedToGenerateThumbnail:"Failed to generate thumbnail: {{error}}"}},projects:{title:"Projects",subtitle:"Organize and track your 3D printing projects",newProject:"New Project",editProject:"Edit Project",deleteProject:"Delete Project",projectName:"Project Name",description:"Description",noProjects:"No projects yet",noProjectsFiltered:"No {{status}} projects",noProjectsFilteredHelp:"You don't have any {{status}} projects. Projects will appear here when their status changes.",createFirst:"Create your first project to start organizing related prints, tracking progress, and managing your builds.",createFirstButton:"Create Your First Project",create:"Create",files:"Files",prints:"Prints",plates:"plates",parts:"parts",lastModified:"Last Modified",deleteConfirm:"Are you sure you want to delete this project? Archives and queue items will be unlinked but not deleted.",addFiles:"Add Files",removeFile:"Remove File",viewDetails:"View Details",namePlaceholder:"e.g., Voron 2.4 Build",descriptionPlaceholder:"Optional description...",color:"Color",targetPlates:"Target Plates",targetPlatesPlaceholder:"e.g., 25",targetPlatesHelp:"Number of print jobs",targetParts:"Target Parts",targetPartsPlaceholder:"e.g., 150",targetPartsHelp:"Total objects needed",tagsLabel:"Tags (comma-separated)",tagsPlaceholder:"e.g., voron, functional, gift",dueDate:"Due Date",priority:"Priority",priorityLow:"Low",priorityNormal:"Normal",priorityHigh:"High",priorityUrgent:"Urgent",statusActive:"Active",statusCompleted:"Completed",statusArchived:"Archived",done:"Done",completed:"completed",failed:"failed",inQueue:"in queue",noPrintsYet:"No prints yet",printJobs:"Print jobs (plates)",partsPrinted:"Parts printed",failedParts:"Failed parts",import:"Import",export:"Export",importProject:"Import project",exportAll:"Export all projects",loading:"Loading projects...",noEditPermission:"You do not have permission to edit projects",noDeletePermission:"You do not have permission to delete projects",noCreatePermission:"You do not have permission to create projects",noImportPermission:"You do not have permission to import projects",noExportPermission:"You do not have permission to export projects",toast:{created:"Project created",updated:"Project updated",deleted:"Project deleted",imported:"Project imported",multipleImported:"{{count}} projects imported",importFailed:"Import failed",exported:"Projects exported (metadata only)"}},projectDetail:{notFound:"Project not found",backToProjects:"Back to Projects",export:"Export",exportProject:"Export project",noExportPermission:"You do not have permission to export projects",noEditPermission:"You do not have permission to edit projects",partOf:"Part of:",priorityLabel:"Priority:",noPrints:"No prints in this project yet",status:{active:"Active",completed:"Completed",archived:"Archived"},priority:{low:"Low",normal:"Normal",high:"High",urgent:"Urgent"},dueDate:{overdue:"Overdue",today:"Due today",daysLeft:"{{count}} days left"},progress:{platesProgress:"Plates Progress",partsProgress:"Parts Progress",printJobs:"print jobs",parts:"parts",percentComplete:"{{percent}}% complete",remaining:"{{count}} remaining"},stats:{printJobs:"Print Jobs",total:"total",failed:"{{count}} failed",partsPrinted:"{{count}} parts printed",printTime:"Print Time",filamentUsed:"Filament Used"},cost:{title:"Cost Tracking",filamentCost:"Filament Cost",energy:"Energy",totalCost:"Total Cost",total:"Total",includesBom:"incl. BOM",budget:"Budget",remaining:"Remaining"},subProjects:{title:"Sub-projects ({{count}})"},notes:{title:"Notes",noEditPermission:"You do not have permission to edit notes",placeholder:"Add notes about this project...",empty:"No notes yet. Click Edit to add notes."},files:{title:"Files",linkFolders:"Link folders from the File Manager",forQuickAccess:"to this project for quick access.",fileCount:"{{count}} file(s)",empty:"No folders linked. Go to File Manager and link a folder to this project.",noFiles:"No files in this folder.",print:"Print Now",addToQueue:"Add to Queue"},bom:{title:"Bill of Materials",acquired:"{{completed}}/{{total}} acquired",showAll:"Show all",hideDone:"Hide done",addPart:"Add Part",noAddPermission:"You do not have permission to add parts",partNamePlaceholder:"Part name (e.g., M3x8 screws)",partName:"Part name",qty:"Qty",price:"Price ({{currency}})",sourcingUrlPlaceholder:"Sourcing URL (optional)",remarksPlaceholder:"Remarks (optional)",deletePart:"Delete Part",deleteConfirm:'Are you sure you want to delete "{{name}}"?',noUpdatePermission:"You do not have permission to update parts",noEditPermission:"You do not have permission to edit parts",noDeletePermission:"You do not have permission to delete parts",totalCost:"Total cost:",empty:"No parts in the bill of materials. Add hardware, electronics, or other components to track what needs to be sourced."},timeline:{title:"Activity Timeline",empty:"No activity yet."},template:{saveAsTemplate:"Save as Template",noCreatePermission:"You do not have permission to create templates"},queue:{title:"Queue",viewAll:"View all",printing:"{{count}} printing",queued:"{{count}} queued"},prints:{title:"Prints ({{count}})"},toast:{projectUpdated:"Project updated",partAdded:"Part added",partRemoved:"Part removed",exportFailed:"Export failed",projectExported:"Project exported",templateCreated:"Template created"}},system:{title:"System Information",version:"Version",uptime:"Uptime",cpuUsage:"CPU Usage",memoryUsage:"Memory Usage",diskUsage:"Disk Usage",networkInfo:"Network Info",logs:"Logs",debugMode:"Debug Mode",enableDebug:"Enable Debug Logging",disableDebug:"Disable Debug Logging",downloadLogs:"Download Logs",clearLogs:"Clear Logs",dockerInfo:"Docker Info",containerName:"Container Name",imageName:"Image Name",platform:"Platform",architecture:"Architecture"},library:{title:"Filament Library",addFilament:"Add Filament",editFilament:"Edit Filament",deleteFilament:"Delete Filament",vendor:"Vendor",material:"Material",color:"Color",kFactor:"K Factor",temperature:"Temperature",noFilaments:"No filaments in library",deleteConfirm:"Are you sure you want to delete this filament?",importFromPrinter:"Import from Printer",exportToFile:"Export to File"},spoolman:{title:"Spoolman Integration",enabled:"Spoolman Enabled",url:"Spoolman URL",connected:"Connected",disconnected:"Not Connected",testConnection:"Test Connection",sync:"Sync",syncing:"Syncing...",lastSync:"Last Sync",linkToSpoolman:"Link to Spoolman",openInSpoolman:"Open in Spoolman",unlinkSpool:"Unlink Spool",unlinkConfirmTitle:"Unlink Spool?",unlinkConfirmMessage:"This will disconnect the spool from Spoolman. The spool data in Spoolman will remain unchanged.",selectSpool:"Select Spool",noUnlinkedSpools:"No unlinked spools available",linkSuccess:"Spool linked to Spoolman successfully",linkFailed:"Failed to link spool",unlinkSuccess:"Spool unlinked from Spoolman successfully",unlinkFailed:"Failed to unlink spool",spoolId:"Spool ID",fillSourceLabel:"(Spoolman)",weight:"Weight",remaining:"Remaining",disableWeightSync:"Disable AMS Estimated Weight Sync",disableWeightSyncDesc:"Don't update remaining capacity from AMS estimates. Use this if you prefer Spoolman's usage tracking over AMS percentage-based estimates. New spools will still use the AMS estimate as their initial weight.",reportPartialUsage:"Report Partial Usage for Failed Prints",reportPartialUsageDesc:"When a print fails or is cancelled, report the estimated filament used up to that point based on layer progress."},inventory:{title:"Spool Inventory",addSpool:"Add Spool",editSpool:"Edit Spool",material:"Material",selectMaterial:"Select material...",subtype:"Subtype",brand:"Brand",searchBrand:"Search brand...",useCustomBrand:'Use "{{brand}}"',useCustomMaterial:"Use custom material: {{material}}",colorName:"Color Name",colorNamePlaceholder:"Jade White, Fire Red...",color:"Color",hexColor:"Hex Color",pickColor:"Pick custom color",labelWeight:"Label Weight",coreWeight:"Empty Spool Weight",searchSpoolWeight:"Search spool weight...",weightUsed:"Used",currentWeight:"Remaining Weight",measuredWeight:"Measured Weight",spoolName:"Spool",costPerKg:"Cost per kg",measuredWeightError:"Measured weight must be between {{min}}g and {{max}}g.",slicerFilament:"Slicer Filament",slicerFilamentName:"Slicer Preset Name",slicerPreset:"Slicer Preset",searchPresets:"Search filament presets...",selectedPreset:"Selected",noPresetsFound:"No presets found",tempOverrides:"Temperature Overrides",note:"Note",notePlaceholder:"Any additional notes about this spool...",archive:"Archive",restore:"Restore",noSpools:"No spools yet. Add your first spool to get started.",noManualSpools:"No manually added spools available. Add a spool to your inventory first.",kProfiles:"K-Profiles",addKProfile:"Add K-Profile",assignSpool:"Assign Spool",unassignSpool:"Unassign",assignSuccess:"Spool assigned and AMS slot configured",assignFailed:"Failed to assign spool",selectSpool:"Select a spool to assign to this slot",assigned:"Assigned",assigning:"Assigning...",searchSpools:"Search spools...",showAllSpools:"Show all spools",allMaterials:"All Materials",filterByBrand:"Filter by brand...",showArchived:"Show archived",quickAdd:"Quick Add (Stock)",quantity:"Quantity",stock:"Stock",configured:"Configured",spoolsCreated:"{{count}} spools created",spoolCreated:"Spool created",spoolUpdated:"Spool updated",spoolDeleted:"Spool deleted",spoolArchived:"Spool archived",spoolRestored:"Spool restored",deleteConfirm:"Are you sure you want to delete this spool? This cannot be undone.",archiveConfirm:"Are you sure you want to archive this spool?",advancedSettings:"Advanced Settings",filamentInfoTab:"Filament Info",paProfileTab:"PA Profile",filamentInfo:"Filament",additional:"Additional",loadingPresets:"Loading cloud presets...",cloudConnected:"Cloud connected",cloudNotConnected:"Cloud not connected (using defaults)",recentColors:"Recent",searchColors:"Search colors...",searchResults:"Search results",allColors:"All colors",commonColors:"Common colors",showLess:"Show less",showAll:"Show all",noColorsFound:"No colors match your search",noResults:"No matches found",selectMaterialFirst:"Please select a material first in the Filament Info tab.",noPrintersConfigured:"No printers configured. Add printers to use PA profiles.",matchingFilter:"Matching",anyBrand:"Any brand",anyVariant:"Any variant",autoSelect:"Auto-select",matches:"matches",match:"match",noMatches:"No matches",connected:"Connected",offline:"Offline",printerOffline:"Printer is offline. Connect to view calibration profiles.",noKProfilesMatch:"No K-profiles match the selected filament.",leftNozzle:"Left Nozzle",rightNozzle:"Right Nozzle",profilesSelected:"calibration profile(s) selected",totalInventory:"Total Inventory",totalConsumed:"Total Consumed",byMaterial:"By Material",inPrinter:"In Printer",lowStock:"Low Stock",sinceTracking:"Since tracking started",loadedInAms:"Loaded in AMS/Ext",remaining:"Remaining",weightCheck:"Weight Check",lastWeighed:"Last weighed",neverWeighed:"Never weighed",search:"Search spools...",showing:"Showing",to:"to",of:"of",show:"Show",spools:"spools",spool:"spool",page:"Page",noSpoolsMatch:"No results found",noSpoolsMatchDesc:"Try adjusting your search or filters to find what you're looking for.",active:"Active",archived:"Archived",all:"All",used:"Used",new:"New",clearFilters:"Clear filters",table:"Table",cards:"Cards",net:"Net",groupSimilar:"Group",groupedSpools:"{{count}} identical spools",groupedRows:"rows",columns:"Columns",configureColumns:"Configure Columns",configureColumnsDesc:"Drag to reorder columns or use arrows. Toggle visibility with the eye icon.",visible:"visible",reset:"Reset",cancel:"Cancel",applyChanges:"Apply Changes",moveUp:"Move up",moveDown:"Move down",hideColumn:"Hide column",showColumn:"Show column",linkToSpool:"Link to Spool",tagLinked:"Tag linked to spool",tagLinkFailed:"Failed to link tag",tagAlreadyLinked:"Tag already linked to another spool",unknownTag:"Unknown RFID tag detected",usageHistory:"Usage History",noUsageHistory:"No usage recorded yet",printName:"Print Name",weightConsumed:"Weight Consumed",clearHistory:"Clear",historyCleared:"Usage history cleared",fillSourceLabel:"(Inv)",lowStockThresholdError:"Threshold must be between 0.1 and 99.9",assignMismatchTitle:"Material mismatch",assignMismatchMessage:'The selected spool material "{{spoolMaterial}}" does not match the tray material "{{trayMaterial}}" for {{location}}. Assign anyway?',assignMismatchConfirm:"Assign Anyway",assignPartialMismatchMessage:'The spool material "{{spoolMaterial}}" is similar to but not exactly matching "{{trayMaterial}}" in {{location}}. Do you want to proceed?',assignProfileMismatchMessage:'The spool profile "{{spoolProfile}}" does not match the tray profile "{{trayProfile}}" in {{location}}. Do you want to proceed?'},timelapse:{title:"Timelapse",create:"Create Timelapse",download:"Download",delete:"Delete",preview:"Preview",frameRate:"Frame Rate",quality:"Quality",processing:"Processing...",noTimelapses:"No timelapses available"},ams:{title:"AMS",slot:"Slot",empty:"Empty",emptySlot:"Empty slot",unknown:"Unknown",humidity:"Humidity",temperature:"Temperature",filamentType:"Filament Type",filamentColor:"Color",remaining:"Remaining",history:"AMS History",noHistory:"No history available",configureSlot:"Configure Slot",externalSpool:"External Spool",profile:"Profile",kFactor:"K Factor",fill:"Fill",configure:"Configure",used:"used",remainingUnit:"remaining"},printModal:{title:"Start Print",selectPrinter:"Select Printer",selectPlate:"Select Plate",filamentMapping:"Filament Mapping",totalCost:"Total cost:",slotRemainingShort:" - {{grams}}g left",printSettings:"Print Settings",bedLeveling:"Bed Leveling",flowCalibration:"Flow Calibration",vibrationCalibration:"Vibration Calibration",layerInspection:"First Layer Inspection",timelapse:"Timelapse",startPrint:"Start Print",addToQueue:"Add to Queue",cancel:"Cancel",noPrintersAvailable:"No printers available",printerBusy:"Printer is busy",printerOffline:"Printer is offline",sameTypeDifferentColor:"Same type, different color",filamentTypeNotLoaded:"Filament type not loaded",openCalendar:"Open calendar",leftNozzle:"L",rightNozzle:"R",leftNozzleTooltip:"Left nozzle",rightNozzleTooltip:"Right nozzle",filamentOverride:"Filament Override",filamentOverrideHint:"Optionally override filaments for model-based assignment. The scheduler will match against your selected filaments instead of the original 3MF values.",originalFilament:"Original",overrideWith:"Override with",resetToOriginal:"Reset to original",insufficientFilamentTitle:"Not enough filament",insufficientFilamentMessage:"Some assigned spools have less filament remaining than this print needs:",insufficientFilamentLine:"{{printer}} - {{slot}}: needs {{required}}g, remaining {{remaining}}g",printAnyway:"Print anyway",forceColorMatch:"Force color match",staggerPrinterStarts:"Stagger printer starts",staggerGroupSize:"Group size",staggerInterval:"Interval (min)",staggerPreview:"{{printers}} printers → {{groups}} groups of {{size}}, starting every {{interval}} min",staggerLastGroup:"last group: {{count}}",staggerTotal:"total: {{minutes}} min",staggerToPrinters:"Stagger to {{count}} printers",gcodeInjection:"Inject auto-print G-code"},backup:{title:"Backup & Restore",createBackup:"Create Backup",restoreBackup:"Restore Backup",restoreDescription:"Replace all data from a backup file",downloadBackup:"Download Backup",uploadBackup:"Upload Backup",lastBackup:"Last Backup",autoBackup:"Auto Backup",backupNow:"Backup Now",restoreWarning:"Warning: Restoring a backup will overwrite all current data.",includeArchives:"Include Archives",includeSettings:"Include Settings",includeProfiles:"Include Profiles",backupSuccess:"Backup created successfully",restoreSuccess:"Backup restored successfully",backupFailed:"Backup failed",restoreFailed:"Restore failed",restoreNote:"Virtual Printer will be stopped during restore",githubBackup:"GitHub Backup",enabled:"Enabled",cloudLoginRequired:"Bambu Cloud login required. Sign in under Profiles → Cloud Profiles to enable GitHub backup.",cloudLoginRequiredShort:"Cloud login required",githubDescription:"Automatically sync your profiles to a private GitHub repository for backup and version history.",repositoryUrl:"Repository URL",personalAccessToken:"Personal Access Token",tokenSaved:"(saved)",enterNewToken:"Enter new token to update",tokenHint:"Fine-grained token with Contents read/write permission",branch:"Branch",manualOnly:"Manual only",hourly:"Hourly",daily:"Daily",weekly:"Weekly",includeInBackup:"Include in backup",kProfiles:"K-Profiles",kProfilesDescription:"Pressure advance calibration from connected printers",noPrintersConnected:"No printers connected",printersConnected:"{{connected}}/{{total}} connected",cloudProfiles:"Cloud Profiles",cloudProfilesDescription:"Filament, printer, and process presets from Bambu Cloud",appSettings:"App Settings",appSettingsDescription:"Bambuddy configuration (complete database)",spoolInventory:"Spool Inventory",spoolInventoryDescription:"Filament spools, usage history, and cost tracking",printArchives:"Print Archives",printArchivesDescription:"Print history metadata (no gcode/3MF files)",lastBackupAt:"Last backup:",noBackupsYet:"No backups yet",next:"Next:",startingBackup:"Starting backup...",test:"Test",enableBackup:"Enable Backup",testConnection:"Test Connection",enterRepoUrl:"Enter repository URL",enterRepoAndToken:"Enter repository URL and access token",repoRequired:"Repository URL is required",tokenRequired:"Access token is required",githubBackupEnabled:"GitHub backup enabled",tokenUpdated:"Token updated",settingsSaved:"Settings saved",failedToSave:"Failed to save: {{message}}",backupCompleteFiles:"Backup complete - {{count}} files updated",backupSkippedNoChanges:"Backup skipped - no changes",backupFailed2:"Backup failed: {{message}}",clearedLogs:"Cleared {{count}} logs",failedToClearLogs:"Failed to clear logs: {{message}}",history:"History",clear:"Clear",date:"Date",status:"Status",commit:"Commit",localBackup:"Local Backup",localBackupDescription:"Create a complete backup of your Bambuddy data including the database, archives, uploads, and all files.",downloadBackupLabel:"Download Backup",completeBackupZip:"Complete backup: database + all files (ZIP)",download:"Download",preparingBackup:"Preparing backup...",creatingArchive:"Creating backup archive... This may take a while for large archives.",downloadingFile:"Downloading backup file...",backupDownloaded:"Backup downloaded successfully",failedToCreateBackup:"Failed to create backup: {{message}}",restore:"Restore",restoreReplacesAll:"Restore replaces all data.",restoreReplacesAllDetail:"Your current database and files will be completely replaced. A restart is required after restore.",restoreConfirmTitle:"Restore Backup",restoreConfirmMessage:'Are you sure you want to restore from "{{filename}}"? This will completely replace your current database and all files. The application will need to be restarted after restore.',restoreConfirmButton:"Restore Backup",uploadingFile:"Uploading backup file...",backupRestoredRestart:"Backup restored. Please restart Bambuddy.",failedToRestore:"Failed to restore backup. Please check the file format.",reloadNow:"Reload Now",creatingBackup:"Creating Backup",restoringBackup:"Restoring Backup",preparing:"Preparing...",processing:"Processing...",doNotClosePage:"Please do not close this page or navigate away. This operation may take several minutes for large backups.",restoring:"Restoring...",restoreComplete:"Restore Complete",restoreFailed2:"Restore Failed",importSettings:"Import settings from a backup file",pleaseWaitRestoring:"Please wait while your data is being restored",selectBackupFile:"Click to select backup file (.json or .zip)",duplicateHandling:"How duplicate handling works:",matchPrinters:"Printers",matchPrintersBy:"matched by serial number",matchSmartPlugs:"Smart Plugs",matchSmartPlugsBy:"matched by IP address",matchNotificationProviders:"Notification Providers",matchNotificationProvidersBy:"matched by name",matchFilaments:"Filaments",matchFilamentsBy:"matched by name + type + brand",matchArchives:"Archives",matchArchivesBy:"matched by content hash (always skipped)",matchPendingUploads:"Pending Uploads",matchPendingUploadsBy:"matched by filename",matchSettingsTemplates:"Settings & Templates",matchSettingsTemplatesBy:"always overwritten",replaceExisting:"Replace existing data",keepExisting:"Keep existing data",overwriteDescription:"Overwrite items that already exist with backup data",keepDescription:"Only restore items that don't already exist",overwriteCaution:"Caution:",overwriteWarning:"Overwriting will replace your current configurations with data from the backup. Printer access codes are never overwritten for security.",cancel:"Cancel",processingBackup:"Processing backup file...",itemsRestored:"Items Restored",itemsSkipped:"Items Skipped",restored:"Restored",skippedAlreadyExist:"Skipped (already exist)",filesCategory:"Files (3MF, thumbnails, etc.)",andMore:"...and {{count}} more",newApiKeysGenerated:"New API Keys Generated",keysShownOnce:"These keys are only shown once. Copy them now!",copy:"Copy",noDataFound:"No data was found to restore in the backup file.",close:"Close",scheduledBackup:"Scheduled Backups",scheduledBackupDescription:"Automatically create backup snapshots on a schedule. Output directory can be mounted to a NAS or external storage.",frequency:"Frequency",backupTime:"Time",retention:"Retention",retentionDescription:"Number of backups to keep",outputPath:"Output Path",outputPathPlaceholder:"Default: {{path}}",outputPathDescription:"Leave empty for default location",runNow:"Run Now",backupFiles:"Backup Files",noScheduledBackups:"No backups yet",deleteBackup:"Delete",deleteBackupConfirm:"Delete this backup file?",backupRunning:"Backup in progress...",scheduledBackupComplete:"Backup completed successfully",scheduledBackupFailed:"Backup failed",nextBackup:"Next backup",backupSize:"Size",utc:"UTC",defaultPathLabel:"Default:",categories:{settings:"Settings",notification_providers:"Notification Providers",notification_templates:"Notification Templates",smart_plugs:"Smart Plugs",printers:"Printers",filaments:"Filaments",maintenance_types:"Maintenance Types",archives:"Archives",projects:"Projects",pending_uploads:"Pending Uploads",external_links:"External Links",api_keys:"API Keys"}},tags:{title:"Tags",addTag:"Add Tag",editTag:"Edit Tag",deleteTag:"Delete Tag",tagName:"Tag Name",tagColor:"Tag Color",noTags:"No tags",deleteConfirm:"Are you sure you want to delete this tag?",manageTags:"Manage Tags"},uploadModal:{title:"Upload 3MF Files",dragDrop:"Drag & drop .3mf files here",or:"or",browseFiles:"Browse Files",extractionInfo:"The printer model will be automatically extracted from the 3MF file metadata.",uploaded:"uploaded",failed:"failed",uploading:"Uploading...",upload:"Upload",uploadFailed:"Upload failed"},editArchive:{title:"Edit Archive",name:"Name",namePlaceholder:"Print name",printer:"Printer",noPrinter:"No printer",project:"Project",noProject:"No project",itemsPrinted:"Items Printed",itemsPrintedHelp:"Number of items produced in this print job",notes:"Notes",notesPlaceholder:"Add notes about this print...",externalLink:"External Link",externalLinkPlaceholder:"https://printables.com/model/...",externalLinkHelp:"Link to Printables, Thingiverse, or other source",tags:"Tags",tagsPlaceholder:"Add tags...",addMoreTags:"Add more tags...",matchingTags:'Matching "{{query}}"',existingTags:"Existing tags",clickToAdd:"(click to add)",status:"Status",failureReason:"Failure Reason",selectReason:"Select reason...",photos:"Photos of Printed Result",photosHelp:"Click + to add photos of your printed result",printResult:"Print result",saving:"Saving...",failureReasons:{adhesionFailure:"Adhesion failure",spaghettiDetached:"Spaghetti / Detached",layerShift:"Layer shift",cloggedNozzle:"Clogged nozzle",filamentRunout:"Filament runout",warping:"Warping",stringing:"Stringing",underExtrusion:"Under-extrusion",powerFailure:"Power failure",userCancelled:"User cancelled",other:"Other"},statuses:{completed:"Completed",failed:"Failed",aborted:"Cancelled",printing:"Printing"}},kProfiles:{title:"K-Profiles",noPrintersConfigured:"No Printers Configured",addPrinterInSettings:"Add a printer in Settings to manage K-profiles",noActivePrinters:"No Active Printers",enablePrinterConnection:"Enable a printer connection to view its K-profiles",loadingProfiles:"Loading K-Profiles...",printerOffline:"Printer Offline",printerOfflineDesc:"The selected printer is not connected. Power it on to view K-profiles.",noMatchingProfiles:"No Matching Profiles",noMatchingProfilesDesc:"No profiles match your search criteria",noKProfiles:"No K-Profiles",noKProfilesDesc:"No pressure advance profiles found for {{diameter}}mm nozzle",createFirstProfile:"Create First Profile",printer:"Printer",nozzle:"Nozzle",refresh:"Refresh",addProfile:"Add Profile",export:"Export",import:"Import",select:"Select",selectAll:"Select All",delete:"Delete",searchPlaceholder:"Search by name or filament...",allExtruders:"All Extruders",leftOnly:"Left Only",rightOnly:"Right Only",allFlow:"All Flow",hfOnly:"HF Only",sOnly:"S Only",sortName:"Sort: Name",sortKValue:"Sort: K-Value",sortFilament:"Sort: Filament",leftExtruder:"Left Extruder",rightExtruder:"Right Extruder",modal:{addTitle:"Add K-Profile",editTitle:"Edit K-Profile",profileName:"Profile Name",profileNamePlaceholder:"My PLA Profile",kValue:"K-Value",kValuePlaceholder:"0.020",kValueHelp:"Typical range: 0.01 - 0.06 for PLA, 0.02 - 0.10 for PETG",filament:"Filament",selectFilament:"Select filament...",noFilamentsHelp:"No filaments found. Create a K-profile in Bambu Studio first.",flowType:"Flow Type",highFlow:"High Flow",standard:"Standard",nozzleSize:"Nozzle Size",extruder:"Extruder",extruders:"Extruders",left:"Left",right:"Right",notes:"Notes (stored locally)",notesPlaceholder:"Add notes about this profile...",notesHelp:"Notes are saved in Bambuddy, not on the printer",syncing:"Syncing with printer...",savingExtruder:"Saving to extruder {{current}}/{{total}}...",pleaseWait:"Please wait"},deleteConfirm:{title:"Delete Profile",cannotUndo:"This cannot be undone",message:'Are you sure you want to delete "{{name}}" from the printer?'},bulkDelete:{title:"Delete Profiles",cannotUndo:"This cannot be undone",message:"Are you sure you want to delete {{count}} selected profiles from the printer?"},toast:{profileSaved:"K-profile saved",profilesSaved:"K-profile saved to {{count}} extruders",selectAtLeastOneExtruder:"Please select at least one extruder",profileDeleted:"K-profile deleted",profilesDeleted:"Deleted {{count}} profiles",exportedProfiles:"Exported {{count}} profiles",importedProfiles:"Imported {{count}} of {{total}} profiles",noProfilesToExport:"No profiles to export",invalidFileFormat:"Invalid file format",failedToParseImport:"Failed to parse import file",failedToSaveBatch:"Failed to save K-profiles",noteSaved:"Note saved",failedToSaveNote:"Failed to save note"},permission:{noRead:"You do not have permission to refresh profiles",noCreate:"You do not have permission to add profiles",noUpdate:"You do not have permission to update K-profiles",noDelete:"You do not have permission to delete K-profiles",noExport:"You do not have permission to export profiles",noImport:"You do not have permission to import profiles"}},virtualPrinter:{title:"Virtual Printer",running:"Running",stopped:"Stopped",description:{default:"Enable a virtual printer that appears in Bambu Studio and OrcaSlicer. Files sent to this printer will be archived directly without printing.",proxy:"Enable a proxy that relays slicer traffic to a real printer, allowing remote printing over any network."},enable:{title:"Enable Virtual Printer",visibleInSlicer:'Visible as "Bambuddy" in slicer discovery',proxyingTo:"Proxying to {{name}}",notActive:"Not active"},model:{title:"Printer Model",description:"Select which printer model to emulate.",restartWarning:"Changing the model will restart the virtual printer"},accessCode:{title:"Access Code",isSet:"Access code is set",notSet:"No access code set - required to enable",placeholder:"Enter 8-char code",placeholderChange:"Enter new code to change",hint:"Must be exactly 8 characters. Used by slicers to authenticate.",charCount:"({{count}}/8)"},targetPrinter:{title:"Target Printer",configured:"Proxy target configured",notConfigured:"No target printer selected - required for proxy mode",placeholder:"Select a printer...",hint:"Select the printer to proxy slicer traffic to. The printer must be in LAN mode.",noPrinters:"No printers configured. Add a printer first to use proxy mode."},remoteInterface:{title:"Network Interface Override",configured:"Interface override active",optional:"Optional - use if auto-detected IP is wrong (e.g. multiple NICs, Docker, VPN)",placeholder:"Auto-detect (default)...",hint:"Override the IP address advertised via SSDP and used in the TLS certificate. Useful when Bambuddy has multiple network interfaces."},mode:{title:"Mode",archive:"Archive",archiveDesc:"Archive files immediately",review:"Review",reviewDesc:"Review before archiving",queue:"Queue",queueDesc:"Archive and add to queue",proxy:"Proxy",proxyDesc:"Relay to real printer"},autoDispatch:{title:"Auto-dispatch",description:"Automatically start prints when added to queue. When off, prints wait for manual dispatch."},setupRequired:{title:"Setup Required",description:"The virtual printer feature requires additional system configuration before it will work. This includes port forwarding, firewall rules, and platform-specific settings.",readGuide:"Read the setup guide before enabling"},howItWorks:{title:"How it works",step1:"On the same LAN, virtual printers appear in your slicer (Bambu Studio / OrcaSlicer) automatically via discovery. From other networks, add them manually by IP address and access code.",step2:'In Archive, Review, and Queue modes, use the "Send" button in your slicer to upload 3MF files to Bambuddy. The slicer will show "Print success" — the file is stored, not printed.',step3:"In Proxy mode, the virtual printer relays all traffic to a real printer — prints start immediately as if connected directly."},status:{title:"Status Details",printerName:"Printer Name",model:"Model",serialNumber:"Serial Number",mode:"Mode",pendingFiles:"Pending Files",targetPrinter:"Target Printer",ftpPort:"FTP Port",mqttPort:"MQTT Port",ftpConnections:"FTP Connections",mqttConnections:"MQTT Connections"},toast:{updated:"Virtual printer settings updated",failedToUpdate:"Failed to update settings",accessCodeRequired:"Please set an access code first",targetPrinterRequired:"Please select a target printer first",bindIpRequired:"Please set a bind IP first",accessCodeEmpty:"Access code cannot be empty",accessCodeLength:"Access code must be exactly 8 characters",created:"Virtual printer created",failedToCreate:"Failed to create virtual printer",deleted:"Virtual printer deleted",failedToDelete:"Failed to delete virtual printer"},list:{title:"Virtual Printers",add:"Add",addFirst:"Add Virtual Printer",empty:"No virtual printers configured. Add one to get started."},bindIp:{title:"Bind Interface",placeholder:"Select interface...",hint:"Network interface for this virtual printer to bind to. Must be unique per printer."},proxy:{accessCodeHint:"In proxy mode, use your target printer's access code in the slicer. The connection is forwarded transparently to the real printer."},addDialog:{title:"Add Virtual Printer",name:"Name",hint:"You can configure access code, target printer, and other settings after creating.",create:"Create"},deleteConfirm:{title:"Delete Virtual Printer",message:'Are you sure you want to delete "{{name}}"? This will stop all services for this printer.'}},modelViewer:{openInSlicer:"Open in Slicer",tabs:{model:"3D Model",gcode:"G-code Preview"},notAvailable:"not available",notSliced:"not sliced",plates:"Plates",allPlates:"All Plates",plateNumber:"Plate {{number}}",plateCount:"{{count}} plate",plateCount_other:"{{count}} plates",objectCount:"{{count}} object",objectCount_other:"{{count}} objects",filamentCount:"{{count}} filament",filamentCount_other:"{{count}} filaments",eta:"ETA {{minutes}} min",noPreview:"No preview available for this file",pagination:{pageOf:"Page {{current}} of {{total}}",prev:"Prev",next:"Next"},errors:{failedToLoad:"Failed to load file",noMeshes:"No meshes found in 3MF file",unsupportedFormat:"Unsupported file format"}},maintenanceDescriptions:{lubricateCarbonRods:"Apply lubricant to carbon rods for smooth motion",lubricateRails:"Apply lubricant to linear rails for smooth motion",cleanNozzle:"Clean hotend and nozzle to prevent clogs",checkBelts:"Verify belt tension for accurate prints",cleanBuildPlate:"Clean build plate for better adhesion",checkExtruder:"Inspect extruder gears for wear",checkCooling:"Ensure cooling fans are working properly",generalInspection:"General printer inspection",cleanCarbonRods:"Clean carbon rods to reduce friction",lubricateSteelRods:"Apply lubricant to steel rods for smooth motion",cleanSteelRods:"Clean steel rods to reduce friction",cleanLinearRails:"Wipe linear rails to remove dust and debris",checkPtfeTube:"Inspect PTFE tube for wear or damage",replaceHepaFilter:"Replace HEPA filter for air quality",replaceCarbonFilter:"Replace activated carbon filter",lubricateLeftNozzleRail:"Lubricate left nozzle rail (H2 series)"},smartPlugs:{offline:"Offline",admin:"Admin",openPlugAdminPage:"Open plug admin page",deleteSmartPlug:"Delete Smart Plug",turnOnSmartPlug:"Turn On Smart Plug",turnOffSmartPlug:"Turn Off Smart Plug",turnOn:"Turn On",turnOff:"Turn Off",addSmartPlug:{scanningNetwork:"Scanning network...",chooseEntity:"Choose an entity...",connectionFailed:"Connection failed",searchEntities:"Search entities...",searchPowerSensors:"Search power sensors...",searchEnergySensors:"Search energy sensors...",placeholders:{plugName:"Living Room Plug",mqttStateOnValue:"ON, true, 1",mqttSameAsPower:"Same as power topic, or different"}},linkedTo:"Linked to:",monitorOnly:"Monitor Only",alerts:"Alerts",scheduleOn:"On {{time}}",scheduleOff:"Off {{time}}",on:"On",off:"Off",power:"Power",kwhToday:"kWh Today",settings:"Settings",automationSettings:"Automation Settings",showInSwitchbar:"Show in Switchbar",quickAccessSidebar:"Quick access from sidebar",enabled:"Enabled",enableAutomation:"Enable automation for this plug",autoOn:"Auto On",autoOnDescription:"Turn on when print starts",autoOff:"Auto Off",autoOffDescription:"Turn off when print completes (one-shot)",autoOffPersistent:"Keep Enabled",autoOffPersistentDescription:"Stay enabled between prints instead of one-shot",turnOffDelayMode:"Turn Off Delay Mode",time:"Time",temp:"Temp",delayMinutes:"Delay (minutes)",tempThreshold:"Temperature threshold (°C)",tempThresholdDescription:"Turns off when nozzle cools below this temperature",edit:"Edit",deleteConfirm:'Are you sure you want to delete "{{name}}"? This cannot be undone.',turnOnConfirm:'Are you sure you want to turn on "{{name}}"?',turnOffConfirm:'Are you sure you want to turn off "{{name}}"? This will cut power to the connected device.',failedToTurn:'Failed to turn {{action}} "{{name}}"',unknown:"Unknown",addTitle:"Add Smart Plug",editTitle:"Edit Smart Plug",stopScanning:"Stop Scanning",discoverTasmota:"Discover Tasmota Devices",foundDevices:"Found {{count}} device(s) - click to select:",noDevicesFound:"No Tasmota devices found on your network",haNotConfigured:"Home Assistant is not configured. Set it up in",haSettingsPath:"Settings → Network → Home Assistant",selectEntity:"Select Entity *",ipAddress:"IP Address *",nameLabel:"Name *",username:"Username",password:"Password",authHint:"Leave empty if your Tasmota device doesn't require authentication",linkToPrinter:"Link to Printer",noPrinter:"No printer (manual control only)",linkingDescription:"Linking enables automatic on/off when prints start/complete",powerAlerts:"Power Alerts",alertAbove:"Alert if above (W)",alertBelow:"Alert if below (W)",alertDescription:"Get notified when power consumption crosses these thresholds. Leave empty to disable that direction.",dailySchedule:"Daily Schedule",turnOnAt:"Turn On at",turnOffAt:"Turn Off at",scheduleDescription:"Automatically turn the plug on/off at these times daily. Leave empty to skip that action.",showOnPrinterCard:"Show on Printer Card",displayOnPrinterCard:"Display button on printer card",connectedResult:"Connected!",deviceLabel:"Device: {{name}} - ",stateLabel:"State: {{state}}",test:"Test",delete:"Delete",save:"Save",add:"Add",cancel:"Cancel",failedToStartScan:"Failed to start scan",nameRequired:"Name is required",entityRequired:"Entity is required for Home Assistant plugs",mqttTopicRequired:"At least one MQTT topic must be configured for power, energy, or state monitoring",loadingEntities:"Loading entities...",loading:"Loading...",failedToLoadEntities:"Failed to load entities: {{error}}",noEntitiesMatching:'No entities found matching "{{search}}"',noEntitiesAvailable:"No entities available",searchingEntities:"Searching all entities ({{count}} found)",showingEntities:"Showing switch, light, input_boolean ({{count}} available)",energyMonitoringOptional:"Energy Monitoring (Optional)",energyMonitoringHint:"Search and select sensors that provide power/energy data.",powerSensorW:"Power Sensor (W)",energyTodayKwh:"Energy Today (kWh)",totalEnergyKwh:"Total Energy (kWh)",noMatchingSensors:"No matching sensors",none:"None",mqttNotConfigured:"MQTT broker not configured. Set broker address in",mqttSettingsPath:"Settings → Network → MQTT Publishing",mqttNotConfiguredSuffix:"(you don't need to enable publishing, just fill in the broker details).",mqttMonitorOnlyDescription:"MQTT plugs receive power/energy data via MQTT subscription. On/off control is not available - use your MQTT broker or home automation system.",powerMonitoring:"Power Monitoring",energyMonitoring:"Energy Monitoring",stateMonitoring:"State Monitoring",optional:"optional",topic:"Topic",jsonPath:"JSON Path",multiplier:"Multiplier",onValue:"ON Value",mqttPowerHint:`JSON path extracts value from JSON payload (e.g., "power_l1"). Leave empty if topic publishes raw numeric values. +`+M.stack}}var se=Object.prototype.hasOwnProperty,ge=t.unstable_scheduleCallback,pe=t.unstable_cancelCallback,le=t.unstable_shouldYield,B=t.unstable_requestPaint,R=t.unstable_now,ae=t.unstable_getCurrentPriorityLevel,_e=t.unstable_ImmediatePriority,Se=t.unstable_UserBlockingPriority,ye=t.unstable_NormalPriority,Te=t.unstable_LowPriority,ve=t.unstable_IdlePriority,Ae=t.log,Re=t.unstable_setDisableYieldValue,Me=null,Le=null;function Oe(h){if(typeof Ae=="function"&&Re(h),Le&&typeof Le.setStrictMode=="function")try{Le.setStrictMode(Me,h)}catch{}}var qe=Math.clz32?Math.clz32:he,Qe=Math.log,tt=Math.LN2;function he(h){return h>>>=0,h===0?32:31-(Qe(h)/tt|0)|0}var Fe=256,we=262144,Ve=4194304;function je(h){var y=h&42;if(y!==0)return y;switch(h&-h){case 1:return 1;case 2:return 2;case 4:return 4;case 8:return 8;case 16:return 16;case 32:return 32;case 64:return 64;case 128:return 128;case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:return h&261888;case 262144:case 524288:case 1048576:case 2097152:return h&3932160;case 4194304:case 8388608:case 16777216:case 33554432:return h&62914560;case 67108864:return 67108864;case 134217728:return 134217728;case 268435456:return 268435456;case 536870912:return 536870912;case 1073741824:return 0;default:return h}}function ce(h,y,S){var M=h.pendingLanes;if(M===0)return 0;var $=0,Z=h.suspendedLanes,Ne=h.pingedLanes;h=h.warmLanes;var He=M&134217727;return He!==0?(M=He&~Z,M!==0?$=je(M):(Ne&=He,Ne!==0?$=je(Ne):S||(S=He&~h,S!==0&&($=je(S))))):(He=M&~Z,He!==0?$=je(He):Ne!==0?$=je(Ne):S||(S=M&~h,S!==0&&($=je(S)))),$===0?0:y!==0&&y!==$&&(y&Z)===0&&(Z=$&-$,S=y&-y,Z>=S||Z===32&&(S&4194048)!==0)?y:$}function xe(h,y){return(h.pendingLanes&~(h.suspendedLanes&~h.pingedLanes)&y)===0}function Be(h,y){switch(h){case 1:case 2:case 4:case 8:case 64:return y+250;case 16:case 32:case 128:case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:case 262144:case 524288:case 1048576:case 2097152:return y+5e3;case 4194304:case 8388608:case 16777216:case 33554432:return-1;case 67108864:case 134217728:case 268435456:case 536870912:case 1073741824:return-1;default:return-1}}function Ze(){var h=Ve;return Ve<<=1,(Ve&62914560)===0&&(Ve=4194304),h}function pt(h){for(var y=[],S=0;31>S;S++)y.push(h);return y}function xt(h,y){h.pendingLanes|=y,y!==268435456&&(h.suspendedLanes=0,h.pingedLanes=0,h.warmLanes=0)}function yt(h,y,S,M,$,Z){var Ne=h.pendingLanes;h.pendingLanes=S,h.suspendedLanes=0,h.pingedLanes=0,h.warmLanes=0,h.expiredLanes&=S,h.entangledLanes&=S,h.errorRecoveryDisabledLanes&=S,h.shellSuspendCounter=0;var He=h.entanglements,nt=h.expirationTimes,Nt=h.hiddenUpdates;for(S=Ne&~S;0"u")return null;try{return h.activeElement||h.body}catch{return h.body}}var Nr=/[\n"\\]/g;function Fn(h){return h.replace(Nr,function(y){return"\\"+y.charCodeAt(0).toString(16)+" "})}function Ba(h,y,S,M,$,Z,Ne,He){h.name="",Ne!=null&&typeof Ne!="function"&&typeof Ne!="symbol"&&typeof Ne!="boolean"?h.type=Ne:h.removeAttribute("type"),y!=null?Ne==="number"?(y===0&&h.value===""||h.value!=y)&&(h.value=""+Mn(y)):h.value!==""+Mn(y)&&(h.value=""+Mn(y)):Ne!=="submit"&&Ne!=="reset"||h.removeAttribute("value"),y!=null?ma(h,Ne,Mn(y)):S!=null?ma(h,Ne,Mn(S)):M!=null&&h.removeAttribute("value"),$==null&&Z!=null&&(h.defaultChecked=!!Z),$!=null&&(h.checked=$&&typeof $!="function"&&typeof $!="symbol"),He!=null&&typeof He!="function"&&typeof He!="symbol"&&typeof He!="boolean"?h.name=""+Mn(He):h.removeAttribute("name")}function In(h,y,S,M,$,Z,Ne,He){if(Z!=null&&typeof Z!="function"&&typeof Z!="symbol"&&typeof Z!="boolean"&&(h.type=Z),y!=null||S!=null){if(!(Z!=="submit"&&Z!=="reset"||y!=null)){vn(h);return}S=S!=null?""+Mn(S):"",y=y!=null?""+Mn(y):S,He||y===h.value||(h.value=y),h.defaultValue=y}M=M??$,M=typeof M!="function"&&typeof M!="symbol"&&!!M,h.checked=He?h.checked:!!M,h.defaultChecked=!!M,Ne!=null&&typeof Ne!="function"&&typeof Ne!="symbol"&&typeof Ne!="boolean"&&(h.name=Ne),vn(h)}function ma(h,y,S){y==="number"&&gr(h.ownerDocument)===h||h.defaultValue===""+S||(h.defaultValue=""+S)}function aa(h,y,S,M){if(h=h.options,y){y={};for(var $=0;$"u"||typeof window.document>"u"||typeof window.document.createElement>"u"),nu=!1;if(yo)try{var Ps={};Object.defineProperty(Ps,"passive",{get:function(){nu=!0}}),window.addEventListener("test",Ps,Ps),window.removeEventListener("test",Ps,Ps)}catch{nu=!1}var vo=null,Ts=null,Wo=null;function wo(){if(Wo)return Wo;var h,y=Ts,S=y.length,M,$="value"in vo?vo.value:vo.textContent,Z=$.length;for(h=0;h=dt),ut=" ",Un=!1;function Tr(h,y){switch(h){case"keyup":return kA.indexOf(y.keyCode)!==-1;case"keydown":return y.keyCode!==229;case"keypress":case"mousedown":case"focusout":return!0;default:return!1}}function ar(h){return h=h.detail,typeof h=="object"&&"data"in h?h.data:null}var Rr=!1;function Da(h,y){switch(h){case"compositionend":return ar(y);case"keypress":return y.which!==32?null:(Un=!0,ut);case"textInput":return h=y.data,h===ut&&Un?null:h;default:return null}}function sn(h,y){if(Rr)return h==="compositionend"||!Vy&&Tr(h,y)?(h=wo(),Wo=Ts=vo=null,Rr=!1,h):null;switch(h){case"paste":return null;case"keypress":if(!(y.ctrlKey||y.altKey||y.metaKey)||y.ctrlKey&&y.altKey){if(y.char&&1=y)return{node:S,offset:y-h};h=M}e:{for(;S;){if(S.nextSibling){S=S.nextSibling;break e}S=S.parentNode}S=void 0}S=Vp(S)}}function Ky(h,y){return h&&y?h===y?!0:h&&h.nodeType===3?!1:y&&y.nodeType===3?Ky(h,y.parentNode):"contains"in h?h.contains(y):h.compareDocumentPosition?!!(h.compareDocumentPosition(y)&16):!1:!1}function $S(h){h=h!=null&&h.ownerDocument!=null&&h.ownerDocument.defaultView!=null?h.ownerDocument.defaultView:window;for(var y=gr(h.document);y instanceof h.HTMLIFrameElement;){try{var S=typeof y.contentWindow.location.href=="string"}catch{S=!1}if(S)h=y.contentWindow;else break;y=gr(h.document)}return y}function NA(h){var y=h&&h.nodeName&&h.nodeName.toLowerCase();return y&&(y==="input"&&(h.type==="text"||h.type==="search"||h.type==="tel"||h.type==="url"||h.type==="password")||y==="textarea"||h.contentEditable==="true")}var hce=yo&&"documentMode"in document&&11>=document.documentMode,Gg=null,CA=null,Xy=null,PA=!1;function y5(h,y,S){var M=S.window===S?S.document:S.nodeType===9?S:S.ownerDocument;PA||Gg==null||Gg!==gr(M)||(M=Gg,"selectionStart"in M&&NA(M)?M={start:M.selectionStart,end:M.selectionEnd}:(M=(M.ownerDocument&&M.ownerDocument.defaultView||window).getSelection(),M={anchorNode:M.anchorNode,anchorOffset:M.anchorOffset,focusNode:M.focusNode,focusOffset:M.focusOffset}),Xy&&Um(Xy,M)||(Xy=M,M=E_(CA,"onSelect"),0>=Ne,$-=Ne,cd=1<<32-qe(y)+$|S<<$|M,dd=Z+h}else cd=1<Qn?(xr=fn,fn=null):xr=fn.sibling;var jr=Ct(bt,fn,kt[Qn],Ot);if(jr===null){fn===null&&(fn=xr);break}h&&fn&&jr.alternate===null&&y(bt,fn),mt=Z(jr,mt,Qn),Ar===null?Pn=jr:Ar.sibling=jr,Ar=jr,fn=xr}if(Qn===kt.length)return S(bt,fn),vr&&cu(bt,Qn),Pn;if(fn===null){for(;QnQn?(xr=fn,fn=null):xr=fn.sibling;var lh=Ct(bt,fn,jr.value,Ot);if(lh===null){fn===null&&(fn=xr);break}h&&fn&&lh.alternate===null&&y(bt,fn),mt=Z(lh,mt,Qn),Ar===null?Pn=lh:Ar.sibling=lh,Ar=lh,fn=xr}if(jr.done)return S(bt,fn),vr&&cu(bt,Qn),Pn;if(fn===null){for(;!jr.done;Qn++,jr=kt.next())jr=zt(bt,jr.value,Ot),jr!==null&&(mt=Z(jr,mt,Qn),Ar===null?Pn=jr:Ar.sibling=jr,Ar=jr);return vr&&cu(bt,Qn),Pn}for(fn=M(fn);!jr.done;Qn++,jr=kt.next())jr=Et(fn,bt,Qn,jr.value,Ot),jr!==null&&(h&&jr.alternate!==null&&fn.delete(jr.key===null?Qn:jr.key),mt=Z(jr,mt,Qn),Ar===null?Pn=jr:Ar.sibling=jr,Ar=jr);return h&&fn.forEach(function(Dde){return y(bt,Dde)}),vr&&cu(bt,Qn),Pn}function Xr(bt,mt,kt,Ot){if(typeof kt=="object"&&kt!==null&&kt.type===v&&kt.key===null&&(kt=kt.props.children),typeof kt=="object"&&kt!==null){switch(kt.$$typeof){case f:e:{for(var Pn=kt.key;mt!==null;){if(mt.key===Pn){if(Pn=kt.type,Pn===v){if(mt.tag===7){S(bt,mt.sibling),Ot=$(mt,kt.props.children),Ot.return=bt,bt=Ot;break e}}else if(mt.elementType===Pn||typeof Pn=="object"&&Pn!==null&&Pn.$$typeof===F&&ef(Pn)===mt.type){S(bt,mt.sibling),Ot=$(mt,kt.props),tv(Ot,kt),Ot.return=bt,bt=Ot;break e}S(bt,mt);break}else y(bt,mt);mt=mt.sibling}kt.type===v?(Ot=Xp(kt.props.children,bt.mode,Ot,kt.key),Ot.return=bt,bt=Ot):(Ot=XS(kt.type,kt.key,kt.props,null,bt.mode,Ot),tv(Ot,kt),Ot.return=bt,bt=Ot)}return Ne(bt);case x:e:{for(Pn=kt.key;mt!==null;){if(mt.key===Pn)if(mt.tag===4&&mt.stateNode.containerInfo===kt.containerInfo&&mt.stateNode.implementation===kt.implementation){S(bt,mt.sibling),Ot=$(mt,kt.children||[]),Ot.return=bt,bt=Ot;break e}else{S(bt,mt);break}else y(bt,mt);mt=mt.sibling}Ot=FA(kt,bt.mode,Ot),Ot.return=bt,bt=Ot}return Ne(bt);case F:return kt=ef(kt),Xr(bt,mt,kt,Ot)}if(te(kt))return mn(bt,mt,kt,Ot);if(z(kt)){if(Pn=z(kt),typeof Pn!="function")throw Error(r(150));return kt=Pn.call(kt),Rn(bt,mt,kt,Ot)}if(typeof kt.then=="function")return Xr(bt,mt,n_(kt),Ot);if(kt.$$typeof===C)return Xr(bt,mt,ZS(bt,kt),Ot);r_(bt,kt)}return typeof kt=="string"&&kt!==""||typeof kt=="number"||typeof kt=="bigint"?(kt=""+kt,mt!==null&&mt.tag===6?(S(bt,mt.sibling),Ot=$(mt,kt),Ot.return=bt,bt=Ot):(S(bt,mt),Ot=DA(kt,bt.mode,Ot),Ot.return=bt,bt=Ot),Ne(bt)):S(bt,mt)}return function(bt,mt,kt,Ot){try{ev=0;var Pn=Xr(bt,mt,kt,Ot);return rb=null,Pn}catch(fn){if(fn===nb||fn===e_)throw fn;var Ar=Qo(29,fn,null,bt.mode);return Ar.lanes=Ot,Ar.return=bt,Ar}}}var nf=H5(!0),q5=H5(!1),Vm=!1;function GA(h){h.updateQueue={baseState:h.memoizedState,firstBaseUpdate:null,lastBaseUpdate:null,shared:{pending:null,lanes:0,hiddenCallbacks:null},callbacks:null}}function WA(h,y){h=h.updateQueue,y.updateQueue===h&&(y.updateQueue={baseState:h.baseState,firstBaseUpdate:h.firstBaseUpdate,lastBaseUpdate:h.lastBaseUpdate,shared:h.shared,callbacks:null})}function Gm(h){return{lane:h,tag:0,payload:null,callback:null,next:null}}function Wm(h,y,S){var M=h.updateQueue;if(M===null)return null;if(M=M.shared,(Lr&2)!==0){var $=M.pending;return $===null?y.next=y:(y.next=$.next,$.next=y),M.pending=y,y=KS(h),C5(h,null,S),y}return WS(h,M,y,S),KS(h)}function nv(h,y,S){if(y=y.updateQueue,y!==null&&(y=y.shared,(S&4194048)!==0)){var M=y.lanes;M&=h.pendingLanes,S|=M,y.lanes=S,Kt(h,S)}}function KA(h,y){var S=h.updateQueue,M=h.alternate;if(M!==null&&(M=M.updateQueue,S===M)){var $=null,Z=null;if(S=S.firstBaseUpdate,S!==null){do{var Ne={lane:S.lane,tag:S.tag,payload:S.payload,callback:null,next:null};Z===null?$=Z=Ne:Z=Z.next=Ne,S=S.next}while(S!==null);Z===null?$=Z=y:Z=Z.next=y}else $=Z=y;S={baseState:M.baseState,firstBaseUpdate:$,lastBaseUpdate:Z,shared:M.shared,callbacks:M.callbacks},h.updateQueue=S;return}h=S.lastBaseUpdate,h===null?S.firstBaseUpdate=y:h.next=y,S.lastBaseUpdate=y}var XA=!1;function rv(){if(XA){var h=tb;if(h!==null)throw h}}function av(h,y,S,M){XA=!1;var $=h.updateQueue;Vm=!1;var Z=$.firstBaseUpdate,Ne=$.lastBaseUpdate,He=$.shared.pending;if(He!==null){$.shared.pending=null;var nt=He,Nt=nt.next;nt.next=null,Ne===null?Z=Nt:Ne.next=Nt,Ne=nt;var Lt=h.alternate;Lt!==null&&(Lt=Lt.updateQueue,He=Lt.lastBaseUpdate,He!==Ne&&(He===null?Lt.firstBaseUpdate=Nt:He.next=Nt,Lt.lastBaseUpdate=nt))}if(Z!==null){var zt=$.baseState;Ne=0,Lt=Nt=nt=null,He=Z;do{var Ct=He.lane&-536870913,Et=Ct!==He.lane;if(Et?(br&Ct)===Ct:(M&Ct)===Ct){Ct!==0&&Ct===eb&&(XA=!0),Lt!==null&&(Lt=Lt.next={lane:0,tag:He.tag,payload:He.payload,callback:null,next:null});e:{var mn=h,Rn=He;Ct=y;var Xr=S;switch(Rn.tag){case 1:if(mn=Rn.payload,typeof mn=="function"){zt=mn.call(Xr,zt,Ct);break e}zt=mn;break e;case 3:mn.flags=mn.flags&-65537|128;case 0:if(mn=Rn.payload,Ct=typeof mn=="function"?mn.call(Xr,zt,Ct):mn,Ct==null)break e;zt=m({},zt,Ct);break e;case 2:Vm=!0}}Ct=He.callback,Ct!==null&&(h.flags|=64,Et&&(h.flags|=8192),Et=$.callbacks,Et===null?$.callbacks=[Ct]:Et.push(Ct))}else Et={lane:Ct,tag:He.tag,payload:He.payload,callback:He.callback,next:null},Lt===null?(Nt=Lt=Et,nt=zt):Lt=Lt.next=Et,Ne|=Ct;if(He=He.next,He===null){if(He=$.shared.pending,He===null)break;Et=He,He=Et.next,Et.next=null,$.lastBaseUpdate=Et,$.shared.pending=null}}while(!0);Lt===null&&(nt=zt),$.baseState=nt,$.firstBaseUpdate=Nt,$.lastBaseUpdate=Lt,Z===null&&($.shared.lanes=0),Zm|=Ne,h.lanes=Ne,h.memoizedState=zt}}function $5(h,y){if(typeof h!="function")throw Error(r(191,h));h.call(y)}function V5(h,y){var S=h.callbacks;if(S!==null)for(h.callbacks=null,h=0;hZ?Z:8;var Ne=ie.T,He={};ie.T=He,pj(h,!1,y,S);try{var nt=$(),Nt=ie.S;if(Nt!==null&&Nt(He,nt),nt!==null&&typeof nt=="object"&&typeof nt.then=="function"){var Lt=Sce(nt,M);ov(h,y,Lt,nl(h))}else ov(h,y,M,nl(h))}catch(zt){ov(h,y,{then:function(){},status:"rejected",reason:zt},nl())}finally{J.p=Z,Ne!==null&&He.types!==null&&(Ne.types=He.types),ie.T=Ne}}function Tce(){}function mj(h,y,S,M){if(h.tag!==5)throw Error(r(476));var $=_U(h).queue;SU(h,$,y,oe,S===null?Tce:function(){return kU(h),S(M)})}function _U(h){var y=h.memoizedState;if(y!==null)return y;y={memoizedState:oe,baseState:oe,baseQueue:null,queue:{pending:null,lanes:0,dispatch:null,lastRenderedReducer:hu,lastRenderedState:oe},next:null};var S={};return y.next={memoizedState:S,baseState:S,baseQueue:null,queue:{pending:null,lanes:0,dispatch:null,lastRenderedReducer:hu,lastRenderedState:S},next:null},h.memoizedState=y,h=h.alternate,h!==null&&(h.memoizedState=y),y}function kU(h){var y=_U(h);y.next===null&&(y=h.alternate.memoizedState),ov(h,y.next.queue,{},nl())}function hj(){return ss(kv)}function NU(){return si().memoizedState}function CU(){return si().memoizedState}function Ace(h){for(var y=h.return;y!==null;){switch(y.tag){case 24:case 3:var S=nl();h=Gm(S);var M=Wm(y,h,S);M!==null&&(Ao(M,y,S),nv(M,y,S)),y={cache:HA()},h.payload=y;return}y=y.return}}function jce(h,y,S){var M=nl();S={lane:M,revertLane:0,gesture:null,action:S,hasEagerState:!1,eagerState:null,next:null},h_(h)?TU(y,S):(S=MA(h,y,S,M),S!==null&&(Ao(S,h,M),AU(S,y,M)))}function PU(h,y,S){var M=nl();ov(h,y,S,M)}function ov(h,y,S,M){var $={lane:M,revertLane:0,gesture:null,action:S,hasEagerState:!1,eagerState:null,next:null};if(h_(h))TU(y,$);else{var Z=h.alternate;if(h.lanes===0&&(Z===null||Z.lanes===0)&&(Z=y.lastRenderedReducer,Z!==null))try{var Ne=y.lastRenderedState,He=Z(Ne,S);if($.hasEagerState=!0,$.eagerState=He,ko(He,Ne))return WS(h,y,$,0),ia===null&&GS(),!1}catch{}if(S=MA(h,y,$,M),S!==null)return Ao(S,h,M),AU(S,y,M),!0}return!1}function pj(h,y,S,M){if(M={lane:2,revertLane:Gj(),gesture:null,action:M,hasEagerState:!1,eagerState:null,next:null},h_(h)){if(y)throw Error(r(479))}else y=MA(h,S,M,2),y!==null&&Ao(y,h,2)}function h_(h){var y=h.alternate;return h===Xn||y!==null&&y===Xn}function TU(h,y){ib=s_=!0;var S=h.pending;S===null?y.next=y:(y.next=S.next,S.next=y),h.pending=y}function AU(h,y,S){if((S&4194048)!==0){var M=y.lanes;M&=h.pendingLanes,S|=M,y.lanes=S,Kt(h,S)}}var lv={readContext:ss,use:c_,useCallback:qa,useContext:qa,useEffect:qa,useImperativeHandle:qa,useLayoutEffect:qa,useInsertionEffect:qa,useMemo:qa,useReducer:qa,useRef:qa,useState:qa,useDebugValue:qa,useDeferredValue:qa,useTransition:qa,useSyncExternalStore:qa,useId:qa,useHostTransitionStatus:qa,useFormState:qa,useActionState:qa,useOptimistic:qa,useMemoCache:qa,useCacheRefresh:qa};lv.useEffectEvent=qa;var jU={readContext:ss,use:c_,useCallback:function(h,y){return Qs().memoizedState=[h,y===void 0?null:y],h},useContext:ss,useEffect:hU,useImperativeHandle:function(h,y,S){S=S!=null?S.concat([h]):null,u_(4194308,4,bU.bind(null,y,h),S)},useLayoutEffect:function(h,y){return u_(4194308,4,h,y)},useInsertionEffect:function(h,y){u_(4,2,h,y)},useMemo:function(h,y){var S=Qs();y=y===void 0?null:y;var M=h();if(rf){Oe(!0);try{h()}finally{Oe(!1)}}return S.memoizedState=[M,y],M},useReducer:function(h,y,S){var M=Qs();if(S!==void 0){var $=S(y);if(rf){Oe(!0);try{S(y)}finally{Oe(!1)}}}else $=y;return M.memoizedState=M.baseState=$,h={pending:null,lanes:0,dispatch:null,lastRenderedReducer:h,lastRenderedState:$},M.queue=h,h=h.dispatch=jce.bind(null,Xn,h),[M.memoizedState,h]},useRef:function(h){var y=Qs();return h={current:h},y.memoizedState=h},useState:function(h){h=oj(h);var y=h.queue,S=PU.bind(null,Xn,y);return y.dispatch=S,[h.memoizedState,S]},useDebugValue:dj,useDeferredValue:function(h,y){var S=Qs();return uj(S,h,y)},useTransition:function(){var h=oj(!1);return h=SU.bind(null,Xn,h.queue,!0,!1),Qs().memoizedState=h,[!1,h]},useSyncExternalStore:function(h,y,S){var M=Xn,$=Qs();if(vr){if(S===void 0)throw Error(r(407));S=S()}else{if(S=y(),ia===null)throw Error(r(349));(br&127)!==0||Q5(M,y,S)}$.memoizedState=S;var Z={value:S,getSnapshot:y};return $.queue=Z,hU(J5.bind(null,M,Z,h),[h]),M.flags|=2048,ob(9,{destroy:void 0},Z5.bind(null,M,Z,S,y),null),S},useId:function(){var h=Qs(),y=ia.identifierPrefix;if(vr){var S=dd,M=cd;S=(M&~(1<<32-qe(M)-1)).toString(32)+S,y="_"+y+"R_"+S,S=o_++,0<\/script>",Z=Z.removeChild(Z.firstChild);break;case"select":Z=typeof M.is=="string"?Ne.createElement("select",{is:M.is}):Ne.createElement("select"),M.multiple?Z.multiple=!0:M.size&&(Z.size=M.size);break;default:Z=typeof M.is=="string"?Ne.createElement($,{is:M.is}):Ne.createElement($)}}Z[at]=y,Z[ct]=M;e:for(Ne=y.child;Ne!==null;){if(Ne.tag===5||Ne.tag===6)Z.appendChild(Ne.stateNode);else if(Ne.tag!==4&&Ne.tag!==27&&Ne.child!==null){Ne.child.return=Ne,Ne=Ne.child;continue}if(Ne===y)break e;for(;Ne.sibling===null;){if(Ne.return===null||Ne.return===y)break e;Ne=Ne.return}Ne.sibling.return=Ne.return,Ne=Ne.sibling}y.stateNode=Z;e:switch(cs(Z,$,M),$){case"button":case"input":case"select":case"textarea":M=!!M.autoFocus;break e;case"img":M=!0;break e;default:M=!1}M&&fu(y)}}return wa(y),Tj(y,y.type,h===null?null:h.memoizedProps,y.pendingProps,S),null;case 6:if(h&&y.stateNode!=null)h.memoizedProps!==M&&fu(y);else{if(typeof M!="string"&&y.stateNode===null)throw Error(r(166));if(h=q.current,Zg(y)){if(h=y.stateNode,S=y.memoizedProps,M=null,$=is,$!==null)switch($.tag){case 27:case 5:M=$.memoizedProps}h[at]=y,h=!!(h.nodeValue===S||M!==null&&M.suppressHydrationWarning===!0||XB(h.nodeValue,S)),h||qm(y,!0)}else h=D_(h).createTextNode(M),h[at]=y,y.stateNode=h}return wa(y),null;case 31:if(S=y.memoizedState,h===null||h.memoizedState!==null){if(M=Zg(y),S!==null){if(h===null){if(!M)throw Error(r(318));if(h=y.memoizedState,h=h!==null?h.dehydrated:null,!h)throw Error(r(557));h[at]=y}else Yp(),(y.flags&128)===0&&(y.memoizedState=null),y.flags|=4;wa(y),h=!1}else S=IA(),h!==null&&h.memoizedState!==null&&(h.memoizedState.hydrationErrors=S),h=!0;if(!h)return y.flags&256?(Jo(y),y):(Jo(y),null);if((y.flags&128)!==0)throw Error(r(558))}return wa(y),null;case 13:if(M=y.memoizedState,h===null||h.memoizedState!==null&&h.memoizedState.dehydrated!==null){if($=Zg(y),M!==null&&M.dehydrated!==null){if(h===null){if(!$)throw Error(r(318));if($=y.memoizedState,$=$!==null?$.dehydrated:null,!$)throw Error(r(317));$[at]=y}else Yp(),(y.flags&128)===0&&(y.memoizedState=null),y.flags|=4;wa(y),$=!1}else $=IA(),h!==null&&h.memoizedState!==null&&(h.memoizedState.hydrationErrors=$),$=!0;if(!$)return y.flags&256?(Jo(y),y):(Jo(y),null)}return Jo(y),(y.flags&128)!==0?(y.lanes=S,y):(S=M!==null,h=h!==null&&h.memoizedState!==null,S&&(M=y.child,$=null,M.alternate!==null&&M.alternate.memoizedState!==null&&M.alternate.memoizedState.cachePool!==null&&($=M.alternate.memoizedState.cachePool.pool),Z=null,M.memoizedState!==null&&M.memoizedState.cachePool!==null&&(Z=M.memoizedState.cachePool.pool),Z!==$&&(M.flags|=2048)),S!==h&&S&&(y.child.flags|=8192),x_(y,y.updateQueue),wa(y),null);case 4:return j(),h===null&&Yj(y.stateNode.containerInfo),wa(y),null;case 10:return uu(y.type),wa(y),null;case 19:if(ne(ii),M=y.memoizedState,M===null)return wa(y),null;if($=(y.flags&128)!==0,Z=M.rendering,Z===null)if($)dv(M,!1);else{if($a!==0||h!==null&&(h.flags&128)!==0)for(h=y.child;h!==null;){if(Z=i_(h),Z!==null){for(y.flags|=128,dv(M,!1),h=Z.updateQueue,y.updateQueue=h,x_(y,h),y.subtreeFlags=0,h=S,S=y.child;S!==null;)P5(S,h),S=S.sibling;return me(ii,ii.current&1|2),vr&&cu(y,M.treeForkCount),y.child}h=h.sibling}M.tail!==null&&R()>__&&(y.flags|=128,$=!0,dv(M,!1),y.lanes=4194304)}else{if(!$)if(h=i_(Z),h!==null){if(y.flags|=128,$=!0,h=h.updateQueue,y.updateQueue=h,x_(y,h),dv(M,!0),M.tail===null&&M.tailMode==="hidden"&&!Z.alternate&&!vr)return wa(y),null}else 2*R()-M.renderingStartTime>__&&S!==536870912&&(y.flags|=128,$=!0,dv(M,!1),y.lanes=4194304);M.isBackwards?(Z.sibling=y.child,y.child=Z):(h=M.last,h!==null?h.sibling=Z:y.child=Z,M.last=Z)}return M.tail!==null?(h=M.tail,M.rendering=h,M.tail=h.sibling,M.renderingStartTime=R(),h.sibling=null,S=ii.current,me(ii,$?S&1|2:S&1),vr&&cu(y,M.treeForkCount),h):(wa(y),null);case 22:case 23:return Jo(y),QA(),M=y.memoizedState!==null,h!==null?h.memoizedState!==null!==M&&(y.flags|=8192):M&&(y.flags|=8192),M?(S&536870912)!==0&&(y.flags&128)===0&&(wa(y),y.subtreeFlags&6&&(y.flags|=8192)):wa(y),S=y.updateQueue,S!==null&&x_(y,S.retryQueue),S=null,h!==null&&h.memoizedState!==null&&h.memoizedState.cachePool!==null&&(S=h.memoizedState.cachePool.pool),M=null,y.memoizedState!==null&&y.memoizedState.cachePool!==null&&(M=y.memoizedState.cachePool.pool),M!==S&&(y.flags|=2048),h!==null&&ne(Jp),null;case 24:return S=null,h!==null&&(S=h.memoizedState.cache),y.memoizedState.cache!==S&&(y.flags|=2048),uu(hi),wa(y),null;case 25:return null;case 30:return null}throw Error(r(156,y.tag))}function Rce(h,y){switch(LA(y),y.tag){case 1:return h=y.flags,h&65536?(y.flags=h&-65537|128,y):null;case 3:return uu(hi),j(),h=y.flags,(h&65536)!==0&&(h&128)===0?(y.flags=h&-65537|128,y):null;case 26:case 27:case 5:return K(y),null;case 31:if(y.memoizedState!==null){if(Jo(y),y.alternate===null)throw Error(r(340));Yp()}return h=y.flags,h&65536?(y.flags=h&-65537|128,y):null;case 13:if(Jo(y),h=y.memoizedState,h!==null&&h.dehydrated!==null){if(y.alternate===null)throw Error(r(340));Yp()}return h=y.flags,h&65536?(y.flags=h&-65537|128,y):null;case 19:return ne(ii),null;case 4:return j(),null;case 10:return uu(y.type),null;case 22:case 23:return Jo(y),QA(),h!==null&&ne(Jp),h=y.flags,h&65536?(y.flags=h&-65537|128,y):null;case 24:return uu(hi),null;case 25:return null;default:return null}}function eB(h,y){switch(LA(y),y.tag){case 3:uu(hi),j();break;case 26:case 27:case 5:K(y);break;case 4:j();break;case 31:y.memoizedState!==null&&Jo(y);break;case 13:Jo(y);break;case 19:ne(ii);break;case 10:uu(y.type);break;case 22:case 23:Jo(y),QA(),h!==null&&ne(Jp);break;case 24:uu(hi)}}function uv(h,y){try{var S=y.updateQueue,M=S!==null?S.lastEffect:null;if(M!==null){var $=M.next;S=$;do{if((S.tag&h)===h){M=void 0;var Z=S.create,Ne=S.inst;M=Z(),Ne.destroy=M}S=S.next}while(S!==$)}}catch(He){qr(y,y.return,He)}}function Ym(h,y,S){try{var M=y.updateQueue,$=M!==null?M.lastEffect:null;if($!==null){var Z=$.next;M=Z;do{if((M.tag&h)===h){var Ne=M.inst,He=Ne.destroy;if(He!==void 0){Ne.destroy=void 0,$=y;var nt=S,Nt=He;try{Nt()}catch(Lt){qr($,nt,Lt)}}}M=M.next}while(M!==Z)}}catch(Lt){qr(y,y.return,Lt)}}function tB(h){var y=h.updateQueue;if(y!==null){var S=h.stateNode;try{V5(y,S)}catch(M){qr(h,h.return,M)}}}function nB(h,y,S){S.props=af(h.type,h.memoizedProps),S.state=h.memoizedState;try{S.componentWillUnmount()}catch(M){qr(h,y,M)}}function mv(h,y){try{var S=h.ref;if(S!==null){switch(h.tag){case 26:case 27:case 5:var M=h.stateNode;break;case 30:M=h.stateNode;break;default:M=h.stateNode}typeof S=="function"?h.refCleanup=S(M):S.current=M}}catch($){qr(h,y,$)}}function ud(h,y){var S=h.ref,M=h.refCleanup;if(S!==null)if(typeof M=="function")try{M()}catch($){qr(h,y,$)}finally{h.refCleanup=null,h=h.alternate,h!=null&&(h.refCleanup=null)}else if(typeof S=="function")try{S(null)}catch($){qr(h,y,$)}else S.current=null}function rB(h){var y=h.type,S=h.memoizedProps,M=h.stateNode;try{e:switch(y){case"button":case"input":case"select":case"textarea":S.autoFocus&&M.focus();break e;case"img":S.src?M.src=S.src:S.srcSet&&(M.srcset=S.srcSet)}}catch($){qr(h,h.return,$)}}function Aj(h,y,S){try{var M=h.stateNode;rde(M,h.type,S,y),M[ct]=y}catch($){qr(h,h.return,$)}}function aB(h){return h.tag===5||h.tag===3||h.tag===26||h.tag===27&&rh(h.type)||h.tag===4}function jj(h){e:for(;;){for(;h.sibling===null;){if(h.return===null||aB(h.return))return null;h=h.return}for(h.sibling.return=h.return,h=h.sibling;h.tag!==5&&h.tag!==6&&h.tag!==18;){if(h.tag===27&&rh(h.type)||h.flags&2||h.child===null||h.tag===4)continue e;h.child.return=h,h=h.child}if(!(h.flags&2))return h.stateNode}}function Mj(h,y,S){var M=h.tag;if(M===5||M===6)h=h.stateNode,y?(S.nodeType===9?S.body:S.nodeName==="HTML"?S.ownerDocument.body:S).insertBefore(h,y):(y=S.nodeType===9?S.body:S.nodeName==="HTML"?S.ownerDocument.body:S,y.appendChild(h),S=S._reactRootContainer,S!=null||y.onclick!==null||(y.onclick=qt));else if(M!==4&&(M===27&&rh(h.type)&&(S=h.stateNode,y=null),h=h.child,h!==null))for(Mj(h,y,S),h=h.sibling;h!==null;)Mj(h,y,S),h=h.sibling}function y_(h,y,S){var M=h.tag;if(M===5||M===6)h=h.stateNode,y?S.insertBefore(h,y):S.appendChild(h);else if(M!==4&&(M===27&&rh(h.type)&&(S=h.stateNode),h=h.child,h!==null))for(y_(h,y,S),h=h.sibling;h!==null;)y_(h,y,S),h=h.sibling}function iB(h){var y=h.stateNode,S=h.memoizedProps;try{for(var M=h.type,$=y.attributes;$.length;)y.removeAttributeNode($[0]);cs(y,M,S),y[at]=h,y[ct]=S}catch(Z){qr(h,h.return,Z)}}var gu=!1,gi=!1,Ej=!1,sB=typeof WeakSet=="function"?WeakSet:Set,qi=null;function Lce(h,y){if(h=h.containerInfo,Jj=U_,h=$S(h),NA(h)){if("selectionStart"in h)var S={start:h.selectionStart,end:h.selectionEnd};else e:{S=(S=h.ownerDocument)&&S.defaultView||window;var M=S.getSelection&&S.getSelection();if(M&&M.rangeCount!==0){S=M.anchorNode;var $=M.anchorOffset,Z=M.focusNode;M=M.focusOffset;try{S.nodeType,Z.nodeType}catch{S=null;break e}var Ne=0,He=-1,nt=-1,Nt=0,Lt=0,zt=h,Ct=null;t:for(;;){for(var Et;zt!==S||$!==0&&zt.nodeType!==3||(He=Ne+$),zt!==Z||M!==0&&zt.nodeType!==3||(nt=Ne+M),zt.nodeType===3&&(Ne+=zt.nodeValue.length),(Et=zt.firstChild)!==null;)Ct=zt,zt=Et;for(;;){if(zt===h)break t;if(Ct===S&&++Nt===$&&(He=Ne),Ct===Z&&++Lt===M&&(nt=Ne),(Et=zt.nextSibling)!==null)break;zt=Ct,Ct=zt.parentNode}zt=Et}S=He===-1||nt===-1?null:{start:He,end:nt}}else S=null}S=S||{start:0,end:0}}else S=null;for(eM={focusedElem:h,selectionRange:S},U_=!1,qi=y;qi!==null;)if(y=qi,h=y.child,(y.subtreeFlags&1028)!==0&&h!==null)h.return=y,qi=h;else for(;qi!==null;){switch(y=qi,Z=y.alternate,h=y.flags,y.tag){case 0:if((h&4)!==0&&(h=y.updateQueue,h=h!==null?h.events:null,h!==null))for(S=0;S title"))),cs(Z,M,S),Z[at]=h,Ke(Z),M=Z;break e;case"link":var Ne=m8("link","href",$).get(M+(S.href||""));if(Ne){for(var He=0;HeXr&&(Ne=Xr,Xr=Rn,Rn=Ne);var bt=Wy(He,Rn),mt=Wy(He,Xr);if(bt&&mt&&(Et.rangeCount!==1||Et.anchorNode!==bt.node||Et.anchorOffset!==bt.offset||Et.focusNode!==mt.node||Et.focusOffset!==mt.offset)){var kt=zt.createRange();kt.setStart(bt.node,bt.offset),Et.removeAllRanges(),Rn>Xr?(Et.addRange(kt),Et.extend(mt.node,mt.offset)):(kt.setEnd(mt.node,mt.offset),Et.addRange(kt))}}}}for(zt=[],Et=He;Et=Et.parentNode;)Et.nodeType===1&&zt.push({element:Et,left:Et.scrollLeft,top:Et.scrollTop});for(typeof He.focus=="function"&&He.focus(),He=0;HeS?32:S,ie.T=null,S=zj,zj=null;var Z=eh,Ne=wu;if(Pi=0,mb=eh=null,wu=0,(Lr&6)!==0)throw Error(r(331));var He=Lr;if(Lr|=4,bB(Z.current),pB(Z,Z.current,Ne,S),Lr=He,xv(0,!1),Le&&typeof Le.onPostCommitFiberRoot=="function")try{Le.onPostCommitFiberRoot(Me,Z)}catch{}return!0}finally{J.p=$,ie.T=M,RB(h,y)}}function OB(h,y,S){y=jl(S,y),y=xj(h.stateNode,y,2),h=Wm(h,y,2),h!==null&&(xt(h,2),md(h))}function qr(h,y,S){if(h.tag===3)OB(h,h,S);else for(;y!==null;){if(y.tag===3){OB(y,h,S);break}else if(y.tag===1){var M=y.stateNode;if(typeof y.type.getDerivedStateFromError=="function"||typeof M.componentDidCatch=="function"&&(Jm===null||!Jm.has(M))){h=jl(S,h),S=IU(2),M=Wm(y,S,2),M!==null&&(zU(S,M,y,h),xt(M,2),md(M));break}}y=y.return}}function qj(h,y,S){var M=h.pingCache;if(M===null){M=h.pingCache=new zce;var $=new Set;M.set(y,$)}else $=M.get(y),$===void 0&&($=new Set,M.set(y,$));$.has(S)||(Rj=!0,$.add(S),h=$ce.bind(null,h,y,S),y.then(h,h))}function $ce(h,y,S){var M=h.pingCache;M!==null&&M.delete(y),h.pingedLanes|=h.suspendedLanes&S,h.warmLanes&=~S,ia===h&&(br&S)===S&&($a===4||$a===3&&(br&62914560)===br&&300>R()-S_?(Lr&2)===0&&hb(h,0):Lj|=S,ub===br&&(ub=0)),md(h)}function IB(h,y){y===0&&(y=Ze()),h=Kp(h,y),h!==null&&(xt(h,y),md(h))}function Vce(h){var y=h.memoizedState,S=0;y!==null&&(S=y.retryLane),IB(h,S)}function Gce(h,y){var S=0;switch(h.tag){case 31:case 13:var M=h.stateNode,$=h.memoizedState;$!==null&&(S=$.retryLane);break;case 19:M=h.stateNode;break;case 22:M=h.stateNode._retryCache;break;default:throw Error(r(314))}M!==null&&M.delete(y),IB(h,S)}function Wce(h,y){return ge(h,y)}var A_=null,fb=null,$j=!1,j_=!1,Vj=!1,nh=0;function md(h){h!==fb&&h.next===null&&(fb===null?A_=fb=h:fb=fb.next=h),j_=!0,$j||($j=!0,Xce())}function xv(h,y){if(!Vj&&j_){Vj=!0;do for(var S=!1,M=A_;M!==null;){if(h!==0){var $=M.pendingLanes;if($===0)var Z=0;else{var Ne=M.suspendedLanes,He=M.pingedLanes;Z=(1<<31-qe(42|h)+1)-1,Z&=$&~(Ne&~He),Z=Z&201326741?Z&201326741|1:Z?Z|2:0}Z!==0&&(S=!0,HB(M,Z))}else Z=br,Z=ce(M,M===ia?Z:0,M.cancelPendingCommit!==null||M.timeoutHandle!==-1),(Z&3)===0||xe(M,Z)||(S=!0,HB(M,Z));M=M.next}while(S);Vj=!1}}function Kce(){zB()}function zB(){j_=$j=!1;var h=0;nh!==0&&ide()&&(h=nh);for(var y=R(),S=null,M=A_;M!==null;){var $=M.next,Z=UB(M,y);Z===0?(M.next=null,S===null?A_=$:S.next=$,$===null&&(fb=S)):(S=M,(h!==0||(Z&3)!==0)&&(j_=!0)),M=$}Pi!==0&&Pi!==5||xv(h),nh!==0&&(nh=0)}function UB(h,y){for(var S=h.suspendedLanes,M=h.pingedLanes,$=h.expirationTimes,Z=h.pendingLanes&-62914561;0He)break;var Lt=nt.transferSize,zt=nt.initiatorType;Lt&&YB(zt)&&(nt=nt.responseEnd,Ne+=Lt*(nt"u"?null:document;function l8(h,y,S){var M=gb;if(M&&typeof y=="string"&&y){var $=Fn(y);$='link[rel="'+h+'"][href="'+$+'"]',typeof S=="string"&&($+='[crossorigin="'+S+'"]'),o8.has($)||(o8.add($),h={rel:h,crossOrigin:S,href:y},M.querySelector($)===null&&(y=M.createElement("link"),cs(y,"link",h),Ke(y),M.head.appendChild(y)))}}function pde(h){Su.D(h),l8("dns-prefetch",h,null)}function fde(h,y){Su.C(h,y),l8("preconnect",h,y)}function gde(h,y,S){Su.L(h,y,S);var M=gb;if(M&&h&&y){var $='link[rel="preload"][as="'+Fn(y)+'"]';y==="image"&&S&&S.imageSrcSet?($+='[imagesrcset="'+Fn(S.imageSrcSet)+'"]',typeof S.imageSizes=="string"&&($+='[imagesizes="'+Fn(S.imageSizes)+'"]')):$+='[href="'+Fn(h)+'"]';var Z=$;switch(y){case"style":Z=bb(h);break;case"script":Z=xb(h)}Ll.has(Z)||(h=m({rel:"preload",href:y==="image"&&S&&S.imageSrcSet?void 0:h,as:y},S),Ll.set(Z,h),M.querySelector($)!==null||y==="style"&&M.querySelector(Sv(Z))||y==="script"&&M.querySelector(_v(Z))||(y=M.createElement("link"),cs(y,"link",h),Ke(y),M.head.appendChild(y)))}}function bde(h,y){Su.m(h,y);var S=gb;if(S&&h){var M=y&&typeof y.as=="string"?y.as:"script",$='link[rel="modulepreload"][as="'+Fn(M)+'"][href="'+Fn(h)+'"]',Z=$;switch(M){case"audioworklet":case"paintworklet":case"serviceworker":case"sharedworker":case"worker":case"script":Z=xb(h)}if(!Ll.has(Z)&&(h=m({rel:"modulepreload",href:h},y),Ll.set(Z,h),S.querySelector($)===null)){switch(M){case"audioworklet":case"paintworklet":case"serviceworker":case"sharedworker":case"worker":case"script":if(S.querySelector(_v(Z)))return}M=S.createElement("link"),cs(M,"link",h),Ke(M),S.head.appendChild(M)}}}function xde(h,y,S){Su.S(h,y,S);var M=gb;if(M&&h){var $=et(M).hoistableStyles,Z=bb(h);y=y||"default";var Ne=$.get(Z);if(!Ne){var He={loading:0,preload:null};if(Ne=M.querySelector(Sv(Z)))He.loading=5;else{h=m({rel:"stylesheet",href:h,"data-precedence":y},S),(S=Ll.get(Z))&&oM(h,S);var nt=Ne=M.createElement("link");Ke(nt),cs(nt,"link",h),nt._p=new Promise(function(Nt,Lt){nt.onload=Nt,nt.onerror=Lt}),nt.addEventListener("load",function(){He.loading|=1}),nt.addEventListener("error",function(){He.loading|=2}),He.loading|=4,R_(Ne,y,M)}Ne={type:"stylesheet",instance:Ne,count:1,state:He},$.set(Z,Ne)}}}function yde(h,y){Su.X(h,y);var S=gb;if(S&&h){var M=et(S).hoistableScripts,$=xb(h),Z=M.get($);Z||(Z=S.querySelector(_v($)),Z||(h=m({src:h,async:!0},y),(y=Ll.get($))&&lM(h,y),Z=S.createElement("script"),Ke(Z),cs(Z,"link",h),S.head.appendChild(Z)),Z={type:"script",instance:Z,count:1,state:null},M.set($,Z))}}function vde(h,y){Su.M(h,y);var S=gb;if(S&&h){var M=et(S).hoistableScripts,$=xb(h),Z=M.get($);Z||(Z=S.querySelector(_v($)),Z||(h=m({src:h,async:!0,type:"module"},y),(y=Ll.get($))&&lM(h,y),Z=S.createElement("script"),Ke(Z),cs(Z,"link",h),S.head.appendChild(Z)),Z={type:"script",instance:Z,count:1,state:null},M.set($,Z))}}function c8(h,y,S,M){var $=($=q.current)?F_($):null;if(!$)throw Error(r(446));switch(h){case"meta":case"title":return null;case"style":return typeof S.precedence=="string"&&typeof S.href=="string"?(y=bb(S.href),S=et($).hoistableStyles,M=S.get(y),M||(M={type:"style",instance:null,count:0,state:null},S.set(y,M)),M):{type:"void",instance:null,count:0,state:null};case"link":if(S.rel==="stylesheet"&&typeof S.href=="string"&&typeof S.precedence=="string"){h=bb(S.href);var Z=et($).hoistableStyles,Ne=Z.get(h);if(Ne||($=$.ownerDocument||$,Ne={type:"stylesheet",instance:null,count:0,state:{loading:0,preload:null}},Z.set(h,Ne),(Z=$.querySelector(Sv(h)))&&!Z._p&&(Ne.instance=Z,Ne.state.loading=5),Ll.has(h)||(S={rel:"preload",as:"style",href:S.href,crossOrigin:S.crossOrigin,integrity:S.integrity,media:S.media,hrefLang:S.hrefLang,referrerPolicy:S.referrerPolicy},Ll.set(h,S),Z||wde($,h,S,Ne.state))),y&&M===null)throw Error(r(528,""));return Ne}if(y&&M!==null)throw Error(r(529,""));return null;case"script":return y=S.async,S=S.src,typeof S=="string"&&y&&typeof y!="function"&&typeof y!="symbol"?(y=xb(S),S=et($).hoistableScripts,M=S.get(y),M||(M={type:"script",instance:null,count:0,state:null},S.set(y,M)),M):{type:"void",instance:null,count:0,state:null};default:throw Error(r(444,h))}}function bb(h){return'href="'+Fn(h)+'"'}function Sv(h){return'link[rel="stylesheet"]['+h+"]"}function d8(h){return m({},h,{"data-precedence":h.precedence,precedence:null})}function wde(h,y,S,M){h.querySelector('link[rel="preload"][as="style"]['+y+"]")?M.loading=1:(y=h.createElement("link"),M.preload=y,y.addEventListener("load",function(){return M.loading|=1}),y.addEventListener("error",function(){return M.loading|=2}),cs(y,"link",S),Ke(y),h.head.appendChild(y))}function xb(h){return'[src="'+Fn(h)+'"]'}function _v(h){return"script[async]"+h}function u8(h,y,S){if(y.count++,y.instance===null)switch(y.type){case"style":var M=h.querySelector('style[data-href~="'+Fn(S.href)+'"]');if(M)return y.instance=M,Ke(M),M;var $=m({},S,{"data-href":S.href,"data-precedence":S.precedence,href:null,precedence:null});return M=(h.ownerDocument||h).createElement("style"),Ke(M),cs(M,"style",$),R_(M,S.precedence,h),y.instance=M;case"stylesheet":$=bb(S.href);var Z=h.querySelector(Sv($));if(Z)return y.state.loading|=4,y.instance=Z,Ke(Z),Z;M=d8(S),($=Ll.get($))&&oM(M,$),Z=(h.ownerDocument||h).createElement("link"),Ke(Z);var Ne=Z;return Ne._p=new Promise(function(He,nt){Ne.onload=He,Ne.onerror=nt}),cs(Z,"link",M),y.state.loading|=4,R_(Z,S.precedence,h),y.instance=Z;case"script":return Z=xb(S.src),($=h.querySelector(_v(Z)))?(y.instance=$,Ke($),$):(M=S,($=Ll.get(Z))&&(M=m({},S),lM(M,$)),h=h.ownerDocument||h,$=h.createElement("script"),Ke($),cs($,"link",M),h.head.appendChild($),y.instance=$);case"void":return null;default:throw Error(r(443,y.type))}else y.type==="stylesheet"&&(y.state.loading&4)===0&&(M=y.instance,y.state.loading|=4,R_(M,S.precedence,h));return y.instance}function R_(h,y,S){for(var M=S.querySelectorAll('link[rel="stylesheet"][data-precedence],style[data-precedence]'),$=M.length?M[M.length-1]:null,Z=$,Ne=0;Ne title"):null)}function Sde(h,y,S){if(S===1||y.itemProp!=null)return!1;switch(h){case"meta":case"title":return!0;case"style":if(typeof y.precedence!="string"||typeof y.href!="string"||y.href==="")break;return!0;case"link":if(typeof y.rel!="string"||typeof y.href!="string"||y.href===""||y.onLoad||y.onError)break;return y.rel==="stylesheet"?(h=y.disabled,typeof y.precedence=="string"&&h==null):!0;case"script":if(y.async&&typeof y.async!="function"&&typeof y.async!="symbol"&&!y.onLoad&&!y.onError&&y.src&&typeof y.src=="string")return!0}return!1}function p8(h){return!(h.type==="stylesheet"&&(h.state.loading&3)===0)}function _de(h,y,S,M){if(S.type==="stylesheet"&&(typeof M.media!="string"||matchMedia(M.media).matches!==!1)&&(S.state.loading&4)===0){if(S.instance===null){var $=bb(M.href),Z=y.querySelector(Sv($));if(Z){y=Z._p,y!==null&&typeof y=="object"&&typeof y.then=="function"&&(h.count++,h=O_.bind(h),y.then(h,h)),S.state.loading|=4,S.instance=Z,Ke(Z);return}Z=y.ownerDocument||y,M=d8(M),($=Ll.get($))&&oM(M,$),Z=Z.createElement("link"),Ke(Z);var Ne=Z;Ne._p=new Promise(function(He,nt){Ne.onload=He,Ne.onerror=nt}),cs(Z,"link",M),S.instance=Z}h.stylesheets===null&&(h.stylesheets=new Map),h.stylesheets.set(S,y),(y=S.state.preload)&&(S.state.loading&3)===0&&(h.count++,S=O_.bind(h),y.addEventListener("load",S),y.addEventListener("error",S))}}var cM=0;function kde(h,y){return h.stylesheets&&h.count===0&&z_(h,h.stylesheets),0cM?50:800)+y);return h.unsuspend=S,function(){h.unsuspend=null,clearTimeout(M),clearTimeout($)}}:null}function O_(){if(this.count--,this.count===0&&(this.imgCount===0||!this.waitingForImages)){if(this.stylesheets)z_(this,this.stylesheets);else if(this.unsuspend){var h=this.unsuspend;this.unsuspend=null,h()}}}var I_=null;function z_(h,y){h.stylesheets=null,h.unsuspend!==null&&(h.count++,I_=new Map,y.forEach(Nde,h),I_=null,O_.call(h))}function Nde(h,y){if(!(y.state.loading&4)){var S=I_.get(h);if(S)var M=S.get(null);else{S=new Map,I_.set(h,S);for(var $=h.querySelectorAll("link[data-precedence],style[data-precedence]"),Z=0;Z<$.length;Z++){var Ne=$[Z];(Ne.nodeName==="LINK"||Ne.getAttribute("media")!=="not all")&&(S.set(Ne.dataset.precedence,Ne),M=Ne)}M&&S.set(null,M)}$=y.instance,Ne=$.getAttribute("data-precedence"),Z=S.get(Ne)||M,Z===M&&S.set(null,$),S.set(Ne,$),this.count++,M=O_.bind(this),$.addEventListener("load",M),$.addEventListener("error",M),Z?Z.parentNode.insertBefore($,Z.nextSibling):(h=h.nodeType===9?h.head:h,h.insertBefore($,h.firstChild)),y.state.loading|=4}}var kv={$$typeof:C,Provider:null,Consumer:null,_currentValue:oe,_currentValue2:oe,_threadCount:0};function Cde(h,y,S,M,$,Z,Ne,He,nt){this.tag=1,this.containerInfo=h,this.pingCache=this.current=this.pendingChildren=null,this.timeoutHandle=-1,this.callbackNode=this.next=this.pendingContext=this.context=this.cancelPendingCommit=null,this.callbackPriority=0,this.expirationTimes=pt(-1),this.entangledLanes=this.shellSuspendCounter=this.errorRecoveryDisabledLanes=this.expiredLanes=this.warmLanes=this.pingedLanes=this.suspendedLanes=this.pendingLanes=0,this.entanglements=pt(0),this.hiddenUpdates=pt(null),this.identifierPrefix=M,this.onUncaughtError=$,this.onCaughtError=Z,this.onRecoverableError=Ne,this.pooledCache=null,this.pooledCacheLanes=0,this.formState=nt,this.incompleteTransitions=new Map}function f8(h,y,S,M,$,Z,Ne,He,nt,Nt,Lt,zt){return h=new Cde(h,y,S,Ne,nt,Nt,Lt,zt,He),y=1,Z===!0&&(y|=24),Z=Qo(3,null,null,y),h.current=Z,Z.stateNode=h,y=HA(),y.refCount++,h.pooledCache=y,y.refCount++,Z.memoizedState={element:M,isDehydrated:S,cache:y},GA(Z),h}function g8(h){return h?(h=Xg,h):Xg}function b8(h,y,S,M,$,Z){$=g8($),M.context===null?M.context=$:M.pendingContext=$,M=Gm(y),M.payload={element:S},Z=Z===void 0?null:Z,Z!==null&&(M.callback=Z),S=Wm(h,M,y),S!==null&&(Ao(S,h,y),nv(S,h,y))}function x8(h,y){if(h=h.memoizedState,h!==null&&h.dehydrated!==null){var S=h.retryLane;h.retryLane=S!==0&&S"u"||typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE!="function"))try{__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(t)}catch(e){console.error(e)}}return t(),xM.exports=Hde(),xM.exports}var $de=qde();const Hn=t=>typeof t=="string",jv=()=>{let t,e;const n=new Promise((r,i)=>{t=r,e=i});return n.resolve=t,n.reject=e,n},I8=t=>t==null?"":""+t,Vde=(t,e,n)=>{t.forEach(r=>{e[r]&&(n[r]=e[r])})},Gde=/###/g,z8=t=>t&&t.indexOf("###")>-1?t.replace(Gde,"."):t,U8=t=>!t||Hn(t),P0=(t,e,n)=>{const r=Hn(e)?e.split("."):e;let i=0;for(;i{const{obj:r,k:i}=P0(t,e,Object);if(r!==void 0||e.length===1){r[i]=n;return}let s=e[e.length-1],o=e.slice(0,e.length-1),l=P0(t,o,Object);for(;l.obj===void 0&&o.length;)s=`${o[o.length-1]}.${s}`,o=o.slice(0,o.length-1),l=P0(t,o,Object),l?.obj&&typeof l.obj[`${l.k}.${s}`]<"u"&&(l.obj=void 0);l.obj[`${l.k}.${s}`]=n},Wde=(t,e,n,r)=>{const{obj:i,k:s}=P0(t,e,Object);i[s]=i[s]||[],i[s].push(n)},hN=(t,e)=>{const{obj:n,k:r}=P0(t,e);if(n&&Object.prototype.hasOwnProperty.call(n,r))return n[r]},Kde=(t,e,n)=>{const r=hN(t,n);return r!==void 0?r:hN(e,n)},BY=(t,e,n)=>{for(const r in e)r!=="__proto__"&&r!=="constructor"&&(r in t?Hn(t[r])||t[r]instanceof String||Hn(e[r])||e[r]instanceof String?n&&(t[r]=e[r]):BY(t[r],e[r],n):t[r]=e[r]);return t},vb=t=>t.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g,"\\$&");var Xde={"&":"&","<":"<",">":">",'"':""","'":"'","/":"/"};const Yde=t=>Hn(t)?t.replace(/[&<>"'\/]/g,e=>Xde[e]):t;class Qde{constructor(e){this.capacity=e,this.regExpMap=new Map,this.regExpQueue=[]}getRegExp(e){const n=this.regExpMap.get(e);if(n!==void 0)return n;const r=new RegExp(e);return this.regExpQueue.length===this.capacity&&this.regExpMap.delete(this.regExpQueue.shift()),this.regExpMap.set(e,r),this.regExpQueue.push(e),r}}const Zde=[" ",",","?","!",";"],Jde=new Qde(20),eue=(t,e,n)=>{e=e||"",n=n||"";const r=Zde.filter(o=>e.indexOf(o)<0&&n.indexOf(o)<0);if(r.length===0)return!0;const i=Jde.getRegExp(`(${r.map(o=>o==="?"?"\\?":o).join("|")})`);let s=!i.test(t);if(!s){const o=t.indexOf(n);o>0&&!i.test(t.substring(0,o))&&(s=!0)}return s},H3=(t,e,n=".")=>{if(!t)return;if(t[e])return Object.prototype.hasOwnProperty.call(t,e)?t[e]:void 0;const r=e.split(n);let i=t;for(let s=0;s-1&&ct?.replace("_","-"),tue={type:"logger",log(t){this.output("log",t)},warn(t){this.output("warn",t)},error(t){this.output("error",t)},output(t,e){console?.[t]?.apply?.(console,e)}};class pN{constructor(e,n={}){this.init(e,n)}init(e,n={}){this.prefix=n.prefix||"i18next:",this.logger=e||tue,this.options=n,this.debug=n.debug}log(...e){return this.forward(e,"log","",!0)}warn(...e){return this.forward(e,"warn","",!0)}error(...e){return this.forward(e,"error","")}deprecate(...e){return this.forward(e,"warn","WARNING DEPRECATED: ",!0)}forward(e,n,r,i){return i&&!this.debug?null:(Hn(e[0])&&(e[0]=`${r}${this.prefix} ${e[0]}`),this.logger[n](e))}create(e){return new pN(this.logger,{prefix:`${this.prefix}:${e}:`,...this.options})}clone(e){return e=e||this.options,e.prefix=e.prefix||this.prefix,new pN(this.logger,e)}}var Ad=new pN;let zP=class{constructor(){this.observers={}}on(e,n){return e.split(" ").forEach(r=>{this.observers[r]||(this.observers[r]=new Map);const i=this.observers[r].get(n)||0;this.observers[r].set(n,i+1)}),this}off(e,n){if(this.observers[e]){if(!n){delete this.observers[e];return}this.observers[e].delete(n)}}emit(e,...n){this.observers[e]&&Array.from(this.observers[e].entries()).forEach(([i,s])=>{for(let o=0;o{for(let o=0;o-1&&this.options.ns.splice(n,1)}getResource(e,n,r,i={}){const s=i.keySeparator!==void 0?i.keySeparator:this.options.keySeparator,o=i.ignoreJSONStructure!==void 0?i.ignoreJSONStructure:this.options.ignoreJSONStructure;let l;e.indexOf(".")>-1?l=e.split("."):(l=[e,n],r&&(Array.isArray(r)?l.push(...r):Hn(r)&&s?l.push(...r.split(s)):l.push(r)));const c=hN(this.data,l);return!c&&!n&&!r&&e.indexOf(".")>-1&&(e=l[0],n=l[1],r=l.slice(2).join(".")),c||!o||!Hn(r)?c:H3(this.data?.[e]?.[n],r,s)}addResource(e,n,r,i,s={silent:!1}){const o=s.keySeparator!==void 0?s.keySeparator:this.options.keySeparator;let l=[e,n];r&&(l=l.concat(o?r.split(o):r)),e.indexOf(".")>-1&&(l=e.split("."),i=n,n=l[1]),this.addNamespaces(n),B8(this.data,l,i),s.silent||this.emit("added",e,n,r,i)}addResources(e,n,r,i={silent:!1}){for(const s in r)(Hn(r[s])||Array.isArray(r[s]))&&this.addResource(e,n,s,r[s],{silent:!0});i.silent||this.emit("added",e,n,r)}addResourceBundle(e,n,r,i,s,o={silent:!1,skipCopy:!1}){let l=[e,n];e.indexOf(".")>-1&&(l=e.split("."),i=r,r=n,n=l[1]),this.addNamespaces(n);let c=hN(this.data,l)||{};o.skipCopy||(r=JSON.parse(JSON.stringify(r))),i?BY(c,r,s):c={...c,...r},B8(this.data,l,c),o.silent||this.emit("added",e,n,r)}removeResourceBundle(e,n){this.hasResourceBundle(e,n)&&delete this.data[e][n],this.removeNamespaces(n),this.emit("removed",e,n)}hasResourceBundle(e,n){return this.getResource(e,n)!==void 0}getResourceBundle(e,n){return n||(n=this.options.defaultNS),this.getResource(e,n)}getDataByLanguage(e){return this.data[e]}hasLanguageSomeTranslations(e){const n=this.getDataByLanguage(e);return!!(n&&Object.keys(n)||[]).find(i=>n[i]&&Object.keys(n[i]).length>0)}toJSON(){return this.data}}var HY={processors:{},addPostProcessor(t){this.processors[t.name]=t},handle(t,e,n,r,i){return t.forEach(s=>{e=this.processors[s]?.process(e,n,r,i)??e}),e}};const qY=Symbol("i18next/PATH_KEY");function nue(){const t=[],e=Object.create(null);let n;return e.get=(r,i)=>(n?.revoke?.(),i===qY?t:(t.push(i),n=Proxy.revocable(r,e),n.proxy)),Proxy.revocable(Object.create(null),e).proxy}function q3(t,e){const{[qY]:n}=t(nue());return n.join(e?.keySeparator??".")}const q8={},SM=t=>!Hn(t)&&typeof t!="boolean"&&typeof t!="number";class fN extends zP{constructor(e,n={}){super(),Vde(["resourceStore","languageUtils","pluralResolver","interpolator","backendConnector","i18nFormat","utils"],e,this),this.options=n,this.options.keySeparator===void 0&&(this.options.keySeparator="."),this.logger=Ad.create("translator")}changeLanguage(e){e&&(this.language=e)}exists(e,n={interpolation:{}}){const r={...n};if(e==null)return!1;const i=this.resolve(e,r);if(i?.res===void 0)return!1;const s=SM(i.res);return!(r.returnObjects===!1&&s)}extractFromKey(e,n){let r=n.nsSeparator!==void 0?n.nsSeparator:this.options.nsSeparator;r===void 0&&(r=":");const i=n.keySeparator!==void 0?n.keySeparator:this.options.keySeparator;let s=n.ns||this.options.defaultNS||[];const o=r&&e.indexOf(r)>-1,l=!this.options.userDefinedKeySeparator&&!n.keySeparator&&!this.options.userDefinedNsSeparator&&!n.nsSeparator&&!eue(e,r,i);if(o&&!l){const c=e.match(this.interpolator.nestingRegexp);if(c&&c.length>0)return{key:e,namespaces:Hn(s)?[s]:s};const d=e.split(r);(r!==i||r===i&&this.options.ns.indexOf(d[0])>-1)&&(s=d.shift()),e=d.join(i)}return{key:e,namespaces:Hn(s)?[s]:s}}translate(e,n,r){let i=typeof n=="object"?{...n}:n;if(typeof i!="object"&&this.options.overloadTranslationOptionHandler&&(i=this.options.overloadTranslationOptionHandler(arguments)),typeof i=="object"&&(i={...i}),i||(i={}),e==null)return"";typeof e=="function"&&(e=q3(e,{...this.options,...i})),Array.isArray(e)||(e=[String(e)]);const s=i.returnDetails!==void 0?i.returnDetails:this.options.returnDetails,o=i.keySeparator!==void 0?i.keySeparator:this.options.keySeparator,{key:l,namespaces:c}=this.extractFromKey(e[e.length-1],i),d=c[c.length-1];let u=i.nsSeparator!==void 0?i.nsSeparator:this.options.nsSeparator;u===void 0&&(u=":");const m=i.lng||this.language,p=i.appendNamespaceToCIMode||this.options.appendNamespaceToCIMode;if(m?.toLowerCase()==="cimode")return p?s?{res:`${d}${u}${l}`,usedKey:l,exactUsedKey:l,usedLng:m,usedNS:d,usedParams:this.getUsedParamsDetails(i)}:`${d}${u}${l}`:s?{res:l,usedKey:l,exactUsedKey:l,usedLng:m,usedNS:d,usedParams:this.getUsedParamsDetails(i)}:l;const f=this.resolve(e,i);let x=f?.res;const v=f?.usedKey||l,b=f?.exactUsedKey||l,g=["[object Number]","[object Function]","[object RegExp]"],_=i.joinArrays!==void 0?i.joinArrays:this.options.joinArrays,C=!this.i18nFormat||this.i18nFormat.handleAsObject,P=i.count!==void 0&&!Hn(i.count),N=fN.hasDefaultValue(i),A=P?this.pluralResolver.getSuffix(m,i.count,i):"",T=i.ordinal&&P?this.pluralResolver.getSuffix(m,i.count,{ordinal:!1}):"",F=P&&!i.ordinal&&i.count===0,k=F&&i[`defaultValue${this.options.pluralSeparator}zero`]||i[`defaultValue${A}`]||i[`defaultValue${T}`]||i.defaultValue;let D=x;C&&!x&&N&&(D=k);const H=SM(D),z=Object.prototype.toString.apply(D);if(C&&D&&H&&g.indexOf(z)<0&&!(Hn(_)&&Array.isArray(D))){if(!i.returnObjects&&!this.options.returnObjects){this.options.returnedObjectHandler||this.logger.warn("accessing an object - but returnObjects options is not enabled!");const X=this.options.returnedObjectHandler?this.options.returnedObjectHandler(v,D,{...i,ns:c}):`key '${l} (${this.language})' returned an object instead of string.`;return s?(f.res=X,f.usedParams=this.getUsedParamsDetails(i),f):X}if(o){const X=Array.isArray(D),L=X?[]:{},te=X?b:v;for(const ie in D)if(Object.prototype.hasOwnProperty.call(D,ie)){const J=`${te}${o}${ie}`;N&&!x?L[ie]=this.translate(J,{...i,defaultValue:SM(k)?k[ie]:void 0,joinArrays:!1,ns:c}):L[ie]=this.translate(J,{...i,joinArrays:!1,ns:c}),L[ie]===J&&(L[ie]=D[ie])}x=L}}else if(C&&Hn(_)&&Array.isArray(x))x=x.join(_),x&&(x=this.extendTranslation(x,e,i,r));else{let X=!1,L=!1;!this.isValidLookup(x)&&N&&(X=!0,x=k),this.isValidLookup(x)||(L=!0,x=l);const ie=(i.missingKeyNoValueFallbackToKey||this.options.missingKeyNoValueFallbackToKey)&&L?void 0:x,J=N&&k!==x&&this.options.updateMissing;if(L||X||J){if(this.logger.log(J?"updateKey":"missingKey",m,d,l,J?k:x),o){const W=this.resolve(l,{...i,keySeparator:!1});W&&W.res&&this.logger.warn("Seems the loaded translations were in flat JSON format instead of nested. Either set keySeparator: false on init or make sure your translations are published in nested format.")}let oe=[];const fe=this.languageUtils.getFallbackCodes(this.options.fallbackLng,i.lng||this.language);if(this.options.saveMissingTo==="fallback"&&fe&&fe[0])for(let W=0;W{const be=N&&me!==x?me:ie;this.options.missingKeyHandler?this.options.missingKeyHandler(W,d,ne,be,J,i):this.backendConnector?.saveMissing&&this.backendConnector.saveMissing(W,d,ne,be,J,i),this.emit("missingKey",W,d,ne,x)};this.options.saveMissing&&(this.options.saveMissingPlurals&&P?oe.forEach(W=>{const ne=this.pluralResolver.getSuffixes(W,i);F&&i[`defaultValue${this.options.pluralSeparator}zero`]&&ne.indexOf(`${this.options.pluralSeparator}zero`)<0&&ne.push(`${this.options.pluralSeparator}zero`),ne.forEach(me=>{re([W],l+me,i[`defaultValue${me}`]||k)})}):re(oe,l,k))}x=this.extendTranslation(x,e,i,f,r),L&&x===l&&this.options.appendNamespaceToMissingKey&&(x=`${d}${u}${l}`),(L||X)&&this.options.parseMissingKeyHandler&&(x=this.options.parseMissingKeyHandler(this.options.appendNamespaceToMissingKey?`${d}${u}${l}`:l,X?x:void 0,i))}return s?(f.res=x,f.usedParams=this.getUsedParamsDetails(i),f):x}extendTranslation(e,n,r,i,s){if(this.i18nFormat?.parse)e=this.i18nFormat.parse(e,{...this.options.interpolation.defaultVariables,...r},r.lng||this.language||i.usedLng,i.usedNS,i.usedKey,{resolved:i});else if(!r.skipInterpolation){r.interpolation&&this.interpolator.init({...r,interpolation:{...this.options.interpolation,...r.interpolation}});const c=Hn(e)&&(r?.interpolation?.skipOnVariables!==void 0?r.interpolation.skipOnVariables:this.options.interpolation.skipOnVariables);let d;if(c){const m=e.match(this.interpolator.nestingRegexp);d=m&&m.length}let u=r.replace&&!Hn(r.replace)?r.replace:r;if(this.options.interpolation.defaultVariables&&(u={...this.options.interpolation.defaultVariables,...u}),e=this.interpolator.interpolate(e,u,r.lng||this.language||i.usedLng,r),c){const m=e.match(this.interpolator.nestingRegexp),p=m&&m.length;ds?.[0]===m[0]&&!r.context?(this.logger.warn(`It seems you are nesting recursively key: ${m[0]} in key: ${n[0]}`),null):this.translate(...m,n),r)),r.interpolation&&this.interpolator.reset()}const o=r.postProcess||this.options.postProcess,l=Hn(o)?[o]:o;return e!=null&&l?.length&&r.applyPostProcessor!==!1&&(e=HY.handle(l,e,n,this.options&&this.options.postProcessPassResolved?{i18nResolved:{...i,usedParams:this.getUsedParamsDetails(r)},...r}:r,this)),e}resolve(e,n={}){let r,i,s,o,l;return Hn(e)&&(e=[e]),e.forEach(c=>{if(this.isValidLookup(r))return;const d=this.extractFromKey(c,n),u=d.key;i=u;let m=d.namespaces;this.options.fallbackNS&&(m=m.concat(this.options.fallbackNS));const p=n.count!==void 0&&!Hn(n.count),f=p&&!n.ordinal&&n.count===0,x=n.context!==void 0&&(Hn(n.context)||typeof n.context=="number")&&n.context!=="",v=n.lngs?n.lngs:this.languageUtils.toResolveHierarchy(n.lng||this.language,n.fallbackLng);m.forEach(b=>{this.isValidLookup(r)||(l=b,!q8[`${v[0]}-${b}`]&&this.utils?.hasLoadedNamespace&&!this.utils?.hasLoadedNamespace(l)&&(q8[`${v[0]}-${b}`]=!0,this.logger.warn(`key "${i}" for languages "${v.join(", ")}" won't get resolved as namespace "${l}" was not yet loaded`,"This means something IS WRONG in your setup. You access the t function before i18next.init / i18next.loadNamespace / i18next.changeLanguage was done. Wait for the callback or Promise to resolve before accessing it!!!")),v.forEach(g=>{if(this.isValidLookup(r))return;o=g;const _=[u];if(this.i18nFormat?.addLookupKeys)this.i18nFormat.addLookupKeys(_,u,g,b,n);else{let P;p&&(P=this.pluralResolver.getSuffix(g,n.count,n));const N=`${this.options.pluralSeparator}zero`,A=`${this.options.pluralSeparator}ordinal${this.options.pluralSeparator}`;if(p&&(n.ordinal&&P.indexOf(A)===0&&_.push(u+P.replace(A,this.options.pluralSeparator)),_.push(u+P),f&&_.push(u+N)),x){const T=`${u}${this.options.contextSeparator||"_"}${n.context}`;_.push(T),p&&(n.ordinal&&P.indexOf(A)===0&&_.push(T+P.replace(A,this.options.pluralSeparator)),_.push(T+P),f&&_.push(T+N))}}let C;for(;C=_.pop();)this.isValidLookup(r)||(s=C,r=this.getResource(g,b,C,n))}))})}),{res:r,usedKey:i,exactUsedKey:s,usedLng:o,usedNS:l}}isValidLookup(e){return e!==void 0&&!(!this.options.returnNull&&e===null)&&!(!this.options.returnEmptyString&&e==="")}getResource(e,n,r,i={}){return this.i18nFormat?.getResource?this.i18nFormat.getResource(e,n,r,i):this.resourceStore.getResource(e,n,r,i)}getUsedParamsDetails(e={}){const n=["defaultValue","ordinal","context","replace","lng","lngs","fallbackLng","ns","keySeparator","nsSeparator","returnObjects","returnDetails","joinArrays","postProcess","interpolation"],r=e.replace&&!Hn(e.replace);let i=r?e.replace:e;if(r&&typeof e.count<"u"&&(i.count=e.count),this.options.interpolation.defaultVariables&&(i={...this.options.interpolation.defaultVariables,...i}),!r){i={...i};for(const s of n)delete i[s]}return i}static hasDefaultValue(e){const n="defaultValue";for(const r in e)if(Object.prototype.hasOwnProperty.call(e,r)&&n===r.substring(0,n.length)&&e[r]!==void 0)return!0;return!1}}class $8{constructor(e){this.options=e,this.supportedLngs=this.options.supportedLngs||!1,this.logger=Ad.create("languageUtils")}getScriptPartFromCode(e){if(e=ew(e),!e||e.indexOf("-")<0)return null;const n=e.split("-");return n.length===2||(n.pop(),n[n.length-1].toLowerCase()==="x")?null:this.formatLanguageCode(n.join("-"))}getLanguagePartFromCode(e){if(e=ew(e),!e||e.indexOf("-")<0)return e;const n=e.split("-");return this.formatLanguageCode(n[0])}formatLanguageCode(e){if(Hn(e)&&e.indexOf("-")>-1){let n;try{n=Intl.getCanonicalLocales(e)[0]}catch{}return n&&this.options.lowerCaseLng&&(n=n.toLowerCase()),n||(this.options.lowerCaseLng?e.toLowerCase():e)}return this.options.cleanCode||this.options.lowerCaseLng?e.toLowerCase():e}isSupportedCode(e){return(this.options.load==="languageOnly"||this.options.nonExplicitSupportedLngs)&&(e=this.getLanguagePartFromCode(e)),!this.supportedLngs||!this.supportedLngs.length||this.supportedLngs.indexOf(e)>-1}getBestMatchFromCodes(e){if(!e)return null;let n;return e.forEach(r=>{if(n)return;const i=this.formatLanguageCode(r);(!this.options.supportedLngs||this.isSupportedCode(i))&&(n=i)}),!n&&this.options.supportedLngs&&e.forEach(r=>{if(n)return;const i=this.getScriptPartFromCode(r);if(this.isSupportedCode(i))return n=i;const s=this.getLanguagePartFromCode(r);if(this.isSupportedCode(s))return n=s;n=this.options.supportedLngs.find(o=>{if(o===s)return o;if(!(o.indexOf("-")<0&&s.indexOf("-")<0)&&(o.indexOf("-")>0&&s.indexOf("-")<0&&o.substring(0,o.indexOf("-"))===s||o.indexOf(s)===0&&s.length>1))return o})}),n||(n=this.getFallbackCodes(this.options.fallbackLng)[0]),n}getFallbackCodes(e,n){if(!e)return[];if(typeof e=="function"&&(e=e(n)),Hn(e)&&(e=[e]),Array.isArray(e))return e;if(!n)return e.default||[];let r=e[n];return r||(r=e[this.getScriptPartFromCode(n)]),r||(r=e[this.formatLanguageCode(n)]),r||(r=e[this.getLanguagePartFromCode(n)]),r||(r=e.default),r||[]}toResolveHierarchy(e,n){const r=this.getFallbackCodes((n===!1?[]:n)||this.options.fallbackLng||[],e),i=[],s=o=>{o&&(this.isSupportedCode(o)?i.push(o):this.logger.warn(`rejecting language code not found in supportedLngs: ${o}`))};return Hn(e)&&(e.indexOf("-")>-1||e.indexOf("_")>-1)?(this.options.load!=="languageOnly"&&s(this.formatLanguageCode(e)),this.options.load!=="languageOnly"&&this.options.load!=="currentOnly"&&s(this.getScriptPartFromCode(e)),this.options.load!=="currentOnly"&&s(this.getLanguagePartFromCode(e))):Hn(e)&&s(this.formatLanguageCode(e)),r.forEach(o=>{i.indexOf(o)<0&&s(this.formatLanguageCode(o))}),i}}const V8={zero:0,one:1,two:2,few:3,many:4,other:5},G8={select:t=>t===1?"one":"other",resolvedOptions:()=>({pluralCategories:["one","other"]})};class rue{constructor(e,n={}){this.languageUtils=e,this.options=n,this.logger=Ad.create("pluralResolver"),this.pluralRulesCache={}}addRule(e,n){this.rules[e]=n}clearCache(){this.pluralRulesCache={}}getRule(e,n={}){const r=ew(e==="dev"?"en":e),i=n.ordinal?"ordinal":"cardinal",s=JSON.stringify({cleanedCode:r,type:i});if(s in this.pluralRulesCache)return this.pluralRulesCache[s];let o;try{o=new Intl.PluralRules(r,{type:i})}catch{if(!Intl)return this.logger.error("No Intl support, please use an Intl polyfill!"),G8;if(!e.match(/-|_/))return G8;const c=this.languageUtils.getLanguagePartFromCode(e);o=this.getRule(c,n)}return this.pluralRulesCache[s]=o,o}needsPlural(e,n={}){let r=this.getRule(e,n);return r||(r=this.getRule("dev",n)),r?.resolvedOptions().pluralCategories.length>1}getPluralFormsOfKey(e,n,r={}){return this.getSuffixes(e,r).map(i=>`${n}${i}`)}getSuffixes(e,n={}){let r=this.getRule(e,n);return r||(r=this.getRule("dev",n)),r?r.resolvedOptions().pluralCategories.sort((i,s)=>V8[i]-V8[s]).map(i=>`${this.options.prepend}${n.ordinal?`ordinal${this.options.prepend}`:""}${i}`):[]}getSuffix(e,n,r={}){const i=this.getRule(e,r);return i?`${this.options.prepend}${r.ordinal?`ordinal${this.options.prepend}`:""}${i.select(n)}`:(this.logger.warn(`no plural rule found for: ${e}`),this.getSuffix("dev",n,r))}}const W8=(t,e,n,r=".",i=!0)=>{let s=Kde(t,e,n);return!s&&i&&Hn(n)&&(s=H3(t,n,r),s===void 0&&(s=H3(e,n,r))),s},_M=t=>t.replace(/\$/g,"$$$$");class aue{constructor(e={}){this.logger=Ad.create("interpolator"),this.options=e,this.format=e?.interpolation?.format||(n=>n),this.init(e)}init(e={}){e.interpolation||(e.interpolation={escapeValue:!0});const{escape:n,escapeValue:r,useRawValueToEscape:i,prefix:s,prefixEscaped:o,suffix:l,suffixEscaped:c,formatSeparator:d,unescapeSuffix:u,unescapePrefix:m,nestingPrefix:p,nestingPrefixEscaped:f,nestingSuffix:x,nestingSuffixEscaped:v,nestingOptionsSeparator:b,maxReplaces:g,alwaysFormat:_}=e.interpolation;this.escape=n!==void 0?n:Yde,this.escapeValue=r!==void 0?r:!0,this.useRawValueToEscape=i!==void 0?i:!1,this.prefix=s?vb(s):o||"{{",this.suffix=l?vb(l):c||"}}",this.formatSeparator=d||",",this.unescapePrefix=u?"":m||"-",this.unescapeSuffix=this.unescapePrefix?"":u||"",this.nestingPrefix=p?vb(p):f||vb("$t("),this.nestingSuffix=x?vb(x):v||vb(")"),this.nestingOptionsSeparator=b||",",this.maxReplaces=g||1e3,this.alwaysFormat=_!==void 0?_:!1,this.resetRegExp()}reset(){this.options&&this.init(this.options)}resetRegExp(){const e=(n,r)=>n?.source===r?(n.lastIndex=0,n):new RegExp(r,"g");this.regexp=e(this.regexp,`${this.prefix}(.+?)${this.suffix}`),this.regexpUnescape=e(this.regexpUnescape,`${this.prefix}${this.unescapePrefix}(.+?)${this.unescapeSuffix}${this.suffix}`),this.nestingRegexp=e(this.nestingRegexp,`${this.nestingPrefix}((?:[^()"']+|"[^"]*"|'[^']*'|\\((?:[^()]|"[^"]*"|'[^']*')*\\))*?)${this.nestingSuffix}`)}interpolate(e,n,r,i){let s,o,l;const c=this.options&&this.options.interpolation&&this.options.interpolation.defaultVariables||{},d=f=>{if(f.indexOf(this.formatSeparator)<0){const g=W8(n,c,f,this.options.keySeparator,this.options.ignoreJSONStructure);return this.alwaysFormat?this.format(g,void 0,r,{...i,...n,interpolationkey:f}):g}const x=f.split(this.formatSeparator),v=x.shift().trim(),b=x.join(this.formatSeparator).trim();return this.format(W8(n,c,v,this.options.keySeparator,this.options.ignoreJSONStructure),b,r,{...i,...n,interpolationkey:v})};this.resetRegExp();const u=i?.missingInterpolationHandler||this.options.missingInterpolationHandler,m=i?.interpolation?.skipOnVariables!==void 0?i.interpolation.skipOnVariables:this.options.interpolation.skipOnVariables;return[{regex:this.regexpUnescape,safeValue:f=>_M(f)},{regex:this.regexp,safeValue:f=>this.escapeValue?_M(this.escape(f)):_M(f)}].forEach(f=>{for(l=0;s=f.regex.exec(e);){const x=s[1].trim();if(o=d(x),o===void 0)if(typeof u=="function"){const b=u(e,s,i);o=Hn(b)?b:""}else if(i&&Object.prototype.hasOwnProperty.call(i,x))o="";else if(m){o=s[0];continue}else this.logger.warn(`missed to pass in variable ${x} for interpolating ${e}`),o="";else!Hn(o)&&!this.useRawValueToEscape&&(o=I8(o));const v=f.safeValue(o);if(e=e.replace(s[0],v),m?(f.regex.lastIndex+=o.length,f.regex.lastIndex-=s[0].length):f.regex.lastIndex=0,l++,l>=this.maxReplaces)break}}),e}nest(e,n,r={}){let i,s,o;const l=(c,d)=>{const u=this.nestingOptionsSeparator;if(c.indexOf(u)<0)return c;const m=c.split(new RegExp(`${u}[ ]*{`));let p=`{${m[1]}`;c=m[0],p=this.interpolate(p,o);const f=p.match(/'/g),x=p.match(/"/g);((f?.length??0)%2===0&&!x||x.length%2!==0)&&(p=p.replace(/'/g,'"'));try{o=JSON.parse(p),d&&(o={...d,...o})}catch(v){return this.logger.warn(`failed parsing options string in nesting for key ${c}`,v),`${c}${u}${p}`}return o.defaultValue&&o.defaultValue.indexOf(this.prefix)>-1&&delete o.defaultValue,c};for(;i=this.nestingRegexp.exec(e);){let c=[];o={...r},o=o.replace&&!Hn(o.replace)?o.replace:o,o.applyPostProcessor=!1,delete o.defaultValue;const d=/{.*}/.test(i[1])?i[1].lastIndexOf("}")+1:i[1].indexOf(this.formatSeparator);if(d!==-1&&(c=i[1].slice(d).split(this.formatSeparator).map(u=>u.trim()).filter(Boolean),i[1]=i[1].slice(0,d)),s=n(l.call(this,i[1].trim(),o),o),s&&i[0]===e&&!Hn(s))return s;Hn(s)||(s=I8(s)),s||(this.logger.warn(`missed to resolve ${i[1]} for nesting ${e}`),s=""),c.length&&(s=c.reduce((u,m)=>this.format(u,m,r.lng,{...r,interpolationkey:i[1].trim()}),s.trim())),e=e.replace(i[0],s),this.regexp.lastIndex=0}return e}}const iue=t=>{let e=t.toLowerCase().trim();const n={};if(t.indexOf("(")>-1){const r=t.split("(");e=r[0].toLowerCase().trim();const i=r[1].substring(0,r[1].length-1);e==="currency"&&i.indexOf(":")<0?n.currency||(n.currency=i.trim()):e==="relativetime"&&i.indexOf(":")<0?n.range||(n.range=i.trim()):i.split(";").forEach(o=>{if(o){const[l,...c]=o.split(":"),d=c.join(":").trim().replace(/^'+|'+$/g,""),u=l.trim();n[u]||(n[u]=d),d==="false"&&(n[u]=!1),d==="true"&&(n[u]=!0),isNaN(d)||(n[u]=parseInt(d,10))}})}return{formatName:e,formatOptions:n}},K8=t=>{const e={};return(n,r,i)=>{let s=i;i&&i.interpolationkey&&i.formatParams&&i.formatParams[i.interpolationkey]&&i[i.interpolationkey]&&(s={...s,[i.interpolationkey]:void 0});const o=r+JSON.stringify(s);let l=e[o];return l||(l=t(ew(r),i),e[o]=l),l(n)}},sue=t=>(e,n,r)=>t(ew(n),r)(e);class oue{constructor(e={}){this.logger=Ad.create("formatter"),this.options=e,this.init(e)}init(e,n={interpolation:{}}){this.formatSeparator=n.interpolation.formatSeparator||",";const r=n.cacheInBuiltFormats?K8:sue;this.formats={number:r((i,s)=>{const o=new Intl.NumberFormat(i,{...s});return l=>o.format(l)}),currency:r((i,s)=>{const o=new Intl.NumberFormat(i,{...s,style:"currency"});return l=>o.format(l)}),datetime:r((i,s)=>{const o=new Intl.DateTimeFormat(i,{...s});return l=>o.format(l)}),relativetime:r((i,s)=>{const o=new Intl.RelativeTimeFormat(i,{...s});return l=>o.format(l,s.range||"day")}),list:r((i,s)=>{const o=new Intl.ListFormat(i,{...s});return l=>o.format(l)})}}add(e,n){this.formats[e.toLowerCase().trim()]=n}addCached(e,n){this.formats[e.toLowerCase().trim()]=K8(n)}format(e,n,r,i={}){const s=n.split(this.formatSeparator);if(s.length>1&&s[0].indexOf("(")>1&&s[0].indexOf(")")<0&&s.find(l=>l.indexOf(")")>-1)){const l=s.findIndex(c=>c.indexOf(")")>-1);s[0]=[s[0],...s.splice(1,l)].join(this.formatSeparator)}return s.reduce((l,c)=>{const{formatName:d,formatOptions:u}=iue(c);if(this.formats[d]){let m=l;try{const p=i?.formatParams?.[i.interpolationkey]||{},f=p.locale||p.lng||i.locale||i.lng||r;m=this.formats[d](l,f,{...u,...i,...p})}catch(p){this.logger.warn(p)}return m}else this.logger.warn(`there was no format function for ${d}`);return l},e)}}const lue=(t,e)=>{t.pending[e]!==void 0&&(delete t.pending[e],t.pendingCount--)};class cue extends zP{constructor(e,n,r,i={}){super(),this.backend=e,this.store=n,this.services=r,this.languageUtils=r.languageUtils,this.options=i,this.logger=Ad.create("backendConnector"),this.waitingReads=[],this.maxParallelReads=i.maxParallelReads||10,this.readingCalls=0,this.maxRetries=i.maxRetries>=0?i.maxRetries:5,this.retryTimeout=i.retryTimeout>=1?i.retryTimeout:350,this.state={},this.queue=[],this.backend?.init?.(r,i.backend,i)}queueLoad(e,n,r,i){const s={},o={},l={},c={};return e.forEach(d=>{let u=!0;n.forEach(m=>{const p=`${d}|${m}`;!r.reload&&this.store.hasResourceBundle(d,m)?this.state[p]=2:this.state[p]<0||(this.state[p]===1?o[p]===void 0&&(o[p]=!0):(this.state[p]=1,u=!1,o[p]===void 0&&(o[p]=!0),s[p]===void 0&&(s[p]=!0),c[m]===void 0&&(c[m]=!0)))}),u||(l[d]=!0)}),(Object.keys(s).length||Object.keys(o).length)&&this.queue.push({pending:o,pendingCount:Object.keys(o).length,loaded:{},errors:[],callback:i}),{toLoad:Object.keys(s),pending:Object.keys(o),toLoadLanguages:Object.keys(l),toLoadNamespaces:Object.keys(c)}}loaded(e,n,r){const i=e.split("|"),s=i[0],o=i[1];n&&this.emit("failedLoading",s,o,n),!n&&r&&this.store.addResourceBundle(s,o,r,void 0,void 0,{skipCopy:!0}),this.state[e]=n?-1:2,n&&r&&(this.state[e]=0);const l={};this.queue.forEach(c=>{Wde(c.loaded,[s],o),lue(c,e),n&&c.errors.push(n),c.pendingCount===0&&!c.done&&(Object.keys(c.loaded).forEach(d=>{l[d]||(l[d]={});const u=c.loaded[d];u.length&&u.forEach(m=>{l[d][m]===void 0&&(l[d][m]=!0)})}),c.done=!0,c.errors.length?c.callback(c.errors):c.callback())}),this.emit("loaded",l),this.queue=this.queue.filter(c=>!c.done)}read(e,n,r,i=0,s=this.retryTimeout,o){if(!e.length)return o(null,{});if(this.readingCalls>=this.maxParallelReads){this.waitingReads.push({lng:e,ns:n,fcName:r,tried:i,wait:s,callback:o});return}this.readingCalls++;const l=(d,u)=>{if(this.readingCalls--,this.waitingReads.length>0){const m=this.waitingReads.shift();this.read(m.lng,m.ns,m.fcName,m.tried,m.wait,m.callback)}if(d&&u&&i{this.read.call(this,e,n,r,i+1,s*2,o)},s);return}o(d,u)},c=this.backend[r].bind(this.backend);if(c.length===2){try{const d=c(e,n);d&&typeof d.then=="function"?d.then(u=>l(null,u)).catch(l):l(null,d)}catch(d){l(d)}return}return c(e,n,l)}prepareLoading(e,n,r={},i){if(!this.backend)return this.logger.warn("No backend was added via i18next.use. Will not load resources."),i&&i();Hn(e)&&(e=this.languageUtils.toResolveHierarchy(e)),Hn(n)&&(n=[n]);const s=this.queueLoad(e,n,r,i);if(!s.toLoad.length)return s.pending.length||i(),null;s.toLoad.forEach(o=>{this.loadOne(o)})}load(e,n,r){this.prepareLoading(e,n,{},r)}reload(e,n,r){this.prepareLoading(e,n,{reload:!0},r)}loadOne(e,n=""){const r=e.split("|"),i=r[0],s=r[1];this.read(i,s,"read",void 0,void 0,(o,l)=>{o&&this.logger.warn(`${n}loading namespace ${s} for language ${i} failed`,o),!o&&l&&this.logger.log(`${n}loaded namespace ${s} for language ${i}`,l),this.loaded(e,o,l)})}saveMissing(e,n,r,i,s,o={},l=()=>{}){if(this.services?.utils?.hasLoadedNamespace&&!this.services?.utils?.hasLoadedNamespace(n)){this.logger.warn(`did not save key "${r}" as the namespace "${n}" was not yet loaded`,"This means something IS WRONG in your setup. You access the t function before i18next.init / i18next.loadNamespace / i18next.changeLanguage was done. Wait for the callback or Promise to resolve before accessing it!!!");return}if(!(r==null||r==="")){if(this.backend?.create){const c={...o,isUpdate:s},d=this.backend.create.bind(this.backend);if(d.length<6)try{let u;d.length===5?u=d(e,n,r,i,c):u=d(e,n,r,i),u&&typeof u.then=="function"?u.then(m=>l(null,m)).catch(l):l(null,u)}catch(u){l(u)}else d(e,n,r,i,l,c)}!e||!e[0]||this.store.addResource(e[0],n,r,i)}}}const X8=()=>({debug:!1,initAsync:!0,ns:["translation"],defaultNS:["translation"],fallbackLng:["dev"],fallbackNS:!1,supportedLngs:!1,nonExplicitSupportedLngs:!1,load:"all",preload:!1,simplifyPluralSuffix:!0,keySeparator:".",nsSeparator:":",pluralSeparator:"_",contextSeparator:"_",partialBundledLanguages:!1,saveMissing:!1,updateMissing:!1,saveMissingTo:"fallback",saveMissingPlurals:!0,missingKeyHandler:!1,missingInterpolationHandler:!1,postProcess:!1,postProcessPassResolved:!1,returnNull:!1,returnEmptyString:!0,returnObjects:!1,joinArrays:!1,returnedObjectHandler:!1,parseMissingKeyHandler:!1,appendNamespaceToMissingKey:!1,appendNamespaceToCIMode:!1,overloadTranslationOptionHandler:t=>{let e={};if(typeof t[1]=="object"&&(e=t[1]),Hn(t[1])&&(e.defaultValue=t[1]),Hn(t[2])&&(e.tDescription=t[2]),typeof t[2]=="object"||typeof t[3]=="object"){const n=t[3]||t[2];Object.keys(n).forEach(r=>{e[r]=n[r]})}return e},interpolation:{escapeValue:!0,format:t=>t,prefix:"{{",suffix:"}}",formatSeparator:",",unescapePrefix:"-",nestingPrefix:"$t(",nestingSuffix:")",nestingOptionsSeparator:",",maxReplaces:1e3,skipOnVariables:!0},cacheInBuiltFormats:!0}),Y8=t=>(Hn(t.ns)&&(t.ns=[t.ns]),Hn(t.fallbackLng)&&(t.fallbackLng=[t.fallbackLng]),Hn(t.fallbackNS)&&(t.fallbackNS=[t.fallbackNS]),t.supportedLngs?.indexOf?.("cimode")<0&&(t.supportedLngs=t.supportedLngs.concat(["cimode"])),typeof t.initImmediate=="boolean"&&(t.initAsync=t.initImmediate),t),K_=()=>{},due=t=>{Object.getOwnPropertyNames(Object.getPrototypeOf(t)).forEach(n=>{typeof t[n]=="function"&&(t[n]=t[n].bind(t))})};class T0 extends zP{constructor(e={},n){if(super(),this.options=Y8(e),this.services={},this.logger=Ad,this.modules={external:[]},due(this),n&&!this.isInitialized&&!e.isClone){if(!this.options.initAsync)return this.init(e,n),this;setTimeout(()=>{this.init(e,n)},0)}}init(e={},n){this.isInitializing=!0,typeof e=="function"&&(n=e,e={}),e.defaultNS==null&&e.ns&&(Hn(e.ns)?e.defaultNS=e.ns:e.ns.indexOf("translation")<0&&(e.defaultNS=e.ns[0]));const r=X8();this.options={...r,...this.options,...Y8(e)},this.options.interpolation={...r.interpolation,...this.options.interpolation},e.keySeparator!==void 0&&(this.options.userDefinedKeySeparator=e.keySeparator),e.nsSeparator!==void 0&&(this.options.userDefinedNsSeparator=e.nsSeparator);const i=d=>d?typeof d=="function"?new d:d:null;if(!this.options.isClone){this.modules.logger?Ad.init(i(this.modules.logger),this.options):Ad.init(null,this.options);let d;this.modules.formatter?d=this.modules.formatter:d=oue;const u=new $8(this.options);this.store=new H8(this.options.resources,this.options);const m=this.services;m.logger=Ad,m.resourceStore=this.store,m.languageUtils=u,m.pluralResolver=new rue(u,{prepend:this.options.pluralSeparator,simplifyPluralSuffix:this.options.simplifyPluralSuffix}),this.options.interpolation.format&&this.options.interpolation.format!==r.interpolation.format&&this.logger.deprecate("init: you are still using the legacy format function, please use the new approach: https://www.i18next.com/translation-function/formatting"),d&&(!this.options.interpolation.format||this.options.interpolation.format===r.interpolation.format)&&(m.formatter=i(d),m.formatter.init&&m.formatter.init(m,this.options),this.options.interpolation.format=m.formatter.format.bind(m.formatter)),m.interpolator=new aue(this.options),m.utils={hasLoadedNamespace:this.hasLoadedNamespace.bind(this)},m.backendConnector=new cue(i(this.modules.backend),m.resourceStore,m,this.options),m.backendConnector.on("*",(f,...x)=>{this.emit(f,...x)}),this.modules.languageDetector&&(m.languageDetector=i(this.modules.languageDetector),m.languageDetector.init&&m.languageDetector.init(m,this.options.detection,this.options)),this.modules.i18nFormat&&(m.i18nFormat=i(this.modules.i18nFormat),m.i18nFormat.init&&m.i18nFormat.init(this)),this.translator=new fN(this.services,this.options),this.translator.on("*",(f,...x)=>{this.emit(f,...x)}),this.modules.external.forEach(f=>{f.init&&f.init(this)})}if(this.format=this.options.interpolation.format,n||(n=K_),this.options.fallbackLng&&!this.services.languageDetector&&!this.options.lng){const d=this.services.languageUtils.getFallbackCodes(this.options.fallbackLng);d.length>0&&d[0]!=="dev"&&(this.options.lng=d[0])}!this.services.languageDetector&&!this.options.lng&&this.logger.warn("init: no languageDetector is used and no lng is defined"),["getResource","hasResourceBundle","getResourceBundle","getDataByLanguage"].forEach(d=>{this[d]=(...u)=>this.store[d](...u)}),["addResource","addResources","addResourceBundle","removeResourceBundle"].forEach(d=>{this[d]=(...u)=>(this.store[d](...u),this)});const l=jv(),c=()=>{const d=(u,m)=>{this.isInitializing=!1,this.isInitialized&&!this.initializedStoreOnce&&this.logger.warn("init: i18next is already initialized. You should call init just once!"),this.isInitialized=!0,this.options.isClone||this.logger.log("initialized",this.options),this.emit("initialized",this.options),l.resolve(m),n(u,m)};if(this.languages&&!this.isInitialized)return d(null,this.t.bind(this));this.changeLanguage(this.options.lng,d)};return this.options.resources||!this.options.initAsync?c():setTimeout(c,0),l}loadResources(e,n=K_){let r=n;const i=Hn(e)?e:this.language;if(typeof e=="function"&&(r=e),!this.options.resources||this.options.partialBundledLanguages){if(i?.toLowerCase()==="cimode"&&(!this.options.preload||this.options.preload.length===0))return r();const s=[],o=l=>{if(!l||l==="cimode")return;this.services.languageUtils.toResolveHierarchy(l).forEach(d=>{d!=="cimode"&&s.indexOf(d)<0&&s.push(d)})};i?o(i):this.services.languageUtils.getFallbackCodes(this.options.fallbackLng).forEach(c=>o(c)),this.options.preload?.forEach?.(l=>o(l)),this.services.backendConnector.load(s,this.options.ns,l=>{!l&&!this.resolvedLanguage&&this.language&&this.setResolvedLanguage(this.language),r(l)})}else r(null)}reloadResources(e,n,r){const i=jv();return typeof e=="function"&&(r=e,e=void 0),typeof n=="function"&&(r=n,n=void 0),e||(e=this.languages),n||(n=this.options.ns),r||(r=K_),this.services.backendConnector.reload(e,n,s=>{i.resolve(),r(s)}),i}use(e){if(!e)throw new Error("You are passing an undefined module! Please check the object you are passing to i18next.use()");if(!e.type)throw new Error("You are passing a wrong module! Please check the object you are passing to i18next.use()");return e.type==="backend"&&(this.modules.backend=e),(e.type==="logger"||e.log&&e.warn&&e.error)&&(this.modules.logger=e),e.type==="languageDetector"&&(this.modules.languageDetector=e),e.type==="i18nFormat"&&(this.modules.i18nFormat=e),e.type==="postProcessor"&&HY.addPostProcessor(e),e.type==="formatter"&&(this.modules.formatter=e),e.type==="3rdParty"&&this.modules.external.push(e),this}setResolvedLanguage(e){if(!(!e||!this.languages)&&!(["cimode","dev"].indexOf(e)>-1)){for(let n=0;n-1)&&this.store.hasLanguageSomeTranslations(r)){this.resolvedLanguage=r;break}}!this.resolvedLanguage&&this.languages.indexOf(e)<0&&this.store.hasLanguageSomeTranslations(e)&&(this.resolvedLanguage=e,this.languages.unshift(e))}}changeLanguage(e,n){this.isLanguageChangingTo=e;const r=jv();this.emit("languageChanging",e);const i=l=>{this.language=l,this.languages=this.services.languageUtils.toResolveHierarchy(l),this.resolvedLanguage=void 0,this.setResolvedLanguage(l)},s=(l,c)=>{c?this.isLanguageChangingTo===e&&(i(c),this.translator.changeLanguage(c),this.isLanguageChangingTo=void 0,this.emit("languageChanged",c),this.logger.log("languageChanged",c)):this.isLanguageChangingTo=void 0,r.resolve((...d)=>this.t(...d)),n&&n(l,(...d)=>this.t(...d))},o=l=>{!e&&!l&&this.services.languageDetector&&(l=[]);const c=Hn(l)?l:l&&l[0],d=this.store.hasLanguageSomeTranslations(c)?c:this.services.languageUtils.getBestMatchFromCodes(Hn(l)?[l]:l);d&&(this.language||i(d),this.translator.language||this.translator.changeLanguage(d),this.services.languageDetector?.cacheUserLanguage?.(d)),this.loadResources(d,u=>{s(u,d)})};return!e&&this.services.languageDetector&&!this.services.languageDetector.async?o(this.services.languageDetector.detect()):!e&&this.services.languageDetector&&this.services.languageDetector.async?this.services.languageDetector.detect.length===0?this.services.languageDetector.detect().then(o):this.services.languageDetector.detect(o):o(e),r}getFixedT(e,n,r){const i=(s,o,...l)=>{let c;typeof o!="object"?c=this.options.overloadTranslationOptionHandler([s,o].concat(l)):c={...o},c.lng=c.lng||i.lng,c.lngs=c.lngs||i.lngs,c.ns=c.ns||i.ns,c.keyPrefix!==""&&(c.keyPrefix=c.keyPrefix||r||i.keyPrefix);const d=this.options.keySeparator||".";let u;return c.keyPrefix&&Array.isArray(s)?u=s.map(m=>(typeof m=="function"&&(m=q3(m,{...this.options,...o})),`${c.keyPrefix}${d}${m}`)):(typeof s=="function"&&(s=q3(s,{...this.options,...o})),u=c.keyPrefix?`${c.keyPrefix}${d}${s}`:s),this.t(u,c)};return Hn(e)?i.lng=e:i.lngs=e,i.ns=n,i.keyPrefix=r,i}t(...e){return this.translator?.translate(...e)}exists(...e){return this.translator?.exists(...e)}setDefaultNamespace(e){this.options.defaultNS=e}hasLoadedNamespace(e,n={}){if(!this.isInitialized)return this.logger.warn("hasLoadedNamespace: i18next was not initialized",this.languages),!1;if(!this.languages||!this.languages.length)return this.logger.warn("hasLoadedNamespace: i18n.languages were undefined or empty",this.languages),!1;const r=n.lng||this.resolvedLanguage||this.languages[0],i=this.options?this.options.fallbackLng:!1,s=this.languages[this.languages.length-1];if(r.toLowerCase()==="cimode")return!0;const o=(l,c)=>{const d=this.services.backendConnector.state[`${l}|${c}`];return d===-1||d===0||d===2};if(n.precheck){const l=n.precheck(this,o);if(l!==void 0)return l}return!!(this.hasResourceBundle(r,e)||!this.services.backendConnector.backend||this.options.resources&&!this.options.partialBundledLanguages||o(r,e)&&(!i||o(s,e)))}loadNamespaces(e,n){const r=jv();return this.options.ns?(Hn(e)&&(e=[e]),e.forEach(i=>{this.options.ns.indexOf(i)<0&&this.options.ns.push(i)}),this.loadResources(i=>{r.resolve(),n&&n(i)}),r):(n&&n(),Promise.resolve())}loadLanguages(e,n){const r=jv();Hn(e)&&(e=[e]);const i=this.options.preload||[],s=e.filter(o=>i.indexOf(o)<0&&this.services.languageUtils.isSupportedCode(o));return s.length?(this.options.preload=i.concat(s),this.loadResources(o=>{r.resolve(),n&&n(o)}),r):(n&&n(),Promise.resolve())}dir(e){if(e||(e=this.resolvedLanguage||(this.languages?.length>0?this.languages[0]:this.language)),!e)return"rtl";try{const i=new Intl.Locale(e);if(i&&i.getTextInfo){const s=i.getTextInfo();if(s&&s.direction)return s.direction}}catch{}const n=["ar","shu","sqr","ssh","xaa","yhd","yud","aao","abh","abv","acm","acq","acw","acx","acy","adf","ads","aeb","aec","afb","ajp","apc","apd","arb","arq","ars","ary","arz","auz","avl","ayh","ayl","ayn","ayp","bbz","pga","he","iw","ps","pbt","pbu","pst","prp","prd","ug","ur","ydd","yds","yih","ji","yi","hbo","men","xmn","fa","jpr","peo","pes","prs","dv","sam","ckb"],r=this.services?.languageUtils||new $8(X8());return e.toLowerCase().indexOf("-latn")>1?"ltr":n.indexOf(r.getLanguagePartFromCode(e))>-1||e.toLowerCase().indexOf("-arab")>1?"rtl":"ltr"}static createInstance(e={},n){const r=new T0(e,n);return r.createInstance=T0.createInstance,r}cloneInstance(e={},n=K_){const r=e.forkResourceStore;r&&delete e.forkResourceStore;const i={...this.options,...e,isClone:!0},s=new T0(i);if((e.debug!==void 0||e.prefix!==void 0)&&(s.logger=s.logger.clone(e)),["store","services","language"].forEach(l=>{s[l]=this[l]}),s.services={...this.services},s.services.utils={hasLoadedNamespace:s.hasLoadedNamespace.bind(s)},r){const l=Object.keys(this.store.data).reduce((c,d)=>(c[d]={...this.store.data[d]},c[d]=Object.keys(c[d]).reduce((u,m)=>(u[m]={...c[d][m]},u),c[d]),c),{});s.store=new H8(l,i),s.services.resourceStore=s.store}return s.translator=new fN(s.services,i),s.translator.on("*",(l,...c)=>{s.emit(l,...c)}),s.init(i,n),s.translator.options=i,s.translator.backendConnector.services.utils={hasLoadedNamespace:s.hasLoadedNamespace.bind(s)},s}toJSON(){return{options:this.options,store:this.store,language:this.language,languages:this.languages,resolvedLanguage:this.resolvedLanguage}}}const xo=T0.createInstance();xo.createInstance;xo.dir;xo.init;xo.loadResources;xo.reloadResources;xo.use;xo.changeLanguage;xo.getFixedT;xo.t;xo.exists;xo.setDefaultNamespace;xo.hasLoadedNamespace;xo.loadNamespaces;xo.loadLanguages;const uue=(t,e,n,r)=>{const i=[n,{code:e,...r||{}}];if(t?.services?.logger?.forward)return t.services.logger.forward(i,"warn","react-i18next::",!0);sg(i[0])&&(i[0]=`react-i18next:: ${i[0]}`),t?.services?.logger?.warn?t.services.logger.warn(...i):console?.warn&&console.warn(...i)},Q8={},$Y=(t,e,n,r)=>{sg(n)&&Q8[n]||(sg(n)&&(Q8[n]=new Date),uue(t,e,n,r))},VY=(t,e)=>()=>{if(t.isInitialized)e();else{const n=()=>{setTimeout(()=>{t.off("initialized",n)},0),e()};t.on("initialized",n)}},$3=(t,e,n)=>{t.loadNamespaces(e,VY(t,n))},Z8=(t,e,n,r)=>{if(sg(n)&&(n=[n]),t.options.preload&&t.options.preload.indexOf(e)>-1)return $3(t,n,r);n.forEach(i=>{t.options.ns.indexOf(i)<0&&t.options.ns.push(i)}),t.loadLanguages(e,VY(t,r))},mue=(t,e,n={})=>!e.languages||!e.languages.length?($Y(e,"NO_LANGUAGES","i18n.languages were undefined or empty",{languages:e.languages}),!0):e.hasLoadedNamespace(t,{lng:n.lng,precheck:(r,i)=>{if(n.bindI18n&&n.bindI18n.indexOf("languageChanging")>-1&&r.services.backendConnector.backend&&r.isLanguageChangingTo&&!i(r.isLanguageChangingTo,t))return!1}}),sg=t=>typeof t=="string",hue=t=>typeof t=="object"&&t!==null,pue=/&(?:amp|#38|lt|#60|gt|#62|apos|#39|quot|#34|nbsp|#160|copy|#169|reg|#174|hellip|#8230|#x2F|#47);/g,fue={"&":"&","&":"&","<":"<","<":"<",">":">",">":">","'":"'","'":"'",""":'"',""":'"'," ":" "," ":" ","©":"©","©":"©","®":"®","®":"®","…":"…","…":"…","/":"/","/":"/"},gue=t=>fue[t],bue=t=>t.replace(pue,gue);let V3={bindI18n:"languageChanged",bindI18nStore:"",transEmptyNodeValue:"",transSupportBasicHtmlNodes:!0,transWrapTextNodes:"",transKeepBasicHtmlNodesFor:["br","strong","i","p"],useSuspense:!0,unescape:bue,transDefaultProps:void 0};const xue=(t={})=>{V3={...V3,...t}},yue=()=>V3;let GY;const vue=t=>{GY=t},wue=()=>GY,Sue={type:"3rdParty",init(t){xue(t.options.react),vue(t)}},_ue=w.createContext();class kue{constructor(){this.usedNamespaces={}}addUsedNamespaces(e){e.forEach(n=>{this.usedNamespaces[n]||(this.usedNamespaces[n]=!0)})}getUsedNamespaces(){return Object.keys(this.usedNamespaces)}}var kM={exports:{}},NM={};var J8;function Nue(){if(J8)return NM;J8=1;var t=Rg();function e(m,p){return m===p&&(m!==0||1/m===1/p)||m!==m&&p!==p}var n=typeof Object.is=="function"?Object.is:e,r=t.useState,i=t.useEffect,s=t.useLayoutEffect,o=t.useDebugValue;function l(m,p){var f=p(),x=r({inst:{value:f,getSnapshot:p}}),v=x[0].inst,b=x[1];return s(function(){v.value=f,v.getSnapshot=p,c(v)&&b({inst:v})},[m,f,p]),i(function(){return c(v)&&b({inst:v}),m(function(){c(v)&&b({inst:v})})},[m]),o(f),f}function c(m){var p=m.getSnapshot;m=m.value;try{var f=p();return!n(m,f)}catch{return!0}}function d(m,p){return p()}var u=typeof window>"u"||typeof window.document>"u"||typeof window.document.createElement>"u"?d:l;return NM.useSyncExternalStore=t.useSyncExternalStore!==void 0?t.useSyncExternalStore:u,NM}var eH;function WY(){return eH||(eH=1,kM.exports=Nue()),kM.exports}var hO=WY();const Cue=(t,e)=>sg(e)?e:hue(e)&&sg(e.defaultValue)?e.defaultValue:Array.isArray(t)?t[t.length-1]:t,Pue={t:Cue,ready:!1},Tue=()=>()=>{},Dt=(t,e={})=>{const{i18n:n}=e,{i18n:r,defaultNS:i}=w.useContext(_ue)||{},s=n||r||wue();s&&!s.reportNamespaces&&(s.reportNamespaces=new kue),s||$Y(s,"NO_I18NEXT_INSTANCE","useTranslation: You will need to pass in an i18next instance by using initReactI18next");const o=w.useMemo(()=>({...yue(),...s?.options?.react,...e}),[s,e]),{useSuspense:l,keyPrefix:c}=o,d=i||s?.options?.defaultNS,u=sg(d)?[d]:d||["translation"],m=w.useMemo(()=>u,u);s?.reportNamespaces?.addUsedNamespaces?.(m);const p=w.useRef(0),f=w.useCallback(k=>{if(!s)return Tue;const{bindI18n:D,bindI18nStore:H}=o,z=()=>{p.current+=1,k()};return D&&s.on(D,z),H&&s.store.on(H,z),()=>{D&&D.split(" ").forEach(X=>s.off(X,z)),H&&H.split(" ").forEach(X=>s.store.off(X,z))}},[s,o]),x=w.useRef(),v=w.useCallback(()=>{if(!s)return Pue;const k=!!(s.isInitialized||s.initializedStoreOnce)&&m.every(te=>mue(te,s,o)),D=e.lng||s.language,H=p.current,z=x.current;if(z&&z.ready===k&&z.lng===D&&z.keyPrefix===c&&z.revision===H)return z;const L={t:s.getFixedT(D,o.nsMode==="fallback"?m:m[0],c),ready:k,lng:D,keyPrefix:c,revision:H};return x.current=L,L},[s,m,c,o,e.lng]),[b,g]=w.useState(0),{t:_,ready:C}=hO.useSyncExternalStore(f,v,v);w.useEffect(()=>{if(s&&!C&&!l){const k=()=>g(D=>D+1);e.lng?Z8(s,e.lng,m,k):$3(s,m,k)}},[s,e.lng,m,C,l,b]);const P=s||{},N=w.useRef(null),A=w.useRef(),T=k=>{const D=Object.getOwnPropertyDescriptors(k);D.__original&&delete D.__original;const H=Object.create(Object.getPrototypeOf(k),D);if(!Object.prototype.hasOwnProperty.call(H,"__original"))try{Object.defineProperty(H,"__original",{value:k,writable:!1,enumerable:!1,configurable:!1})}catch{}return H},F=w.useMemo(()=>{const k=P,D=k?.language;let H=k;k&&(N.current&&N.current.__original===k?A.current!==D?(H=T(k),N.current=H,A.current=D):H=N.current:(H=T(k),N.current=H,A.current=D));const z=[_,H,C];return z.t=_,z.i18n=H,z.ready=C,z},[_,P,C,P.resolvedLanguage,P.language,P.languages]);if(s&&l&&!C)throw new Promise(k=>{const D=()=>k();e.lng?Z8(s,e.lng,m,D):$3(s,m,D)});return F},{slice:Aue,forEach:jue}=[];function Mue(t){return jue.call(Aue.call(arguments,1),e=>{if(e)for(const n in e)t[n]===void 0&&(t[n]=e[n])}),t}function Eue(t){return typeof t!="string"?!1:[/<\s*script.*?>/i,/<\s*\/\s*script\s*>/i,/<\s*img.*?on\w+\s*=/i,/<\s*\w+\s*on\w+\s*=.*?>/i,/javascript\s*:/i,/vbscript\s*:/i,/expression\s*\(/i,/eval\s*\(/i,/alert\s*\(/i,/document\.cookie/i,/document\.write\s*\(/i,/window\.location/i,/innerHTML/i].some(n=>n.test(t))}const tH=/^[\u0009\u0020-\u007e\u0080-\u00ff]+$/,Due=function(t,e){const r=arguments.length>2&&arguments[2]!==void 0?arguments[2]:{path:"/"},i=encodeURIComponent(e);let s=`${t}=${i}`;if(r.maxAge>0){const o=r.maxAge-0;if(Number.isNaN(o))throw new Error("maxAge should be a Number");s+=`; Max-Age=${Math.floor(o)}`}if(r.domain){if(!tH.test(r.domain))throw new TypeError("option domain is invalid");s+=`; Domain=${r.domain}`}if(r.path){if(!tH.test(r.path))throw new TypeError("option path is invalid");s+=`; Path=${r.path}`}if(r.expires){if(typeof r.expires.toUTCString!="function")throw new TypeError("option expires is invalid");s+=`; Expires=${r.expires.toUTCString()}`}if(r.httpOnly&&(s+="; HttpOnly"),r.secure&&(s+="; Secure"),r.sameSite)switch(typeof r.sameSite=="string"?r.sameSite.toLowerCase():r.sameSite){case!0:s+="; SameSite=Strict";break;case"lax":s+="; SameSite=Lax";break;case"strict":s+="; SameSite=Strict";break;case"none":s+="; SameSite=None";break;default:throw new TypeError("option sameSite is invalid")}return r.partitioned&&(s+="; Partitioned"),s},nH={create(t,e,n,r){let i=arguments.length>4&&arguments[4]!==void 0?arguments[4]:{path:"/",sameSite:"strict"};n&&(i.expires=new Date,i.expires.setTime(i.expires.getTime()+n*60*1e3)),r&&(i.domain=r),document.cookie=Due(t,e,i)},read(t){const e=`${t}=`,n=document.cookie.split(";");for(let r=0;r-1&&(r=window.location.hash.substring(window.location.hash.indexOf("?")));const s=r.substring(1).split("&");for(let o=0;o0&&s[o].substring(0,l)===e&&(n=s[o].substring(l+1))}}return n}},Lue={name:"hash",lookup(t){let{lookupHash:e,lookupFromHashIndex:n}=t,r;if(typeof window<"u"){const{hash:i}=window.location;if(i&&i.length>2){const s=i.substring(1);if(e){const o=s.split("&");for(let l=0;l0&&o[l].substring(0,c)===e&&(r=o[l].substring(c+1))}}if(r)return r;if(!r&&n>-1){const o=i.match(/\/([a-zA-Z-]*)/g);return Array.isArray(o)?o[typeof n=="number"?n:0]?.replace("/",""):void 0}}}return r}};let wb=null;const rH=()=>{if(wb!==null)return wb;try{if(wb=typeof window<"u"&&window.localStorage!==null,!wb)return!1;const t="i18next.translate.boo";window.localStorage.setItem(t,"foo"),window.localStorage.removeItem(t)}catch{wb=!1}return wb};var Oue={name:"localStorage",lookup(t){let{lookupLocalStorage:e}=t;if(e&&rH())return window.localStorage.getItem(e)||void 0},cacheUserLanguage(t,e){let{lookupLocalStorage:n}=e;n&&rH()&&window.localStorage.setItem(n,t)}};let Sb=null;const aH=()=>{if(Sb!==null)return Sb;try{if(Sb=typeof window<"u"&&window.sessionStorage!==null,!Sb)return!1;const t="i18next.translate.boo";window.sessionStorage.setItem(t,"foo"),window.sessionStorage.removeItem(t)}catch{Sb=!1}return Sb};var Iue={name:"sessionStorage",lookup(t){let{lookupSessionStorage:e}=t;if(e&&aH())return window.sessionStorage.getItem(e)||void 0},cacheUserLanguage(t,e){let{lookupSessionStorage:n}=e;n&&aH()&&window.sessionStorage.setItem(n,t)}},zue={name:"navigator",lookup(t){const e=[];if(typeof navigator<"u"){const{languages:n,userLanguage:r,language:i}=navigator;if(n)for(let s=0;s0?e:void 0}},Uue={name:"htmlTag",lookup(t){let{htmlTag:e}=t,n;const r=e||(typeof document<"u"?document.documentElement:null);return r&&typeof r.getAttribute=="function"&&(n=r.getAttribute("lang")),n}},Bue={name:"path",lookup(t){let{lookupFromPathIndex:e}=t;if(typeof window>"u")return;const n=window.location.pathname.match(/\/([a-zA-Z-]*)/g);return Array.isArray(n)?n[typeof e=="number"?e:0]?.replace("/",""):void 0}},Hue={name:"subdomain",lookup(t){let{lookupFromSubdomainIndex:e}=t;const n=typeof e=="number"?e+1:1,r=typeof window<"u"&&window.location?.hostname?.match(/^(\w{2,5})\.(([a-z0-9-]{1,63}\.[a-z]{2,6})|localhost)/i);if(r)return r[n]}};let KY=!1;try{document.cookie,KY=!0}catch{}const XY=["querystring","cookie","localStorage","sessionStorage","navigator","htmlTag"];KY||XY.splice(1,1);const que=()=>({order:XY,lookupQuerystring:"lng",lookupCookie:"i18next",lookupLocalStorage:"i18nextLng",lookupSessionStorage:"i18nextLng",caches:["localStorage"],excludeCacheFor:["cimode"],convertDetectedLanguage:t=>t});class YY{constructor(e){let n=arguments.length>1&&arguments[1]!==void 0?arguments[1]:{};this.type="languageDetector",this.detectors={},this.init(e,n)}init(){let e=arguments.length>0&&arguments[0]!==void 0?arguments[0]:{languageUtils:{}},n=arguments.length>1&&arguments[1]!==void 0?arguments[1]:{},r=arguments.length>2&&arguments[2]!==void 0?arguments[2]:{};this.services=e,this.options=Mue(n,this.options||{},que()),typeof this.options.convertDetectedLanguage=="string"&&this.options.convertDetectedLanguage.indexOf("15897")>-1&&(this.options.convertDetectedLanguage=i=>i.replace("-","_")),this.options.lookupFromUrlIndex&&(this.options.lookupFromPathIndex=this.options.lookupFromUrlIndex),this.i18nOptions=r,this.addDetector(Fue),this.addDetector(Rue),this.addDetector(Oue),this.addDetector(Iue),this.addDetector(zue),this.addDetector(Uue),this.addDetector(Bue),this.addDetector(Hue),this.addDetector(Lue)}addDetector(e){return this.detectors[e.name]=e,this}detect(){let e=arguments.length>0&&arguments[0]!==void 0?arguments[0]:this.options.order,n=[];return e.forEach(r=>{if(this.detectors[r]){let i=this.detectors[r].lookup(this.options);i&&typeof i=="string"&&(i=[i]),i&&(n=n.concat(i))}}),n=n.filter(r=>r!=null&&!Eue(r)).map(r=>this.options.convertDetectedLanguage(r)),this.services&&this.services.languageUtils&&this.services.languageUtils.getBestMatchFromCodes?n:n.length>0?n[0]:null}cacheUserLanguage(e){let n=arguments.length>1&&arguments[1]!==void 0?arguments[1]:this.options.caches;n&&(this.options.excludeCacheFor&&this.options.excludeCacheFor.indexOf(e)>-1||n.forEach(r=>{this.detectors[r]&&this.detectors[r].cacheUserLanguage(e,this.options)}))}}YY.type="languageDetector";const $ue={nav:{printers:"Printers",archives:"Archives",queue:"Queue",stats:"Statistics",profiles:"Profiles",maintenance:"Maintenance",projects:"Projects",inventory:"Filament",files:"File Manager",notifications:"Notifications",settings:"Settings",system:"System",collapseSidebar:"Collapse sidebar",expandSidebar:"Expand sidebar",update:"Update",updateAvailable:"Update available: v{{version}}",updateAvailableBanner:"Version {{version}} is available!",viewUpdate:"View update",viewOnGithub:"View on GitHub",keyboardShortcuts:"Keyboard shortcuts (?)",switchToLight:"Switch to light mode",switchToDark:"Switch to dark mode",smartSwitches:"Smart Switches",logout:"Logout"},common:{save:"Save",saving:"Saving...",cancel:"Cancel",delete:"Delete",edit:"Edit",add:"Add",close:"Close",confirm:"Confirm",loading:"Loading...",error:"Error",success:"Success",warning:"Warning",enabled:"Enabled",disabled:"Disabled",yes:"Yes",no:"No",on:"On",off:"Off",all:"All",none:"None",search:"Search",filter:"Filter",sort:"Sort",refresh:"Refresh",download:"Download",upload:"Upload",uploading:"Uploading...",uploadFailed:"Upload failed",actions:"Actions",status:"Status",name:"Name",description:"Description",date:"Date",time:"Time",hours:"hours",minutes:"minutes",seconds:"seconds",days:"days",enable:"Enable",disable:"Disable",permissions:"Permissions",noPrinters:"No printers configured",noData:"No data available",linkNotFound:"Link not found",required:"Required",optional:"Optional",dismiss:"Dismiss",apply:"Apply",reset:"Reset",export:"Export",import:"Import",clear:"Clear",selectAll:"Select All",deselectAll:"Deselect All",noChange:"— No change —",unchanged:"Unchanged",unassigned:"Unassigned",unknown:"Unknown",unknownError:"Unknown error",today:"Today",tomorrow:"Tomorrow",asap:"ASAP",overdue:"Overdue",now:"Now",collapse:"Collapse",expand:"Expand",viewArchive:"View archive",viewInFileManager:"View in File Manager",addedBy:"Added by {{username}}",prints:"prints",more:"+{{count}} more",ascending:"Ascending",descending:"Descending",back:"Back",copy:"Copy",copied:"Copied!",printer:"Printer",remove:"Remove",type:"Type",print:"Print",rename:"Rename",move:"Move",create:"Create",duplicate:"Duplicate",left:"Left",right:"Right"},printers:{title:"Printers",addPrinter:"Add Printer",editPrinter:"Edit Printer",deletePrinter:"Delete Printer",printerName:"Printer Name",serialNumber:"Serial Number",ipAddress:"IP Address / Hostname",accessCode:"Access Code",model:"Model",nozzleCount:"Nozzle Count",autoArchive:"Auto Archive",status:{available:"Available",idle:"Idle",printing:"Printing",paused:"Paused",offline:"Offline",problem:"Problem",error:"Error",finished:"Finished",unknown:"Unknown"},temperatures:{nozzle:"Nozzle",bed:"Bed",chamber:"Chamber"},progress:"{{percent}}% complete",timeRemaining:"{{time}} remaining",deleteConfirm:'Are you sure you want to delete "{{name}}"?',maintenanceOk:"Maintenance OK",maintenanceWarning:"{{count}} warning",maintenanceWarning_plural:"{{count}} warnings",maintenanceDue:"{{count}} due",maintenanceDue_plural:"{{count}} due",sort:{name:"Name",status:"Status",model:"Model",location:"Location",ascending:"Sort ascending",descending:"Sort descending"},cardSize:{small:"Small cards",medium:"Medium cards",large:"Large cards",extraLarge:"Extra large cards"},hideOffline:"Hide offline",nextAvailable:"Next available",powerOn:"Power On",offlinePrintersWithPlugs:"Offline printers with smart plugs",noPrintersConfigured:"No printers configured yet",search:"Search printers...",noSearchResults:"No printers match your search or filters",filter:{allStatuses:"All statuses",allLocations:"All locations"},readyToPrint:"Ready to print",external:"External",extL:"Ext-L",extR:"Ext-R",deleteArchives:"Delete print archives",noLabel:"No label",printPreview:"Print preview",width:"Width",height:"Height",noObjectsFound:"No objects found",objectsLoadedOnPrintStart:"Objects are loaded when a print starts",willBeSkipped:"Will be skipped",name:"Name",serialCannotBeChanged:"Serial number cannot be changed",locationHelp:"Used to group printers and filter queue jobs",wifiSignal:{veryWeak:"Very weak",weak:"Weak",fair:"Fair",good:"Good",excellent:"Excellent"},maintenanceUpToDate:"All maintenance up to date - Click to view",chamberLightOn:"Turn on chamber light",chamberLightOff:"Turn off chamber light",files:"Files",browseFiles:"Browse printer files",autoOffAfterPrint:"Auto power-off after print",autoOffExecuted:"Auto-off was executed - turn printer on to reset",hmsErrors:"HMS Errors",viewHmsErrors:"View {{count}} HMS error(s)",resume:"Resume",pause:"Pause",stop:"Stop",camera:"Camera",skipObject:"Skip Object",reconnect:"Reconnect",forceRefresh:"Force Refresh",forceRefreshSuccess:"Refresh requested",mqttDebug:"MQTT Debug",printerInformation:"Printer Information",copyToClipboard:"Copy",copied:"Copied!",state:"State",wifiSignalLabel:"WiFi Signal",developerMode:"Developer Mode",enabled:"Enabled",disabled:"Disabled",addedOn:"Added",sdCard:"SD Card",inserted:"Inserted",notInserted:"Not inserted",totalPrintHours:"Print Hours",activeNozzle:"Active: {{nozzle}} nozzle",nozzleRack:"Nozzle Rack",nozzleDocked:"Docked",nozzleMounted:"Mounted",nozzleActive:"Active",nozzleIdle:"Idle",nozzleDiameter:"Diameter",nozzleType:"Type",nozzleStatus:"Status",nozzleFilament:"Filament",nozzleWear:"Wear",nozzleMaxTemp:"Max Temp",nozzleSerial:"Serial",nozzleHardenedSteel:"Hardened Steel",nozzleStainlessSteel:"Stainless Steel",nozzleTungstenCarbide:"Tungsten Carbide",nozzleFlow:"Flow",nozzleHighFlow:"High Flow",nozzleStandardFlow:"Standard",firmwareUpdate:"Firmware Update",firmwareInstructions:"On the printer's touchscreen, go to",firmwareNav:"Navigate to",settings:"Settings",firmware:"Firmware",discoverPrinters:"Discover Printers",searching:"Searching...",manualEntry:"Manual Entry",addFromCloud:"Add from Cloud",toast:{printerDeleted:"Printer deleted",missingSpoolAssignment:"Print started on {{printer}}. Missing spool assignment for: {{slots}}",printerAdded:"Printer added",printerUpdated:"Printer updated",failedToDelete:"Failed to delete printer",failedToAdd:"Failed to add printer",failedToUpdate:"Failed to update printer",commandSent:"Command sent",failedToSendCommand:"Failed to send command",turnedOn:"{{name}} turned on",failedToPowerOn:"Failed to power on {{name}}",scriptTriggered:"Script triggered",printStopped:"Print stopped",printPaused:"Print paused",printResumed:"Print resumed",referenceDeleted:"Reference deleted",detectionAreaSaved:"Detection area saved",failedToRunScript:"Failed to run script",failedToStopPrint:"Failed to stop print",failedToPausePrint:"Failed to pause print",failedToResumePrint:"Failed to resume print",failedToControlChamberLight:"Failed to control chamber light",failedToSetSpeed:"Failed to set print speed",failedToUpdateSetting:"Failed to update setting",failedToSkipObjects:"Failed to skip objects",failedToRereadRfid:"Failed to re-read RFID",failedToCheckPlate:"Failed to check plate",failedToUpdateLabel:"Failed to update label",failedToDeleteReference:"Failed to delete reference",failedToSaveDetectionArea:"Failed to save detection area",plateCheckEnabled:"Plate check enabled",plateCheckDisabled:"Plate check disabled",calibrationSaved:"Calibration saved!",calibrationFailed:"Calibration failed",rfidRereadInitiated:"RFID re-read initiated"},connection:{connected:"Connected",offline:"Offline"},plateStatus:{markCleared:"Mark plate as cleared",cleared:"Plate Clear",notCleared:"Plate not Clear",inUse:"Plate in Use"},queue:{inQueue:"{{count}} print in queue",inQueue_plural:"{{count}} prints in queue"},controls:"Controls",rfid:{reread:"Re-read RFID"},bedJog:{title:"Move build plate",bed:"Bed",step:"Step (mm)",up:"Move plate up",down:"Move plate down",disabledWhilePrinting:"Disabled while printing",notHomedTitle:"Printer is not homed",notHomedMessage:"The printer has not been homed since the last print. Run auto-home first for safe positioning (parks the toolhead, then homes X, Y, and Z), or move anyway — soft endstops will be bypassed.",homeZ:"Auto Home",moveAnyway:"Move anyway",homingStarted:"Auto-homing printer…"},permission:{noAdd:"You do not have permission to add printers",noEdit:"You do not have permission to edit printers",noDelete:"You do not have permission to delete printers",noControl:"You do not have permission to control printers",noFiles:"You do not have permission to access printer files",noAmsRfid:"You do not have permission to re-read AMS RFID",noSmartPlugControl:"You do not have permission to control smart plugs",noCamera:"You do not have permission to view cameras"},modal:{addTitle:"Add Printer",editTitle:"Edit Printer",myPrinter:"My Printer",selectModel:"Select model...",locationGroup:"Location / Group (optional)",locationPlaceholder:"e.g., Workshop, Office, Basement",autoArchiveLabel:"Auto-archive completed prints",fromPrinterSettings:"From printer settings",modelOptional:"Model (optional)",saveChanges:"Save Changes"},skipObjects:{tooltip:"Skip objects",onlyWhilePrinting:"Skip objects (only while printing)",requiresMultiple:"Skip objects (requires 2+ objects)",title:"Skip Objects",matchIdsInfo:"Match IDs with your printer display",printerShowsIds:"The printer screen shows object IDs on the build plate",skipSelected:"Skip Selected",skipping:"Skipping...",noObjectsSelected:"No objects selected",selectObjectsToSkip:"Select objects you want to skip from the current print",skipped:"skipped",objectsSkipped:"Objects skipped",activeCount:"{{count}} active",waitForLayer:"Wait for layer 2+ to skip objects (currently layer {{layer}})",skip:"Skip",confirmTitle:"Skip Object?",confirmMessage:'Are you sure you want to skip "{{name}}"? This cannot be undone.'},confirm:{deleteTitle:"Delete Printer",deleteMessage:'Are you sure you want to delete "{{name}}"? This will remove all connection settings.',deleteArchivesNote:"All print history for this printer will be permanently deleted.",keepArchivesNote:"Print history will be kept but no longer associated with this printer.",stopTitle:"Stop Print",stopMessage:'Are you sure you want to stop the current print on "{{name}}"? This will cancel the print job.',stopButton:"Stop Print",pauseTitle:"Pause Print",pauseMessage:'Are you sure you want to pause the current print on "{{name}}"?',pauseButton:"Pause Print",resumeTitle:"Resume Print",resumeMessage:'Are you sure you want to resume the print on "{{name}}"?',resumeButton:"Resume Print",powerOnTitle:"Power On Printer",powerOnMessage:'Are you sure you want to turn ON the power for "{{name}}"?',powerOnButton:"Power On",powerOffTitle:"Power Off Printer",powerOffMessage:'Are you sure you want to turn OFF the power for "{{name}}"?',powerOffWarning:'WARNING: "{{name}}" is currently printing! Are you sure you want to turn OFF the power? This will interrupt the print and may damage the printer.',powerOffButton:"Power Off"},bulk:{select:"Select",selectAll:"Select All",selectByLocation:"Select by Location",selected:"{{count}} selected",actions:{stop:"Stop",pause:"Pause",resume:"Resume",clearPlate:"Clear Bed",clearHMS:"Clear Notifications"},confirm:{stopTitle:"Stop {{count}} Prints",stopMessage:"This will cancel active prints on {{count}} printer(s). This action cannot be undone.",stopButton:"Stop All",pauseTitle:"Pause {{count}} Prints",pauseMessage:"This will pause active prints on {{count}} printer(s).",pauseButton:"Pause All",clearPlateTitle:"Clear {{count}} Print Beds",clearPlateMessage:"This will clear the print bed on {{count}} printer(s) and may trigger queued jobs.",clearPlateButton:"Clear All"},success:"{{action}} completed on {{count}} printer(s)",partial:"{{succeeded}} succeeded, {{failed}} failed",noneApplicable:"No selected printers are in the right state for this action",selectByState:"Select by State"},discovery:{title:"Discover Printers",searching:"Searching...",scanning:"Scanning...",scanProgress:"Scanning... {{scanned}}/{{total}}",foundPrinters:"Found {{count}} printer(s)",noPrintersFound:"No printers found",noPrintersFoundSubnet:"No printers found in the specified subnet.",noPrintersFoundNetwork:"No printers found on the network.",allConfigured:"All discovered printers are already configured.",alreadyAdded:"Already added",select:"Select",manualEntry:"Manual Entry",addFromCloud:"Add from Cloud",subnetToScan:"Subnet to scan",dockerNote:"Docker detected. Enter your printer's subnet in CIDR notation. Requires network_mode: host in docker-compose.yml.",scanSubnet:"Scan Subnet for Printers",discoverNetwork:"Discover Printers on Network",scanningSubnet:"Scanning subnet for Bambu printers...",scanningNetwork:"Scanning network...",serialRequired:"Serial required",unknown:"Unknown",failedToStart:"Failed to start discovery"},drying:{start:"Start Drying",stop:"Stop Drying",temperature:"Temperature",duration:"Duration",hours:"hours",timeRemaining:"{{time}} left",active:"Drying",notSupported:"Drying not supported",powerRequired:"Connect AMS power adapter to enable drying",startingDrying:"Starting drying...",stoppingDrying:"Stopping drying...",rotateTray:"Rotate spool during drying"},filaments:"Filaments",openCameraOverlay:"Open camera overlay",openCameraWindow:"Open camera in new window",firmwareUpdateAvailable:"Firmware update available: {{current}} → {{latest}}",firmwareUpToDate:"Firmware {{version}} — Up to date",firmwareUpdateButton:"Update",plateDetection:{noPermission:"You do not have permission to update printers",enabledClick:"Plate check enabled - Click to disable",disabledClick:"Plate check disabled - Click to enable",manageCalibration:"Manage plate detection calibration",calibrationRequired:"Calibration Required",calibrationInstructions:"Please ensure the build plate is completely empty, then click Calibrate.",calibrationDescription:"Calibration captures a reference image of the empty plate. Future checks will compare against this reference to detect objects.",calibrationTip:"Tip: You can store up to 5 calibrations for different plates. The system automatically uses the best match when checking.",plateEmpty:"Plate appears empty",objectsDetected:"Objects detected on plate",confidence:"Confidence",difference:"Difference",analysisPreview:"Analysis preview:",analysisLegend:"Green box = detection area, Red overlay = differences from calibration",savedReferences:"Saved References ({{count}}/{{max}})",deleteReference:"Delete reference",labelPlaceholder:"Label...",clickToEdit:"{{label}} - Click to edit",clickToAddLabel:"Click to add label"},speed:{title:"Print Speed",silent:"Silent (50%)",standard:"Standard (100%)",sport:"Sport (124%)",ludicrous:"Ludicrous (166%)"},airduct:{title:"Airduct Mode",cooling:"Cooling",heating:"Heating"},noSdCard:"No SD",door:{open:"Open",closed:"Closed"},fans:{partCooling:"Part Cooling Fan",auxiliary:"Auxiliary Fan",chamber:"Chamber Fan"},clickToViewHmsErrors:"Click to view HMS errors",estimatedCompletion:"Estimated completion time",plateNumber:"Plate {{number}}",slotOptions:"Slot options",amsPopup:{friendlyName:"AMS Name",friendlyNamePlaceholder:"e.g. AMS Friendly Name",serialNumber:"Serial Number",firmwareVersion:"Firmware",save:"Save",clear:"Clear",noEditPermission:"You do not have permission to rename AMS units"},firmwareModal:{title:"Firmware Update",titleUpToDate:"Firmware Info",currentVersion:"Current:",latestVersion:"Latest:",releaseNotes:"Release Notes",checkingPrereqs:"Checking prerequisites...",sdCardReady:"SD card ready. Click below to upload firmware.",uploadedSuccess:"Firmware uploaded to SD card!",applyInstructions:"To apply the update on your printer:",step1:"On the printer's touchscreen, go to Settings",step2:"Navigate to Firmware",step3:"Select Update from SD card",step4:"The update will take 10-20 minutes",done:"Done",starting:"Starting...",uploadFirmware:"Upload Firmware",uploadFailed:"Failed to start upload: {{error}}",uploadedToast:"Firmware uploaded! Trigger update from printer screen.",availableVersions:"Available versions",usable:"Usable",unavailable:"Unavailable",installed:"Installed",newerBadge:"newer",olderBadge:"older",currentBadge:"current"},accessCodePlaceholder:"Leave empty to keep current",roi:{title:"Detection Area (ROI)",xStart:"X Start",yStart:"Y Start",width:"Width",height:"Height",instruction:"Adjust the detection area to focus on the build plate. The green box in the preview shows the current area."},developerModeWarning:"Developer LAN mode is not enabled on: {{names}}. Some features may not work.",howToEnable:"How to enable",incompatibleFile:"This file was sliced for {{slicedFor}}, but this printer is a {{printerModel}}",dropNotPrintable:"Only .gcode and .gcode.3mf files can be printed",dropToPrint:"Drop to print",cannotPrint:"Printer busy"},archives:{title:"Print Archives",searchPlaceholder:"Search archives...",filterByPrinter:"Filter by printer",filterByStatus:"Filter by status",sortBy:"Sort by",sortNewest:"Newest first",sortOldest:"Oldest first",sortName:"Name",sortDuration:"Duration",sortLargest:"Largest first",sortSmallest:"Smallest first",sortSize:"Size",noArchives:"No archives found",noArchivesSearch:"No archives match your search",originalPrintNotVisible:"Original print not visible - try clearing filters",noArchivesYet:"No archives yet",prints:"prints",pagination:{showing:"Showing",to:"to",of:"of",show:"Show",page:"Page",all:"All"},loadingArchives:"Loading archives...",releaseToUpload:"Release to upload",showAll:"Show all",showFavoritesOnly:"Show favorites only",gridView:"Grid view",listView:"List view",calendarView:"Calendar view",logView:"Print Log",manageTags:"Manage Tags",showFailedPrints:"Show failed prints",hideFailedPrints:"Hide failed prints",hideDuplicates:"Hide Duplicates",viewOriginalPrint:"Click to view original print (#{{id}})",printTime:"Print Time",filamentUsed:"Filament Used",cost:"Cost",reprint:"Reprint",preview:"Preview",deleteArchive:"Delete Archive",deleteConfirm:"Are you sure you want to delete this archive?",favorite:"Favorite",unfavorite:"Remove from favorites",viewDetails:"View Details",status:{completed:"Completed",failed:"Failed",stopped:"Stopped"},toast:{source3mfAttached:"Source 3MF attached: {{filename}}",failedUploadSource3mf:"Failed to upload source 3MF",source3mfRemoved:"Source 3MF removed",failedRemoveSource3mf:"Failed to remove source 3MF",f3dAttached:"F3D attached: {{filename}}",failedUploadF3d:"Failed to upload F3D",f3dRemoved:"F3D removed",failedRemoveF3d:"Failed to remove F3D",timelapseAttached:"Timelapse attached: {{filename}}",timelapseAlreadyAttached:"Timelapse already attached",noMatchingTimelapse:"No matching timelapse found",failedScanTimelapse:"Failed to scan for timelapse",failedAttachTimelapse:"Failed to attach timelapse",timelapseRemoved:"Timelapse removed",failedRemoveTimelapse:"Failed to remove timelapse",timelapseUploaded:"Timelapse uploaded: {{filename}}",failedUploadTimelapse:"Failed to upload timelapse",archiveDeleted:"Archive deleted",failedDeleteArchive:"Failed to delete archive",addedToFavorites:"Added to favorites",removedFromFavorites:"Removed from favorites",projectUpdated:"Project updated",failedUpdateProject:"Failed to update project",linkCopied:"Link copied to clipboard",failedCopyLink:"Failed to copy link",photoDeleted:"Photo deleted",failedDeletePhoto:"Failed to delete photo",failedDeleteArchives:"Failed to delete archives",failedUpdateFavorites:"Failed to update favorites",exportDownloaded:"Export downloaded",exportFailed:"Export failed"},menu:{print:"Print",schedule:"Schedule",openInBambuStudio:"Open in Slicer",slice:"Slice",externalLink:"External Link",viewOnMakerWorld:"View on MakerWorld",preview3d:"3D Preview",viewTimelapse:"View Timelapse",scanForTimelapse:"Scan for Timelapse",uploadTimelapse:"Upload Timelapse",removeTimelapse:"Remove Timelapse",downloadSource3mf:"Download Source 3MF",uploadSource3mf:"Upload Source 3MF",replaceSource3mf:"Replace Source 3MF",removeSource3mf:"Remove Source 3MF",uploadF3d:"Upload F3D",replaceF3d:"Replace F3D",downloadF3d:"Download F3D",removeF3d:"Remove F3D",download:"Download",copyDownloadLink:"Copy Download Link",qrCode:"QR Code",viewPhotos:"View Photos",viewPhotosCount:"View Photos ({{count}})",projectPage:"Project Page",addToFavorites:"Add to Favorites",removeFromFavorites:"Remove from Favorites",edit:"Edit",goToProject:"Go to Project: {{name}}",addToProject:"Add to Project",removeFromProject:"Remove from Project",loading:"Loading...",noProjectsAvailable:"No projects available",select:"Select",deselect:"Deselect",delete:"Delete"},permission:{noReprint:"You do not have permission to reprint this archive",noAddToQueue:"You do not have permission to add to queue",noUpdateArchives:"You do not have permission to update archives",noUploadFiles:"You do not have permission to upload files",noDownload:"You do not have permission to download archives",noCopyLink:"You do not have permission to copy download links",noDelete:"You do not have permission to delete this archive",noCreate:"You do not have permission to create archives"},card:{previousPlate:"Previous plate",nextPlate:"Next plate",plateNumber:"Plate {{index}}",moreOptions:"Right-click for more options",addToFavorites:"Add to favorites",removeFromFavorites:"Remove from favorites",cancelled:"cancelled",failed:"failed",duplicate:"duplicate",duplicateTitle:"This model has been printed before",openSource3mf:"Open source 3MF in Bambu Studio (right-click for more options)",downloadF3d:"Download Fusion 360 design file",viewTimelapse:"View timelapse",viewPhoto:"View 1 photo",viewPhotos:"View {{count}} photos",openFolder:"Open folder: {{name}}",slicedFile:"Sliced file - ready to print",sourceFile:"Source file only - no AMS mapping available",gcode:"GCODE",source:"SOURCE",project:"Project: {{name}}",estimated:"Estimated: {{time}}",actual:"Actual: {{time}}",accuracy:"Accuracy: {{percent}}%",filament:"{{weight}}g",layer:"{{count}} layer",layers:"{{count}} layers",object:"{{count}} object",objects:"{{count}} objects",slicedFor:"Sliced for {{model}}",uploadedBy:"Uploaded By",noPermissionReprint:"You do not have permission to reprint",noFileForReprint:"No 3MF file available — the file could not be downloaded from the printer when the print was recorded",noPermissionEdit:"You do not have permission to edit archives",noPermissionDelete:"You do not have permission to delete archives",reprint:"Reprint",schedulePrint:"Schedule Print",schedule:"Schedule",openInBambuStudio:"Open in Slicer",openInBambuStudioToSlice:"Open in Slicer to slice",slice:"Slice",externalLink:"External Link",makerWorld:"MakerWorld: {{designer}}",viewProject:"View project",noExternalLink:"No external link",preview3d:"3D Preview",download:"Download",edit:"Edit",delete:"Delete"},modal:{deleteArchive:"Delete Archive",deleteConfirm:'Are you sure you want to delete "{{name}}"? This action cannot be undone.',deleteButton:"Delete",removeSource3mf:"Remove Source 3MF",removeSource3mfConfirm:'Are you sure you want to remove the source 3MF file from "{{name}}"? This will delete the original slicer project file.',removeButton:"Remove",removeF3d:"Remove F3D",removeF3dConfirm:'Are you sure you want to remove the Fusion 360 design file from "{{name}}"?',removeTimelapse:"Remove Timelapse",removeTimelapseConfirm:'Are you sure you want to remove the timelapse video from "{{name}}"?',timelapse:"{{name}} - Timelapse",selectTimelapse:"Select Timelapse",selectTimelapseDesc:"No auto-match found. Select the timelapse for this print:",deleteArchives:"Delete Archives",deleteArchivesConfirm:"Are you sure you want to delete {{count}} archive(s)? This action cannot be undone.",deleteCount:"Delete {{count}}"},page:{title:"Archives",printsCount:"{{filtered}} of {{total}} prints",dropFilesHere:"Drop .3mf files here",releaseToUpload:"Release to upload",only3mfSupported:"Only .3mf files are supported",close:"Close",selected:"{{count}} selected",selectAll:"Select All",tags:"Tags",project:"Project",favorite:"Favorite",delete:"Delete",toggledFavorites:"Toggled favorites for {{count}} archive(s)",failedUpdateFavorites:"Failed to update favorites",archivesDeleted:"{{count}} archive(s) deleted",failedDeleteArchives:"Failed to delete archives",photoDeleted:"Photo deleted",failedDeletePhoto:"Failed to delete photo"},list:{name:"Name",printer:"Printer",date:"Date",size:"Size",actions:"Actions",hasTimelapse:"Has timelapse"},log:{date:"Date",printName:"Print Name",printer:"Printer",user:"User",status:"Status",duration:"Duration",filament:"Filament",allPrinters:"All Printers",allUsers:"All Users",allStatuses:"All Statuses",cancelled:"Cancelled",skipped:"Skipped",dateFrom:"From",dateTo:"To",noEntries:"No print log entries found",showing:"Showing {{count}} of {{total}} entries",rowsPerPage:"Rows",page:"Page",prev:"Prev",next:"Next",clearLog:"Clear Log",clearLogTitle:"Clear Print Log",clearLogConfirm:"All print log entries will be permanently deleted. Archives and queue items are not affected. This action cannot be undone. Are you sure?",clearLogButton:"Clear All",cleared:"{{count}} log entries cleared",clearFailed:"Failed to clear print log"}},queue:{title:"Print Queue",subtitle:"Schedule and manage your print jobs",addToQueue:"Add to Queue",print:"Print",reprint:"Re-print",schedulePrint:"Schedule Print",editQueueItem:"Edit Queue Item",printToPrinters:"Print to {{count}} Printers",queueToPrinters:"Queue to {{count}} Printers",queueSelectedPlates:"Queue {{count}} Plates",selectAllPlates:"Select All {{count}} Plates",deselectAll:"Deselect All",printQueued:"Print queued",itemsQueued:"{{count}} items queued",sending:"Sending...",sendingProgress:"Sending {{current}}/{{total}}...",adding:"Adding...",addingProgress:"Adding {{current}}/{{total}}...",savingProgress:"Saving {{current}}/{{total}}...",clearQueue:"Clear Queue",clearHistory:"Clear History",emptyQueue:"Queue is empty",position:"Position",scheduledTime:"Scheduled Time",moveUp:"Move Up",moveDown:"Move Down",startNow:"Start Now",printingInProgress:"Printing in progress...",viewArchive:"View archive",viewInFileManager:"View in File Manager",itemCount:"{{count}} item",itemCount_plural:"{{count}} items",dragToReorder:"Drag to reorder (ASAP only)",reorderHint:"Position only affects ASAP items. Scheduled items run at their set time.",sjf:{label:"SJF",tooltip:"Shortest Job First — scheduler prioritizes shorter prints"},addedBy:"Added by {{name}}",nextInQueue:"Next in queue",clearPlate:"Clear Plate & Start Next",clearPlateSuccess:"Plate cleared — ready for next print",plateReady:"Plate cleared — ready for next print",plateNumber:"Plate {{index}}",quantity:"Quantity",quantityHint:"Creates {{count}} queue items",activeBatches:"Active Batches",batchProgress:"{{completed}} of {{total}} completed",cancelBatch:"Cancel Remaining",batchCancelled:"Remaining batch items cancelled",cancelBatchConfirmTitle:"Cancel Batch",cancelBatchConfirmMessage:"Cancel all remaining pending items in this batch?",batch:"Batch",sections:{currentlyPrinting:"Currently Printing",queued:"Queued",history:"History"},status:{pending:"Pending",waiting:"Waiting",printing:"Printing",paused:"Paused",completed:"Completed",failed:"Failed",skipped:"Skipped",cancelled:"Cancelled"},summary:{printing:"Printing",queued:"Queued",totalTime:"Total Queue Time",totalWeight:"Total Queue Weight",history:"History"},filter:{allPrinters:"All Printers",unassigned:"Unassigned",allStatus:"All Status",allLocations:"All Locations",any:"Any"},sort:{byPosition:"Sort by Position",byName:"Sort by Name",byPrinter:"Sort by Printer",bySchedule:"Sort by Schedule",byDate:"Sort by Date",ascendingOldest:"Ascending (oldest first)",descendingNewest:"Descending (newest first)"},badges:{staged:"Staged",requiresPrevious:"Requires previous success",autoPowerOff:"Auto power off",gcodeInjection:"G-code"},empty:{title:"No prints scheduled",description:'Schedule a print from the Archives page using the "Schedule" option in the context menu, or drag and drop files to get started.'},time:{asap:"ASAP",overdue:"Overdue",now:"Now",lessThanMinute:"In less than a minute",inMinutes:"In {{count}} min",inHours:"In {{count}} hours"},actions:{stopPrint:"Stop Print",startPrint:"Start Print",requeue:"Re-queue"},bulkEdit:{title:"Edit {{count}} Item",title_plural:"Edit {{count}} Items",description:"Only changed settings will be applied to selected items.",printer:"Printer",noChange:"— No change —",queueOptions:"Queue Options",staged:"Staged (manual start)",autoPowerOff:"Auto power off after print",requirePrevious:"Require previous success",printOptions:"Print Options",bedLevelling:"Bed levelling",flowCalibration:"Flow calibration",vibrationCalibration:"Vibration calibration",layerInspection:"First layer inspection",timelapse:"Timelapse",useAms:"Use AMS",applyChanges:"Apply Changes",selectAll:"Select All",deselectAll:"Deselect All",selected:"{{count}} selected",editSelected:"Edit Selected",cancelSelected:"Cancel Selected"},confirm:{cancelTitle:"Cancel Scheduled Print",cancelMessage:'Are you sure you want to cancel "{{name}}"?',stopTitle:"Stop Print",stopMessage:'Are you sure you want to stop the current print "{{name}}"? This will cancel the print job on the printer.',removeTitle:"Remove from History",removeMessage:'Are you sure you want to remove "{{name}}" from the queue history?',clearHistoryTitle:"Clear History",clearHistoryMessage:"Are you sure you want to remove all {{count}} item(s) from the history?",cancelButton:"Cancel Print",stopButton:"Stop Print",thisPrint:"this print",thisItem:"this item"},toast:{cancelled:"Queue item cancelled",cancelFailed:"Failed to cancel item",removed:"Queue item removed",removeFailed:"Failed to remove item",stopped:"Print stopped",stopFailed:"Failed to stop print",released:"Print released to queue",startFailed:"Failed to start print",reorderFailed:"Failed to reorder queue",historyCleared:"Cleared {{count}} history item(s)",clearHistoryFailed:"Failed to clear history",updateFailed:"Failed to update items",bulkCancelled:"Cancelled {{count}} item(s)",bulkCancelFailed:"Failed to cancel items"},timeline:{listView:"List",timelineView:"Timeline",unassigned:"Unassigned",noData:"No scheduled prints for this day",allDoneBy:"All prints estimated done by {{time}}",staged:"Staged",filterAll:"Show All",filterPrinting:"Printing",filterQueued:"Queued",time:{anyMoment:"any moment",minutesLeft:"{{minutes}}m left",hoursLeft:"{{hours}}h left",hoursMinutesLeft:"{{hours}}h {{minutes}}m left"},day:{previous:"Previous day",next:"Next day",today:"Today"}},permissions:{noStopPrint:"You do not have permission to stop prints",noStartPrint:"You do not have permission to start prints",noEdit:"You do not have permission to edit this queue item",noCancel:"You do not have permission to cancel this queue item",noRequeue:"You do not have permission to re-queue items",noRemove:"You do not have permission to remove this queue item",noClearHistory:"You do not have permission to clear all history",noEditItems:"You do not have permission to edit queue items",noCancelItems:"You do not have permission to cancel queue items"}},backgroundDispatch:{unknownFile:"Unknown file",unknownPrinter:"Unknown printer",startingPrints:"Starting prints",progressSummary:"{{complete}}/{{total}} complete • Dispatched: {{dispatched}} • Processing: {{processing}}",expandDetails:"Expand dispatch details",collapseDetails:"Collapse dispatch details",dismissToast:"Dismiss dispatch toast",cancelDispatchJob:"Cancel dispatch job",cancel:"Cancel",cancelling:"Cancelling…",status:{dispatched:"Dispatched",processing:"Processing",completed:"Completed",failed:"Failed",cancelled:"Cancelled"},toast:{cancellingUpload:"Cancelling upload...",cancelled:"Dispatch cancelled",cancelFailed:"Failed to cancel dispatch",completeWithFailures:"Background dispatch complete: {{completed}} succeeded, {{failed}} failed",completeSuccess:"Background dispatch complete: {{completed}} succeeded",printStartedRemaining:"{{completed}} print(s) started, {{remaining}} more sending..."}},stats:{title:"Dashboard",subtitle:"Drag widgets to rearrange. Click the eye icon to hide.",overview:"Overview",totalPrints:"Total Prints",successRate:"Success Rate",totalPrintTime:"Total Print Time",printTime:"Print Time",totalFilament:"Total Filament Used",filamentUsed:"Filament Used",filamentCost:"Filament Cost",totalCost:"Total Cost",energyUsed:"Energy Used",energyCost:"Energy Cost",energyWarmingUpTooltip:"Energy tracking is still collecting hourly snapshots. Date-range totals will become accurate once at least one snapshot exists before the selected range. Early values may undercount.",averagePrintTime:"Average Print Time",printsPerDay:"Prints per Day",byPrinter:"By Printer",printsByPrinter:"Prints by Printer",byMaterial:"By Material",byMonth:"By Month",last7Days:"Last 7 Days",last30Days:"Last 30 Days",last90Days:"Last 90 Days",allTime:"All Time",quickStats:"Quick Stats",printActivity:"Print Activity",filamentTypes:"Filament Types",filamentTrends:"Filament Trends",failureAnalysis:"Failure Analysis",timeAccuracy:"Time Accuracy",successful:"Successful:",failed:"Failed:",perfectEstimate:"100% = perfect estimate",noTimeAccuracyData:"No time accuracy data yet",noFilamentData:"No filament data available",noPrinterData:"No printer data available",noPrintData:"No print data available",noPrintDataLast30Days:"No print data in the last 30 days",failureReasons:"Failure Reasons",topFailureReasons:"Top Failure Reasons",failedPrintsCount:"{{failed}} / {{total}} prints failed",lastWeekRate:"Last week: {{rate}}%",resetLayout:"Reset Layout",recalculateCosts:"Recalculate Costs",recalculateCostsHint:"Recalculate all archive costs using current filament prices",exportStats:"Export Stats",exportAsCsv:"Export as CSV",exportAsExcel:"Export as Excel",hiddenCount:"{{count}} Hidden",exportDownloaded:"Export downloaded",exportFailed:"Export failed",layoutReset:"Layout reset",recalculatedCosts:"Recalculated costs for {{count}} archives",recalculateFailed:"Failed to recalculate costs",loadingStats:"Loading statistics...",noPermissionResetLayout:"You do not have permission to reset layout",noPermissionRecalculate:"You do not have permission to recalculate costs",noPrintDataInRange:"No print data in selected range",periodFilament:"Period Filament",periodCost:"Period Cost",avgPerPrint:"Avg per Print",usageOverTime:"Usage Over Time",filamentByWeight:"Weight",printDuration:"Print Duration",printerUtilization:"Printer Utilization",filamentSuccess:"Success by Material",printHabits:"Print Habits",printTimeOfDay:"Print Time of Day",colorDistribution:"Color Distribution",noColorData:"No color data available",records:"Records",longestPrint:"Longest Print",heaviestPrint:"Heaviest Print",mostExpensivePrint:"Most Expensive",busiestDay:"Busiest Day",successStreak:"Success Streak",streakPrint:"consecutive print",streakPrints:"{{count}} consecutive prints",printerStats:"Printer Stats",hours:"hours",avgPrints:"Avg. prints",noArchiveData:"No print data available",filamentByTime:"Time",avgWeight:"Avg. weight",avgTime:"Avg. time",filamentByPrints:"Prints",timeframe:{today:"Today","this-week":"This Week","this-month":"This Month","last-7":"Last 7 Days","last-30":"Last 30 Days","last-90":"Last 90 Days","this-year":"This Year","all-time":"All Time",custom:"Custom Range",from:"From",to:"To"},allUsers:"All Users",noUser:"No User (System)",filterByUser:"Filter by User"},maintenance:{title:"Maintenance",overview:"Overview",allOk:"All maintenance up to date",dueCount:"{{count}} item due",dueCount_plural:"{{count}} items due",warningCount:"{{count}} warning",warningCount_plural:"{{count}} warnings",totalPrintTime:"Total Print Time",nextMaintenance:"Next Maintenance",nothingDue:"Nothing due",tasks:"Tasks",lastPerformed:"Last performed",interval:"Interval",hoursRemaining:"{{hours}}h remaining",hoursOverdue:"{{hours}}h overdue",markDone:"Mark as Done",performMaintenance:"Perform Maintenance",history:"History",noHistory:"No maintenance history",editPrintHours:"Edit Print Hours",currentHours:"Current Hours",statusTab:"Status",settingsTab:"Settings",overdueCount:"{{count}} overdue",dueSoonCount:"{{count}} due soon",dueSoon:"Due soon",allGood:"All good",overdueBy:"Overdue by {{duration}}",dueIn:"Due in {{duration}}",timeLeft:"{{duration}} left",day:"1 day",days:"{{count}} days",week:"1 week",weeks:"{{count}} weeks",month:"1 month",months:"{{count}} months",year:"1 year",maintenanceTypes:"Maintenance Types",maintenanceTypesDescription:"System types and your custom maintenance tasks",addCustomType:"Add Custom Type",restoreDefaults:"Restore Default Tasks",intervalType:"Interval Type",intervalValue:"Interval ({{type}})",icon:"Icon",documentationLink:"Documentation Link (optional)",assignToPrinters:"Assign to Printers",selectAtLeastOnePrinter:"Select at least one printer",addType:"Add Type",custom:"Custom",printHours:"Print Hours",calendarDays:"Calendar Days",exampleName:"e.g., Replace HEPA Filter",viewDocumentation:"View documentation",timeBasedInterval:"Time-based interval",intervalOverrides:"Interval Overrides",intervalOverridesDescription:"Customize intervals for specific printers",assignedToPrinters:"Assigned to printers:",noPrintersAssigned:"No printers assigned",addPrinterShort:"Add:",printersAssignedClick:"{{count}} printer(s) assigned - click to manage",removeFromPrinter:"Remove from this printer",types:{lubricateCarbonRods:"Lubricate Carbon Rods",lubricateRails:"Lubricate Linear Rails",cleanNozzle:"Clean Nozzle/Hotend",checkBelts:"Check Belt Tension",cleanBuildPlate:"Clean Build Plate",checkExtruder:"Check Extruder Gears",checkCooling:"Check Cooling Fans",generalInspection:"General Inspection",cleanCarbonRods:"Clean Carbon Rods",lubricateSteelRods:"Lubricate Steel Rods",cleanSteelRods:"Clean Steel Rods",cleanLinearRails:"Clean Linear Rails",checkPtfeTube:"Check PTFE Tube",replaceHepaFilter:"Replace HEPA Filter",replaceCarbonFilter:"Replace Carbon Filter",lubricateLeftNozzleRail:"Lubricate Left Nozzle Rail"},maintenanceComplete:"Maintenance marked as complete",typeUpdated:"Maintenance type updated",typeDeleted:"Maintenance type deleted",defaultsRestored:"Restored {{count}} default task(s)",printHoursUpdated:"Print hours updated",printerAssigned:"Printer assigned",printerRemoved:"Printer removed",deleteTypeConfirm:'Delete "{{name}}"?',deleteSystemTypeTitle:"Delete default maintenance task?",deleteSystemTypeMessage:'Are you sure you want to delete the default maintenance task "{{name}}"?',noPermissionUpdate:"You do not have permission to update maintenance items",noPermissionPerform:"You do not have permission to perform maintenance",noPermissionEditTypes:"You do not have permission to edit maintenance types",noPermissionDeleteTypes:"You do not have permission to delete maintenance types",noPermissionEditHours:"You do not have permission to edit print hours",noPermissionRemovePrinter:"You do not have permission to remove printer assignments",noPermissionAssignPrinter:"You do not have permission to assign printers",noPermissionEditIntervals:"You do not have permission to edit intervals",configureSettings:"Configure maintenance types and intervals"},settings:{title:"Settings",general:"General",tabs:{general:"General",smartPlugs:"Smart Plugs",notifications:"Notifications",queue:"Workflow",filament:"Filament",network:"Network",apiKeys:"API Keys",virtualPrinter:"Virtual Printer",spoolbuddy:"SpoolBuddy",failureDetection:"Failure Detection",users:"Authentication",backup:"Backup",emailAuth:"Email Authentication",ldap:"LDAP",twoFa:"Two-Factor Auth",oidc:"SSO / OIDC"},spoolbuddy:{infoTitle:"SpoolBuddy devices",infoBody:"SpoolBuddy kiosks register themselves automatically via heartbeat. Unregister a device here if it is no longer in use or if a stale duplicate was left behind by a daemon crash.",duplicatesTitle:"{{count}} devices registered",duplicatesBody:"Only the first registered device is used by the kiosk UI. If one of these is a stale duplicate from a crash, unregister it — an online device will re-register itself on its next heartbeat.",empty:"No SpoolBuddy devices registered yet.",online:"Online",offline:"Offline",unregister:"Unregister",unregisterSuccess:"Device unregistered",unregisterError:"Failed to unregister device",confirmTitle:"Unregister SpoolBuddy device?",confirmBody:'This will remove "{{hostname}}" ({{deviceId}}) from the database. If the device is online, it will re-register itself on its next heartbeat.',ipAddress:"IP address",firmware:"Firmware",lastSeen:"Last seen",daemonUptime:"Daemon uptime",systemUptime:"System uptime",never:"never",nfc:"NFC",scale:"Scale",cpuTemp:"CPU temp",memory:"Memory",disk:"Disk",update:"Update",updateConfirmTitle:"Update Spoolbuddy daemon?",updateConfirmBody:'Trigger a software update on "{{hostname}}"? The daemon will restart once the update is applied.',restartBrowser:"Restart Browser",restartBrowserConfirmTitle:"Restart kiosk browser?",restartBrowserConfirmBody:'Restart the kiosk browser on "{{hostname}}"? The display will blank briefly.',restartDaemon:"Restart Daemon",restartDaemonConfirmTitle:"Restart Spoolbuddy daemon?",restartDaemonConfirmBody:'Restart the Spoolbuddy daemon on "{{hostname}}"? The device will go offline for a few seconds.',reboot:"Reboot",rebootConfirmTitle:"Reboot device?",rebootConfirmBody:'Reboot "{{hostname}}"? The device will be offline for around a minute.',shutdown:"Shutdown",shutdownConfirmTitle:"Shutdown device?",shutdownConfirmBody:'Shutdown "{{hostname}}"? You will need physical access to power it back on.',commandConfirm:"Confirm",commandQueued:"Command queued",commandError:"Failed to send command"},ldap:{title:"LDAP Authentication",enabledDesc:"LDAP authentication is enabled",disabledDesc:"LDAP authentication is disabled",disabledHint:"Configure and save LDAP settings below, then enable.",enabled:"LDAP authentication enabled",disabled:"LDAP authentication disabled",feature1:"Users can login with LDAP credentials",feature2:"Local admin account remains as fallback",feature3:"LDAP groups are mapped to BamBuddy groups on login",serverConfig:"LDAP Server Configuration",serverUrl:"Server URL",serverUrlHint:"Use ldaps:// for SSL or ldap:// with StartTLS",security:"Security",securityHint:"StartTLS upgrades a plain connection to TLS. LDAPS uses TLS from the start.",bindDn:"Bind DN (Service Account)",bindPassword:"Bind Password",searchBase:"Search Base DN",userFilter:"User Search Filter",userFilterHint:"{username} is replaced with the login username. Use (uid={username}) for OpenLDAP.",autoProvision:"Auto-provision users",autoProvisionHint:"Automatically create a BamBuddy account on first LDAP login",defaultGroup:"Default group",defaultGroupNone:"— None (no fallback) —",defaultGroupHint:"Fallback group assigned when an LDAP user authenticates but is not listed in any mapped LDAP group. Leave empty to leave unmapped users without permissions.",groupMapping:"Group Mapping (JSON)",groupMappingHint:"Map LDAP group DNs to BamBuddy groups. Available groups: ",testConnection:"Test Connection",settingsSaved:"LDAP settings saved",errors:{serverRequired:"LDAP server URL is required",searchBaseRequired:"Search base DN is required",enableAuthFirst:"Enable authentication first",configureLdapFirst:"Save LDAP settings first"}},email:{smtpSettings:"SMTP Configuration",smtpHost:"SMTP Server",smtpPort:"SMTP Port",security:"Security",authentication:"Authentication",username:"Username",password:"Password",fromEmail:"From Email",fromName:"From Name",testConnection:"Test SMTP Connection",testRecipient:"Test Recipient Email",sendTest:"Send Test Email",sending:"Sending...",save:"Save Settings",saving:"Saving...",advancedAuth:"Advanced Authentication",advancedAuthEnabled:"Advanced Authentication is enabled",advancedAuthEnabledDesc:"Email-based user management features are active. New users will receive auto-generated passwords via email, and users can reset their passwords through the forgot password feature.",advancedAuthDisabled:"Advanced Authentication is disabled",advancedAuthDisabledDesc:"Enable advanced authentication to activate email-based features for user management.",enable:"Enable",disable:"Disable",feature1:"Passwords are auto-generated and emailed to new users",feature2:"Users can login with username or email",feature3:"Forgot password feature is available",feature4:"Admins can reset user passwords via email",errors:{requiredFields:"Please fill in all required fields",usernameRequired:"Username is required when authentication is enabled",enterTestEmail:"Please enter a test email address",smtpServerAndEmail:"Please fill in SMTP Server and From Email before testing",usernamePasswordRequired:"Username and Password are required when authentication is enabled",configureSmtpFirst:"Please configure and test SMTP settings first",enableAuthFirst:"Please enable authentication first to use email-based features."},success:{settingsSaved:"SMTP settings saved successfully"},securityOptions:{starttls:"STARTTLS (Port 587)",ssl:"SSL/TLS (Port 465)",none:"None (Port 25)"},authOptions:{enabled:"Enabled",disabled:"Disabled"}},appearance:"Appearance",notifications:"Notifications",smartPlugs:"Smart Plugs",spoolman:"Spoolman",updates:"Updates",language:"Language",languageDescription:"Select your preferred language",theme:"Theme",themeLight:"Light",themeDark:"Dark",themeSystem:"System",defaultView:"Default View",defaultViewDescription:"Page to show when opening the app",checkForUpdates:"Check for Updates",autoUpdate:"Auto Update",currentVersion:"Current Version",latestVersion:"Latest Version",upToDate:"You are up to date",updateAvailable:"Update available",notificationLanguage:"Notification Language",notificationLanguageDescription:"Language for push notifications",bedCooledThreshold:"Bed Cooled Threshold",bedCooledThresholdDescription:"Temperature below which the bed is considered cooled after a print",userNotificationsEnabled:"User Notifications",userNotificationsEnabledDescription:"Enable the user notifications menu and email notifications for print job events. Requires Advanced Authentication.",userNotificationsDisabledHint:"Enable Advanced Authentication to use user notifications.",notificationProviders:"Notification Providers",addProvider:"Add Provider",editProvider:"Edit Provider",providerType:"Provider Type",testNotification:"Test Notification",testSuccess:"Test notification sent successfully",testFailed:"Failed to send test notification",quietHours:"Quiet Hours",quietHoursDescription:"Do not disturb during these hours",quietHoursStart:"Start",quietHoursEnd:"End",events:{title:"Notification Events",printStart:"Print Started",printComplete:"Print Completed",printFailed:"Print Failed",printStopped:"Print Stopped",printProgress:"Progress Milestones",printProgressDescription:"Notify at 25%, 50%, 75%",printerOffline:"Printer Offline",printerError:"Printer Error",filamentLow:"Low Filament",maintenanceDue:"Maintenance Due",maintenanceDueDescription:"Notify when maintenance is needed"},smartPlug:{title:"Smart Plugs",add:"Add Smart Plug",edit:"Edit Smart Plug",name:"Name",ipAddress:"IP Address",linkedPrinter:"Linked Printer",autoOn:"Auto Power On",autoOnDescription:"Turn on when print starts",autoOff:"Auto Power Off",autoOffDescription:"Turn off after print completes",offDelay:"Off Delay",offDelayMinutes:"Minutes after print",offDelayTemp:"When nozzle below temperature",currentState:"Current State",turnOn:"Turn On",turnOff:"Turn Off"},filamentTracking:"Filament Tracking",filamentTrackingDesc:"Choose how to track your filament spools. You can use the built-in inventory or connect an external Spoolman server.",filamentChecks:"Filament checks",disableFilamentWarnings:"Disable filament warnings",disableFilamentWarningsDesc:"Don't show warnings about insufficient filament when printing or queueing",preferLowestFilament:"Prefer lowest remaining filament",preferLowestFilamentDesc:"When multiple spools match, use the one with the least filament remaining",trackingModeBuiltIn:"Built-in Inventory",trackingModeBuiltInDesc:"RFID auto-matching and usage tracking included",trackingModeSpoolmanDesc:"External filament management server",builtInFeatureRfid:"Automatically detects Bambu Lab RFID spools in AMS",builtInFeatureUsage:"Tracks filament consumption per print",builtInFeatureCatalog:"Manage spools, colors, and K-factor profiles",builtInFeatureThirdParty:"Third-party spools can be assigned to inventory spools",amsSyncButton:"Sync Weights from AMS",amsSyncTitle:"Sync Spool Weights from AMS",amsSyncMessage:"This will overwrite all inventory spool weights with the current AMS remain% values from connected printers. Use this to recover from corrupted weight data. Printers must be online.",amsSyncing:"Syncing...",amsSyncSuccess:"{{synced}} spool(s) synced, {{skipped}} skipped",amsSyncError:"Failed to sync weights from AMS",spoolmanUrl:"Spoolman URL",spoolmanUrlHint:"URL of your Spoolman server (e.g., http://localhost:7912)",spoolmanConnected:"Connected",spoolmanDisconnected:"Disconnected",status:"Status",connect:"Connect",disconnect:"Disconnect",howSyncWorks:"How Sync Works",syncInfoRfidOnly:"Only official Bambu Lab spools with RFID are synced",syncInfoAutoCreate:"New spools are auto-created in Spoolman on first sync",syncInfoThirdPartySkipped:"Non-Bambu Lab spools (third-party, refilled) are skipped",linkingExistingSpools:"Linking Existing Spools",linkingExistingSpoolsDesc:'To link existing Spoolman spools to your AMS, hover over an AMS slot and click "Link to Spoolman".',syncMode:"Sync Mode",syncModeAuto:"Automatic",syncModeManual:"Manual Only",syncModeAutoDesc:"AMS data syncs automatically when changes are detected",syncModeManualDesc:"Only sync when manually triggered",syncAmsData:"Sync AMS Data",syncAmsDataDesc:"Manually sync printer AMS data to Spoolman",allPrinters:"All Printers",noDefaultPrinter:"No default (ask each time)",sidebarOrder:"Sidebar order",saveThumbnails:"Save thumbnails",captureFinishPhoto:"Capture finish photo",noPrintersConfigured:"No printers configured",archiveMode:{always:"Always create archive entry",never:"Never create archive entry",ask:"Ask each time"},checkForUpdatesLabel:"Check for updates",checkPrinterFirmware:"Check printer firmware",includeBetaUpdates:"Include beta versions",includeBetaUpdatesDesc:"Notify about beta and prerelease versions when checking for updates",enableRetry:"Enable retry",homeAssistantDescription:"Control smart plugs via Home Assistant",environmentManagedLabel:"(Environment Managed)",autoEnabledViaEnv:"Automatically enabled via environment variables",urlFromEnvReadOnly:"Value set by HA_URL environment variable (read-only)",tokenFromEnvReadOnly:"Value set by HA_TOKEN environment variable (read-only)",mqttConnectedTo:"Connected to",prometheusDescription:"Expose printer data in Prometheus format",noSmartPlugsTitle:"No smart plugs configured",noSmartPlugsDescription:"Add a Tasmota-based smart plug to track energy usage and automate power control.",noProvidersTitle:"No providers configured",noProvidersDescription:"Add a provider to receive alerts.",noTemplatesAvailable:"No templates available. Restart the backend to seed default templates.",apiPermissionView:"View printer status and queue",apiPermissionEdit:"Add and remove items from print queue",apiKeysEmptyTitle:"No API keys",apiKeysEmptyDescription:"Create an API key to integrate with external services.",noUsersFound:"No users found",noGroupsFound:"No groups found",noGroupsAvailable:"No groups available",passwordsDoNotMatch:"Passwords do not match",systemGroupWarning:"System group names cannot be changed",authDisabledTitle:"Authentication is Disabled",authDisabledFeature1:"Require login to access the system",authDisabledFeature2:"Create multiple users with group-based permissions",authDisabledFeature3:"Control access with 50+ granular permissions",userHasCreated:"This user has created:",userItemsQuestion:"What would you like to do with these items?",deleteUserConfirm:"Are you sure you want to delete this user?",actionCannotBeUndone:"This action cannot be undone.",addFirstSmartPlug:"Add Your First Smart Plug",providers:"Providers",log:"Log",testAll:"Test All",testResults:"Test Results",testPassedCount:"{{count}} passed",testFailedCount:"{{count}} failed",messageTemplates:"Message Templates",messageTemplatesDescription:"Customize notification messages for each event.",apiKeys:"API Keys",apiKeysDescription:"Create API keys for external integrations and webhooks.",createKey:"Create Key",apiKeyCreated:"API Key Created Successfully",apiKeyCopyWarning:"Copy this key now - it won't be shown again!",useInApiBrowser:"Use in API Browser",createNewApiKey:"Create New API Key",keyName:"Key Name",keyNamePlaceholder:"e.g., Home Assistant, OctoPrint",readStatus:"Read Status",readStatusDescription:"View printer status and queue",manageQueue:"Manage Queue",manageQueueDescription:"Add and remove items from print queue",controlPrinter:"Control Printer",controlPrinterDescription:"Pause, resume, and stop prints",unnamedKey:"Unnamed Key",lastUsed:"Last used",read:"Read",control:"Control",createFirstKey:"Create Your First Key",webhookEndpoints:"Webhook Endpoints",webhookApiKeyHint:"Use your API key in the X-API-Key header.",webhook:{getAllStatus:"Get all printer status",getSpecificStatus:"Get specific printer status",addToQueue:"Add to print queue",pausePrint:"Pause print",resumePrint:"Resume print",stopPrint:"Stop print"},apiBrowser:"API Browser",apiBrowserDescription:"Explore and test all available API endpoints.",apiKeyForTesting:"API Key for Testing",apiKeyPlaceholder:"Paste your API key here to test authenticated endpoints...",apiKeyHint:"This key will be sent as X-API-Key header with requests.",deleteApiKeyTitle:"Delete API Key",deleteApiKeyMessage:"Are you sure you want to delete this API key? Any integrations using this key will stop working.",deleteKey:"Delete Key",amsDisplayThresholds:"AMS Display Thresholds",amsThresholdsDescription:"Configure color thresholds for AMS humidity and temperature indicators.",humidity:"Humidity",goodGreen:"Good (green)",fairOrange:"Fair (orange)",aboveFairBad:"Above fair threshold shows as red (bad)",fairAlsoDryingThreshold:"This threshold is also used to trigger auto-drying when enabled",temperature:"Temperature",goodBlue:"Good (blue)",aboveFairHot:"Above fair threshold shows as red (hot)",historyRetention:"History Retention",keepSensorHistory:"Keep sensor history for",historyRetentionDescription:"Older humidity and temperature data will be automatically deleted",defaultPrintOptions:"Default Print Options",defaultPrintOptionsDescription:"Set default values for print options when starting new prints. These can be overridden per print in the print dialog.",defaultBedLevelling:"Bed Levelling",defaultBedLevellingDesc:"Auto-level bed before print",defaultFlowCali:"Flow Calibration",defaultFlowCaliDesc:"Calibrate extrusion flow",defaultVibrationCali:"Vibration Calibration",defaultVibrationCaliDesc:"Reduce ringing artifacts",defaultLayerInspect:"First Layer Inspection",defaultLayerInspectDesc:"AI inspection of first layer",defaultTimelapse:"Timelapse",defaultTimelapseDesc:"Record timelapse video",staggeredStart:"Staggered Start",staggeredStartDescription:"Default group size and interval when staggering multi-printer batch starts. Can be overridden per batch in the print modal.",plateClear:"Plate-Clear Confirmation",requirePlateClear:"Require plate-clear confirmation",requirePlateClearDescription:'When enabled, the scheduler waits for per-printer plate-clear confirmation before starting queued prints on printers with finished jobs. Disabling this also hides the plate status badge and the "Mark plate as cleared" button on printer cards.',gcodeInjection:"G-code Injection",gcodeInjectionDescription:'Configure custom G-code to inject at the start and/or end of prints for auto-print systems like Farmloop, SwapMod, AutoClear, and Printflow 3D. Snippets are configured per printer model and applied when "Inject G-code" is enabled on a queue item.',gcodeInjectionNoPrinters:"No printers found. Add printers to configure G-code snippets.",gcodeStartLabel:"Start G-code",gcodeEndLabel:"End G-code",gcodeStartPlaceholder:"G-code prepended before the print starts...",gcodeEndPlaceholder:"G-code appended after the print ends...",staggerGroupSize:"Group size",staggerGroupSizeHelp:"Printers to start simultaneously per group",staggerInterval:"Interval (minutes)",staggerIntervalHelp:"Delay between each group starting",queueDrying:"Queue Auto-Drying",queueDryingDescription:"Automatically dry AMS filament when printer is idle between queued prints. Uses humidity threshold above to trigger drying.",queueDryingEnabled:"Enable auto-drying",queueDryingEnabledDescription:"Start AMS drying automatically when printer is idle and humidity is above threshold",queueDryingBlock:"Wait for drying to complete",queueDryingBlockDescription:"Block the print queue until drying finishes. When off, prints take priority over drying.",ambientDryingEnabled:"Ambient drying",ambientDryingEnabledDescription:"Automatically dry filament on idle printers when humidity exceeds threshold, even without queued prints.",dryingPresets:"Drying Presets",dryingPresetsDescription:"Temperature and duration per filament type. AMS 2 Pro uses lower temps, AMS-HT supports higher temps.",dryingFilament:"Filament",printModal:"Print Modal",expandCustomMapping:"Expand custom mapping by default",expandCustomMappingDescription:"When printing to multiple printers, show per-printer AMS mapping expanded",authentication:"Authentication",authEnabledDescription:"Your instance is secured with user authentication",authDisabledDescription:"Enable to require login and manage user access",authDisabledMessage:"Enable authentication to create user accounts, manage permissions, and secure your Bambuddy instance.",enableAuthentication:"Enable Authentication",currentUser:"Current User",changePassword:"Change Password",admin:"Admin",users:"Users",addUser:"Add User",groups:"Groups",addGroup:"Add Group",system:"System",noDescription:"No description",userCount:"{{count}} users",permissionCount:"{{count}} permissions",createUser:"Create User",username:"Username",enterUsername:"Enter username",password:"Password",enterPassword:"Enter password (min 6 characters)",confirmPassword:"Confirm Password",confirmPasswordPlaceholder:"Confirm password",viewReleaseOnGitHub:"View release on GitHub",turnAllPlugsOn:"Turn all plugs on",turnAllPlugsOff:"Turn all plugs off",clearNotificationLogs:"Clear Notification Logs",clearLogsMessage:"This will permanently delete all notification logs older than 30 days. This action cannot be undone.",clearLogs:"Clear Logs",resetUiPreferences:"Reset UI Preferences",resetUiPreferencesMessage:"This will reset all UI preferences to defaults: sidebar order, theme, dashboard layout, view modes, and sorting preferences. Your printers, archives, and server settings will NOT be affected. The page will reload after clearing.",resetPreferences:"Reset Preferences",deleteGroupTitle:"Delete Group",deleteGroupMessage:"Are you sure you want to delete this group? Users in this group will lose these permissions.",deleteGroup:"Delete Group",disableAuthenticationTitle:"Disable Authentication",disableAuthenticationMessage:"Are you sure you want to disable authentication? This will make your Bambuddy instance accessible without login. All users will remain in the database but authentication will be disabled.",disableAuthentication:"Disable Authentication",configureBambuddy:"Configure Bambuddy",systemDefault:"System Default",archiveSettings:"Archive Settings",newWindow:"New Window",embeddedOverlay:"Embedded Overlay",preferredSlicer:"Preferred Slicer",preferredSlicerDescription:"Choose which slicer application to open files with",externalCameras:"External Cameras",costTracking:"Cost Tracking",printsOnly:"Prints Only",totalConsumption:"Total Consumption",dataManagement:"Data Management",storageUsage:"Storage Usage",storageUsageDescription:"Breakdown of data usage by category",storageUsageTotal:"Total",storageUsageErrors:"Errors",storageUsageOtherBreakdown:"Other (includes static assets, scripts, and configuration files)",storageUsageSystem:"System",storageUsageData:"Data",storageUsageUnavailable:"Storage usage information unavailable",clearNotificationLogsDescription:"Delete notification logs older than 30 days",resetUiPreferencesDescription:"Reset sidebar order, theme, view modes, and layout preferences. Printers, archives, and settings are not affected.",enableHomeAssistant:"Enable Home Assistant",enableMqtt:"Enable MQTT",useTls:"Use TLS",enableMetricsEndpoint:"Enable Metrics Endpoint",availableMetrics:"Available Metrics",editUser:"Edit User",deleteUserTitle:"Delete User",groupName:"Group Name",leaveEmptyForAnonymous:"Leave empty for anonymous",leaveEmptyForNoAuth:"Leave empty for no authentication",enterNewPassword:"Enter new password",confirmNewPassword:"Confirm new password",enterGroupName:"Enter group name",enterDescriptionOptional:"Enter description (optional)",enterCurrentPassword:"Enter current password",enterNewPasswordMin6:"Enter new password (min 6 characters)",toast:{keyCopied:"Key copied to clipboard",copyFailed:"Failed to copy key",keyAddedToBrowser:"Key added to API Browser",clearLogsFailed:"Failed to clear logs",uiPreferencesReset:"UI preferences reset. Refreshing...",authDisabled:"Authentication disabled successfully",authDisableFailed:"Failed to disable authentication",apiKeyCreated:"API key created",apiKeyDeleted:"API key deleted",userCreated:"User created successfully",userUpdated:"User updated successfully",userDeleted:"User deleted successfully",groupCreated:"Group created successfully",groupUpdated:"Group updated successfully",groupDeleted:"Group deleted successfully",fillRequiredFields:"Please fill in all required fields",passwordsDoNotMatch:"Passwords do not match",passwordTooShort:"Password must be at least 6 characters",enterGroupName:"Please enter a group name",settingsSaved:"Settings saved",cameraSettingsSaved:"Camera settings saved",enterCameraUrl:"Please enter a camera URL",passwordChanged:"Password changed successfully",connectionFailed:"Connection failed",testFailed:"Test failed",cameraConnected:"Camera connected{{resolution}}"},testConnection:"Test Connection",catalog:{spoolCatalog:"Spool Catalog",spoolCatalogDescription:"Empty spool weights by brand/type. Used for automatic weight lookup when adding spools.",searchCatalog:"Search catalog...",addNewEntry:"Add New Entry",namePlaceholder:"Name (e.g., Bambu Lab - Plastic)",weight:"Weight",type:"Type",default:"Default",custom:"Custom",noMatch:"No entries match your search",empty:"No entries in catalog",deleteEntry:"Delete Entry",deleteConfirm:'Are you sure you want to delete "{{name}}"?',resetCatalog:"Reset Catalog",resetConfirm:"Reset catalog to defaults? This will remove all custom entries.",loadFailed:"Failed to load spool catalog",nameWeightRequired:"Name and weight are required",entryAdded:"Entry added",addFailed:"Failed to add entry",entryUpdated:"Entry updated",updateFailed:"Failed to update entry",entryDeleted:"Entry deleted",deleteFailed:"Failed to delete entry",resetSuccess:"Catalog reset to defaults",resetFailed:"Failed to reset catalog",exported:"Exported {{count}} entries",imported:"Imported {{added}} entries ({{skipped}} skipped)",importFailed:"Failed to import: invalid JSON format",exportTooltip:"Export catalog to JSON",importTooltip:"Import catalog from JSON",resetTooltip:"Reset to defaults",selectedCount:"{{count}} selected",deleteSelected:"Delete Selected",bulkDeleteConfirm:"Are you sure you want to delete {{count}} entries?",bulkDeleted:"Deleted {{count}} entries",bulkDeleteFailed:"Failed to delete entries"},colorCatalog:{title:"Color Catalog",description:"Filament colors by manufacturer/material. Used for automatic color lookup when adding spools.",searchColors:"Search colors...",allManufacturers:"All manufacturers",addNewColor:"Add New Color",manufacturer:"Manufacturer",colorName:"Color Name",hex:"Hex",materialOptional:"Material (optional)",showing:"Showing {{filtered}} of {{total}} colors",noMatch:"No colors match your search",empty:"No colors in catalog",deleteColor:"Delete Color",deleteConfirm:'Are you sure you want to delete "{{name}}"?',resetCatalog:"Reset Color Catalog",resetConfirm:"Reset catalog to defaults? This will remove all custom colors.",sync:"Sync",starting:"Starting...",syncTooltip:"Sync from FilamentColors.xyz (2000+ colors, may take a minute)",loadFailed:"Failed to load color catalog",fieldsRequired:"Manufacturer, color name, and hex color are required",colorAdded:"Color added",addFailed:"Failed to add color",colorUpdated:"Color updated",updateFailed:"Failed to update color",colorDeleted:"Color deleted",deleteFailed:"Failed to delete color",resetSuccess:"Color catalog reset to defaults",resetFailed:"Failed to reset catalog",syncUpToDate:"Already up to date ({{count}} colors checked)",syncComplete:"Added {{added}} new colors ({{skipped}} already existed)",syncError:"Sync error",syncFailed:"Failed to sync from FilamentColors.xyz",exported:"Exported {{count}} colors",imported:"Imported {{added}} colors ({{skipped}} skipped)",importFailed:"Failed to import: invalid JSON format",selectedCount:"{{count}} selected",deleteSelected:"Delete Selected",bulkDeleteConfirm:"Are you sure you want to delete {{count}} colors?",bulkDeleted:"Deleted {{count}} colors",bulkDeleteFailed:"Failed to delete colors"},dateFormat:"Date Format",dateFormatUs:"US (MM/DD/YYYY)",dateFormatEu:"EU (DD/MM/YYYY)",dateFormatIso:"ISO (YYYY-MM-DD)",timeFormat:"Time Format",timeFormat12:"12-hour (3:30 PM)",timeFormat24:"24-hour (15:30)",defaultPrinter:"Default Printer",defaultPrinterDescription:"Pre-select this printer for uploads, reprints, and other operations.",slicerBambuStudio:"Bambu Studio",slicerOrcaSlicer:"OrcaSlicer",sidebarOrderDescription:"Drag items in the sidebar to reorder. Reset to default order here.",setDefault:"Set Default",sidebarOrderSetDefaultHint:"Set default applies the current menu order to users who haven't customized theirs.",sidebarDefaultSet:"Default menu order has been set.",sidebarDefaultCleared:"Default menu order cleared.",sidebarDefaultFailed:"Failed to set default menu order.",reset:"Reset",darkMode:"Dark Mode",lightMode:"Light Mode",active:"(active)",background:"Background",accent:"Accent",style:"Style",bgNeutral:"Neutral",bgWarm:"Warm",bgCool:"Cool",bgOled:"OLED Black",bgSlate:"Slate Blue",bgForest:"Forest Green",accentGreen:"Green",accentTeal:"Teal",accentBlue:"Blue",accentOrange:"Orange",accentPurple:"Purple",accentRed:"Red",styleClassic:"Classic",styleGlow:"Glow",styleVibrant:"Vibrant",themeToggleHint:"Toggle between dark and light mode using the sun/moon icon in the sidebar.",autoArchivePrints:"Auto-archive prints",autoArchiveDescription:"Automatically save 3MF files when prints complete",saveThumbnailsDescription:"Extract and save preview images from 3MF files",captureFinishPhotoDescription:"Take a photo from printer camera when print completes",ffmpegNotInstalled:"ffmpeg not installed",ffmpegRequired:"Camera capture requires ffmpeg. Install it via brew install ffmpeg (macOS) or apt install ffmpeg (Linux).",camera:"Camera",cameraViewMode:"Camera View Mode",cameraOverlayDescription:"Camera opens in a resizable overlay on the main screen",cameraWindowDescription:"Camera opens in a separate browser window",externalCamerasDescription:"Configure external cameras to replace the built-in printer camera. Supports MJPEG streams, RTSP, HTTP snapshots, and USB cameras (V4L2). When enabled, the external camera is used for live view and finish photos.",cameraPlaceholderUsb:"Device path (/dev/video0)",cameraPlaceholderUrl:"Camera URL (rtsp://... or http://...)",cameraTypeMjpeg:"MJPEG Stream",cameraTypeRtsp:"RTSP Stream",cameraTypeSnapshot:"HTTP Snapshot",cameraTypeUsb:"USB Camera (V4L2)",cameraRotation:"Rotation",test:"Test",connected:"Connected",disconnected:"Disconnected",currency:"Currency",defaultFilamentCost:"Default filament cost (per kg)",electricityCost:"Electricity cost per kWh",energyDisplayMode:"Energy display mode",energyModePrintDescription:"Dashboard shows sum of energy used during prints",energyModeTotalDescription:"Dashboard shows lifetime energy from smart plugs",fileManager:"File Manager",createArchiveEntry:"Create Archive Entry When Printing",createArchiveEntryDescription:"When printing from File Manager, optionally create an archive entry",lowDiskSpaceWarning:"Low Disk Space Warning",lowDiskSpaceDescription:"Show warning when free disk space falls below this threshold",printerFirmware:"Printer Firmware",checkFirmwareDescription:"Check for printer firmware updates from Bambu Lab",bambuddySoftware:"Bambuddy Software",autoCheckDescription:"Automatically check for new versions on startup",checkNow:"Check now",updateAvailableVersion:"Update available: v{{version}}",releaseNotes:"Release Notes",updateViaDocker:"Update via Docker Compose:",installUpdate:"Install Update",latestVersionRunning:"You're running the latest version",failedToCheckUpdates:"Failed to check for updates: {{error}}",backupRestore:"Backup & Restore",backupRestoreDescription:"Export/import settings and configure GitHub backup",goToBackup:"Go to Backup",externalUrl:"External URL",externalUrlDescription:"The external URL where Bambuddy is accessible. Used for notification images and external integrations.",bambuddyUrl:"Bambuddy URL",externalUrlHint:"Include protocol and port (e.g., http://192.168.1.100:8000)",ftpRetry:"FTP Retry",ftpRetryDescription:"Retry FTP operations when printer WiFi is unreliable. Applies to 3MF downloads, print uploads, timelapse downloads, and firmware updates.",autoRetryDescription:"Automatically retry failed FTP operations",retryAttempts:"Retry attempts",retryDelay:"Retry delay",connectionTimeout:"Connection timeout",time_one:"{{count}} time",time_other:"{{count}} times",second_one:"{{count}} second",second_other:"{{count}} seconds",nSeconds:"{{count}} seconds",increaseForWeakWifi:"Increase for printers with weak WiFi",homeAssistant:"Home Assistant",homeAssistantFullDescription:"Connect to Home Assistant to control smart plugs via HA's REST API. Supports switch, light, input_boolean, and script entities.",homeAssistantUrl:"Home Assistant URL",longLivedAccessToken:"Long-Lived Access Token",haTokenHint:"Create a token in HA: Profile → Long-Lived Access Tokens → Create Token",connectionSuccessful:"Connection Successful",connectionFailed:"Connection Failed",haConnectionSuccess:"Successfully connected to Home Assistant.",haConnectionFailed:"Failed to connect to Home Assistant.",mqttPublishing:"MQTT Publishing",mqttDescription:"Publish BamBuddy events to an external MQTT broker for integration with Node-RED, Home Assistant, and other automation systems.",mqttEnableDescription:"Publish events to external MQTT broker",brokerHostname:"Broker hostname",port:"Port",usernameOptional:"Username (optional)",passwordOptional:"Password (optional)",topicPrefix:"Topic prefix",topicPrefixHint:"Topics will be: {{prefix}}/printers//status, etc.",prometheusMetrics:"Prometheus Metrics",prometheusEndpointDescription:"Expose printer metrics at /api/v1/metrics for Prometheus/Grafana monitoring.",bearerTokenOptional:"Bearer Token (optional)",bearerTokenHint:"If set, requests must include Authorization: Bearer ",metricsConnectionStatus:"Connection status",metricsPrinterState:"Printer state (idle/printing/etc)",metricsPrintProgress:"Print progress 0-100%",metricsBedTemp:"Bed temperature",metricsNozzleTemp:"Nozzle temperature",metricsPrintsTotal:"Total prints by result",metricsMore:"...and more (layers, fans, queue, filament usage)",smartPlugsDescription:"Connect smart plugs (Tasmota or Home Assistant) to automate power control and track energy usage for your printers.",allOn:"All On",allOff:"All Off",addSmartPlug:"Add Smart Plug",energySummary:"Energy Summary",currentPower:"Current Power",plugsOnline:"{{reachable}}/{{total}} plugs online",today:"Today",yesterday:"Yesterday",total:"Total",enablePlugsForSummary:"Enable plugs to see energy summary",addNotificationProvider:"Add",systemBadge:"(System)",creating:"Creating...",changing:"Changing...",deleteUserAndItems:"Delete user AND their items",deleteUserKeepItems:"Delete user, keep items (become ownerless)",ok:"OK",twoFa:{totpTitle:"Authenticator App (TOTP)",totpDesc:"Use an authenticator app like Google Authenticator, Aegis or Authy.",emailOtpTitle:"Email OTP",emailOtpDesc:"Send a one-time code to {{email}} when you log in.",emailOtpNoEmail:"Add an email address to your account to enable this method.",addEmailFirst:"Your account has no email address. Ask an admin to add one before enabling Email OTP.",setupTotp:"Set up Authenticator App",setupAuthApp:"Set up Authenticator App",setupInstructions:"Scan the QR code below with your authenticator app, then confirm with a code.",manualEntry:"Can't scan? Enter this secret manually:",scannedContinue:"I've scanned the code — continue",enterCodeToConfirm:"Enter the 6-digit code from your authenticator app to confirm setup.",activate:"Activate",disableTotp:"Disable Authenticator",disableConfirmHint:"Enter a valid TOTP code or a backup code to disable the authenticator.",totpDisabled:"Authenticator app disabled.",emailOtpEnabled:"Email OTP enabled.",emailOtpDisabled:"Email OTP disabled.",smtpRequired:"Please configure and test SMTP settings first.",invalidCode:"Invalid code. Please try again.",enableEmailOtp:"Enable Email OTP",disableEmailOtp:"Disable Email OTP",emailSetupEnterCode:"A verification code has been sent to your email address. Enter it below to confirm you own this inbox.",verifyAndEnable:"Verify & Enable",emailDisablePasswordHint:"Enter your account password to confirm disabling email OTP.",passwordPlaceholder:"Enter your password",backupCodesTitle:"Save your backup codes",backupCodesWarning:"Save these codes somewhere safe. Each code can only be used once and they will not be shown again.",backupCodesRemaining:"{{count}} backup codes remaining",savedCodes:"I've saved my codes",regenBackup:"Regenerate Backup Codes",regenBackupHint:"Enter your current TOTP code to generate 10 new backup codes. All existing backup codes will be invalidated.",newBackupCodes:"New backup codes",linkedAccounts:"Linked SSO Accounts",linkedAccountsDesc:"These external identity providers are linked to your account.",oidcUnlinked:"Account unlinked."},oidc:{title:"SSO / OIDC Providers",desc:"Configure OpenID Connect providers to allow single sign-on via external identity providers.",addProvider:"Add Provider",newProvider:"New Provider",empty:"No OIDC providers configured yet.",created:"Provider created.",updated:"Provider updated.",deleted:"Provider deleted.",deleteTitle:"Delete Provider",deleteMessage:'Delete "{{name}}"? All linked user accounts will be disconnected.',form:{name:"Display Name",issuerUrl:"Issuer URL",clientId:"Client ID",clientSecret:"Client Secret",scopes:"Scopes",iconUrl:"Icon URL (optional)",enabled:"Enabled",autoCreate:"Auto-create users",autoCreateDesc:"Automatically create a local account on first login.",autoLink:"Auto-link existing accounts",autoLinkDesc:"Link existing local accounts by matching email on first login.",secretHint:"leave blank to keep current",secretPlaceholder:"new secret"}}},notification:{printStarted:{title:"Print Started",body:"{{printer}}: {{filename}} has started printing"},printCompleted:{title:"Print Completed",body:"{{printer}}: {{filename}} completed successfully"},printFailed:{title:"Print Failed",body:"{{printer}}: {{filename}} has failed"},printStopped:{title:"Print Stopped",body:"{{printer}}: {{filename}} was stopped"},printProgress:{title:"Print Progress",body:"{{printer}}: {{filename}} is {{percent}}% complete"},printerOffline:{title:"Printer Offline",body:"{{printer}} is offline"},printerError:{title:"Printer Error",body:"{{printer}}: {{error}}"},filamentLow:{title:"Low Filament",body:"{{printer}}: Filament is running low"},maintenanceDue:{title:"Maintenance Due",body:"{{printer}}: {{items}} need attention"}},errors:{generic:"Something went wrong",networkError:"Network error. Please check your connection.",notFound:"Not found",unauthorized:"Unauthorized",serverError:"Server error",validationError:"Please check your input",printerConnectionFailed:"Failed to connect to printer",saveFailed:"Failed to save changes",deleteFailed:"Failed to delete",loadFailed:"Failed to load data"},hmsErrors:{title:"Errors - {{name}}",noErrors:"No errors",viewOnWiki:"View on Bambu Lab Wiki",clearInstructions:"Clear errors on the printer to dismiss them here.",clearErrors:"Clear Errors",clearSuccess:"HMS errors cleared",clearFailed:"Failed to clear HMS errors"},mqttDebug:{title:"MQTT Debug Log",searchPlaceholder:"Search topic or payload...",noMessages:"No messages logged yet",startLoggingHint:'Click "Start Logging" to begin capturing MQTT messages',noMessagesMatch:"No messages match your filter",adjustFilterHint:"Try adjusting your search or filter criteria",incoming:"Incoming",outgoing:"Outgoing",loggingStopped:"Logging stopped",loggingActive:"Logging active - messages will auto-refresh",startLogging:"Start Logging",stopLogging:"Stop Logging",clearLog:"Clear Log",topic:"Topic",timestamp:"Timestamp",direction:"Direction",all:"All"},printerFiles:{title:"File Manager",storageUsed:"Used:",storageFree:"Free:",filterPlaceholder:"Filter files...",deleteButton:"Delete",deleteFiles:"Delete {{count}} Files",deleteFileConfirm:'Delete "{{name}}"? This cannot be undone.',deleteFilesConfirm:"Delete {{count}} selected files? This cannot be undone.",noFiles:"No files on printer",loadingFiles:"Loading files...",failedToLoad:"Failed to load files",toast:{filesDeleted:"Deleted {{count}} file(s)",deleteFailed:"Delete failed: {{error}}"}},confirm:{delete:"Are you sure you want to delete this?",unsavedChanges:"You have unsaved changes. Are you sure you want to leave?",clearQueue:"Are you sure you want to clear the queue?"},login:{title:"Bambuddy Login",subtitle:"Sign in to your account",username:"Username",usernamePlaceholder:"Enter your username",usernameOrEmail:"Username or Email",usernameOrEmailPlaceholder:"Username or @ Email",password:"Password",passwordPlaceholder:"Enter your password",signIn:"Sign in",signingIn:"Logging in...",forgotPassword:"Forgot your password?",loginSuccess:"Logged in successfully",loginFailed:"Login failed",enterCredentials:"Please enter username and password",enterEmail:"Please enter your email address",oidcLoginFailed:"OIDC login failed",oidcErrors:{providerError:"The identity provider returned an error",missingParameters:"OIDC callback is missing required parameters",invalidState:"OIDC state is invalid or has already been used",stateExpired:"OIDC login session expired — please try again",providerNotFound:"OIDC provider not found",discoveryFailed:"Failed to fetch OIDC discovery document",invalidDiscovery:"OIDC discovery document is invalid",networkError:"Network error during OIDC token exchange",badResponse:"Unexpected response during OIDC token exchange",noIdToken:"OIDC provider did not return an ID token",validationFailed:"OIDC token validation failed",nonceMismatch:"OIDC nonce mismatch — possible replay attack",missingSubClaim:"OIDC token is missing the sub claim",noLinkedAccount:"No local account is linked to this OIDC identity",accountInactive:"Your account is inactive",userResolutionFailed:"Failed to resolve your account",internalError:"An internal error occurred during OIDC login",tokenExchangeFailed:"OIDC token exchange failed"},forgotPasswordTitle:"Forgot Password",forgotPasswordMessage:"If you've forgotten your password, please contact your system administrator to reset it.",forgotPasswordEmailMessage:"Enter your email address and we'll send you a new password.",emailAddress:"Email Address",emailPlaceholder:"your.email@example.com",cancel:"Cancel",sending:"Sending...",sendResetEmail:"Send Reset Email",howToReset:"How to reset your password:",resetStep1:"Contact your Bambuddy administrator",resetStep2:"Ask them to reset your password in User Management",resetStep3:"They can set a new temporary password for you",resetStep4:"Log in with the new password and change it in Settings",gotIt:"Got it",resetPassword:{title:"Set New Password",subtitle:"Enter and confirm your new password below.",newPassword:"New Password",newPasswordPlaceholder:"At least 8 characters",confirmPassword:"Confirm Password",confirmPasswordPlaceholder:"Repeat new password",saving:"Saving…",submit:"Set New Password",backToLogin:"Back to login",passwordsDoNotMatch:"Passwords do not match",passwordTooShort:"Password must be at least 8 characters",resetFailed:"Password reset failed. The link may have expired."},twoFA:{title:"Two-Factor Authentication",subtitle:"Your account is protected with 2FA. Enter the verification code below.",methodAuthenticator:"Authenticator App",methodEmail:"Email Code",methodBackup:"Backup Code",instructionsTotp:"Open your authenticator app and enter the 6-digit code for Bambuddy.",instructionsEmail:"A 6-digit code has been sent to your email address. It expires in 10 minutes.",instructionsEmailNotSent:"Click the button below to receive a verification code via email.",instructionsBackup:"Enter one of your 8-character backup recovery codes. Each code can only be used once.",sendCodeButton:"Send Code via Email",sendingCode:"Sending...",resendCode:"Resend code",codeLabel:"Verification Code",backupCodeLabel:"Backup Code",codePlaceholder:"000000",backupCodePlaceholder:"XXXXXXXX",verifyButton:"Verify",verifyingButton:"Verifying...",backToLogin:"← Back to login",orContinueWith:"or continue with",signInWith:"Sign in with {{provider}}",enterCode:"Please enter the verification code",sendCodeFailed:"Failed to send verification code",invalidCode:"Invalid code. Please try again."}},setup:{title:"Bambuddy Setup",subtitle:"Configure authentication for your Bambuddy instance",enableAuth:"Enable Authentication",adminAccount:"Admin Account",adminAccountDesc:"If admin users already exist, authentication will be enabled using the existing admin accounts. Leave the fields below empty to use existing admins, or enter new credentials to create a new admin user.",adminUsername:"Admin Username",adminPassword:"Admin Password",optionalIfAdminExists:"(optional if admin users exist)",adminUsernamePlaceholder:"Enter admin username (optional)",adminPasswordPlaceholder:"Enter admin password (optional)",confirmPassword:"Confirm Password",confirmPasswordPlaceholder:"Confirm admin password",settingUp:"Setting up...",completeSetup:"Complete Setup",toast:{authEnabledAdminCreated:"Authentication enabled and admin user created",authEnabledExistingAdmins:"Authentication enabled using existing admin users",setupCompleted:"Setup completed",enterBothCredentials:"Please enter both admin username and password, or leave both empty to use existing admin users",passwordsDoNotMatch:"Passwords do not match",passwordTooShort:"Password must be at least 6 characters"}},changePassword:{title:"Change Password",currentPassword:"Current Password",currentPasswordPlaceholder:"Enter current password",newPassword:"New Password",newPasswordPlaceholder:"Enter new password (min 6 characters)",confirmPassword:"Confirm New Password",confirmPasswordPlaceholder:"Confirm new password",passwordsDoNotMatch:"Passwords do not match",passwordTooShort:"Password must be at least 6 characters",changing:"Changing...",success:"Password changed successfully",failed:"Failed to change password"},plateAlert:{title:"Print Paused!",message:"Objects detected on build plate. The print has been automatically paused. Please clear the plate and resume the print.",understand:"I Understand"},camera:{title:"Camera View",invalidPrinterId:"Invalid printer ID",live:"Live",snapshot:"Snapshot",restartStream:"Restart stream",refreshSnapshot:"Refresh snapshot",fullscreen:"Fullscreen",exitFullscreen:"Exit fullscreen",connectingToCamera:"Connecting to camera...",capturingSnapshot:"Capturing snapshot...",connectionLost:"Connection lost",connectionFailed:"Camera connection failed",reconnecting:"Reconnecting in {{countdown}}s... (attempt {{attempt}}/{{max}})",reconnectNow:"Reconnect now",cameraUnavailable:"Camera unavailable",cameraUnavailableDesc:"Make sure the printer is powered on and connected.",noCamera:"No camera available",retry:"Retry",cameraStream:"Camera stream",zoomOut:"Zoom out",zoomIn:"Zoom in",resetZoom:"Reset zoom",recording:"Recording",startRecording:"Start Recording",stopRecording:"Stop Recording",chamberLight:"Toggle chamber light"},groups:{title:"Group Management",subtitle:"Manage permission groups for access control",backToSettings:"Back to Settings",createGroup:"Create Group",noPermission:"You do not have permission to access this page.",system:"System",noDescription:"No description",usersCount:"{{count}} users",permissionsCount:"{{count}} permissions",edit:"Edit",delete:"Delete",toast:{created:"Group created successfully",updated:"Group updated successfully",deleted:"Group deleted successfully",enterGroupName:"Please enter a group name"},modal:{editGroup:"Edit Group",createGroup:"Create Group",cancel:"Cancel",saving:"Saving...",creating:"Creating...",saveChanges:"Save Changes"},form:{groupName:"Group Name",groupNamePlaceholder:"Enter group name",systemGroupWarning:"System group names cannot be changed",description:"Description",descriptionPlaceholder:"Enter description (optional)",permissions:"Permissions ({{count}} selected)"},deleteModal:{title:"Delete Group",message:"Are you sure you want to delete this group? Users in this group will lose these permissions.",confirm:"Delete Group"},editor:{title:"Edit Group",createTitle:"Create Group",search:"Search permissions...",selectAll:"Select All",clearAll:"Clear All",permissionsSelected:"{{count}} selected",noResults:"No permissions match your search"}},users:{title:"User Management",subtitle:"Manage users and their access to your Bambuddy instance",backToSettings:"Back to Settings",createUser:"Create User",noPermission:"You do not have permission to access this page.",admin:"Admin",noGroups:"No groups",active:"Active",inactive:"Inactive",edit:"Edit",delete:"Delete",system:"System",noGroupsAvailable:"No groups available",table:{username:"Username",groups:"Groups",status:"Status",actions:"Actions"},toast:{created:"User created successfully",updated:"User updated successfully",deleted:"User deleted successfully",fillRequired:"Please fill in all required fields",passwordsDoNotMatch:"Passwords do not match",passwordTooShort:"Password must be at least 6 characters"},modal:{createUser:"Create User",editUser:"Edit User",cancel:"Cancel",creating:"Creating...",saving:"Saving...",saveChanges:"Save Changes",advancedAuthSubtitle:"with Advanced Authentication"},form:{username:"Username",usernamePlaceholder:"Enter username",email:"Email",emailPlaceholder:"user@example.com",password:"Password",passwordPlaceholder:"Enter password",confirmPassword:"Confirm Password",confirmPasswordPlaceholder:"Confirm password",newPasswordPlaceholder:"Enter new password",confirmNewPasswordPlaceholder:"Confirm new password",leaveBlankToKeep:"leave blank to keep current",groups:"Groups",optional:"optional",autoGeneratedPassword:"A secure password will be automatically generated and emailed to the user.",passwordManagedByAdvancedAuth:'Password is managed by Advanced Authentication. Use "Reset Password" to send a new password to the user via email.',resetPassword:"Reset Password",resettingPassword:"Resetting Password..."},deleteModal:{title:"Delete User",message:"Are you sure you want to delete this user? This action cannot be undone.",confirm:"Delete User"}},streamOverlay:{title:"Stream Overlay",invalidPrinterId:"Invalid printer ID",cameraStream:"Camera stream",progress:"Progress",eta:"ETA",printerIdle:"Printer is idle",printerOffline:"Printer offline",status:{printing:"Printing",paused:"Paused",finished:"Finished",failed:"Failed",idle:"Idle",unknown:"Unknown"}},profiles:{title:"Profiles",subtitle:"Manage your slicer presets and pressure advance calibrations",tabs:{cloud:"Cloud Profiles",local:"Local Profiles",kprofiles:"K-Profiles"},localProfiles:{title:"Local Profiles",subtitle:"Import and manage slicer presets from OrcaSlicer",import:"Import Profiles",importDesc:"Drop .bbscfg, .bbsflmt, .orca_filament, .zip, or .json files here",importing:"Importing...",search:"Search local presets...",noPresets:"No local presets yet",badge:"Local",edit:"Edit",delete:"Delete",cancel:"Cancel",deleteConfirmTitle:"Delete Preset",deleteConfirm:"Are you sure you want to delete this preset? This cannot be undone.",source:"Source",inheritsFrom:"Inherits",filamentType:"Type",vendor:"Vendor",compatiblePrinters:"Printers",nozzleTemp:"Nozzle Temp",cost:"Cost",density:"Density",pressureAdvance:"Pressure Advance",filament:"Filament",process:"Process",printer:"Printer",toast:{importSuccess:"{{count}} preset(s) imported",importSkipped:"{{count}} preset(s) skipped (duplicates)",importError:"{{count}} error(s) during import",deleted:"Preset deleted",updated:"Preset updated"}},connectedAs:"Connected as",logout:"Logout",noLogoutPermission:"You do not have permission to logout",failedToLoad:"Failed to load profiles",retry:"Retry",time:{justNow:"Just now",minsAgo:"{{count}}m ago",hoursAgo:"{{count}}h ago",daysAgo:"{{count}}d ago"},toast:{loggedOut:"Logged out"},login:{title:"Connect to Bambu Cloud",subtitle:"Sync your slicer presets across devices",email:"Email",password:"Password",region:"Region",regionGlobal:"Global",regionChina:"China",verificationCode:"Verification Code",totpCode:"Authenticator Code",checkEmail:"Check your email ({{email}}) for a 6-digit code",enterTotpHint:"Enter the 6-digit code from your authenticator app",accessToken:"Access Token",accessTokenHint:"Paste your Bambu Lab access token (from Bambu Studio)",back:"Back",loginButton:"Login",verifyButton:"Verify",setTokenButton:"Set Token",useToken:"Use access token instead",useEmail:"Login with email instead",toast:{loggedIn:"Logged in successfully",codeSent:"Verification code sent to your email",enterTotp:"Enter code from your authenticator app",tokenSet:"Token set successfully"}},presets:{myPreset:"My preset (editable)",duplicate:"Duplicate",editable:"Editable",failedToLoadDetails:"Failed to load preset details",deleteConfirm:"Delete this preset?",deleteWarning:'This will permanently delete "{{name}}" from Bambu Cloud. This cannot be undone.',noDuplicatePermission:"You do not have permission to duplicate presets",noEditPermission:"You do not have permission to edit presets",noDeletePermission:"You do not have permission to delete presets",types:{filament:"Filament preset",printer:"Printer preset",process:"Process preset"},toast:{deleted:"Preset deleted",created:"Preset created",updated:"Preset updated",duplicated:"Preset duplicated",fieldAdded:'Field "{{key}}" added',exported:"Preset exported"},baseLabel:"Base: {{name}}",currentLabel:"Current: {{name}}",newPreset:"New Preset",editPreset:"Edit Preset",duplicatePreset:"Duplicate Preset",createNewPreset:"Create New Preset",customizeSettings:"Customize settings for your new preset",compareWithBase:"Compare with base preset",compare:"Compare",basePreset:"Base Preset",selectBasePreset:"Select base preset...",presetName:"Preset Name",myCustomPreset:"My custom preset",inheritsFrom:"Inherits from",dropJsonToImport:"Drop JSON to import",tabs:{common:"Common",allFields:"All Fields"},availableFields:"Available Fields",searchFieldsPlaceholder:"Search fields...",noMatchingFields:"No matching fields",allFieldsAdded:"All fields added",addCustomField:"Add custom field",yourOverrides:"Your Overrides",noOverridesYet:"No overrides yet",clickFieldsToAdd:"Click fields on the left to add them",saveAsTemplate:"Save as template",jsonTip:"Tip: Drag & drop a .json file anywhere on this modal to import settings"},cloudView:{searchPlaceholder:"Search presets...",templates:"Templates",refresh:"Refresh",newPreset:"New Preset",clearFilters:"Clear filters",compareMode:"Compare Mode",selectAnotherPreset:"Select another {{type}} preset",clickTwoPresets:"Click two presets of the same type to compare",selectFirst:"1. Select first",selectSecond:"2. Select second",compareNow:"Compare Now",lastSynced:"Last synced:",showingCount:"Showing {{showing}} of {{total}} presets",noPresetsFound:"No presets found",columns:{filament:"Filament",process:"Process",printer:"Printer"},noFilamentPresets:"No filament presets",noProcessPresets:"No process presets",noPrinterPresets:"No printer presets",filters:{type:"Type",owner:"Owner",printer:"Printer",nozzle:"Nozzle",filament:"Filament",layer:"Layer",all:"All",myPresets:"My Presets",builtIn:"Built-in",process:"Process"},noTemplatesPermission:"You do not have permission to manage templates",noRefreshPermission:"You do not have permission to refresh profiles",noCreatePermission:"You do not have permission to create presets"},templates:{title:"Quick Templates",noTemplates:"No templates yet",createFirst:"Create templates from the preset editor",typeFilter:"Type:",deleteTitle:"Delete Template",deleteWarning:"This action cannot be undone",deleteConfirm:'Are you sure you want to delete "{{name}}"?',namePlaceholder:"Template name",descriptionPlaceholder:"Description",settingsJson:"Settings (JSON)",fieldsCount:"{{count}} fields",shownInModals:"Shown in modals",hiddenInModals:"Hidden in modals",apply:"Apply",toast:{deleted:"Template deleted",updated:"Template updated",created:"Template created",applied:"Template applied"}}},support:{debugLoggingActive:"Debug logging is active",manageLogs:"Manage",collectItem7:"Printer connectivity and firmware versions",collectItem8:"Integration status (Spoolman, MQTT, HA)",collectItem9:"Network interfaces (subnets only)",collectItem10:"Python package versions",collectItem11:"Database health checks",collectItem12:"Docker environment details"},fileManager:{title:"File Manager",subtitle:"Organize and manage your print files",uploadFiles:"Upload Files",newFolder:"New Folder",folderName:"Folder Name",folderNamePlaceholder:"e.g., Functional Parts",renameFile:"Rename File",renameFolder:"Rename Folder",moveFiles:"Move {{count}} File(s)",rootNoFolder:"Root (No Folder)",current:"current",linkFolder:"Link Folder",linkFolderDescription:'Link "{{name}}" to a project or archive for quick access.',project:"Project",archive:"Archive",noProjectsFound:"No projects found",noArchivesFound:"No archives found",unlink:"Unlink",link:"Link",dragDropFiles:"Drag & drop files here",dropFilesHere:"Drop files here",orClickToBrowse:"or click to browse",allFileTypesSupported:"All file types supported. ZIP files will be extracted.",zipFilesDetected:"ZIP files detected",zipExtractOptions:"ZIP files will be extracted. Choose how to handle folder structure:",preserveZipStructure:"Preserve folder structure from ZIP",createFolderFromZip:"Create folder from ZIP filename",stlThumbnailGeneration:"STL thumbnail generation",zipMayContainStl:"ZIP files may contain STL files. Thumbnails can be generated during extraction.",thumbnailsCanBeGenerated:"Thumbnails can be generated for STL files. Large models may take longer to process.",generateThumbnailsForStl:"Generate thumbnails for STL files",threemfDetected:"3MF files detected",threemfExtractionInfo:"Printer model, material, color, and print settings will be automatically extracted from 3MF files.",willBeExtracted:"Will be extracted",filesExtracted:"{{count}} files extracted",uploadComplete:"Upload complete: {{succeeded}} succeeded",uploadFailed:"Upload failed",zipFilesFailed:"{{count}} files failed",uploading:"Uploading...",changeLink:"Change Link...",linkTo:"Link to...",linkToProjectOrArchive:"Link to project or archive",addToQueue:"Add to Queue",schedulePrint:"Schedule",generateThumbnail:"Generate Thumbnail",generateThumbnails:"Generate Thumbnails",generateThumbnailsForMissing:"Generate thumbnails for STL files missing them",gridView:"Grid view",listView:"List view",lowDiskSpaceWarning:"Low disk space warning",lowDiskSpaceDetails:"Only {{free}} free of {{total}} total. Threshold is set to {{threshold}} GB in settings.",files:"Files",folders:"Folders",size:"Size",free:"Free",allFiles:"All Files",wrap:"Wrap",enableTextWrapping:"Enable text wrapping",disableTextWrapping:"Disable text wrapping",collapse:"Collapse",collapseFoldersByDefault:"Collapse folders by default",expandFoldersByDefault:"Expand folders by default",dragToResizeTooltip:"Drag to resize, double-click to reset",searchFiles:"Search files...",allTypes:"All types",prints:"Prints",ascending:"Ascending",descending:"Descending",resultsCount:"{{showing}} of {{total}} files",selectAll:"Select All",deselectAll:"Deselect All",selected:"{{count}} selected",adding:"Adding...",loadingFiles:"Loading files...",folderIsEmpty:"Folder is empty",noFilesYet:"No files yet",folderEmptyDescription:"Upload files or move files into this folder to get started.",noFilesDescription:"Upload files to start organizing your print-related files.",noMatchingFiles:"No matching files",noMatchingFilesDescription:"No files match your current search or filter criteria.",clearFilters:"Clear filters",printedCount:"Printed {{count}}x",uploadedBy:"Uploaded By",deleteFolder:"Delete Folder",deleteFile:"Delete File",deleteFilesCount:"Delete {{count}} Files",deleteFolderConfirm:"Are you sure you want to delete this folder? All files inside will also be deleted.",deleteFileConfirm:"Are you sure you want to delete this file?",deleteFilesConfirm:"Are you sure you want to delete {{count}} selected files? This action cannot be undone.",deleting:"Deleting...",noPermissionRenameFolder:"You do not have permission to rename folders",noPermissionLinkFolder:"You do not have permission to link folders",noPermissionDeleteFolder:"You do not have permission to delete folders",noPermissionPrint:"You do not have permission to print",noPermissionAddToQueue:"You do not have permission to add to queue",noPermissionDownload:"You do not have permission to download files",noPermissionRenameFile:"You do not have permission to rename this file",noPermissionGenerateThumbnail:"You do not have permission to generate thumbnails",noPermissionDeleteFile:"You do not have permission to delete this file",noPermissionCreateFolder:"You do not have permission to create folders",noPermissionUpload:"You do not have permission to upload files",noPermissionMoveFiles:"You do not have permission to move files",noPermissionDeleteFiles:"You do not have permission to delete files",linkExternal:"Link External",linkExternalFolder:"Link External Folder",linkExternalFolderDescription:"Mount a host directory (NAS, USB, network share) into the File Manager. Files are not copied — they are accessed directly from the original path.",externalFolderNamePlaceholder:"e.g., NAS Prints",externalPath:"Host Path",externalPathHelp:"Absolute path to the directory on the Docker host. Must be bind-mounted into the container.",readOnly:"Read Only",readOnlyHelp:"prevents uploads and deletions",showHiddenFiles:"Show hidden files (dotfiles)",externalFolder:"External Folder",scanFolder:"Scan",toast:{folderCreated:"Folder created",folderDeleted:"Folder deleted",fileDeleted:"File deleted",filesDeleted:"Deleted {{count}} files",filesMoved:"Files moved",folderLinked:"Folder linked",folderUnlinked:"Folder unlinked",externalFolderLinked:"External folder linked and scanned",folderScanned:"Scan complete: {{added}} added, {{removed}} removed",addedToQueue:"Added {{count}} file(s) to queue",addedToQueuePartial:"Added {{added}} file(s), {{failed}} failed",failedToAddToQueue:"Failed to add files: {{error}}",fileRenamed:"File renamed",folderRenamed:"Folder renamed",thumbnailsGenerated:"Generated {{count}} thumbnail(s)",thumbnailsGeneratedPartial:"Generated {{succeeded}} thumbnail(s), {{failed}} failed",noStlMissingThumbnails:"No STL files missing thumbnails",failedToGenerateThumbnails:"Failed to generate thumbnails: {{error}}",thumbnailGenerated:"Thumbnail generated",failedToGenerateThumbnail:"Failed to generate thumbnail: {{error}}"}},projects:{title:"Projects",subtitle:"Organize and track your 3D printing projects",newProject:"New Project",editProject:"Edit Project",deleteProject:"Delete Project",projectName:"Project Name",description:"Description",noProjects:"No projects yet",noProjectsFiltered:"No {{status}} projects",noProjectsFilteredHelp:"You don't have any {{status}} projects. Projects will appear here when their status changes.",createFirst:"Create your first project to start organizing related prints, tracking progress, and managing your builds.",createFirstButton:"Create Your First Project",create:"Create",files:"Files",prints:"Prints",plates:"plates",parts:"parts",lastModified:"Last Modified",deleteConfirm:"Are you sure you want to delete this project? Archives and queue items will be unlinked but not deleted.",addFiles:"Add Files",removeFile:"Remove File",viewDetails:"View Details",namePlaceholder:"e.g., Voron 2.4 Build",descriptionPlaceholder:"Optional description...",color:"Color",targetPlates:"Target Plates",targetPlatesPlaceholder:"e.g., 25",targetPlatesHelp:"Number of print jobs",targetParts:"Target Parts",targetPartsPlaceholder:"e.g., 150",targetPartsHelp:"Total objects needed",tagsLabel:"Tags (comma-separated)",tagsPlaceholder:"e.g., voron, functional, gift",dueDate:"Due Date",priority:"Priority",priorityLow:"Low",priorityNormal:"Normal",priorityHigh:"High",priorityUrgent:"Urgent",statusActive:"Active",statusCompleted:"Completed",statusArchived:"Archived",done:"Done",completed:"completed",failed:"failed",inQueue:"in queue",noPrintsYet:"No prints yet",printJobs:"Print jobs (plates)",partsPrinted:"Parts printed",failedParts:"Failed parts",import:"Import",export:"Export",importProject:"Import project",exportAll:"Export all projects",loading:"Loading projects...",noEditPermission:"You do not have permission to edit projects",noDeletePermission:"You do not have permission to delete projects",noCreatePermission:"You do not have permission to create projects",noImportPermission:"You do not have permission to import projects",noExportPermission:"You do not have permission to export projects",toast:{created:"Project created",updated:"Project updated",deleted:"Project deleted",imported:"Project imported",multipleImported:"{{count}} projects imported",importFailed:"Import failed",exported:"Projects exported (metadata only)"}},projectDetail:{notFound:"Project not found",backToProjects:"Back to Projects",export:"Export",exportProject:"Export project",noExportPermission:"You do not have permission to export projects",noEditPermission:"You do not have permission to edit projects",partOf:"Part of:",priorityLabel:"Priority:",noPrints:"No prints in this project yet",status:{active:"Active",completed:"Completed",archived:"Archived"},priority:{low:"Low",normal:"Normal",high:"High",urgent:"Urgent"},dueDate:{overdue:"Overdue",today:"Due today",daysLeft:"{{count}} days left"},progress:{platesProgress:"Plates Progress",partsProgress:"Parts Progress",printJobs:"print jobs",parts:"parts",percentComplete:"{{percent}}% complete",remaining:"{{count}} remaining"},stats:{printJobs:"Print Jobs",total:"total",failed:"{{count}} failed",partsPrinted:"{{count}} parts printed",printTime:"Print Time",filamentUsed:"Filament Used"},cost:{title:"Cost Tracking",filamentCost:"Filament Cost",energy:"Energy",totalCost:"Total Cost",total:"Total",includesBom:"incl. BOM",budget:"Budget",remaining:"Remaining"},subProjects:{title:"Sub-projects ({{count}})"},notes:{title:"Notes",noEditPermission:"You do not have permission to edit notes",placeholder:"Add notes about this project...",empty:"No notes yet. Click Edit to add notes."},files:{title:"Files",linkFolders:"Link folders from the File Manager",forQuickAccess:"to this project for quick access.",fileCount:"{{count}} file(s)",empty:"No folders linked. Go to File Manager and link a folder to this project.",noFiles:"No files in this folder.",print:"Print Now",addToQueue:"Add to Queue"},bom:{title:"Bill of Materials",acquired:"{{completed}}/{{total}} acquired",showAll:"Show all",hideDone:"Hide done",addPart:"Add Part",noAddPermission:"You do not have permission to add parts",partNamePlaceholder:"Part name (e.g., M3x8 screws)",partName:"Part name",qty:"Qty",price:"Price ({{currency}})",sourcingUrlPlaceholder:"Sourcing URL (optional)",remarksPlaceholder:"Remarks (optional)",deletePart:"Delete Part",deleteConfirm:'Are you sure you want to delete "{{name}}"?',noUpdatePermission:"You do not have permission to update parts",noEditPermission:"You do not have permission to edit parts",noDeletePermission:"You do not have permission to delete parts",totalCost:"Total cost:",empty:"No parts in the bill of materials. Add hardware, electronics, or other components to track what needs to be sourced."},timeline:{title:"Activity Timeline",empty:"No activity yet."},template:{saveAsTemplate:"Save as Template",noCreatePermission:"You do not have permission to create templates"},queue:{title:"Queue",viewAll:"View all",printing:"{{count}} printing",queued:"{{count}} queued"},prints:{title:"Prints ({{count}})"},toast:{projectUpdated:"Project updated",partAdded:"Part added",partRemoved:"Part removed",exportFailed:"Export failed",projectExported:"Project exported",templateCreated:"Template created"}},system:{title:"System Information",version:"Version",uptime:"Uptime",cpuUsage:"CPU Usage",memoryUsage:"Memory Usage",diskUsage:"Disk Usage",networkInfo:"Network Info",logs:"Logs",debugMode:"Debug Mode",enableDebug:"Enable Debug Logging",disableDebug:"Disable Debug Logging",downloadLogs:"Download Logs",clearLogs:"Clear Logs",dockerInfo:"Docker Info",containerName:"Container Name",imageName:"Image Name",platform:"Platform",architecture:"Architecture"},library:{title:"Filament Library",addFilament:"Add Filament",editFilament:"Edit Filament",deleteFilament:"Delete Filament",vendor:"Vendor",material:"Material",color:"Color",kFactor:"K Factor",temperature:"Temperature",noFilaments:"No filaments in library",deleteConfirm:"Are you sure you want to delete this filament?",importFromPrinter:"Import from Printer",exportToFile:"Export to File"},spoolman:{title:"Spoolman Integration",enabled:"Spoolman Enabled",url:"Spoolman URL",connected:"Connected",disconnected:"Not Connected",testConnection:"Test Connection",sync:"Sync",syncing:"Syncing...",lastSync:"Last Sync",linkToSpoolman:"Link to Spoolman",openInSpoolman:"Open in Spoolman",unlinkSpool:"Unlink Spool",unlinkConfirmTitle:"Unlink Spool?",unlinkConfirmMessage:"This will disconnect the spool from Spoolman. The spool data in Spoolman will remain unchanged.",selectSpool:"Select Spool",noUnlinkedSpools:"No unlinked spools available",linkSuccess:"Spool linked to Spoolman successfully",linkFailed:"Failed to link spool",unlinkSuccess:"Spool unlinked from Spoolman successfully",unlinkFailed:"Failed to unlink spool",spoolId:"Spool ID",fillSourceLabel:"(Spoolman)",weight:"Weight",remaining:"Remaining",disableWeightSync:"Disable AMS Estimated Weight Sync",disableWeightSyncDesc:"Don't update remaining capacity from AMS estimates. Use this if you prefer Spoolman's usage tracking over AMS percentage-based estimates. New spools will still use the AMS estimate as their initial weight.",reportPartialUsage:"Report Partial Usage for Failed Prints",reportPartialUsageDesc:"When a print fails or is cancelled, report the estimated filament used up to that point based on layer progress."},inventory:{title:"Spool Inventory",addSpool:"Add Spool",editSpool:"Edit Spool",material:"Material",selectMaterial:"Select material...",subtype:"Subtype",brand:"Brand",searchBrand:"Search brand...",useCustomBrand:'Use "{{brand}}"',useCustomMaterial:"Use custom material: {{material}}",colorName:"Color Name",colorNamePlaceholder:"Jade White, Fire Red...",color:"Color",hexColor:"Hex Color",pickColor:"Pick custom color",labelWeight:"Label Weight",coreWeight:"Empty Spool Weight",searchSpoolWeight:"Search spool weight...",weightUsed:"Used",currentWeight:"Remaining Weight",measuredWeight:"Measured Weight",spoolName:"Spool",costPerKg:"Cost per kg",measuredWeightError:"Measured weight must be between {{min}}g and {{max}}g.",slicerFilament:"Slicer Filament",slicerFilamentName:"Slicer Preset Name",slicerPreset:"Slicer Preset",searchPresets:"Search filament presets...",selectedPreset:"Selected",noPresetsFound:"No presets found",tempOverrides:"Temperature Overrides",note:"Note",notePlaceholder:"Any additional notes about this spool...",archive:"Archive",restore:"Restore",noSpools:"No spools yet. Add your first spool to get started.",noManualSpools:"No manually added spools available. Add a spool to your inventory first.",kProfiles:"K-Profiles",addKProfile:"Add K-Profile",assignSpool:"Assign Spool",unassignSpool:"Unassign",assignSuccess:"Spool assigned and AMS slot configured",assignFailed:"Failed to assign spool",selectSpool:"Select a spool to assign to this slot",assigned:"Assigned",assigning:"Assigning...",searchSpools:"Search spools...",showAllSpools:"Show all spools",allMaterials:"All Materials",filterByBrand:"Filter by brand...",showArchived:"Show archived",quickAdd:"Quick Add (Stock)",quantity:"Quantity",stock:"Stock",configured:"Configured",spoolsCreated:"{{count}} spools created",spoolCreated:"Spool created",spoolUpdated:"Spool updated",spoolDeleted:"Spool deleted",spoolArchived:"Spool archived",spoolRestored:"Spool restored",deleteConfirm:"Are you sure you want to delete this spool? This cannot be undone.",archiveConfirm:"Are you sure you want to archive this spool?",advancedSettings:"Advanced Settings",filamentInfoTab:"Filament Info",paProfileTab:"PA Profile",filamentInfo:"Filament",additional:"Additional",loadingPresets:"Loading cloud presets...",cloudConnected:"Cloud connected",cloudNotConnected:"Cloud not connected (using defaults)",recentColors:"Recent",searchColors:"Search colors...",searchResults:"Search results",allColors:"All colors",commonColors:"Common colors",showLess:"Show less",showAll:"Show all",noColorsFound:"No colors match your search",noResults:"No matches found",selectMaterialFirst:"Please select a material first in the Filament Info tab.",noPrintersConfigured:"No printers configured. Add printers to use PA profiles.",matchingFilter:"Matching",anyBrand:"Any brand",anyVariant:"Any variant",autoSelect:"Auto-select",matches:"matches",match:"match",noMatches:"No matches",connected:"Connected",offline:"Offline",printerOffline:"Printer is offline. Connect to view calibration profiles.",noKProfilesMatch:"No K-profiles match the selected filament.",leftNozzle:"Left Nozzle",rightNozzle:"Right Nozzle",profilesSelected:"calibration profile(s) selected",totalInventory:"Total Inventory",totalConsumed:"Total Consumed",byMaterial:"By Material",inPrinter:"In Printer",lowStock:"Low Stock",sinceTracking:"Since tracking started",loadedInAms:"Loaded in AMS/Ext",remaining:"Remaining",weightCheck:"Weight Check",lastWeighed:"Last weighed",neverWeighed:"Never weighed",search:"Search spools...",showing:"Showing",to:"to",of:"of",show:"Show",spools:"spools",spool:"spool",page:"Page",noSpoolsMatch:"No results found",noSpoolsMatchDesc:"Try adjusting your search or filters to find what you're looking for.",active:"Active",archived:"Archived",all:"All",used:"Used",new:"New",clearFilters:"Clear filters",table:"Table",cards:"Cards",net:"Net",groupSimilar:"Group",groupedSpools:"{{count}} identical spools",groupedRows:"rows",columns:"Columns",configureColumns:"Configure Columns",configureColumnsDesc:"Drag to reorder columns or use arrows. Toggle visibility with the eye icon.",visible:"visible",reset:"Reset",cancel:"Cancel",applyChanges:"Apply Changes",moveUp:"Move up",moveDown:"Move down",hideColumn:"Hide column",showColumn:"Show column",linkToSpool:"Link to Spool",tagLinked:"Tag linked to spool",tagLinkFailed:"Failed to link tag",tagAlreadyLinked:"Tag already linked to another spool",unknownTag:"Unknown RFID tag detected",usageHistory:"Usage History",noUsageHistory:"No usage recorded yet",printName:"Print Name",weightConsumed:"Weight Consumed",clearHistory:"Clear",historyCleared:"Usage history cleared",fillSourceLabel:"(Inv)",lowStockThresholdError:"Threshold must be between 0.1 and 99.9",assignMismatchTitle:"Material mismatch",assignMismatchMessage:'The selected spool material "{{spoolMaterial}}" does not match the tray material "{{trayMaterial}}" for {{location}}. Assign anyway?',assignMismatchConfirm:"Assign Anyway",assignPartialMismatchMessage:'The spool material "{{spoolMaterial}}" is similar to but not exactly matching "{{trayMaterial}}" in {{location}}. Do you want to proceed?',assignProfileMismatchMessage:'The spool profile "{{spoolProfile}}" does not match the tray profile "{{trayProfile}}" in {{location}}. Do you want to proceed?'},timelapse:{title:"Timelapse",create:"Create Timelapse",download:"Download",delete:"Delete",preview:"Preview",frameRate:"Frame Rate",quality:"Quality",processing:"Processing...",noTimelapses:"No timelapses available"},ams:{title:"AMS",slot:"Slot",empty:"Empty",emptySlot:"Empty slot",unknown:"Unknown",humidity:"Humidity",temperature:"Temperature",filamentType:"Filament Type",filamentColor:"Color",remaining:"Remaining",history:"AMS History",noHistory:"No history available",configureSlot:"Configure Slot",externalSpool:"External Spool",profile:"Profile",kFactor:"K Factor",fill:"Fill",configure:"Configure",used:"used",remainingUnit:"remaining"},printModal:{title:"Start Print",selectPrinter:"Select Printer",selectPlate:"Select Plate",filamentMapping:"Filament Mapping",totalCost:"Total cost:",slotRemainingShort:" - {{grams}}g left",printSettings:"Print Settings",bedLeveling:"Bed Leveling",flowCalibration:"Flow Calibration",vibrationCalibration:"Vibration Calibration",layerInspection:"First Layer Inspection",timelapse:"Timelapse",startPrint:"Start Print",addToQueue:"Add to Queue",cancel:"Cancel",noPrintersAvailable:"No printers available",printerBusy:"Printer is busy",printerOffline:"Printer is offline",sameTypeDifferentColor:"Same type, different color",filamentTypeNotLoaded:"Filament type not loaded",openCalendar:"Open calendar",leftNozzle:"L",rightNozzle:"R",leftNozzleTooltip:"Left nozzle",rightNozzleTooltip:"Right nozzle",filamentOverride:"Filament Override",filamentOverrideHint:"Optionally override filaments for model-based assignment. The scheduler will match against your selected filaments instead of the original 3MF values.",originalFilament:"Original",overrideWith:"Override with",resetToOriginal:"Reset to original",insufficientFilamentTitle:"Not enough filament",insufficientFilamentMessage:"Some assigned spools have less filament remaining than this print needs:",insufficientFilamentLine:"{{printer}} - {{slot}}: needs {{required}}g, remaining {{remaining}}g",printAnyway:"Print anyway",forceColorMatch:"Force color match",staggerPrinterStarts:"Stagger printer starts",staggerGroupSize:"Group size",staggerInterval:"Interval (min)",staggerPreview:"{{printers}} printers → {{groups}} groups of {{size}}, starting every {{interval}} min",staggerLastGroup:"last group: {{count}}",staggerTotal:"total: {{minutes}} min",staggerToPrinters:"Stagger to {{count}} printers",gcodeInjection:"Inject auto-print G-code"},backup:{title:"Backup & Restore",createBackup:"Create Backup",restoreBackup:"Restore Backup",restoreDescription:"Replace all data from a backup file",downloadBackup:"Download Backup",uploadBackup:"Upload Backup",lastBackup:"Last Backup",autoBackup:"Auto Backup",backupNow:"Backup Now",restoreWarning:"Warning: Restoring a backup will overwrite all current data.",includeArchives:"Include Archives",includeSettings:"Include Settings",includeProfiles:"Include Profiles",backupSuccess:"Backup created successfully",restoreSuccess:"Backup restored successfully",backupFailed:"Backup failed",restoreFailed:"Restore failed",restoreNote:"Virtual Printer will be stopped during restore",githubBackup:"GitHub Backup",enabled:"Enabled",cloudLoginRequired:"Bambu Cloud login required. Sign in under Profiles → Cloud Profiles to enable GitHub backup.",cloudLoginRequiredShort:"Cloud login required",githubDescription:"Automatically sync your profiles to a private GitHub repository for backup and version history.",repositoryUrl:"Repository URL",personalAccessToken:"Personal Access Token",tokenSaved:"(saved)",enterNewToken:"Enter new token to update",tokenHint:"Fine-grained token with Contents read/write permission",branch:"Branch",manualOnly:"Manual only",hourly:"Hourly",daily:"Daily",weekly:"Weekly",includeInBackup:"Include in backup",kProfiles:"K-Profiles",kProfilesDescription:"Pressure advance calibration from connected printers",noPrintersConnected:"No printers connected",printersConnected:"{{connected}}/{{total}} connected",cloudProfiles:"Cloud Profiles",cloudProfilesDescription:"Filament, printer, and process presets from Bambu Cloud",appSettings:"App Settings",appSettingsDescription:"Bambuddy configuration (complete database)",spoolInventory:"Spool Inventory",spoolInventoryDescription:"Filament spools, usage history, and cost tracking",printArchives:"Print Archives",printArchivesDescription:"Print history metadata (no gcode/3MF files)",lastBackupAt:"Last backup:",noBackupsYet:"No backups yet",next:"Next:",startingBackup:"Starting backup...",test:"Test",enableBackup:"Enable Backup",testConnection:"Test Connection",enterRepoUrl:"Enter repository URL",enterRepoAndToken:"Enter repository URL and access token",repoRequired:"Repository URL is required",tokenRequired:"Access token is required",githubBackupEnabled:"GitHub backup enabled",tokenUpdated:"Token updated",settingsSaved:"Settings saved",failedToSave:"Failed to save: {{message}}",backupCompleteFiles:"Backup complete - {{count}} files updated",backupSkippedNoChanges:"Backup skipped - no changes",backupFailed2:"Backup failed: {{message}}",clearedLogs:"Cleared {{count}} logs",failedToClearLogs:"Failed to clear logs: {{message}}",history:"History",clear:"Clear",date:"Date",status:"Status",commit:"Commit",localBackup:"Local Backup",localBackupDescription:"Create a complete backup of your Bambuddy data including the database, archives, uploads, and all files.",downloadBackupLabel:"Download Backup",completeBackupZip:"Complete backup: database + all files (ZIP)",download:"Download",preparingBackup:"Preparing backup...",creatingArchive:"Creating backup archive... This may take a while for large archives.",downloadingFile:"Downloading backup file...",backupDownloaded:"Backup downloaded successfully",failedToCreateBackup:"Failed to create backup: {{message}}",restore:"Restore",restoreReplacesAll:"Restore replaces all data.",restoreReplacesAllDetail:"Your current database and files will be completely replaced. A restart is required after restore.",restoreConfirmTitle:"Restore Backup",restoreConfirmMessage:'Are you sure you want to restore from "{{filename}}"? This will completely replace your current database and all files. The application will need to be restarted after restore.',restoreConfirmButton:"Restore Backup",uploadingFile:"Uploading backup file...",backupRestoredRestart:"Backup restored. Please restart Bambuddy.",failedToRestore:"Failed to restore backup. Please check the file format.",reloadNow:"Reload Now",creatingBackup:"Creating Backup",restoringBackup:"Restoring Backup",preparing:"Preparing...",processing:"Processing...",doNotClosePage:"Please do not close this page or navigate away. This operation may take several minutes for large backups.",restoring:"Restoring...",restoreComplete:"Restore Complete",restoreFailed2:"Restore Failed",importSettings:"Import settings from a backup file",pleaseWaitRestoring:"Please wait while your data is being restored",selectBackupFile:"Click to select backup file (.json or .zip)",duplicateHandling:"How duplicate handling works:",matchPrinters:"Printers",matchPrintersBy:"matched by serial number",matchSmartPlugs:"Smart Plugs",matchSmartPlugsBy:"matched by IP address",matchNotificationProviders:"Notification Providers",matchNotificationProvidersBy:"matched by name",matchFilaments:"Filaments",matchFilamentsBy:"matched by name + type + brand",matchArchives:"Archives",matchArchivesBy:"matched by content hash (always skipped)",matchPendingUploads:"Pending Uploads",matchPendingUploadsBy:"matched by filename",matchSettingsTemplates:"Settings & Templates",matchSettingsTemplatesBy:"always overwritten",replaceExisting:"Replace existing data",keepExisting:"Keep existing data",overwriteDescription:"Overwrite items that already exist with backup data",keepDescription:"Only restore items that don't already exist",overwriteCaution:"Caution:",overwriteWarning:"Overwriting will replace your current configurations with data from the backup. Printer access codes are never overwritten for security.",cancel:"Cancel",processingBackup:"Processing backup file...",itemsRestored:"Items Restored",itemsSkipped:"Items Skipped",restored:"Restored",skippedAlreadyExist:"Skipped (already exist)",filesCategory:"Files (3MF, thumbnails, etc.)",andMore:"...and {{count}} more",newApiKeysGenerated:"New API Keys Generated",keysShownOnce:"These keys are only shown once. Copy them now!",copy:"Copy",noDataFound:"No data was found to restore in the backup file.",close:"Close",scheduledBackup:"Scheduled Backups",scheduledBackupDescription:"Automatically create backup snapshots on a schedule. Output directory can be mounted to a NAS or external storage.",frequency:"Frequency",backupTime:"Time",retention:"Retention",retentionDescription:"Number of backups to keep",outputPath:"Output Path",outputPathPlaceholder:"Default: {{path}}",outputPathDescription:"Leave empty for default location",runNow:"Run Now",backupFiles:"Backup Files",noScheduledBackups:"No backups yet",deleteBackup:"Delete",deleteBackupConfirm:"Delete this backup file?",backupRunning:"Backup in progress...",scheduledBackupComplete:"Backup completed successfully",scheduledBackupFailed:"Backup failed",nextBackup:"Next backup",backupSize:"Size",utc:"UTC",defaultPathLabel:"Default:",categories:{settings:"Settings",notification_providers:"Notification Providers",notification_templates:"Notification Templates",smart_plugs:"Smart Plugs",printers:"Printers",filaments:"Filaments",maintenance_types:"Maintenance Types",archives:"Archives",projects:"Projects",pending_uploads:"Pending Uploads",external_links:"External Links",api_keys:"API Keys"}},tags:{title:"Tags",addTag:"Add Tag",editTag:"Edit Tag",deleteTag:"Delete Tag",tagName:"Tag Name",tagColor:"Tag Color",noTags:"No tags",deleteConfirm:"Are you sure you want to delete this tag?",manageTags:"Manage Tags"},uploadModal:{title:"Upload 3MF Files",dragDrop:"Drag & drop .3mf files here",or:"or",browseFiles:"Browse Files",extractionInfo:"The printer model will be automatically extracted from the 3MF file metadata.",uploaded:"uploaded",failed:"failed",uploading:"Uploading...",upload:"Upload",uploadFailed:"Upload failed"},editArchive:{title:"Edit Archive",name:"Name",namePlaceholder:"Print name",printer:"Printer",noPrinter:"No printer",project:"Project",noProject:"No project",itemsPrinted:"Items Printed",itemsPrintedHelp:"Number of items produced in this print job",notes:"Notes",notesPlaceholder:"Add notes about this print...",externalLink:"External Link",externalLinkPlaceholder:"https://printables.com/model/...",externalLinkHelp:"Link to Printables, Thingiverse, or other source",tags:"Tags",tagsPlaceholder:"Add tags...",addMoreTags:"Add more tags...",matchingTags:'Matching "{{query}}"',existingTags:"Existing tags",clickToAdd:"(click to add)",status:"Status",failureReason:"Failure Reason",selectReason:"Select reason...",photos:"Photos of Printed Result",photosHelp:"Click + to add photos of your printed result",printResult:"Print result",saving:"Saving...",failureReasons:{adhesionFailure:"Adhesion failure",spaghettiDetached:"Spaghetti / Detached",layerShift:"Layer shift",cloggedNozzle:"Clogged nozzle",filamentRunout:"Filament runout",warping:"Warping",stringing:"Stringing",underExtrusion:"Under-extrusion",powerFailure:"Power failure",userCancelled:"User cancelled",other:"Other"},statuses:{completed:"Completed",failed:"Failed",aborted:"Cancelled",printing:"Printing"}},kProfiles:{title:"K-Profiles",noPrintersConfigured:"No Printers Configured",addPrinterInSettings:"Add a printer in Settings to manage K-profiles",noActivePrinters:"No Active Printers",enablePrinterConnection:"Enable a printer connection to view its K-profiles",loadingProfiles:"Loading K-Profiles...",printerOffline:"Printer Offline",printerOfflineDesc:"The selected printer is not connected. Power it on to view K-profiles.",noMatchingProfiles:"No Matching Profiles",noMatchingProfilesDesc:"No profiles match your search criteria",noKProfiles:"No K-Profiles",noKProfilesDesc:"No pressure advance profiles found for {{diameter}}mm nozzle",createFirstProfile:"Create First Profile",printer:"Printer",nozzle:"Nozzle",refresh:"Refresh",addProfile:"Add Profile",export:"Export",import:"Import",select:"Select",selectAll:"Select All",delete:"Delete",searchPlaceholder:"Search by name or filament...",allExtruders:"All Extruders",leftOnly:"Left Only",rightOnly:"Right Only",allFlow:"All Flow",hfOnly:"HF Only",sOnly:"S Only",sortName:"Sort: Name",sortKValue:"Sort: K-Value",sortFilament:"Sort: Filament",leftExtruder:"Left Extruder",rightExtruder:"Right Extruder",modal:{addTitle:"Add K-Profile",editTitle:"Edit K-Profile",profileName:"Profile Name",profileNamePlaceholder:"My PLA Profile",kValue:"K-Value",kValuePlaceholder:"0.020",kValueHelp:"Typical range: 0.01 - 0.06 for PLA, 0.02 - 0.10 for PETG",filament:"Filament",selectFilament:"Select filament...",noFilamentsHelp:"No filaments found. Create a K-profile in Bambu Studio first.",flowType:"Flow Type",highFlow:"High Flow",standard:"Standard",nozzleSize:"Nozzle Size",extruder:"Extruder",extruders:"Extruders",left:"Left",right:"Right",notes:"Notes (stored locally)",notesPlaceholder:"Add notes about this profile...",notesHelp:"Notes are saved in Bambuddy, not on the printer",syncing:"Syncing with printer...",savingExtruder:"Saving to extruder {{current}}/{{total}}...",pleaseWait:"Please wait"},deleteConfirm:{title:"Delete Profile",cannotUndo:"This cannot be undone",message:'Are you sure you want to delete "{{name}}" from the printer?'},bulkDelete:{title:"Delete Profiles",cannotUndo:"This cannot be undone",message:"Are you sure you want to delete {{count}} selected profiles from the printer?"},toast:{profileSaved:"K-profile saved",profilesSaved:"K-profile saved to {{count}} extruders",selectAtLeastOneExtruder:"Please select at least one extruder",profileDeleted:"K-profile deleted",profilesDeleted:"Deleted {{count}} profiles",exportedProfiles:"Exported {{count}} profiles",importedProfiles:"Imported {{count}} of {{total}} profiles",noProfilesToExport:"No profiles to export",invalidFileFormat:"Invalid file format",failedToParseImport:"Failed to parse import file",failedToSaveBatch:"Failed to save K-profiles",noteSaved:"Note saved",failedToSaveNote:"Failed to save note"},permission:{noRead:"You do not have permission to refresh profiles",noCreate:"You do not have permission to add profiles",noUpdate:"You do not have permission to update K-profiles",noDelete:"You do not have permission to delete K-profiles",noExport:"You do not have permission to export profiles",noImport:"You do not have permission to import profiles"}},virtualPrinter:{title:"Virtual Printer",running:"Running",stopped:"Stopped",description:{default:"Enable a virtual printer that appears in Bambu Studio and OrcaSlicer. Files sent to this printer will be archived directly without printing.",proxy:"Enable a proxy that relays slicer traffic to a real printer, allowing remote printing over any network."},enable:{title:"Enable Virtual Printer",visibleInSlicer:'Visible as "Bambuddy" in slicer discovery',proxyingTo:"Proxying to {{name}}",notActive:"Not active"},model:{title:"Printer Model",description:"Select which printer model to emulate.",restartWarning:"Changing the model will restart the virtual printer"},accessCode:{title:"Access Code",isSet:"Access code is set",notSet:"No access code set - required to enable",placeholder:"Enter 8-char code",placeholderChange:"Enter new code to change",hint:"Must be exactly 8 characters. Used by slicers to authenticate.",charCount:"({{count}}/8)"},targetPrinter:{title:"Target Printer",configured:"Proxy target configured",notConfigured:"No target printer selected - required for proxy mode",placeholder:"Select a printer...",hint:"Select the printer to proxy slicer traffic to. The printer must be in LAN mode.",noPrinters:"No printers configured. Add a printer first to use proxy mode."},remoteInterface:{title:"Network Interface Override",configured:"Interface override active",optional:"Optional - use if auto-detected IP is wrong (e.g. multiple NICs, Docker, VPN)",placeholder:"Auto-detect (default)...",hint:"Override the IP address advertised via SSDP and used in the TLS certificate. Useful when Bambuddy has multiple network interfaces."},mode:{title:"Mode",archive:"Archive",archiveDesc:"Archive files immediately",review:"Review",reviewDesc:"Review before archiving",queue:"Queue",queueDesc:"Archive and add to queue",proxy:"Proxy",proxyDesc:"Relay to real printer"},autoDispatch:{title:"Auto-dispatch",description:"Automatically start prints when added to queue. When off, prints wait for manual dispatch."},setupRequired:{title:"Setup Required",description:"The virtual printer feature requires additional system configuration before it will work. This includes port forwarding, firewall rules, and platform-specific settings.",readGuide:"Read the setup guide before enabling"},howItWorks:{title:"How it works",step1:"On the same LAN, virtual printers appear in your slicer (Bambu Studio / OrcaSlicer) automatically via discovery. From other networks, add them manually by IP address and access code.",step2:'In Archive, Review, and Queue modes, use the "Send" button in your slicer to upload 3MF files to Bambuddy. The slicer will show "Print success" — the file is stored, not printed.',step3:"In Proxy mode, the virtual printer relays all traffic to a real printer — prints start immediately as if connected directly."},status:{title:"Status Details",printerName:"Printer Name",model:"Model",serialNumber:"Serial Number",mode:"Mode",pendingFiles:"Pending Files",targetPrinter:"Target Printer",ftpPort:"FTP Port",mqttPort:"MQTT Port",ftpConnections:"FTP Connections",mqttConnections:"MQTT Connections"},toast:{updated:"Virtual printer settings updated",failedToUpdate:"Failed to update settings",accessCodeRequired:"Please set an access code first",targetPrinterRequired:"Please select a target printer first",bindIpRequired:"Please set a bind IP first",accessCodeEmpty:"Access code cannot be empty",accessCodeLength:"Access code must be exactly 8 characters",created:"Virtual printer created",failedToCreate:"Failed to create virtual printer",deleted:"Virtual printer deleted",failedToDelete:"Failed to delete virtual printer"},list:{title:"Virtual Printers",add:"Add",addFirst:"Add Virtual Printer",empty:"No virtual printers configured. Add one to get started."},bindIp:{title:"Bind Interface",placeholder:"Select interface...",hint:"Network interface for this virtual printer to bind to. Must be unique per printer."},proxy:{accessCodeHint:"In proxy mode, use your target printer's access code in the slicer. The connection is forwarded transparently to the real printer."},addDialog:{title:"Add Virtual Printer",name:"Name",hint:"You can configure access code, target printer, and other settings after creating.",create:"Create"},deleteConfirm:{title:"Delete Virtual Printer",message:'Are you sure you want to delete "{{name}}"? This will stop all services for this printer.'}},modelViewer:{openInSlicer:"Open in Slicer",tabs:{model:"3D Model",gcode:"G-code Preview"},notAvailable:"not available",notSliced:"not sliced",plates:"Plates",allPlates:"All Plates",plateNumber:"Plate {{number}}",plateCount:"{{count}} plate",plateCount_other:"{{count}} plates",objectCount:"{{count}} object",objectCount_other:"{{count}} objects",filamentCount:"{{count}} filament",filamentCount_other:"{{count}} filaments",eta:"ETA {{minutes}} min",noPreview:"No preview available for this file",pagination:{pageOf:"Page {{current}} of {{total}}",prev:"Prev",next:"Next"},errors:{failedToLoad:"Failed to load file",noMeshes:"No meshes found in 3MF file",unsupportedFormat:"Unsupported file format"}},maintenanceDescriptions:{lubricateCarbonRods:"Apply lubricant to carbon rods for smooth motion",lubricateRails:"Apply lubricant to linear rails for smooth motion",cleanNozzle:"Clean hotend and nozzle to prevent clogs",checkBelts:"Verify belt tension for accurate prints",cleanBuildPlate:"Clean build plate for better adhesion",checkExtruder:"Inspect extruder gears for wear",checkCooling:"Ensure cooling fans are working properly",generalInspection:"General printer inspection",cleanCarbonRods:"Clean carbon rods to reduce friction",lubricateSteelRods:"Apply lubricant to steel rods for smooth motion",cleanSteelRods:"Clean steel rods to reduce friction",cleanLinearRails:"Wipe linear rails to remove dust and debris",checkPtfeTube:"Inspect PTFE tube for wear or damage",replaceHepaFilter:"Replace HEPA filter for air quality",replaceCarbonFilter:"Replace activated carbon filter",lubricateLeftNozzleRail:"Lubricate left nozzle rail (H2 series)"},smartPlugs:{offline:"Offline",admin:"Admin",openPlugAdminPage:"Open plug admin page",deleteSmartPlug:"Delete Smart Plug",turnOnSmartPlug:"Turn On Smart Plug",turnOffSmartPlug:"Turn Off Smart Plug",turnOn:"Turn On",turnOff:"Turn Off",addSmartPlug:{scanningNetwork:"Scanning network...",chooseEntity:"Choose an entity...",connectionFailed:"Connection failed",searchEntities:"Search entities...",searchPowerSensors:"Search power sensors...",searchEnergySensors:"Search energy sensors...",placeholders:{plugName:"Living Room Plug",mqttStateOnValue:"ON, true, 1",mqttSameAsPower:"Same as power topic, or different"}},linkedTo:"Linked to:",monitorOnly:"Monitor Only",alerts:"Alerts",scheduleOn:"On {{time}}",scheduleOff:"Off {{time}}",on:"On",off:"Off",power:"Power",kwhToday:"kWh Today",settings:"Settings",automationSettings:"Automation Settings",showInSwitchbar:"Show in Switchbar",quickAccessSidebar:"Quick access from sidebar",enabled:"Enabled",enableAutomation:"Enable automation for this plug",autoOn:"Auto On",autoOnDescription:"Turn on when print starts",autoOff:"Auto Off",autoOffDescription:"Turn off when print completes (one-shot)",autoOffPersistent:"Keep Enabled",autoOffPersistentDescription:"Stay enabled between prints instead of one-shot",turnOffDelayMode:"Turn Off Delay Mode",time:"Time",temp:"Temp",delayMinutes:"Delay (minutes)",tempThreshold:"Temperature threshold (°C)",tempThresholdDescription:"Turns off when nozzle cools below this temperature",edit:"Edit",deleteConfirm:'Are you sure you want to delete "{{name}}"? This cannot be undone.',turnOnConfirm:'Are you sure you want to turn on "{{name}}"?',turnOffConfirm:'Are you sure you want to turn off "{{name}}"? This will cut power to the connected device.',failedToTurn:'Failed to turn {{action}} "{{name}}"',unknown:"Unknown",addTitle:"Add Smart Plug",editTitle:"Edit Smart Plug",stopScanning:"Stop Scanning",discoverTasmota:"Discover Tasmota Devices",foundDevices:"Found {{count}} device(s) - click to select:",noDevicesFound:"No Tasmota devices found on your network",haNotConfigured:"Home Assistant is not configured. Set it up in",haSettingsPath:"Settings → Network → Home Assistant",selectEntity:"Select Entity *",ipAddress:"IP Address *",nameLabel:"Name *",username:"Username",password:"Password",authHint:"Leave empty if your Tasmota device doesn't require authentication",linkToPrinter:"Link to Printer",noPrinter:"No printer (manual control only)",linkingDescription:"Linking enables automatic on/off when prints start/complete",powerAlerts:"Power Alerts",alertAbove:"Alert if above (W)",alertBelow:"Alert if below (W)",alertDescription:"Get notified when power consumption crosses these thresholds. Leave empty to disable that direction.",dailySchedule:"Daily Schedule",turnOnAt:"Turn On at",turnOffAt:"Turn Off at",scheduleDescription:"Automatically turn the plug on/off at these times daily. Leave empty to skip that action.",showOnPrinterCard:"Show on Printer Card",displayOnPrinterCard:"Display button on printer card",connectedResult:"Connected!",deviceLabel:"Device: {{name}} - ",stateLabel:"State: {{state}}",test:"Test",delete:"Delete",save:"Save",add:"Add",cancel:"Cancel",failedToStartScan:"Failed to start scan",nameRequired:"Name is required",entityRequired:"Entity is required for Home Assistant plugs",mqttTopicRequired:"At least one MQTT topic must be configured for power, energy, or state monitoring",loadingEntities:"Loading entities...",loading:"Loading...",failedToLoadEntities:"Failed to load entities: {{error}}",noEntitiesMatching:'No entities found matching "{{search}}"',noEntitiesAvailable:"No entities available",searchingEntities:"Searching all entities ({{count}} found)",showingEntities:"Showing switch, light, input_boolean ({{count}} available)",energyMonitoringOptional:"Energy Monitoring (Optional)",energyMonitoringHint:"Search and select sensors that provide power/energy data.",powerSensorW:"Power Sensor (W)",energyTodayKwh:"Energy Today (kWh)",totalEnergyKwh:"Total Energy (kWh)",noMatchingSensors:"No matching sensors",none:"None",mqttNotConfigured:"MQTT broker not configured. Set broker address in",mqttSettingsPath:"Settings → Network → MQTT Publishing",mqttNotConfiguredSuffix:"(you don't need to enable publishing, just fill in the broker details).",mqttMonitorOnlyDescription:"MQTT plugs receive power/energy data via MQTT subscription. On/off control is not available - use your MQTT broker or home automation system.",powerMonitoring:"Power Monitoring",energyMonitoring:"Energy Monitoring",stateMonitoring:"State Monitoring",optional:"optional",topic:"Topic",jsonPath:"JSON Path",multiplier:"Multiplier",onValue:"ON Value",mqttPowerHint:`JSON path extracts value from JSON payload (e.g., "power_l1"). Leave empty if topic publishes raw numeric values. Use multiplier 0.001 for mW→W, 1000 for kW→W.`,mqttEnergyHint:`JSON path extracts value from JSON payload. Leave empty for raw values. Use multiplier 0.001 for Wh→kWh, 1000 for MWh→kWh.`,mqttStateHint:`JSON path extracts value from JSON payload. Leave empty for raw values. ON value: the exact string that means "ON". Leave empty for auto-detect (ON, true, 1).`,restControl:"Control",restOnUrl:"Turn ON URL",restOffUrl:"Turn OFF URL",restOnBody:"ON Request Body",restOffBody:"OFF Request Body",restMethod:"HTTP Method",restHeaders:"Custom Headers (JSON)",restStatusUrl:"Status URL",restStatusPath:"State JSON Path",restStatusOnValue:"ON Value",restPowerUrl:"Power URL",restPowerPath:"Power JSON Path",restPowerMultiplier:"Power Multiplier",restEnergyUrl:"Energy URL",restEnergyPath:"Energy JSON Path",restEnergyMultiplier:"Energy Multiplier",restUrlRequired:"At least one URL (ON or OFF) is required for REST plugs",restHeadersHint:'e.g. {"Authorization": "Bearer your-token"}',restBodyHint:'e.g. ON, {"state": "on"}',restStatusHint:"URL to poll for current state",restPathHint:"e.g. state or data.power.status",restPowerUrlHint:"Separate URL for power data (uses Status URL if empty)",restEnergyUrlHint:"Separate URL for energy data (uses Status URL if empty)",restEnergyHint:"Each value can use its own URL or fall back to the Status URL. Use multipliers for unit conversion (e.g. 0.001 to convert Wh to kWh).",testConnection:"Test Connection",connectionSuccess:"Connection successful",noSwitchesInSwitchbar:"No switches in switchbar",enableSwitchbarHint:'Enable "Show in Switchbar" in Settings > Smart Plugs'},notifications:{providerTypes:{callmebot:"CallMeBot/WhatsApp",ntfy:"ntfy",pushover:"Pushover",telegram:"Telegram",email:"Email",discord:"Discord",webhook:"Webhook",homeassistant:"Home Assistant"},providerDescriptions:{email:"SMTP email notifications",telegram:"Notifications via Telegram bot",discord:"Send to Discord channel via webhook",ntfy:"Free, self-hostable push notifications",pushover:"Simple, reliable push notifications",callmebot:"Free WhatsApp notifications via CallMeBot",webhook:"Generic HTTP POST to any URL",homeassistant:"Persistent notifications in Home Assistant dashboard"},lastSuccess:"Last: {{date}}",error:"Error",printer:"Printer:",allPrinters:"All printers",sendTestNotification:"Send Test Notification",eventSettings:"Event Settings",enabled:"Enabled",sendFromProvider:"Send notifications from this provider",printEvents:"Print Events",printerStatus:"Printer Status",amsAlarms:"AMS Alarms",amsHtAlarms:"AMS-HT Alarms",printQueue:"Print Queue",start:"Start",plateCheck:"Plate Check",complete:"Complete",failed:"Failed",stopped:"Stopped",progress:"Progress",offline:"Offline",lowFilament:"Low Filament",maintenance:"Maintenance",amsHumidity:"AMS Humidity",amsTemp:"AMS Temp",amsHtHumidity:"AMS-HT Humidity",amsHtTemp:"AMS-HT Temp",bedCooled:"Bed Cooled",firstLayer:"First Layer",quiet:"Quiet",digest:"Digest {{time}}",printStarted:"Print Started",plateNotEmpty:"Plate Not Empty",plateNotEmptyDescription:"Objects detected before print",printCompleted:"Print Completed",bedCooledLabel:"Bed Cooled",bedCooledDescription:"Bed cooled below threshold after print",firstLayerCompleteLabel:"First Layer Complete",firstLayerCompleteDescription:"Notify with snapshot when first layer finishes",missingSpoolAssignmentLabel:"Missing Spool Assignment",missingSpoolAssignmentDescription:"Notify when print starts and required trays have no assigned spool",printFailed:"Print Failed",printStopped:"Print Stopped",progressMilestones:"Progress Milestones",progressMilestonesDescription:"Notify at 25%, 50%, 75%",printerOffline:"Printer Offline",printerError:"Printer Error",lowFilamentLabel:"Low Filament",maintenanceDue:"Maintenance Due",maintenanceDueDescription:"Notify when maintenance is needed",amsHumidityHigh:"AMS Humidity High",amsHumidityHighDescription:"Regular AMS humidity exceeds threshold",amsTemperatureHigh:"AMS Temperature High",amsTemperatureHighDescription:"Regular AMS temperature exceeds threshold",amsHtHumidityHigh:"AMS-HT Humidity High",amsHtHumidityHighDescription:"AMS-HT humidity exceeds threshold",amsHtTemperatureHigh:"AMS-HT Temperature High",amsHtTemperatureHighDescription:"AMS-HT temperature exceeds threshold",jobAdded:"Job Added",jobAddedDescription:"Job added to queue",jobAssigned:"Job Assigned",jobAssignedDescription:"Model-based job assigned to printer",jobStarted:"Job Started",jobStartedDescription:"Queue job started printing",jobWaiting:"Job Waiting",jobWaitingDescription:"Job waiting for filament or printer",jobSkipped:"Job Skipped",jobSkippedDescription:"Job skipped (previous failed)",jobFailed:"Job Failed",jobFailedDescription:"Job failed to start",queueComplete:"Queue Complete",queueCompleteDescription:"All queue jobs finished",quietHours:"Quiet Hours",noNotificationsDuring:"No notifications during these hours",editProviderToChangeQuietHours:"Edit provider to change quiet hours",dailyDigest:"Daily Digest",batchNotifications:"Batch notifications into a single daily summary",sendAt:"Send at {{time}}",editProviderToChangeDigestTime:"Edit provider to change digest time",edit:"Edit",deleteProvider:"Delete Notification Provider",deleteConfirm:'Are you sure you want to delete "{{name}}"? This cannot be undone.',delete:"Delete",addTitle:"Add Notification Provider",editTitle:"Edit Notification Provider",nameLabel:"Name *",namePlaceholder:"My Notifications",providerTypeLabel:"Provider Type *",configuration:"Configuration",testConfiguration:"Test Configuration",printerFilter:"Printer Filter",onlyFromPrinter:"Only send notifications for events from this printer",quietHoursDnd:"Quiet Hours (Do Not Disturb)",quietStart:"Start",quietEnd:"End",dailyDigestLabel:"Daily Digest",sendDigestAt:"Send digest at",digestCollected:"Events will be collected and sent as a single summary at this time",notificationEvents:"Notification Events",progressPercent:"(25%, 50%, 75%)",bedCooledAfterPrint:"(after print completes)",cancel:"Cancel",save:"Save",add:"Add",nameRequired:"Name is required",fieldRequired:"{{field}} is required",phoneNumber:"Phone Number",apiKey:"API Key",serverUrl:"Server URL",topic:"Topic",authToken:"Auth Token",userKey:"User Key",appToken:"App Token",priority:"Priority",botToken:"Bot Token",chatId:"Chat ID",smtpServer:"SMTP Server",smtpPort:"SMTP Port",security:"Security",authentication:"Authentication",username:"Username",password:"Password",fromEmail:"From Email",toEmail:"To Email",webhookUrl:"Webhook URL",payloadFormat:"Payload Format",authorization:"Authorization",titleFieldName:"Title Field Name",messageFieldName:"Message Field Name",editTemplate:"Edit Template: {{name}}",titleLabel:"Title",bodyLabel:"Body",titlePlaceholder:"Notification title...",bodyPlaceholder:"Notification body...",availableVariables:"Available Variables",clickToInsert:"Click to insert at cursor position in body",livePreview:"Live Preview",hide:"Hide",show:"Show",loadingPreview:"Loading preview...",enterTemplateContent:"Enter template content to see preview",titlePreview:"Title:",bodyPreview:"Body:",resetToDefault:"Reset to Default",titleRequired:"Title is required",bodyRequired:"Body is required",notificationLog:"Notification Log",showFailedOnly:"Failed only",last24Hours:"Last 24 hours",last7Days:"Last 7 days",last30Days:"Last 30 days",last90Days:"Last 90 days",justNow:"Just now",noFailedNotifications:"No failed notifications",noNotificationsLogged:"No notifications logged",unknownProvider:"Unknown Provider",logTitle:"Title",logMessage:"Message",logError:"Error",logProvider:"Provider: {{type}}",logTime:"Time: {{time}}",refresh:"Refresh",clearOld:"Clear Old",statsSummary:"Last {{days}} days:",statsNotifications:"notifications",statsSent:"{{count}} sent",statsFailed:"{{count}} failed",eventTypes:{print_start:"Print Started",print_complete:"Print Complete",print_failed:"Print Failed",print_stopped:"Print Stopped",print_progress:"Progress",printer_offline:"Printer Offline",printer_error:"Printer Error",filament_low:"Low Filament",maintenance_due:"Maintenance Due",test:"Test"},userEmail:{title:"Notifications",emailNotifications:"Email Notifications",emailNotificationsDesc:"Receive email notifications for your own print jobs. Emails are sent using the system SMTP settings configured in Advanced Authentication.",sendingTo:"Notifications will be sent to",noEmailWarning:"Your account does not have an email address. Contact an administrator to add one.",printJobNotifications:"Print Job Notifications",printJobNotificationsDesc:"Choose which events trigger email notifications for print jobs you submit.",printJobStarts:"Print Job Starts",printJobStartsDesc:"Get notified when your print job begins.",printJobFinishes:"Print Job Finishes",printJobFinishesDesc:"Get notified when your print job completes successfully.",printErrors:"Print Errors",printErrorsDesc:"Get notified when your print job fails or encounters an error.",printJobStops:"Print Job Stops",printJobStopsDesc:"Get notified when your print job is cancelled or stopped.",saveSuccess:"Notification preferences saved.",saveError:"Failed to save notification preferences."}},richTextEditor:{bold:"Bold",italic:"Italic",underline:"Underline",bulletList:"Bullet List",numberedList:"Numbered List",alignLeft:"Align Left",alignCenter:"Align Center",alignRight:"Align Right",addLink:"Add Link",removeLink:"Remove Link"},externalLinks:{noLinksConfigured:"No external links configured",deleteLink:"Delete Link",removeCustomIcon:"Remove custom icon",openInNewTab:"Open in new tab",placeholders:{linkName:"My Link"}},keyboardShortcuts:{title:"Keyboard Shortcuts",navigation:"Navigation",archivesSection:"Archives",kProfilesSection:"K-Profiles",generalSection:"General",shortcuts:{goToPrinters:"Go to Printers",goToArchives:"Go to Archives",goToQueue:"Go to Queue",goToStats:"Go to Statistics",goToProfiles:"Go to Cloud Profiles",goToSettings:"Go to Settings",focusSearch:"Focus search",openUploadModal:"Open upload modal",clearSelection:"Clear selection / blur input",contextMenu:"Context menu on cards",refreshProfiles:"Refresh profiles",newProfile:"New profile",exitSelectionMode:"Exit selection mode",showHelp:"Show this help"},footer:"Press Esc or click outside to close"},notificationLog:{title:"Notification Log",events:{printStarted:"Print Started",printComplete:"Print Complete",printFailed:"Print Failed",printStopped:"Print Stopped",progress:"Progress",printerOffline:"Printer Offline",printerError:"Printer Error",lowFilament:"Low Filament",maintenanceDue:"Maintenance Due",test:"Test"},timeAgo:{justNow:"Just now",minutesAgo:"{{minutes}}m ago",hoursAgo:"{{hours}}h ago"}},restoreBackup:{title:"Restore Backup",restoring:"Restoring...",restoreComplete:"Restore Complete",restoreFailed:"Restore Failed",importSettings:"Import settings from a backup file",pleaseWait:"Please wait while your data is being restored",clickToSelect:"Click to select backup file (.json or .zip)",howDuplicateHandling:"How duplicate handling works:",categories:{printers:"Printers",smartPlugs:"Smart Plugs",notificationProviders:"Notification Providers",filaments:"Filaments",archives:"Archives",pendingUploads:"Pending Uploads",settingsTemplates:"Settings & Templates"},matchingInfo:{printers:"matched by serial number",smartPlugs:"matched by IP address",notificationProviders:"matched by name",filaments:"matched by name + type + brand",archives:"matched by content hash",pendingUploads:"matched by filename",settingsTemplates:"always overwritten"},replaceExisting:"Replace existing data",keepExisting:"Keep existing data",replaceDescription:"Overwrite items that already exist with backup data",keepDescription:"Only restore items that don't already exist",caution:"Caution:",cautionText:"Overwriting will replace your current configurations with backup data. Printer access codes are never overwritten for security.",itemsRestored:"Items Restored",itemsSkipped:"Items Skipped",restored:"Restored",skipped:"Skipped (already exist)",filesLabel:"Files (3MF, thumbnails, etc.)",newApiKeysGenerated:"New API Keys Generated",newApiKeysWarning:"These keys are only shown once. Copy them now!",processingBackup:"Processing backup file...",noDataFound:"No data was found to restore in the backup file.",failedToRestore:"Failed to restore backup. Please check the file format."},backupExport:{title:"Export Backup",selectData:"Select data to include",selectAll:"Select All",selectNone:"Select None",categoryDescriptions:{settings:"Language, theme, update preferences",notifications:"ntfy, Pushover, Discord, etc.",templates:"Custom message templates",smartPlugs:"Tasmota plug configurations",externalLinks:"Sidebar links to external services",printers:"Printer info (access codes excluded)",plateDetection:"Empty plate reference images",filaments:"Filament types and costs",maintenance:"Custom maintenance schedules",archives:"All print data + files (3MF, thumbnails, photos)",projects:"Projects, BOM items, and attachments",pendingUploads:"Virtual printer uploads awaiting review",apiKeys:"Webhook API keys (new keys generated on import)"},requiresPrinters:"Requires Printers to be selected",zipFileWarning:"ZIP file will be created.",zipFileDescription:"Includes all 3MF files, thumbnails, timelapses, and photos. This may take a while and result in a large file.",includeAccessCodes:"Include Access Codes",includeAccessCodesDescription:"For transferring to another machine",includeAccessCodesWarning:"Access codes will be included in plain text. Keep this backup file secure!",categoriesSelected:"{{selectedCount}} categories selected"},pendingUploads:{placeholders:{notes:"Add notes about this print..."},discardUpload:"Discard Upload",archiveAllUploads:"Archive All Uploads",discardAllUploads:"Discard All Uploads",archive:"Archive",timeAgo:{justNow:"Just now",minutesAgo:"{{minutes}}m ago",hoursAgo:"{{hours}}h ago",daysAgo:"{{days}}d ago"}},apiBrowser:{placeholders:{requestBody:"JSON request body...",searchEndpoints:"Search endpoints..."}},configureAmsSlot:{title:"Configure AMS Slot",slotConfigured:"Slot Configured!",configuringSlot:"Configuring slot:",slotLabel:"{{ams}} Slot {{slot}}",searchPresets:"Search presets...",colorPlaceholder:"Color name or hex (e.g., brown, FF8800)",clearCustomColor:"Clear custom color",noCloudPresets:"No cloud presets. Login to Bambu Cloud to sync.",noPresetsAvailable:"No presets available. Login to Bambu Cloud or import local profiles.",noMatchingPresets:"No matching presets found.",custom:"Custom",builtin:"Built-in",settingsSentToPrinter:"Settings sent to printer",filamentProfile:"Filament Profile",kProfileLabel:"K Profile (Pressure Advance)",filteringFor:"Filtering for: {{material}}",noKProfile:"No K profile (use default 0.020)",noMatchingKProfiles:"No matching K profiles found. Default K=0.020 will be used.",selectFilamentFirst:"Select a filament profile first",kFromCalibration:"K={{value}} from printer calibration",customColorLabel:"Custom Color (optional)",presetColors:"{{name}} colors:",showLessColors:"Show less colors",showMoreColors:"Show more colors",clear:"Clear",hexLabel:"Hex: #{{hex}}",resetting:"Resetting...",resetSlot:"Reset Slot",cancel:"Cancel",configuring:"Configuring...",configureSlot:"Configure Slot"},githubBackup:{title:"GitHub Backup",history:"History",downloadBackup:"Download Backup",restoreBackup:"Restore Backup",noBackupsYet:"No backups yet"},emailSettings:{placeholders:{fromName:"BamBuddy"}},tagManagement:{searchTags:"Search tags...",renameTag:"Rename tag",deleteTag:"Delete tag"},notificationTemplates:{placeholders:{title:"Notification title...",body:"Notification body..."}},batchTag:{placeholders:{newTag:"Enter new tag..."}},photoGallery:{deletePhoto:"Delete Photo"},filamentHoverCard:{copySpoolUuid:"Copy spool UUID"},kProfilesView:{hasNote:"Has note",copyProfile:"Copy profile"},layout:{openMenu:"Open menu",noPermissionSystemInfo:"You do not have permission to view system information"},dashboard:{dragToReorder:"Drag to reorder",hideWidget:"Hide widget"},notificationProviderCard:{deleteNotificationProvider:"Delete Notification Provider"},fileManagerModal:{closeFileManager:"Close file manager",sortFiles:"Sort files",goToParentFolder:"Go to parent folder",threeView:"3D View"},embeddedCameraViewer:{refreshStream:"Refresh stream",close:"Close",zoomOut:"Zoom out",resetZoom:"Reset zoom",zoomIn:"Zoom in",dragToResize:"Drag to resize"},timelapseViewer:{skipBack5s:"Skip back 5s",skipForward5s:"Skip forward 5s"},notificationProviders:{descriptions:{email:"SMTP email notifications",telegram:"Notifications via Telegram bot",discord:"Send to Discord channel via webhook",ntfy:"Free, self-hostable push notifications",pushover:"Simple, reliable push notifications",callmebot:"Free WhatsApp notifications via CallMeBot",webhook:"Generic HTTP POST to any URL"}},logViewer:{searchPlaceholder:"Search message or logger name...",noLogEntries:"No log entries found"},switchbarPopover:{noSwitchesInSwitchbar:"No switches in switchbar"},projectPageModal:{placeholders:{title:"Title",designer:"Designer",license:"License",description:"Enter description...",profileTitle:"Profile Title",profileDescription:"Profile description..."}},spoolmanSettings:{},time:{unknown:"-",waiting:"Waiting",justNow:"Just now",now:"Now",minsAgo:"{{count}}m ago",inMins:"in {{count}}m",hoursAgo:"{{count}}h ago",inHours:"in {{count}}h",daysAgo:"{{count}}d ago",inDays:"in {{count}}d"},spoolbuddy:{nav:{dashboard:"Dashboard",ams:"AMS",inventory:"Inventory",writeTag:"Write",settings:"Settings"},status:{nfcReady:"NFC Ready",nfcOff:"NFC Off",offline:"Offline",online:"Online",noPrinters:"No printers",deviceOffline:"Device Offline",waitingConnection:"Waiting for device connection...",systemReady:"System Ready",status:"Status"},dashboard:{readyToScan:"Ready to scan",idleMessage:"Place a spool on the scale to identify it",nfcHint:"NFC tag will be read automatically",device:"Device",syncWeight:"Sync Weight",weightSynced:"Synced!",unknownTag:"Unknown Tag",newTag:"New Tag Detected",onScale:"on scale",linkSpool:"Link to Spool",linkTagTitle:"Link Tag to Spool",linkTag:"Link Tag",selectSpool:"Select a spool to link this tag to:",noUntagged:"No spools without tags found",tagDetected:"Tag detected",noTag:"No tag",tagId:"Tag",grossWeight:"Gross weight",spoolSize:"Spool size",close:"Close",currentSpool:"Current Spool"},modal:{spoolDetected:"Spool Detected",assignToAms:"Assign to AMS",syncWeight:"Sync Weight",weightSynced:"Synced!",syncing:"Syncing...",newTagDetected:"New Tag Detected",addToInventory:"Add to Inventory",assignToAmsTitle:"Assign to AMS",selectSlot:"Select a slot",assign:"Assign",assigning:"Assigning...",assignSuccess:"Assigned!",assignError:"Failed to assign spool. Please try again.",noPrinterSelected:"Select a printer...",noAmsDetected:"No AMS detected on this printer",slot:"Slot"},weight:{noReading:"No reading",stable:"Stable",measuring:"Measuring...",tare:"Tare",calibrate:"Calibrate"},spool:{remaining:"Remaining",material:"Material",brand:"Brand",color:"Color",coreWeight:"Core",labelWeight:"Label",scaleWeight:"Scale",netWeight:"Net",lastUsed:"Last used"},ams:{noData:"No AMS detected",connectAms:"Connect an AMS to see filament slots",noPrinter:"No printer selected",selectPrinter:"Select a printer from the top bar",printerDisconnected:"Printer disconnected",humidity:"Humidity",level:"Level",active:"Active",slot:"Slot",empty:"Empty"},inventory:{search:"Search spools...",empty:"No spools in inventory",noResults:"No matching spools",spools:"spools",addSpool:"Add Spool"},settings:{tabDevice:"Device",tabDisplay:"Display",tabScale:"Scale",tabUpdates:"Updates",nfcReader:"NFC Reader",type:"Type",connection:"Connection",notConnected:"N/A",deviceInfo:"Device Info",hostname:"Host",uptime:"Uptime",systemConfig:"Backend & Auth",backendUrl:"Bambuddy Backend URL",apiToken:"API Token",apiTokenPlaceholder:"Enter API token",saveConfig:"Save Config",systemQueued:"Config queued.",nfcDiagnostic:"NFC Diagnostic",scaleDiagnostic:"Scale Diagnostic",readTagDiagnostic:"Read Tag Diagnostic",testNfc:"Test reader",testScale:"Test accuracy",testReadTag:"Read tag",systemFieldsRequired:"Backend URL is required.",brightness:"Brightness",saved:"Saved",noBacklight:"No DSI backlight detected. Brightness control requires a DSI display.",screenBlank:"Screen Blank Timeout",screenBlankDesc:"Screen turns off after inactivity. Touch to wake.",displayNote:"Brightness is applied as a software filter.",scaleCalibration:"Scale Calibration",currentWeight:"Current weight",tareOffset:"Tare",calFactor:"Factor",knownWeight:"Known weight",calStep1:"Remove all items from the scale and press Set Zero.",calStep2:"Place known weight on scale.",setZero:"Set Zero",calibrateNow:"Calibrate",calibrated:"Calibrated",tareSet:"Tare command sent. Waiting for device...",tareFailed:"Failed to send tare command",zeroSet:"Zero point set. Place known weight on scale.",calibrationDone:"Calibration complete!",calibrationFailed:"Calibration failed",lastCalibrated:"Last calibrated",stable:"Stable",settling:"Settling...",firmware:"Firmware",scale:"Scale",noDevice:"No SpoolBuddy device found",daemonVersion:"Daemon Version",currentVersion:"Current",versionPending:"Waiting for daemon...",checking:"Checking...",checkUpdates:"Check for Updates",updateAvailable:"Update available",updateInstructions:"Update via SSH: run the SpoolBuddy install script to upgrade.",upToDate:"Up to date",includeBeta:"Include beta versions"},writeTag:{tabExisting:"Existing Spool",tabNew:"New Spool",tabReplace:"Replace Tag",searchPlaceholder:"Search by material, color, brand...",noUntaggedSpools:"No spools without tags",noTaggedSpools:"No spools with tags",selectSpool:"Select a spool, then place a blank NTAG on the reader",placeTag:"Place an NTAG on the reader",tagReady:"Tag detected — ready to write",writeTag:"Write Tag",replaceTag:"Replace Tag",writing:"Writing tag...",waiting:"Waiting for SpoolBuddy...",writeSuccess:"Tag written successfully!",writeFailed:"Write failed",queueFailed:"Failed to queue write command",tryAgain:"Try Again",cancel:"Cancel",replaceWarning:"Old tag will be unlinked. New tag will replace it.",deviceOffline:"SpoolBuddy is offline",material:"Material",colorName:"Color Name",color:"Color",brand:"Brand",weight:"Weight (g)",createSpool:"Create Spool",creating:"Creating...",spoolCreated:"Spool created! Ready to write.",createFailed:"Failed to create spool"},quickMenu:{printerPower:"Printer Power",systemControls:"System",restartDaemon:"Restart Daemon",restartBrowser:"Restart Browser",reboot:"Reboot",shutdown:"Shutdown",swipeToClose:"Swipe down to close",confirmTitle:"Confirm",confirmShutdown:"Are you sure you want to shut down the SpoolBuddy? You will need physical access to turn it back on.",confirmReboot:"Are you sure you want to reboot the SpoolBuddy?",confirmRestartDaemon:"Restart the SpoolBuddy daemon? NFC and scale will be temporarily unavailable.",confirmRestartBrowser:"Restart the kiosk browser? The display will briefly go blank.",confirm:"Confirm",confirmPlugOn:"Turn on {{name}}?",confirmPlugOff:"Turn off {{name}}?",turnOn:"Turn On",turnOff:"Turn Off"}},bugReport:{title:"Report a Bug",description:"Description",descriptionPlaceholder:"What went wrong? Please describe the issue...",email:"Email (optional)",emailPlaceholder:"your@email.com",emailPrivacy:"If provided, your email will be included in a collapsed section of the GitHub issue so the maintainer can follow up.",screenshot:"Screenshot",uploadOrPaste:"Upload, paste, or drag an image",dataCollectedSummary:"What data is included in the report?",dataIncluded:"Included:",dataIncludedList:"App version, OS, architecture, Python version, database stats (counts only), printer models, nozzle counts, firmware versions, connectivity status, integration status (Spoolman, MQTT, HA), non-sensitive settings, network interface count, Docker details, dependency versions.",dataNeverIncluded:"Never included:",dataNeverIncludedList:"Printer names, serial numbers, access codes, passwords, IP addresses, email addresses, API keys, tokens, webhook URLs, hostnames, or usernames.",submit:"Submit",startLogging:"Start Debug Logging",stepEnableLogging:"Debug logging enabled",stepReproduce:"Reproduce the issue now",stepStopLogging:"Stop & submit report",stopAndSubmit:"Stop & Submit",maxDuration:"Auto-stops after {{minutes}} min",stoppingLogs:"Collecting logs & submitting...",submitting:"Submitting bug report...",submitSuccess:"Bug report submitted successfully!",submitFailed:"Failed to submit bug report",thankYou:"Thank you!",submitted:"Your bug report has been submitted.",viewIssue:"View Issue",unexpectedError:"An unexpected error occurred"},failureDetection:{title:"AI Failure Detection",description:"Monitor prints with a self-hosted Obico ML API and act on detected failures automatically.",mlUrl:"Obico ML API URL",mlUrlHint:"Base URL of your self-hosted Obico ml_api container (e.g. http://192.168.1.10:3333).",test:"Test",testSuccess:"ML API reachable and healthy.",testFailed:"Could not reach the ML API.",sensitivity:"Sensitivity",sensitivityLow:"Low (fewer false positives)",sensitivityMedium:"Medium (balanced)",sensitivityHigh:"High (detect early, more false positives)",sensitivityHint:"Adjusts the confidence thresholds that trigger warnings and failures.",action:"Action on detected failure",actionNotify:"Notify only",actionPause:"Pause print",actionPauseOff:"Pause and cut power",pollInterval:"Poll interval (seconds)",pollIntervalHint:"How often to check each printer while it is printing. Minimum 5s, maximum 120s.",externalUrlMissing:"External URL is not set.",externalUrlHint:"The ML API fetches the camera snapshot by URL. Set the External URL in General settings so the ML API container can reach Bambuddy.",perPrinterTitle:"Monitored Printers",perPrinterHint:"Choose which printers the detection service watches.",monitorAll:"Monitor all connected printers",statusTitle:"Status",serviceRunning:"Service running",thresholds:"Low / High thresholds",activePrinters:"Active prints",noActivePrints:"No prints currently running.",historyTitle:"Recent Detections",noHistory:"No detections yet."}},Vue={nav:{printers:"Drucker",archives:"Archiv",queue:"Warteschlange",stats:"Statistiken",profiles:"Profile",maintenance:"Wartung",projects:"Projekte",inventory:"Filament",files:"Dateimanager",notifications:"Benachrichtigungen",settings:"Einstellungen",system:"System",collapseSidebar:"Seitenleiste einklappen",expandSidebar:"Seitenleiste ausklappen",update:"Update",updateAvailable:"Update verfügbar: v{{version}}",updateAvailableBanner:"Version {{version}} ist verfügbar!",viewUpdate:"Update anzeigen",viewOnGithub:"Auf GitHub ansehen",keyboardShortcuts:"Tastaturkürzel (?)",switchToLight:"Zum hellen Modus wechseln",switchToDark:"Zum dunklen Modus wechseln",smartSwitches:"Smart Switches",logout:"Abmelden"},common:{save:"Speichern",saving:"Speichern...",cancel:"Abbrechen",delete:"Löschen",edit:"Bearbeiten",add:"Hinzufügen",close:"Schließen",confirm:"Bestätigen",loading:"Lädt...",error:"Fehler",success:"Erfolg",warning:"Warnung",enabled:"Aktiviert",disabled:"Deaktiviert",yes:"Ja",no:"Nein",on:"An",off:"Aus",all:"Alle",none:"Keine",search:"Suchen",filter:"Filtern",sort:"Sortieren",refresh:"Aktualisieren",download:"Herunterladen",upload:"Hochladen",uploading:"Hochladen...",uploadFailed:"Hochladen fehlgeschlagen",actions:"Aktionen",status:"Status",name:"Name",description:"Beschreibung",date:"Datum",time:"Zeit",hours:"Stunden",minutes:"Minuten",seconds:"Sekunden",days:"Tage",enable:"Aktivieren",disable:"Deaktivieren",permissions:"Berechtigungen",noPrinters:"Keine Drucker konfiguriert",noData:"Keine Daten verfügbar",linkNotFound:"Link nicht gefunden",required:"Erforderlich",optional:"Optional",dismiss:"Schließen",apply:"Anwenden",reset:"Zurücksetzen",export:"Exportieren",import:"Importieren",clear:"Leeren",selectAll:"Alle auswählen",deselectAll:"Auswahl aufheben",noChange:"— Keine Änderung —",unchanged:"Unverändert",unassigned:"Nicht zugewiesen",unknown:"Unbekannt",unknownError:"Unbekannter Fehler",today:"Heute",tomorrow:"Morgen",asap:"Sofort",overdue:"Überfällig",now:"Jetzt",collapse:"Einklappen",expand:"Ausklappen",viewArchive:"Archiv anzeigen",viewInFileManager:"Im Dateimanager anzeigen",addedBy:"Hinzugefügt von {{username}}",prints:"Drucke",more:"+{{count}} weitere",ascending:"Aufsteigend",descending:"Absteigend",back:"Zurück",copy:"Kopieren",copied:"Kopiert!",printer:"Drucker",remove:"Entfernen",type:"Typ",print:"Drucken",rename:"Umbenennen",move:"Verschieben",create:"Erstellen",duplicate:"Duplizieren",left:"Links",right:"Rechts"},printers:{title:"Drucker",addPrinter:"Drucker hinzufügen",editPrinter:"Drucker bearbeiten",deletePrinter:"Drucker löschen",printerName:"Druckername",serialNumber:"Seriennummer",ipAddress:"IP-Adresse / Hostname",accessCode:"Zugangscode",model:"Modell",nozzleCount:"Düsenanzahl",autoArchive:"Automatische Archivierung",status:{available:"Verfügbar",idle:"Bereit",printing:"Druckt",paused:"Pausiert",offline:"Offline",problem:"Problem",error:"Fehler",finished:"Fertig",unknown:"Unbekannt"},temperatures:{nozzle:"Düse",bed:"Druckbett",chamber:"Kammer"},progress:"{{percent}}% abgeschlossen",timeRemaining:"Noch {{time}}",deleteConfirm:'Möchten Sie "{{name}}" wirklich löschen?',maintenanceOk:"Wartung OK",maintenanceWarning:"{{count}} Warnung",maintenanceWarning_plural:"{{count}} Warnungen",maintenanceDue:"{{count}} fällig",maintenanceDue_plural:"{{count}} fällig",sort:{name:"Name",status:"Status",model:"Modell",location:"Standort",ascending:"Aufsteigend sortieren",descending:"Absteigend sortieren"},cardSize:{small:"Kleine Karten",medium:"Mittlere Karten",large:"Große Karten",extraLarge:"Extra große Karten"},hideOffline:"Offline ausblenden",nextAvailable:"Nächster verfügbar",powerOn:"Einschalten",offlinePrintersWithPlugs:"Offline-Drucker mit Smart-Plugs",noPrintersConfigured:"Noch keine Drucker konfiguriert",search:"Drucker suchen...",noSearchResults:"Keine Drucker entsprechen deiner Suche oder deinen Filtern",filter:{allStatuses:"Alle Status",allLocations:"Alle Standorte"},readyToPrint:"Druckbereit",external:"Extern",extL:"Ext-L",extR:"Ext-R",deleteArchives:"Druckarchive löschen",noLabel:"Keine Bezeichnung",printPreview:"Druckvorschau",width:"Breite",height:"Höhe",noObjectsFound:"Keine Objekte gefunden",objectsLoadedOnPrintStart:"Objekte werden beim Druckstart geladen",willBeSkipped:"Wird übersprungen",name:"Name",serialCannotBeChanged:"Seriennummer kann nicht geändert werden",locationHelp:"Dient zur Gruppierung von Druckern und zum Filtern von Warteschlangenaufträgen",wifiSignal:{veryWeak:"Sehr schwach",weak:"Schwach",fair:"Ausreichend",good:"Gut",excellent:"Ausgezeichnet"},maintenanceUpToDate:"Alle Wartungen aktuell - Klicken zum Anzeigen",chamberLightOn:"Kammerbeleuchtung einschalten",chamberLightOff:"Kammerbeleuchtung ausschalten",files:"Dateien",browseFiles:"Druckerdateien durchsuchen",autoOffAfterPrint:"Automatisches Ausschalten nach Druck",autoOffExecuted:"Auto-off wurde ausgeführt - Drucker einschalten zum Zurücksetzen",hmsErrors:"HMS-Fehler",viewHmsErrors:"{{count}} HMS-Fehler anzeigen",resume:"Fortsetzen",pause:"Pausieren",stop:"Stoppen",camera:"Kamera",skipObject:"Objekt überspringen",reconnect:"Neu verbinden",forceRefresh:"Aktualisierung erzwingen",forceRefreshSuccess:"Aktualisierung angefordert",mqttDebug:"MQTT-Debug",printerInformation:"Druckerinformationen",copyToClipboard:"Kopieren",copied:"Kopiert!",state:"Zustand",wifiSignalLabel:"WLAN-Signal",developerMode:"Entwicklermodus",enabled:"Aktiviert",disabled:"Deaktiviert",addedOn:"Hinzugefügt",sdCard:"SD-Karte",inserted:"Eingelegt",notInserted:"Nicht eingelegt",totalPrintHours:"Druckstunden",activeNozzle:"Aktiv: {{nozzle}} Düse",nozzleRack:"Düsenhalter",nozzleDocked:"Angedockt",nozzleMounted:"Montiert",nozzleActive:"Aktiv",nozzleIdle:"Inaktiv",nozzleDiameter:"Durchmesser",nozzleType:"Typ",nozzleStatus:"Status",nozzleFilament:"Filament",nozzleWear:"Verschleiß",nozzleMaxTemp:"Max Temp",nozzleSerial:"Seriennr.",nozzleHardenedSteel:"Gehärteter Stahl",nozzleStainlessSteel:"Edelstahl",nozzleTungstenCarbide:"Wolframkarbid",nozzleFlow:"Durchfluss",nozzleHighFlow:"High Flow",nozzleStandardFlow:"Standard",firmwareUpdate:"Firmware-Update",firmwareInstructions:"Gehen Sie auf dem Touchscreen des Druckers zu",firmwareNav:"Navigieren Sie zu",settings:"Einstellungen",firmware:"Firmware",discoverPrinters:"Drucker entdecken",searching:"Suche...",manualEntry:"Manuelle Eingabe",addFromCloud:"Aus Cloud hinzufügen",toast:{printerDeleted:"Drucker gelöscht",missingSpoolAssignment:"Druck gestartet auf {{printer}}. Fehlende Spulenzuordnung für: {{slots}}",printerAdded:"Drucker hinzugefügt",printerUpdated:"Drucker aktualisiert",failedToDelete:"Drucker konnte nicht gelöscht werden",failedToAdd:"Drucker konnte nicht hinzugefügt werden",failedToUpdate:"Drucker konnte nicht aktualisiert werden",commandSent:"Befehl gesendet",failedToSendCommand:"Befehl konnte nicht gesendet werden",turnedOn:"{{name}} eingeschaltet",failedToPowerOn:"{{name}} konnte nicht eingeschaltet werden",scriptTriggered:"Skript ausgelöst",printStopped:"Druck gestoppt",printPaused:"Druck pausiert",printResumed:"Druck fortgesetzt",referenceDeleted:"Referenz gelöscht",detectionAreaSaved:"Erkennungsbereich gespeichert",failedToRunScript:"Skript konnte nicht ausgeführt werden",failedToStopPrint:"Druck konnte nicht gestoppt werden",failedToPausePrint:"Druck konnte nicht pausiert werden",failedToResumePrint:"Druck konnte nicht fortgesetzt werden",failedToControlChamberLight:"Kammerbeleuchtung konnte nicht gesteuert werden",failedToSetSpeed:"Druckgeschwindigkeit konnte nicht eingestellt werden",failedToUpdateSetting:"Einstellung konnte nicht aktualisiert werden",failedToSkipObjects:"Objekte konnten nicht übersprungen werden",failedToRereadRfid:"RFID konnte nicht erneut gelesen werden",failedToCheckPlate:"Platte konnte nicht überprüft werden",failedToUpdateLabel:"Bezeichnung konnte nicht aktualisiert werden",failedToDeleteReference:"Referenz konnte nicht gelöscht werden",failedToSaveDetectionArea:"Erkennungsbereich konnte nicht gespeichert werden",plateCheckEnabled:"Plattenprüfung aktiviert",plateCheckDisabled:"Plattenprüfung deaktiviert",calibrationSaved:"Kalibrierung gespeichert!",calibrationFailed:"Kalibrierung fehlgeschlagen",rfidRereadInitiated:"RFID-Neueinlesen gestartet"},connection:{connected:"Verbunden",offline:"Offline"},plateStatus:{markCleared:"Platte als freigegeben markieren",cleared:"Platte freigegeben",notCleared:"Platte nicht freigegeben",inUse:"Platte in Benutzung"},queue:{inQueue:"{{count}} Druck in Warteschlange",inQueue_plural:"{{count}} Drucke in Warteschlange"},controls:"Steuerung",rfid:{reread:"RFID neu lesen"},bedJog:{title:"Druckbett bewegen",bed:"Bett",step:"Schritt (mm)",up:"Platte hoch",down:"Platte runter",disabledWhilePrinting:"Während des Drucks deaktiviert",notHomedTitle:"Drucker ist nicht referenziert",notHomedMessage:"Der Drucker wurde seit dem letzten Druck nicht referenziert. Führen Sie zuerst die automatische Referenzfahrt aus (parkt den Werkzeugkopf und referenziert dann X, Y und Z) oder bewegen Sie trotzdem — die Software-Endschalter werden dabei umgangen.",homeZ:"Automatische Referenzfahrt",moveAnyway:"Trotzdem bewegen",homingStarted:"Drucker wird automatisch referenziert…"},permission:{noAdd:"Sie haben keine Berechtigung, Drucker hinzuzufügen",noEdit:"Sie haben keine Berechtigung, Drucker zu bearbeiten",noDelete:"Sie haben keine Berechtigung, Drucker zu löschen",noControl:"Sie haben keine Berechtigung, Drucker zu steuern",noFiles:"Sie haben keine Berechtigung, auf Druckerdateien zuzugreifen",noAmsRfid:"Sie haben keine Berechtigung, AMS-RFID erneut zu lesen",noSmartPlugControl:"Sie haben keine Berechtigung, Smart Plugs zu steuern",noCamera:"Sie haben keine Berechtigung, Kameras anzuzeigen"},modal:{addTitle:"Drucker hinzufügen",editTitle:"Drucker bearbeiten",myPrinter:"Mein Drucker",selectModel:"Modell auswählen...",locationGroup:"Standort / Gruppe (optional)",locationPlaceholder:"z.B. Werkstatt, Büro, Keller",autoArchiveLabel:"Abgeschlossene Drucke automatisch archivieren",fromPrinterSettings:"Aus Druckereinstellungen",modelOptional:"Modell (optional)",saveChanges:"Änderungen speichern"},skipObjects:{tooltip:"Objekte überspringen",onlyWhilePrinting:"Objekte überspringen (nur während des Drucks)",requiresMultiple:"Objekte überspringen (erfordert 2+ Objekte)",title:"Objekte überspringen",matchIdsInfo:"IDs mit Drucker-Display abgleichen",printerShowsIds:"Der Druckerbildschirm zeigt Objekt-IDs auf der Bauplatte",skipSelected:"Ausgewählte überspringen",skipping:"Überspringe...",noObjectsSelected:"Keine Objekte ausgewählt",selectObjectsToSkip:"Wählen Sie Objekte aus, die Sie vom aktuellen Druck überspringen möchten",skipped:"übersprungen",objectsSkipped:"Objekte übersprungen",activeCount:"{{count}} aktiv",waitForLayer:"Warten Sie auf Schicht 2+ zum Überspringen von Objekten (aktuell Schicht {{layer}})",skip:"Überspringen",confirmTitle:"Objekt überspringen?",confirmMessage:'Möchten Sie "{{name}}" wirklich überspringen? Dies kann nicht rückgängig gemacht werden.'},confirm:{deleteTitle:"Drucker löschen",deleteMessage:'Möchten Sie "{{name}}" wirklich löschen? Alle Verbindungseinstellungen werden entfernt.',deleteArchivesNote:"Der gesamte Druckverlauf für diesen Drucker wird dauerhaft gelöscht.",keepArchivesNote:"Der Druckverlauf wird beibehalten, aber nicht mehr mit diesem Drucker verknüpft.",stopTitle:"Druck stoppen",stopMessage:'Möchten Sie den aktuellen Druck auf "{{name}}" wirklich stoppen? Der Druckauftrag wird abgebrochen.',stopButton:"Druck stoppen",pauseTitle:"Druck pausieren",pauseMessage:'Möchten Sie den aktuellen Druck auf "{{name}}" wirklich pausieren?',pauseButton:"Druck pausieren",resumeTitle:"Druck fortsetzen",resumeMessage:'Möchten Sie den Druck auf "{{name}}" fortsetzen?',resumeButton:"Druck fortsetzen",powerOnTitle:"Drucker einschalten",powerOnMessage:'Möchten Sie die Stromversorgung für "{{name}}" wirklich EINSCHALTEN?',powerOnButton:"Einschalten",powerOffTitle:"Drucker ausschalten",powerOffMessage:'Möchten Sie die Stromversorgung für "{{name}}" wirklich AUSSCHALTEN?',powerOffWarning:'WARNUNG: "{{name}}" druckt gerade! Möchten Sie die Stromversorgung wirklich AUSSCHALTEN? Dies unterbricht den Druck und kann den Drucker beschädigen.',powerOffButton:"Ausschalten"},bulk:{select:"Auswählen",selectAll:"Alle auswählen",selectByLocation:"Nach Standort auswählen",selected:"{{count}} ausgewählt",actions:{stop:"Stoppen",pause:"Pausieren",resume:"Fortsetzen",clearPlate:"Druckbett leeren",clearHMS:"Benachrichtigungen löschen"},confirm:{stopTitle:"{{count}} Drucke stoppen",stopMessage:"Dies wird aktive Drucke auf {{count}} Drucker(n) abbrechen. Diese Aktion kann nicht rückgängig gemacht werden.",stopButton:"Alle stoppen",pauseTitle:"{{count}} Drucke pausieren",pauseMessage:"Dies wird aktive Drucke auf {{count}} Drucker(n) pausieren.",pauseButton:"Alle pausieren",clearPlateTitle:"{{count}} Druckbetten leeren",clearPlateMessage:"Dies wird das Druckbett auf {{count}} Drucker(n) leeren und kann wartende Aufträge starten.",clearPlateButton:"Alle leeren"},success:"{{action}} auf {{count}} Drucker(n) abgeschlossen",partial:"{{succeeded}} erfolgreich, {{failed}} fehlgeschlagen",noneApplicable:"Keine ausgewählten Drucker sind im richtigen Zustand für diese Aktion",selectByState:"Nach Status auswählen"},discovery:{title:"Drucker entdecken",searching:"Suche...",scanning:"Scanne...",scanProgress:"Scanne... {{scanned}}/{{total}}",foundPrinters:"{{count}} Drucker gefunden",noPrintersFound:"Keine Drucker gefunden",noPrintersFoundSubnet:"Keine Drucker im angegebenen Subnetz gefunden.",noPrintersFoundNetwork:"Keine Drucker im Netzwerk gefunden.",allConfigured:"Alle erkannten Drucker sind bereits konfiguriert.",alreadyAdded:"Bereits hinzugefügt",select:"Auswählen",manualEntry:"Manuelle Eingabe",addFromCloud:"Aus Cloud hinzufügen",subnetToScan:"Zu scannendes Subnetz",dockerNote:"Docker erkannt. Geben Sie das Subnetz Ihres Druckers in CIDR-Notation ein. Erfordert network_mode: host in docker-compose.yml.",scanSubnet:"Subnetz nach Druckern scannen",discoverNetwork:"Drucker im Netzwerk suchen",scanningSubnet:"Subnetz wird nach Bambu-Druckern gescannt...",scanningNetwork:"Netzwerk wird gescannt...",serialRequired:"Seriennummer erforderlich",unknown:"Unbekannt",failedToStart:"Erkennung konnte nicht gestartet werden"},drying:{start:"Trocknung starten",stop:"Trocknung stoppen",temperature:"Temperatur",duration:"Dauer",hours:"Stunden",timeRemaining:"{{time}} verbleibend",active:"Trocknung",notSupported:"Trocknung nicht unterstützt",powerRequired:"AMS-Netzteil anschließen, um Trocknung zu aktivieren",startingDrying:"Trocknung wird gestartet...",stoppingDrying:"Trocknung wird gestoppt...",rotateTray:"Spule während der Trocknung drehen"},filaments:"Filamente",openCameraOverlay:"Kamera-Overlay öffnen",openCameraWindow:"Kamera in neuem Fenster öffnen",firmwareUpdateAvailable:"Firmware-Update verfügbar: {{current}} → {{latest}}",firmwareUpToDate:"Firmware {{version}} — Aktuell",firmwareUpdateButton:"Update",plateDetection:{noPermission:"Sie haben keine Berechtigung, Drucker zu aktualisieren",enabledClick:"Plattenprüfung aktiviert - Klicken zum Deaktivieren",disabledClick:"Plattenprüfung deaktiviert - Klicken zum Aktivieren",manageCalibration:"Platten-Erkennungskalibrierung verwalten",calibrationRequired:"Kalibrierung erforderlich",calibrationInstructions:"Bitte stellen Sie sicher, dass die Druckplatte vollständig leer ist, und klicken Sie dann auf Kalibrieren.",calibrationDescription:"Die Kalibrierung erfasst ein Referenzbild der leeren Platte. Zukünftige Prüfungen vergleichen mit dieser Referenz, um Objekte zu erkennen.",calibrationTip:"Tipp: Sie können bis zu 5 Kalibrierungen für verschiedene Platten speichern. Das System verwendet automatisch die beste Übereinstimmung bei der Prüfung.",plateEmpty:"Platte erscheint leer",objectsDetected:"Objekte auf Platte erkannt",confidence:"Konfidenz",difference:"Differenz",analysisPreview:"Analysevorschau:",analysisLegend:"Grüner Rahmen = Erkennungsbereich, Rote Überlagerung = Unterschiede zur Kalibrierung",savedReferences:"Gespeicherte Referenzen ({{count}}/{{max}})",deleteReference:"Referenz löschen",labelPlaceholder:"Bezeichnung...",clickToEdit:"{{label}} - Zum Bearbeiten klicken",clickToAddLabel:"Zum Hinzufügen einer Bezeichnung klicken"},speed:{title:"Druckgeschwindigkeit",silent:"Leise (50%)",standard:"Standard (100%)",sport:"Sport (124%)",ludicrous:"Ludicrous (166%)"},airduct:{title:"Luftkanal-Modus",cooling:"Kühlen",heating:"Heizen"},noSdCard:"Keine SD",door:{open:"Offen",closed:"Zu"},fans:{partCooling:"Bauteilkühlung",auxiliary:"Hilfsventilator",chamber:"Kammerventilator"},clickToViewHmsErrors:"Klicken, um HMS-Fehler anzuzeigen",estimatedCompletion:"Geschätzte Fertigstellungszeit",plateNumber:"Platte {{number}}",slotOptions:"Slot-Optionen",amsPopup:{friendlyName:"AMS-Name",friendlyNamePlaceholder:"z. B. AMS-Anzeigename",serialNumber:"Seriennummer",firmwareVersion:"Firmware",save:"Speichern",clear:"Löschen",noEditPermission:"Sie haben keine Berechtigung, AMS-Einheiten umzubenennen"},firmwareModal:{title:"Firmware-Update",titleUpToDate:"Firmware-Info",currentVersion:"Aktuell:",latestVersion:"Neueste:",releaseNotes:"Versionshinweise",checkingPrereqs:"Prüfe Voraussetzungen...",sdCardReady:"SD-Karte bereit. Klicken Sie unten, um die Firmware hochzuladen.",uploadedSuccess:"Firmware auf SD-Karte hochgeladen!",applyInstructions:"So wenden Sie das Update auf Ihrem Drucker an:",step1:"Gehen Sie auf dem Touchscreen des Druckers zu Einstellungen",step2:"Navigieren Sie zu Firmware",step3:"Wählen Sie Update von SD-Karte",step4:"Das Update dauert 10-20 Minuten",done:"Fertig",starting:"Starte...",uploadFirmware:"Firmware hochladen",uploadFailed:"Upload fehlgeschlagen: {{error}}",uploadedToast:"Firmware hochgeladen! Starten Sie das Update vom Druckerbildschirm.",availableVersions:"Verfügbare Versionen",usable:"Installierbar",unavailable:"Nicht verfügbar",installed:"Installiert",newerBadge:"neuer",olderBadge:"älter",currentBadge:"aktuell"},accessCodePlaceholder:"Leer lassen, um den aktuellen zu behalten",roi:{title:"Erkennungsbereich (ROI)",xStart:"X-Start",yStart:"Y-Start",width:"Breite",height:"Höhe",instruction:"Passen Sie den Erkennungsbereich an, um sich auf die Druckplatte zu konzentrieren. Der grüne Rahmen in der Vorschau zeigt den aktuellen Bereich."},developerModeWarning:"Der Entwickler-LAN-Modus ist nicht aktiviert auf: {{names}}. Einige Funktionen funktionieren möglicherweise nicht.",howToEnable:"Aktivieren",incompatibleFile:"Diese Datei wurde für {{slicedFor}} geslicet, aber dieser Drucker ist ein {{printerModel}}",dropNotPrintable:"Nur .gcode- und .gcode.3mf-Dateien können gedruckt werden",dropToPrint:"Zum Drucken ablegen",cannotPrint:"Drucker beschäftigt"},archives:{title:"Druckarchiv",searchPlaceholder:"Archiv durchsuchen...",filterByPrinter:"Nach Drucker filtern",filterByStatus:"Nach Status filtern",sortBy:"Sortieren nach",sortNewest:"Neueste zuerst",sortOldest:"Älteste zuerst",sortName:"Name",sortDuration:"Dauer",sortLargest:"Größte zuerst",sortSmallest:"Kleinste zuerst",sortSize:"Größe",noArchives:"Keine Archive gefunden",noArchivesSearch:"Keine Archive entsprechen Ihrer Suche",originalPrintNotVisible:"Ursprünglicher Druck nicht sichtbar - versuchen Sie, die Filter zu löschen",noArchivesYet:"Noch keine Archive",prints:"Drucke",pagination:{showing:"Zeige",to:"bis",of:"von",show:"Zeige",page:"Seite",all:"Alle"},loadingArchives:"Lade Archive...",releaseToUpload:"Loslassen zum Hochladen",showAll:"Alle anzeigen",showFavoritesOnly:"Nur Favoriten anzeigen",gridView:"Rasteransicht",listView:"Listenansicht",calendarView:"Kalenderansicht",logView:"Druckprotokoll",manageTags:"Tags verwalten",showFailedPrints:"Fehlgeschlagene Drucke anzeigen",hideFailedPrints:"Fehlgeschlagene Drucke ausblenden",hideDuplicates:"Duplikate ausblenden",viewOriginalPrint:"Klicken, um den ursprünglichen Druck anzuzeigen (#{{id}})",printTime:"Druckzeit",filamentUsed:"Verbrauchtes Filament",cost:"Kosten",reprint:"Drucken",preview:"Vorschau",deleteArchive:"Archiv löschen",deleteConfirm:"Möchten Sie dieses Archiv wirklich löschen?",favorite:"Favorit",unfavorite:"Aus Favoriten entfernen",viewDetails:"Details anzeigen",status:{completed:"Abgeschlossen",failed:"Fehlgeschlagen",stopped:"Gestoppt"},toast:{source3mfAttached:"Quell-3MF angehängt: {{filename}}",failedUploadSource3mf:"Fehler beim Hochladen der Quell-3MF",source3mfRemoved:"Quell-3MF entfernt",failedRemoveSource3mf:"Fehler beim Entfernen der Quell-3MF",f3dAttached:"F3D angehängt: {{filename}}",failedUploadF3d:"Fehler beim Hochladen der F3D",f3dRemoved:"F3D entfernt",failedRemoveF3d:"Fehler beim Entfernen der F3D",timelapseAttached:"Zeitraffer angehängt: {{filename}}",timelapseAlreadyAttached:"Zeitraffer bereits angehängt",noMatchingTimelapse:"Kein passender Zeitraffer gefunden",failedScanTimelapse:"Fehler beim Suchen nach Zeitraffer",failedAttachTimelapse:"Fehler beim Anhängen des Zeitraffers",timelapseRemoved:"Zeitraffer entfernt",failedRemoveTimelapse:"Fehler beim Entfernen des Zeitraffers",timelapseUploaded:"Zeitraffer hochgeladen: {{filename}}",failedUploadTimelapse:"Fehler beim Hochladen des Zeitraffers",archiveDeleted:"Archiv gelöscht",failedDeleteArchive:"Fehler beim Löschen des Archivs",addedToFavorites:"Zu Favoriten hinzugefügt",removedFromFavorites:"Aus Favoriten entfernt",projectUpdated:"Projekt aktualisiert",failedUpdateProject:"Fehler beim Aktualisieren des Projekts",linkCopied:"Link in die Zwischenablage kopiert",failedCopyLink:"Fehler beim Kopieren des Links",photoDeleted:"Foto gelöscht",failedDeletePhoto:"Fehler beim Löschen des Fotos",failedDeleteArchives:"Fehler beim Löschen der Archive",failedUpdateFavorites:"Fehler beim Aktualisieren der Favoriten",exportDownloaded:"Export heruntergeladen",exportFailed:"Export fehlgeschlagen"},menu:{print:"Drucken",schedule:"Planen",openInBambuStudio:"Im Slicer öffnen",slice:"Slicen",externalLink:"Externer Link",viewOnMakerWorld:"Auf MakerWorld ansehen",preview3d:"3D-Vorschau",viewTimelapse:"Zeitraffer ansehen",scanForTimelapse:"Nach Zeitraffer suchen",uploadTimelapse:"Zeitraffer hochladen",removeTimelapse:"Zeitraffer entfernen",downloadSource3mf:"Quell-3MF herunterladen",uploadSource3mf:"Quell-3MF hochladen",replaceSource3mf:"Quell-3MF ersetzen",removeSource3mf:"Quell-3MF entfernen",uploadF3d:"F3D hochladen",replaceF3d:"F3D ersetzen",downloadF3d:"F3D herunterladen",removeF3d:"F3D entfernen",download:"Herunterladen",copyDownloadLink:"Download-Link kopieren",qrCode:"QR-Code",viewPhotos:"Fotos ansehen",viewPhotosCount:"Fotos ansehen ({{count}})",projectPage:"Projektseite",addToFavorites:"Zu Favoriten hinzufügen",removeFromFavorites:"Aus Favoriten entfernen",edit:"Bearbeiten",goToProject:"Zum Projekt: {{name}}",addToProject:"Zu Projekt hinzufügen",removeFromProject:"Aus Projekt entfernen",loading:"Laden...",noProjectsAvailable:"Keine Projekte verfügbar",select:"Auswählen",deselect:"Abwählen",delete:"Löschen"},permission:{noReprint:"Sie haben keine Berechtigung, dieses Archiv erneut zu drucken",noAddToQueue:"Sie haben keine Berechtigung, zur Warteschlange hinzuzufügen",noUpdateArchives:"Sie haben keine Berechtigung, Archive zu aktualisieren",noUploadFiles:"Sie haben keine Berechtigung, Dateien hochzuladen",noDownload:"Sie haben keine Berechtigung, Archive herunterzuladen",noCopyLink:"Sie haben keine Berechtigung, Download-Links zu kopieren",noDelete:"Sie haben keine Berechtigung, dieses Archiv zu löschen",noCreate:"Sie haben keine Berechtigung, Archive zu erstellen"},card:{previousPlate:"Vorherige Platte",nextPlate:"Nächste Platte",plateNumber:"Platte {{index}}",moreOptions:"Rechtsklick für mehr Optionen",addToFavorites:"Zu Favoriten hinzufügen",removeFromFavorites:"Aus Favoriten entfernen",cancelled:"abgebrochen",failed:"fehlgeschlagen",duplicate:"Duplikat",duplicateTitle:"Dieses Modell wurde bereits zuvor gedruckt",openSource3mf:"Quell-3MF in Bambu Studio öffnen (Rechtsklick für mehr Optionen)",downloadF3d:"Fusion 360 Designdatei herunterladen",viewTimelapse:"Zeitraffer ansehen",viewPhoto:"1 Foto ansehen",viewPhotos:"{{count}} Fotos ansehen",openFolder:"Ordner öffnen: {{name}}",slicedFile:"Geslicte Datei - druckbereit",sourceFile:"Nur Quelldatei - keine AMS-Zuordnung verfügbar",gcode:"GCODE",source:"QUELLE",project:"Projekt: {{name}}",estimated:"Geschätzt: {{time}}",actual:"Tatsächlich: {{time}}",accuracy:"Genauigkeit: {{percent}}%",filament:"{{weight}}g",layer:"{{count}} Schicht",layers:"{{count}} Schichten",object:"{{count}} Objekt",objects:"{{count}} Objekte",slicedFor:"Geslict für {{model}}",uploadedBy:"Hochgeladen von",noPermissionReprint:"Sie haben keine Berechtigung, erneut zu drucken",noFileForReprint:"Keine 3MF-Datei verfügbar — die Datei konnte beim Aufzeichnen des Drucks nicht vom Drucker heruntergeladen werden",noPermissionEdit:"Sie haben keine Berechtigung, Archive zu bearbeiten",noPermissionDelete:"Sie haben keine Berechtigung, Archive zu löschen",reprint:"Drucken",schedulePrint:"Druck planen",schedule:"Planen",openInBambuStudio:"Im Slicer öffnen",openInBambuStudioToSlice:"Im Slicer öffnen zum Slicen",slice:"Slicen",externalLink:"Externer Link",makerWorld:"MakerWorld: {{designer}}",viewProject:"Projekt ansehen",noExternalLink:"Kein externer Link",preview3d:"3D-Vorschau",download:"Herunterladen",edit:"Bearbeiten",delete:"Löschen"},modal:{deleteArchive:"Archiv löschen",deleteConfirm:'Möchten Sie "{{name}}" wirklich löschen? Diese Aktion kann nicht rückgängig gemacht werden.',deleteButton:"Löschen",removeSource3mf:"Quell-3MF entfernen",removeSource3mfConfirm:'Möchten Sie die Quell-3MF-Datei wirklich von "{{name}}" entfernen? Die ursprüngliche Slicer-Projektdatei wird gelöscht.',removeButton:"Entfernen",removeF3d:"F3D entfernen",removeF3dConfirm:'Möchten Sie die Fusion 360 Designdatei wirklich von "{{name}}" entfernen?',removeTimelapse:"Zeitraffer entfernen",removeTimelapseConfirm:'Möchten Sie das Zeitraffervideo wirklich von "{{name}}" entfernen?',timelapse:"{{name}} - Zeitraffer",selectTimelapse:"Zeitraffer auswählen",selectTimelapseDesc:"Keine automatische Übereinstimmung gefunden. Wählen Sie den Zeitraffer für diesen Druck:",deleteArchives:"Archive löschen",deleteArchivesConfirm:"Möchten Sie wirklich {{count}} Archiv(e) löschen? Diese Aktion kann nicht rückgängig gemacht werden.",deleteCount:"{{count}} löschen"},page:{title:"Archive",printsCount:"{{filtered}} von {{total}} Drucken",dropFilesHere:".3mf-Dateien hier ablegen",releaseToUpload:"Loslassen zum Hochladen",only3mfSupported:"Nur .3mf-Dateien werden unterstützt",close:"Schließen",selected:"{{count}} ausgewählt",selectAll:"Alle auswählen",tags:"Tags",project:"Projekt",favorite:"Favorit",delete:"Löschen",toggledFavorites:"Favoriten für {{count}} Archiv(e) umgeschaltet",failedUpdateFavorites:"Fehler beim Aktualisieren der Favoriten",archivesDeleted:"{{count}} Archiv(e) gelöscht",failedDeleteArchives:"Fehler beim Löschen der Archive",photoDeleted:"Foto gelöscht",failedDeletePhoto:"Fehler beim Löschen des Fotos"},list:{name:"Name",printer:"Drucker",date:"Datum",size:"Größe",actions:"Aktionen",hasTimelapse:"Hat Zeitraffer"},log:{date:"Datum",printName:"Druckname",printer:"Drucker",user:"Benutzer",status:"Status",duration:"Dauer",filament:"Filament",allPrinters:"Alle Drucker",allUsers:"Alle Benutzer",allStatuses:"Alle Status",cancelled:"Abgebrochen",skipped:"Übersprungen",dateFrom:"Von",dateTo:"Bis",noEntries:"Keine Druckprotokolleinträge gefunden",showing:"{{count}} von {{total}} Einträgen",rowsPerPage:"Zeilen",page:"Seite",prev:"Zurück",next:"Weiter",clearLog:"Protokoll löschen",clearLogTitle:"Druckprotokoll löschen",clearLogConfirm:"Alle Druckprotokolleinträge werden dauerhaft gelöscht. Archive und Warteschlangeneinträge sind nicht betroffen. Diese Aktion kann nicht rückgängig gemacht werden. Sind Sie sicher?",clearLogButton:"Alle löschen",cleared:"{{count}} Protokolleinträge gelöscht",clearFailed:"Druckprotokoll konnte nicht gelöscht werden"}},queue:{title:"Druckwarteschlange",subtitle:"Planen und verwalten Sie Ihre Druckaufträge",addToQueue:"Zur Warteschlange hinzufügen",print:"Drucken",reprint:"Erneut drucken",schedulePrint:"Druck planen",editQueueItem:"Warteschlangeneintrag bearbeiten",printToPrinters:"Auf {{count}} Druckern drucken",queueToPrinters:"Zu {{count}} Druckern hinzufügen",queueSelectedPlates:"{{count}} Platten in die Warteschlange",selectAllPlates:"Alle {{count}} Platten auswählen",deselectAll:"Alle abwählen",printQueued:"Druck in Warteschlange",itemsQueued:"{{count}} Einträge in Warteschlange",sending:"Wird gesendet...",sendingProgress:"Sende {{current}}/{{total}}...",adding:"Wird hinzugefügt...",addingProgress:"Füge hinzu {{current}}/{{total}}...",savingProgress:"Speichere {{current}}/{{total}}...",clearQueue:"Warteschlange leeren",clearHistory:"Verlauf löschen",emptyQueue:"Warteschlange ist leer",position:"Position",scheduledTime:"Geplante Zeit",moveUp:"Nach oben",moveDown:"Nach unten",startNow:"Jetzt starten",printingInProgress:"Druck läuft...",viewArchive:"Archiv anzeigen",viewInFileManager:"Im Dateimanager anzeigen",itemCount:"{{count}} Element",itemCount_plural:"{{count}} Elemente",dragToReorder:"Ziehen zum Neuordnen (nur Sofort)",reorderHint:"Position betrifft nur Sofort-Elemente. Geplante Elemente werden zur festgelegten Zeit ausgeführt.",sjf:{label:"SJF",tooltip:"Kürzester Auftrag zuerst — Scheduler bevorzugt kürzere Drucke"},addedBy:"Hinzugefügt von {{name}}",nextInQueue:"Nächster in der Warteschlange",clearPlate:"Druckplatte freigeben & Nächsten starten",clearPlateSuccess:"Druckplatte freigegeben — bereit für nächsten Druck",plateReady:"Druckplatte freigegeben — bereit für nächsten Druck",plateNumber:"Platte {{index}}",quantity:"Menge",quantityHint:"Erstellt {{count}} Warteschlangeneinträge",activeBatches:"Aktive Stapel",batchProgress:"{{completed}} von {{total}} abgeschlossen",cancelBatch:"Verbleibende abbrechen",batchCancelled:"Verbleibende Stapeleinträge abgebrochen",cancelBatchConfirmTitle:"Stapel abbrechen",cancelBatchConfirmMessage:"Alle verbleibenden ausstehenden Einträge in diesem Stapel abbrechen?",batch:"Stapel",sections:{currentlyPrinting:"Aktuell druckend",queued:"In Warteschlange",history:"Verlauf"},status:{pending:"Ausstehend",waiting:"Wartend",printing:"Druckt",paused:"Pausiert",completed:"Abgeschlossen",failed:"Fehlgeschlagen",skipped:"Übersprungen",cancelled:"Abgebrochen"},summary:{printing:"Druckt",queued:"In Warteschlange",totalTime:"Gesamte Wartezeit",totalWeight:"Gesamtgewicht der Warteschlange",history:"Verlauf"},filter:{allPrinters:"Alle Drucker",unassigned:"Nicht zugewiesen",allStatus:"Alle Status",allLocations:"Alle Standorte",any:"Beliebig"},sort:{byPosition:"Nach Position sortieren",byName:"Nach Name sortieren",byPrinter:"Nach Drucker sortieren",bySchedule:"Nach Zeitplan sortieren",byDate:"Nach Datum sortieren",ascendingOldest:"Aufsteigend (älteste zuerst)",descendingNewest:"Absteigend (neueste zuerst)"},badges:{staged:"Bereitgestellt",requiresPrevious:"Erfordert vorherigen Erfolg",autoPowerOff:"Automatisch ausschalten",gcodeInjection:"G-code"},empty:{title:"Keine Drucke geplant",description:'Planen Sie einen Druck von der Archivseite über die Option "Planen" im Kontextmenü oder ziehen Sie Dateien hierher.'},time:{asap:"Sofort",overdue:"Überfällig",now:"Jetzt",lessThanMinute:"In weniger als einer Minute",inMinutes:"In {{count}} Min",inHours:"In {{count}} Stunden"},actions:{stopPrint:"Druck stoppen",startPrint:"Druck starten",requeue:"Erneut einreihen"},bulkEdit:{title:"{{count}} Element bearbeiten",title_plural:"{{count}} Elemente bearbeiten",description:"Nur geänderte Einstellungen werden auf ausgewählte Elemente angewendet.",printer:"Drucker",noChange:"— Keine Änderung —",queueOptions:"Warteschlangenoptionen",staged:"Bereitgestellt (manueller Start)",autoPowerOff:"Nach Druck automatisch ausschalten",requirePrevious:"Vorherigen Erfolg erfordern",printOptions:"Druckoptionen",bedLevelling:"Bett-Nivellierung",flowCalibration:"Fluss-Kalibrierung",vibrationCalibration:"Vibrations-Kalibrierung",layerInspection:"Erste-Schicht-Prüfung",timelapse:"Zeitraffer",useAms:"AMS verwenden",applyChanges:"Änderungen übernehmen",selectAll:"Alle auswählen",deselectAll:"Auswahl aufheben",selected:"{{count}} ausgewählt",editSelected:"Ausgewählte bearbeiten",cancelSelected:"Ausgewählte abbrechen"},confirm:{cancelTitle:"Geplanten Druck abbrechen",cancelMessage:'Möchten Sie "{{name}}" wirklich abbrechen?',stopTitle:"Druck stoppen",stopMessage:'Möchten Sie den aktuellen Druck "{{name}}" wirklich stoppen? Der Druckauftrag wird am Drucker abgebrochen.',removeTitle:"Aus Verlauf entfernen",removeMessage:'Möchten Sie "{{name}}" wirklich aus dem Warteschlangenverlauf entfernen?',clearHistoryTitle:"Verlauf löschen",clearHistoryMessage:"Möchten Sie alle {{count}} Element(e) aus dem Verlauf entfernen?",cancelButton:"Druck abbrechen",stopButton:"Druck stoppen",thisPrint:"diesen Druck",thisItem:"dieses Element"},toast:{cancelled:"Warteschlangenelement abgebrochen",cancelFailed:"Element konnte nicht abgebrochen werden",removed:"Warteschlangenelement entfernt",removeFailed:"Element konnte nicht entfernt werden",stopped:"Druck gestoppt",stopFailed:"Druck konnte nicht gestoppt werden",released:"Druck in Warteschlange freigegeben",startFailed:"Druck konnte nicht gestartet werden",reorderFailed:"Warteschlange konnte nicht neu geordnet werden",historyCleared:"{{count}} Verlaufselement(e) gelöscht",clearHistoryFailed:"Verlauf konnte nicht gelöscht werden",updateFailed:"Elemente konnten nicht aktualisiert werden",bulkCancelled:"{{count}} Element(e) abgebrochen",bulkCancelFailed:"Elemente konnten nicht abgebrochen werden"},timeline:{listView:"Liste",timelineView:"Zeitstrahl",unassigned:"Nicht zugewiesen",noData:"Keine geplanten Drucke für diesen Tag",allDoneBy:"Alle Drucke voraussichtlich fertig um {{time}}",staged:"Bereitgestellt",filterAll:"Alle anzeigen",filterPrinting:"Druckend",filterQueued:"Warteschlange",time:{anyMoment:"jeden Moment",minutesLeft:"{{minutes}}m übrig",hoursLeft:"{{hours}}h übrig",hoursMinutesLeft:"{{hours}}h {{minutes}}m übrig"},day:{previous:"Vorheriger Tag",next:"Nächster Tag",today:"Heute"}},permissions:{noStopPrint:"Sie haben keine Berechtigung, Drucke zu stoppen",noStartPrint:"Sie haben keine Berechtigung, Drucke zu starten",noEdit:"Sie haben keine Berechtigung, dieses Warteschlangenelement zu bearbeiten",noCancel:"Sie haben keine Berechtigung, dieses Warteschlangenelement abzubrechen",noRequeue:"Sie haben keine Berechtigung, Elemente erneut einzureihen",noRemove:"Sie haben keine Berechtigung, dieses Warteschlangenelement zu entfernen",noClearHistory:"Sie haben keine Berechtigung, den gesamten Verlauf zu löschen",noEditItems:"Sie haben keine Berechtigung, Warteschlangenelemente zu bearbeiten",noCancelItems:"Sie haben keine Berechtigung, Warteschlangenelemente abzubrechen"}},backgroundDispatch:{unknownFile:"Unbekannte Datei",unknownPrinter:"Unbekannter Drucker",startingPrints:"Starte Drucke",progressSummary:"{{complete}}/{{total}} abgeschlossen • Geplant: {{dispatched}} • In Bearbeitung: {{processing}}",expandDetails:"Dispatch-Details ausklappen",collapseDetails:"Dispatch-Details einklappen",dismissToast:"Dispatch-Hinweis schließen",cancelDispatchJob:"Dispatch-Job abbrechen",cancel:"Abbrechen",cancelling:"Wird abgebrochen…",status:{dispatched:"Geplant",processing:"In Bearbeitung",completed:"Abgeschlossen",failed:"Fehlgeschlagen",cancelled:"Abgebrochen"},toast:{cancellingUpload:"Upload wird abgebrochen...",cancelled:"Dispatch abgebrochen",cancelFailed:"Dispatch konnte nicht abgebrochen werden",completeWithFailures:"Background Dispatch abgeschlossen: {{completed}} erfolgreich, {{failed}} fehlgeschlagen",completeSuccess:"Background Dispatch abgeschlossen: {{completed}} erfolgreich",printStartedRemaining:"{{completed}} Druck(e) gestartet, {{remaining}} weitere werden gesendet..."}},stats:{title:"Dashboard",subtitle:"Widgets zum Neuanordnen ziehen. Auf das Augensymbol klicken zum Ausblenden.",overview:"Übersicht",totalPrints:"Gesamtdrucke",successRate:"Erfolgsrate",totalPrintTime:"Gesamtdruckzeit",printTime:"Druckzeit",totalFilament:"Gesamtverbrauch Filament",filamentUsed:"Filamentverbrauch",filamentCost:"Filamentkosten",totalCost:"Gesamtkosten",energyUsed:"Energieverbrauch",energyCost:"Energiekosten",energyWarmingUpTooltip:"Die Energieerfassung sammelt noch stündliche Snapshots. Zeitraumwerte werden genau, sobald vor dem gewählten Bereich mindestens ein Snapshot vorliegt. Frühe Werte können zu niedrig sein.",averagePrintTime:"Durchschnittliche Druckzeit",printsPerDay:"Drucke pro Tag",byPrinter:"Nach Drucker",printsByPrinter:"Drucke nach Drucker",byMaterial:"Nach Material",byMonth:"Nach Monat",last7Days:"Letzte 7 Tage",last30Days:"Letzte 30 Tage",last90Days:"Letzte 90 Tage",allTime:"Gesamt",quickStats:"Schnellstatistiken",printActivity:"Druckaktivität",filamentTypes:"Filamenttypen",filamentTrends:"Filamenttrends",failureAnalysis:"Fehleranalyse",timeAccuracy:"Zeitgenauigkeit",successful:"Erfolgreich:",failed:"Fehlgeschlagen:",perfectEstimate:"100% = perfekte Schätzung",noTimeAccuracyData:"Noch keine Zeitgenauigkeitsdaten",noFilamentData:"Keine Filamentdaten verfügbar",noPrinterData:"Keine Druckerdaten verfügbar",noPrintData:"Keine Druckdaten verfügbar",noPrintDataLast30Days:"Keine Druckdaten in den letzten 30 Tagen",failureReasons:"Fehlerursachen",topFailureReasons:"Häufigste Fehlerursachen",failedPrintsCount:"{{failed}} / {{total}} Drucke fehlgeschlagen",lastWeekRate:"Letzte Woche: {{rate}}%",resetLayout:"Layout zurücksetzen",recalculateCosts:"Kosten neu berechnen",recalculateCostsHint:"Alle Archivkosten mit aktuellen Filamentpreisen neu berechnen",exportStats:"Statistiken exportieren",exportAsCsv:"Als CSV exportieren",exportAsExcel:"Als Excel exportieren",hiddenCount:"{{count}} ausgeblendet",exportDownloaded:"Export heruntergeladen",exportFailed:"Export fehlgeschlagen",layoutReset:"Layout zurückgesetzt",recalculatedCosts:"Kosten für {{count}} Archive neu berechnet",recalculateFailed:"Kosten konnten nicht neu berechnet werden",loadingStats:"Statistiken werden geladen...",noPermissionResetLayout:"Sie haben keine Berechtigung, das Layout zurückzusetzen",noPermissionRecalculate:"Sie haben keine Berechtigung, Kosten neu zu berechnen",noPrintDataInRange:"Keine Druckdaten im ausgewählten Zeitraum",periodFilament:"Filamentverbrauch",periodCost:"Kosten",avgPerPrint:"Durchschnitt pro Druck",usageOverTime:"Verbrauch im Zeitverlauf",filamentByWeight:"Gewicht",printDuration:"Druckdauer",printerUtilization:"Druckerauslastung",filamentSuccess:"Erfolg nach Material",printHabits:"Druckgewohnheiten",printTimeOfDay:"Druck-Tageszeit",colorDistribution:"Farbverteilung",noColorData:"Keine Farbdaten verfügbar",records:"Rekorde",longestPrint:"Längster Druck",heaviestPrint:"Schwerster Druck",mostExpensivePrint:"Teuerster Druck",busiestDay:"Aktivster Tag",successStreak:"Erfolgsserie",streakPrint:"aufeinanderfolgender Druck",streakPrints:"{{count}} aufeinanderfolgende Drucke",printerStats:"Druckerstatistiken",hours:"Stunden",avgPrints:"Ø Drucke",noArchiveData:"Keine Druckdaten verfügbar",filamentByTime:"Zeitverlauf",avgWeight:"Ø Gewicht",avgTime:"Ø Zeit",filamentByPrints:"Drucke",timeframe:{today:"Heute","this-week":"Diese Woche","this-month":"Dieser Monat","last-7":"Letzte 7 Tage","last-30":"Letzte 30 Tage","last-90":"Letzte 90 Tage","this-year":"Dieses Jahr","all-time":"Gesamt",custom:"Benutzerdefiniert",from:"Von",to:"Bis"},allUsers:"Alle Benutzer",noUser:"Kein Benutzer (System)",filterByUser:"Nach Benutzer filtern"},maintenance:{title:"Wartung",overview:"Übersicht",allOk:"Alle Wartungen aktuell",dueCount:"{{count}} Aufgabe fällig",dueCount_plural:"{{count}} Aufgaben fällig",warningCount:"{{count}} Warnung",warningCount_plural:"{{count}} Warnungen",totalPrintTime:"Gesamtdruckzeit",nextMaintenance:"Nächste Wartung",nothingDue:"Nichts fällig",tasks:"Aufgaben",lastPerformed:"Zuletzt durchgeführt",interval:"Intervall",hoursRemaining:"{{hours}}h verbleibend",hoursOverdue:"{{hours}}h überfällig",markDone:"Als erledigt markieren",performMaintenance:"Wartung durchführen",history:"Verlauf",noHistory:"Kein Wartungsverlauf",editPrintHours:"Druckstunden bearbeiten",currentHours:"Aktuelle Stunden",statusTab:"Status",settingsTab:"Einstellungen",overdueCount:"{{count}} überfällig",dueSoonCount:"{{count}} bald fällig",dueSoon:"Bald fällig",allGood:"Alles in Ordnung",overdueBy:"Überfällig um {{duration}}",dueIn:"Fällig in {{duration}}",timeLeft:"{{duration}} verbleibend",day:"1 Tag",days:"{{count}} Tage",week:"1 Woche",weeks:"{{count}} Wochen",month:"1 Monat",months:"{{count}} Monate",year:"1 Jahr",maintenanceTypes:"Wartungstypen",maintenanceTypesDescription:"Systemtypen und Ihre benutzerdefinierten Wartungsaufgaben",addCustomType:"Benutzerdefinierten Typ hinzufügen",restoreDefaults:"Standardaufgaben wiederherstellen",intervalType:"Intervalltyp",intervalValue:"Intervall ({{type}})",icon:"Symbol",documentationLink:"Dokumentationslink (optional)",assignToPrinters:"Druckern zuweisen",selectAtLeastOnePrinter:"Wählen Sie mindestens einen Drucker",addType:"Typ hinzufügen",custom:"Benutzerdefiniert",printHours:"Druckstunden",calendarDays:"Kalendertage",exampleName:"z.B. HEPA-Filter ersetzen",viewDocumentation:"Dokumentation anzeigen",timeBasedInterval:"Zeitbasiertes Intervall",intervalOverrides:"Intervall-Überschreibungen",intervalOverridesDescription:"Intervalle für bestimmte Drucker anpassen",assignedToPrinters:"Druckern zugewiesen:",noPrintersAssigned:"Keine Drucker zugewiesen",addPrinterShort:"Hinzufügen:",printersAssignedClick:"{{count}} Drucker zugewiesen - klicken zum Verwalten",removeFromPrinter:"Von diesem Drucker entfernen",types:{lubricateCarbonRods:"Karbonstäbe schmieren",lubricateRails:"Linearschienen schmieren",cleanNozzle:"Düse/Hotend reinigen",checkBelts:"Riemenspannung prüfen",cleanBuildPlate:"Druckbett reinigen",checkExtruder:"Extruderzahnräder prüfen",checkCooling:"Kühlungslüfter prüfen",generalInspection:"Allgemeine Inspektion",cleanCarbonRods:"Kohlenstoffstangen reinigen",lubricateSteelRods:"Stahlstangen schmieren",cleanSteelRods:"Stahlstangen reinigen",cleanLinearRails:"Linearschienen reinigen",checkPtfeTube:"PTFE-Schlauch prüfen",replaceHepaFilter:"HEPA-Filter ersetzen",replaceCarbonFilter:"Aktivkohlefilter ersetzen",lubricateLeftNozzleRail:"Linke Düsenschiene schmieren"},maintenanceComplete:"Wartung als abgeschlossen markiert",typeUpdated:"Wartungstyp aktualisiert",typeDeleted:"Wartungstyp gelöscht",defaultsRestored:"{{count}} Standardaufgabe(n) wiederhergestellt",printHoursUpdated:"Druckstunden aktualisiert",printerAssigned:"Drucker zugewiesen",printerRemoved:"Drucker entfernt",deleteTypeConfirm:'"{{name}}" löschen?',deleteSystemTypeTitle:"Standard-Wartungsaufgabe löschen?",deleteSystemTypeMessage:'Möchten Sie die Standard-Wartungsaufgabe "{{name}}" wirklich löschen?',noPermissionUpdate:"Sie haben keine Berechtigung, Wartungselemente zu aktualisieren",noPermissionPerform:"Sie haben keine Berechtigung, Wartungen durchzuführen",noPermissionEditTypes:"Sie haben keine Berechtigung, Wartungstypen zu bearbeiten",noPermissionDeleteTypes:"Sie haben keine Berechtigung, Wartungstypen zu löschen",noPermissionEditHours:"Sie haben keine Berechtigung, Druckstunden zu bearbeiten",noPermissionRemovePrinter:"Sie haben keine Berechtigung, Druckerzuweisungen zu entfernen",noPermissionAssignPrinter:"Sie haben keine Berechtigung, Drucker zuzuweisen",noPermissionEditIntervals:"Sie haben keine Berechtigung, Intervalle zu bearbeiten",configureSettings:"Wartungstypen und Intervalle konfigurieren"},settings:{title:"Einstellungen",general:"Allgemein",tabs:{general:"Allgemein",smartPlugs:"Smart Plugs",notifications:"Benachrichtigungen",queue:"Workflow",filament:"Filament",network:"Netzwerk",apiKeys:"API-Schlüssel",virtualPrinter:"Virtueller Drucker",spoolbuddy:"SpoolBuddy",failureDetection:"Fehlererkennung",users:"Authentifizierung",backup:"Sicherung",emailAuth:"E-Mail-Authentifizierung",ldap:"LDAP",twoFa:"Zwei-Faktor-Auth",oidc:"SSO / OIDC"},spoolbuddy:{infoTitle:"SpoolBuddy-Geräte",infoBody:"SpoolBuddy-Kioske registrieren sich automatisch per Heartbeat. Ein Gerät hier abmelden, wenn es nicht mehr verwendet wird oder wenn ein veralteter Eintrag nach einem Daemon-Absturz übrig geblieben ist.",duplicatesTitle:"{{count}} Geräte registriert",duplicatesBody:"Die Kiosk-Oberfläche verwendet nur das zuerst registrierte Gerät. Falls eines davon ein veralteter Doppeleintrag nach einem Absturz ist, kann es hier entfernt werden — ein laufendes Gerät registriert sich beim nächsten Heartbeat automatisch neu.",empty:"Noch keine SpoolBuddy-Geräte registriert.",online:"Online",offline:"Offline",unregister:"Abmelden",unregisterSuccess:"Gerät abgemeldet",unregisterError:"Gerät konnte nicht abgemeldet werden",confirmTitle:"SpoolBuddy-Gerät abmelden?",confirmBody:'Dies entfernt „{{hostname}}" ({{deviceId}}) aus der Datenbank. Ein laufendes Gerät registriert sich beim nächsten Heartbeat automatisch neu.',ipAddress:"IP-Adresse",firmware:"Firmware",lastSeen:"Zuletzt gesehen",daemonUptime:"Daemon-Laufzeit",systemUptime:"System-Laufzeit",never:"nie",nfc:"NFC",scale:"Waage",cpuTemp:"CPU-Temp.",memory:"Speicher",disk:"Festplatte",update:"Aktualisieren",updateConfirmTitle:"Spoolbuddy-Daemon aktualisieren?",updateConfirmBody:'Software-Update auf „{{hostname}}" auslösen? Der Daemon startet nach dem Update neu.',restartBrowser:"Browser neu starten",restartBrowserConfirmTitle:"Kiosk-Browser neu starten?",restartBrowserConfirmBody:'Kiosk-Browser auf „{{hostname}}" neu starten? Die Anzeige wird kurz schwarz.',restartDaemon:"Daemon neu starten",restartDaemonConfirmTitle:"Spoolbuddy-Daemon neu starten?",restartDaemonConfirmBody:'Spoolbuddy-Daemon auf „{{hostname}}" neu starten? Das Gerät ist für einige Sekunden offline.',reboot:"Neustart",rebootConfirmTitle:"Gerät neu starten?",rebootConfirmBody:'„{{hostname}}" neu starten? Das Gerät ist für etwa eine Minute offline.',shutdown:"Herunterfahren",shutdownConfirmTitle:"Gerät herunterfahren?",shutdownConfirmBody:'„{{hostname}}" herunterfahren? Physischer Zugriff ist nötig, um es wieder einzuschalten.',commandConfirm:"Bestätigen",commandQueued:"Befehl eingereiht",commandError:"Befehl konnte nicht gesendet werden"},ldap:{title:"LDAP-Authentifizierung",enabledDesc:"LDAP-Authentifizierung ist aktiviert",disabledDesc:"LDAP-Authentifizierung ist deaktiviert",disabledHint:"LDAP-Einstellungen unten konfigurieren und speichern, dann aktivieren.",enabled:"LDAP-Authentifizierung aktiviert",disabled:"LDAP-Authentifizierung deaktiviert",feature1:"Benutzer können sich mit LDAP-Anmeldedaten anmelden",feature2:"Lokales Admin-Konto bleibt als Fallback erhalten",feature3:"LDAP-Gruppen werden bei der Anmeldung BamBuddy-Gruppen zugeordnet",serverConfig:"LDAP-Server-Konfiguration",serverUrl:"Server-URL",serverUrlHint:"Verwenden Sie ldap:// für Standard oder ldaps:// für SSL-Verbindungen",security:"Sicherheit",securityHint:"StartTLS aktualisiert eine einfache Verbindung auf TLS. LDAPS verwendet TLS von Anfang an.",bindDn:"Bind-DN (Dienstkonto)",bindPassword:"Bind-Passwort",searchBase:"Such-Basis-DN",userFilter:"Benutzer-Suchfilter",userFilterHint:"{username} wird durch den Anmeldenamen ersetzt. Verwenden Sie (uid={username}) für OpenLDAP.",autoProvision:"Benutzer automatisch anlegen",autoProvisionHint:"Automatisch ein BamBuddy-Konto bei der ersten LDAP-Anmeldung erstellen",defaultGroup:"Standardgruppe",defaultGroupNone:"— Keine (kein Fallback) —",defaultGroupHint:"Fallback-Gruppe, die zugewiesen wird, wenn sich ein LDAP-Benutzer authentifiziert, aber in keiner zugeordneten LDAP-Gruppe enthalten ist. Leer lassen, um nicht zugeordnete Benutzer ohne Berechtigungen zu belassen.",groupMapping:"Gruppenzuordnung (JSON)",groupMappingHint:"LDAP-Gruppen-DNs BamBuddy-Gruppen zuordnen. Verfügbare Gruppen: ",testConnection:"Verbindung testen",settingsSaved:"LDAP-Einstellungen gespeichert",errors:{serverRequired:"LDAP-Server-URL ist erforderlich",searchBaseRequired:"Such-Basis-DN ist erforderlich",enableAuthFirst:"Authentifizierung zuerst aktivieren",configureLdapFirst:"LDAP-Einstellungen zuerst speichern"}},email:{smtpSettings:"SMTP-Konfiguration",smtpHost:"SMTP-Server",smtpPort:"SMTP-Port",security:"Sicherheit",authentication:"Authentifizierung",username:"Benutzername",password:"Passwort",fromEmail:"Absender-E-Mail",fromName:"Absendername",testConnection:"SMTP-Verbindung testen",testRecipient:"Test-Empfänger-E-Mail",sendTest:"Test-E-Mail senden",sending:"Wird gesendet...",save:"Einstellungen speichern",saving:"Wird gespeichert...",advancedAuth:"Erweiterte Authentifizierung",advancedAuthEnabled:"Erweiterte Authentifizierung ist aktiviert",advancedAuthEnabledDesc:"E-Mail-basierte Benutzerverwaltungsfunktionen sind aktiv. Neue Benutzer erhalten automatisch generierte Passwörter per E-Mail und können ihr Passwort über die Passwort vergessen Funktion zurücksetzen.",advancedAuthDisabled:"Erweiterte Authentifizierung ist deaktiviert",advancedAuthDisabledDesc:"Aktivieren Sie die erweiterte Authentifizierung, um E-Mail-basierte Funktionen für die Benutzerverwaltung zu aktivieren.",enable:"Aktivieren",disable:"Deaktivieren",feature1:"Passwörter werden automatisch generiert und an neue Benutzer gesendet",feature2:"Benutzer können sich mit Benutzername oder E-Mail anmelden",feature3:"Passwort vergessen Funktion ist verfügbar",feature4:"Administratoren können Benutzerpasswörter per E-Mail zurücksetzen",errors:{requiredFields:"Bitte füllen Sie alle Pflichtfelder aus",usernameRequired:"Benutzername ist erforderlich, wenn Authentifizierung aktiviert ist",enterTestEmail:"Bitte geben Sie eine Test-E-Mail-Adresse ein",smtpServerAndEmail:"Bitte füllen Sie SMTP-Server und Absender-E-Mail aus, bevor Sie testen",usernamePasswordRequired:"Benutzername und Passwort sind erforderlich, wenn Authentifizierung aktiviert ist",configureSmtpFirst:"Bitte konfigurieren und testen Sie zuerst die SMTP-Einstellungen",enableAuthFirst:"Bitte aktivieren Sie zuerst die Authentifizierung, um E-Mail-basierte Funktionen nutzen zu können."},success:{settingsSaved:"SMTP-Einstellungen erfolgreich gespeichert"},securityOptions:{starttls:"STARTTLS (Port 587)",ssl:"SSL/TLS (Port 465)",none:"Keine (Port 25)"},authOptions:{enabled:"Aktiviert",disabled:"Deaktiviert"}},appearance:"Erscheinungsbild",notifications:"Benachrichtigungen",smartPlugs:"Smart Plugs",spoolman:"Spoolman",updates:"Updates",language:"Sprache",languageDescription:"Wählen Sie Ihre bevorzugte Sprache",theme:"Design",themeLight:"Hell",themeDark:"Dunkel",themeSystem:"System",defaultView:"Standardansicht",defaultViewDescription:"Seite, die beim Öffnen der App angezeigt wird",checkForUpdates:"Nach Updates suchen",autoUpdate:"Automatische Updates",currentVersion:"Aktuelle Version",latestVersion:"Neueste Version",upToDate:"Sie sind auf dem neuesten Stand",updateAvailable:"Update verfügbar",notificationLanguage:"Benachrichtigungssprache",notificationLanguageDescription:"Sprache für Push-Benachrichtigungen",bedCooledThreshold:"Bett-Abkühlung Schwellenwert",bedCooledThresholdDescription:"Temperatur, unter der das Bett nach einem Druck als abgekühlt gilt",userNotificationsEnabled:"Benutzerbenachrichtigungen",userNotificationsEnabledDescription:"Aktiviert das Benutzerbenachrichtigungsmenü und E-Mail-Benachrichtigungen für Druckereignisse. Erfordert Erweiterte Authentifizierung.",userNotificationsDisabledHint:"Erweiterte Authentifizierung aktivieren, um Benutzerbenachrichtigungen zu verwenden.",notificationProviders:"Benachrichtigungsanbieter",addProvider:"Anbieter hinzufügen",editProvider:"Anbieter bearbeiten",providerType:"Anbietertyp",testNotification:"Testbenachrichtigung",testSuccess:"Testbenachrichtigung erfolgreich gesendet",testFailed:"Testbenachrichtigung konnte nicht gesendet werden",quietHours:"Ruhezeiten",quietHoursDescription:"Keine Störungen während dieser Zeiten",quietHoursStart:"Beginn",quietHoursEnd:"Ende",events:{title:"Benachrichtigungsereignisse",printStart:"Druck gestartet",printComplete:"Druck abgeschlossen",printFailed:"Druck fehlgeschlagen",printStopped:"Druck gestoppt",printProgress:"Fortschrittsmeldungen",printProgressDescription:"Bei 25%, 50%, 75% benachrichtigen",printerOffline:"Drucker offline",printerError:"Druckerfehler",filamentLow:"Filament niedrig",maintenanceDue:"Wartung fällig",maintenanceDueDescription:"Benachrichtigen, wenn Wartung erforderlich"},smartPlug:{title:"Smart Plugs",add:"Smart Plug hinzufügen",edit:"Smart Plug bearbeiten",name:"Name",ipAddress:"IP-Adresse",linkedPrinter:"Verknüpfter Drucker",autoOn:"Automatisch einschalten",autoOnDescription:"Einschalten beim Druckstart",autoOff:"Automatisch ausschalten",autoOffDescription:"Ausschalten nach Druckende",offDelay:"Ausschaltverzögerung",offDelayMinutes:"Minuten nach Druck",offDelayTemp:"Wenn Düse unter Temperatur",currentState:"Aktueller Status",turnOn:"Einschalten",turnOff:"Ausschalten"},filamentTracking:"Filament-Verfolgung",filamentTrackingDesc:"Wählen Sie, wie Sie Ihre Filamentspulen verfolgen möchten. Sie können das integrierte Inventar oder einen externen Spoolman-Server verwenden.",filamentChecks:"Filament-Prüfungen",disableFilamentWarnings:"Filament-Warnungen deaktivieren",disableFilamentWarningsDesc:"Keine Warnungen über unzureichendes Filament beim Drucken oder Einreihen anzeigen",preferLowestFilament:"Niedrigsten Filamentrest bevorzugen",preferLowestFilamentDesc:"Bei mehreren passenden Spulen die mit dem geringsten Restfilament verwenden",trackingModeBuiltIn:"Integriertes Inventar",trackingModeBuiltInDesc:"RFID-Erkennung und Verbrauchserfassung inklusive",trackingModeSpoolmanDesc:"Externer Filament-Management-Server",builtInFeatureRfid:"Erkennt automatisch Bambu Lab RFID-Spulen im AMS",builtInFeatureUsage:"Erfasst den Filamentverbrauch pro Druck",builtInFeatureCatalog:"Spulen, Farben und K-Faktor-Profile verwalten",builtInFeatureThirdParty:"Drittanbieter-Spulen können Inventarspulen zugewiesen werden",amsSyncButton:"Gewichte vom AMS synchronisieren",amsSyncTitle:"Spulengewichte vom AMS synchronisieren",amsSyncMessage:"Alle Inventar-Spulengewichte werden mit den aktuellen AMS-Restwerten der verbundenen Drucker überschrieben. Verwenden Sie dies zur Wiederherstellung beschädigter Gewichtsdaten. Drucker müssen online sein.",amsSyncing:"Synchronisiere...",amsSyncSuccess:"{{synced}} Spule(n) synchronisiert, {{skipped}} übersprungen",amsSyncError:"Synchronisierung der Gewichte vom AMS fehlgeschlagen",spoolmanUrl:"Spoolman URL",spoolmanUrlHint:"URL Ihres Spoolman-Servers (z.B. http://localhost:7912)",spoolmanConnected:"Verbunden",spoolmanDisconnected:"Nicht verbunden",status:"Status",connect:"Verbinden",disconnect:"Trennen",howSyncWorks:"So funktioniert die Synchronisierung",syncInfoRfidOnly:"Nur offizielle Bambu Lab Spulen mit RFID werden synchronisiert",syncInfoAutoCreate:"Neue Spulen werden bei der ersten Synchronisierung automatisch in Spoolman erstellt",syncInfoThirdPartySkipped:"Nicht-Bambu-Lab-Spulen (Drittanbieter, nachgefüllt) werden übersprungen",linkingExistingSpools:"Vorhandene Spulen verknüpfen",linkingExistingSpoolsDesc:'Um vorhandene Spoolman-Spulen mit Ihrem AMS zu verknüpfen, fahren Sie über einen AMS-Slot und klicken Sie auf "Mit Spoolman verknüpfen".',syncMode:"Synchronisierungsmodus",syncModeAuto:"Automatisch",syncModeManual:"Nur manuell",syncModeAutoDesc:"AMS-Daten werden automatisch synchronisiert, wenn Änderungen erkannt werden",syncModeManualDesc:"Nur bei manueller Auslösung synchronisieren",syncAmsData:"AMS-Daten synchronisieren",syncAmsDataDesc:"AMS-Daten des Druckers manuell mit Spoolman synchronisieren",allPrinters:"Alle Drucker",noDefaultPrinter:"Kein Standard (jedes Mal fragen)",sidebarOrder:"Seitenleisten-Reihenfolge",saveThumbnails:"Vorschaubilder speichern",captureFinishPhoto:"Abschlussfoto aufnehmen",noPrintersConfigured:"Keine Drucker konfiguriert",archiveMode:{always:"Immer Archiveintrag erstellen",never:"Nie Archiveintrag erstellen",ask:"Jedes Mal fragen"},checkForUpdatesLabel:"Nach Updates suchen",checkPrinterFirmware:"Drucker-Firmware prüfen",includeBetaUpdates:"Beta-Versionen einschließen",includeBetaUpdatesDesc:"Über Beta- und Vorabversionen bei der Updateprüfung benachrichtigen",enableRetry:"Wiederholung aktivieren",homeAssistantDescription:"Smart Plugs über Home Assistant steuern",environmentManagedLabel:"(Umgebungsvariable)",autoEnabledViaEnv:"Automatisch über Umgebungsvariablen aktiviert",urlFromEnvReadOnly:"Wert wird über HA_URL Umgebungsvariable gesetzt (schreibgeschützt)",tokenFromEnvReadOnly:"Wert wird über HA_TOKEN Umgebungsvariable gesetzt (schreibgeschützt)",mqttConnectedTo:"Verbunden mit",prometheusDescription:"Druckerdaten im Prometheus-Format bereitstellen",noSmartPlugsTitle:"Keine Smart Plugs konfiguriert",noSmartPlugsDescription:"Fügen Sie einen Tasmota-basierten Smart Plug hinzu, um den Energieverbrauch zu verfolgen und die Stromsteuerung zu automatisieren.",noProvidersTitle:"Keine Anbieter konfiguriert",noProvidersDescription:"Fügen Sie einen Anbieter hinzu, um Benachrichtigungen zu erhalten.",noTemplatesAvailable:"Keine Vorlagen verfügbar. Starten Sie das Backend neu, um Standardvorlagen zu laden.",apiPermissionView:"Druckerstatus und Warteschlange anzeigen",apiPermissionEdit:"Elemente zur Druckwarteschlange hinzufügen und entfernen",apiKeysEmptyTitle:"Keine API-Schlüssel",apiKeysEmptyDescription:"Erstellen Sie einen API-Schlüssel zur Integration mit externen Diensten.",noUsersFound:"Keine Benutzer gefunden",noGroupsFound:"Keine Gruppen gefunden",noGroupsAvailable:"Keine Gruppen verfügbar",passwordsDoNotMatch:"Passwörter stimmen nicht überein",systemGroupWarning:"System-Gruppennamen können nicht geändert werden",authDisabledTitle:"Authentifizierung ist deaktiviert",authDisabledFeature1:"Anmeldung zum Zugriff auf das System erforderlich",authDisabledFeature2:"Mehrere Benutzer mit gruppenbasierten Berechtigungen erstellen",authDisabledFeature3:"Zugriff mit über 50 granularen Berechtigungen steuern",userHasCreated:"Dieser Benutzer hat erstellt:",userItemsQuestion:"Was möchten Sie mit diesen Elementen tun?",deleteUserConfirm:"Möchten Sie diesen Benutzer wirklich löschen?",actionCannotBeUndone:"Diese Aktion kann nicht rückgängig gemacht werden.",addFirstSmartPlug:"Ersten Smart Plug hinzufügen",providers:"Anbieter",log:"Protokoll",testAll:"Alle testen",testResults:"Testergebnisse",testPassedCount:"{{count}} bestanden",testFailedCount:"{{count}} fehlgeschlagen",messageTemplates:"Nachrichtenvorlagen",messageTemplatesDescription:"Passen Sie Benachrichtigungen für jedes Ereignis an.",apiKeys:"API-Schlüssel",apiKeysDescription:"Erstellen Sie API-Schlüssel für externe Integrationen und Webhooks.",createKey:"Schlüssel erstellen",apiKeyCreated:"API-Schlüssel erfolgreich erstellt",apiKeyCopyWarning:"Kopieren Sie diesen Schlüssel jetzt - er wird nicht mehr angezeigt!",useInApiBrowser:"Im API-Browser verwenden",createNewApiKey:"Neuen API-Schlüssel erstellen",keyName:"Schlüsselname",keyNamePlaceholder:"z.B. Home Assistant, OctoPrint",readStatus:"Status lesen",readStatusDescription:"Druckerstatus und Warteschlange anzeigen",manageQueue:"Warteschlange verwalten",manageQueueDescription:"Elemente zur Druckwarteschlange hinzufügen und entfernen",controlPrinter:"Drucker steuern",controlPrinterDescription:"Drucke pausieren, fortsetzen und stoppen",unnamedKey:"Unbenannter Schlüssel",lastUsed:"Zuletzt verwendet",read:"Lesen",control:"Steuern",createFirstKey:"Ersten Schlüssel erstellen",webhookEndpoints:"Webhook-Endpunkte",webhookApiKeyHint:"Verwenden Sie Ihren API-Schlüssel im X-API-Key-Header.",webhook:{getAllStatus:"Alle Druckerstatus abrufen",getSpecificStatus:"Spezifischen Druckerstatus abrufen",addToQueue:"Zur Druckwarteschlange hinzufügen",pausePrint:"Druck pausieren",resumePrint:"Druck fortsetzen",stopPrint:"Druck stoppen"},apiBrowser:"API-Browser",apiBrowserDescription:"Erkunden und testen Sie alle verfügbaren API-Endpunkte.",apiKeyForTesting:"API-Schlüssel zum Testen",apiKeyPlaceholder:"Fügen Sie hier Ihren API-Schlüssel ein, um authentifizierte Endpunkte zu testen...",apiKeyHint:"Dieser Schlüssel wird als X-API-Key-Header mit Anfragen gesendet.",deleteApiKeyTitle:"API-Schlüssel löschen",deleteApiKeyMessage:"Möchten Sie diesen API-Schlüssel wirklich löschen? Alle Integrationen, die diesen Schlüssel verwenden, funktionieren nicht mehr.",deleteKey:"Schlüssel löschen",amsDisplayThresholds:"AMS-Anzeigeschwellenwerte",amsThresholdsDescription:"Konfigurieren Sie Farbschwellenwerte für AMS-Feuchtigkeits- und Temperaturanzeigen.",humidity:"Luftfeuchtigkeit",goodGreen:"Gut (grün)",fairOrange:"Mittel (orange)",aboveFairBad:"Über dem mittleren Schwellenwert wird rot angezeigt (schlecht)",fairAlsoDryingThreshold:"Dieser Schwellenwert wird auch für die automatische Trocknung verwendet",temperature:"Temperatur",goodBlue:"Gut (blau)",aboveFairHot:"Über dem mittleren Schwellenwert wird rot angezeigt (heiß)",historyRetention:"Verlaufsaufbewahrung",keepSensorHistory:"Sensorverlauf behalten für",historyRetentionDescription:"Ältere Feuchtigkeits- und Temperaturdaten werden automatisch gelöscht",defaultPrintOptions:"Standard-Druckoptionen",defaultPrintOptionsDescription:"Standardwerte für Druckoptionen bei neuen Drucken festlegen. Diese können im Druckdialog pro Druck überschrieben werden.",defaultBedLevelling:"Bett-Nivellierung",defaultBedLevellingDesc:"Bett vor dem Druck automatisch nivellieren",defaultFlowCali:"Fluss-Kalibrierung",defaultFlowCaliDesc:"Extrusionsfluss kalibrieren",defaultVibrationCali:"Vibrationskalibrierung",defaultVibrationCaliDesc:"Ringing-Artefakte reduzieren",defaultLayerInspect:"Erste-Schicht-Inspektion",defaultLayerInspectDesc:"KI-Inspektion der ersten Schicht",defaultTimelapse:"Zeitraffer",defaultTimelapseDesc:"Zeitraffervideo aufnehmen",staggeredStart:"Staggered Start",staggeredStartDescription:"Default group size and interval when staggering multi-printer batch starts. Can be overridden per batch in the print modal.",plateClear:"Druckplatte-Bestätigung",requirePlateClear:"Druckplatte-Bestätigung erforderlich",requirePlateClearDescription:'Wenn aktiviert, wartet der Scheduler auf eine Druckplatten-Bestätigung pro Drucker, bevor geplante Drucke auf Druckern mit abgeschlossenen Aufträgen gestartet werden. Wenn dies deaktiviert ist, werden auch das Druckplatten-Status-Badge und die Schaltfläche "Druckplatte als freigegeben markieren" auf den Druckerkarten ausgeblendet.',gcodeInjection:"G-code Injection",gcodeInjectionDescription:'Konfigurieren Sie benutzerdefinierten G-code, der am Anfang und/oder Ende von Drucken für Auto-Print-Systeme wie Farmloop, SwapMod, AutoClear und Printflow 3D eingefügt wird. Snippets werden pro Druckermodell konfiguriert und angewendet, wenn "G-code einfügen" bei einem Warteschlangen-Element aktiviert ist.',gcodeInjectionNoPrinters:"Keine Drucker gefunden. Fügen Sie Drucker hinzu, um G-code-Snippets zu konfigurieren.",gcodeStartLabel:"Start G-code",gcodeEndLabel:"End G-code",gcodeStartPlaceholder:"G-code, der vor dem Druckstart eingefügt wird...",gcodeEndPlaceholder:"G-code, der nach dem Druckende angefügt wird...",staggerGroupSize:"Group size",staggerGroupSizeHelp:"Printers to start simultaneously per group",staggerInterval:"Interval (minutes)",staggerIntervalHelp:"Delay between each group starting",queueDrying:"Automatische Trocknung",queueDryingDescription:"AMS-Filament automatisch trocknen, wenn der Drucker zwischen Warteschlangen-Drucken im Leerlauf ist. Verwendet den Feuchtigkeitsschwellenwert oben.",queueDryingEnabled:"Automatische Trocknung aktivieren",queueDryingEnabledDescription:"AMS-Trocknung automatisch starten, wenn der Drucker im Leerlauf ist und die Feuchtigkeit über dem Schwellenwert liegt",queueDryingBlock:"Auf Trocknung warten",queueDryingBlockDescription:"Druckwarteschlange blockieren, bis die Trocknung abgeschlossen ist. Wenn aus, haben Drucke Vorrang.",ambientDryingEnabled:"Umgebungstrocknung",ambientDryingEnabledDescription:"Filament auf inaktiven Druckern automatisch trocknen, wenn die Luftfeuchtigkeit den Schwellenwert überschreitet — auch ohne Warteschlange.",dryingPresets:"Trocknungsvoreinstellungen",dryingPresetsDescription:"Temperatur und Dauer pro Filamenttyp. AMS 2 Pro verwendet niedrigere Temperaturen, AMS-HT unterstützt höhere.",dryingFilament:"Filament",printModal:"Druckdialog",expandCustomMapping:"Benutzerdefinierte Zuordnung standardmäßig erweitern",expandCustomMappingDescription:"Bei Druck auf mehrere Drucker die AMS-Zuordnung pro Drucker erweitert anzeigen",authentication:"Authentifizierung",authEnabledDescription:"Ihre Instanz ist mit Benutzerauthentifizierung gesichert",authDisabledDescription:"Aktivieren Sie die Anmeldepflicht und verwalten Sie den Benutzerzugriff",authDisabledMessage:"Aktivieren Sie die Authentifizierung, um Benutzerkonten zu erstellen, Berechtigungen zu verwalten und Ihre Bambuddy-Instanz zu sichern.",enableAuthentication:"Authentifizierung aktivieren",currentUser:"Aktueller Benutzer",changePassword:"Passwort ändern",admin:"Admin",users:"Benutzer",addUser:"Benutzer hinzufügen",groups:"Gruppen",addGroup:"Gruppe hinzufügen",system:"System",noDescription:"Keine Beschreibung",userCount:"{{count}} Benutzer",permissionCount:"{{count}} Berechtigungen",createUser:"Benutzer erstellen",username:"Benutzername",enterUsername:"Benutzername eingeben",password:"Passwort",enterPassword:"Passwort eingeben (min. 6 Zeichen)",confirmPassword:"Passwort bestätigen",confirmPasswordPlaceholder:"Passwort bestätigen",viewReleaseOnGitHub:"Release auf GitHub anzeigen",turnAllPlugsOn:"Alle Stecker einschalten",turnAllPlugsOff:"Alle Stecker ausschalten",clearNotificationLogs:"Benachrichtigungsprotokolle löschen",clearLogsMessage:"Dadurch werden alle Benachrichtigungsprotokolle, die älter als 30 Tage sind, dauerhaft gelöscht. Diese Aktion kann nicht rückgängig gemacht werden.",clearLogs:"Protokolle löschen",resetUiPreferences:"UI-Einstellungen zurücksetzen",resetUiPreferencesMessage:"Dadurch werden alle UI-Einstellungen auf Standardwerte zurückgesetzt: Seitenleisten-Reihenfolge, Theme, Dashboard-Layout, Ansichtsmodi und Sortiereinstellungen. Ihre Drucker, Archive und Servereinstellungen werden NICHT beeinträchtigt. Die Seite wird nach dem Löschen neu geladen.",resetPreferences:"Einstellungen zurücksetzen",deleteGroupTitle:"Gruppe löschen",deleteGroupMessage:"Möchten Sie diese Gruppe wirklich löschen? Benutzer in dieser Gruppe verlieren diese Berechtigungen.",deleteGroup:"Gruppe löschen",disableAuthenticationTitle:"Authentifizierung deaktivieren",disableAuthenticationMessage:"Möchten Sie die Authentifizierung wirklich deaktivieren? Dadurch wird Ihre Bambuddy-Instanz ohne Anmeldung zugänglich. Alle Benutzer bleiben in der Datenbank, aber die Authentifizierung wird deaktiviert.",disableAuthentication:"Authentifizierung deaktivieren",configureBambuddy:"Bambuddy konfigurieren",systemDefault:"Systemstandard",archiveSettings:"Archiv-Einstellungen",newWindow:"Neues Fenster",embeddedOverlay:"Eingebettetes Overlay",preferredSlicer:"Bevorzugter Slicer",preferredSlicerDescription:"Wähle die Slicer-Anwendung zum Öffnen von Dateien",externalCameras:"Externe Kameras",costTracking:"Kostenverfolgung",printsOnly:"Nur Drucke",totalConsumption:"Gesamtverbrauch",dataManagement:"Datenverwaltung",storageUsage:"Speichernutzung",storageUsageDescription:"Aufschlüsselung der Datennutzung nach Kategorie",storageUsageTotal:"Gesamt",storageUsageErrors:"Fehler",storageUsageOtherBreakdown:"Sonstiges (enthält statische Assets, Skripte und Konfigurationsdateien)",storageUsageSystem:"System",storageUsageData:"Daten",storageUsageUnavailable:"Speichernutzungsinformationen nicht verfügbar",clearNotificationLogsDescription:"Benachrichtigungsprotokolle älter als 30 Tage löschen",resetUiPreferencesDescription:"Seitenleisten-Reihenfolge, Theme, Ansichtsmodi und Layout-Einstellungen zurücksetzen. Drucker, Archive und Einstellungen werden nicht beeinflusst.",enableHomeAssistant:"Home Assistant aktivieren",enableMqtt:"MQTT aktivieren",useTls:"TLS verwenden",enableMetricsEndpoint:"Metrik-Endpunkt aktivieren",availableMetrics:"Verfügbare Metriken",editUser:"Benutzer bearbeiten",deleteUserTitle:"Benutzer löschen",groupName:"Gruppenname",leaveEmptyForAnonymous:"Leer lassen für anonym",leaveEmptyForNoAuth:"Leer lassen für keine Authentifizierung",enterNewPassword:"Neues Passwort eingeben",confirmNewPassword:"Neues Passwort bestätigen",enterGroupName:"Gruppenname eingeben",enterDescriptionOptional:"Beschreibung eingeben (optional)",enterCurrentPassword:"Aktuelles Passwort eingeben",enterNewPasswordMin6:"Neues Passwort eingeben (min. 6 Zeichen)",toast:{keyCopied:"Schlüssel in Zwischenablage kopiert",copyFailed:"Schlüssel konnte nicht kopiert werden",keyAddedToBrowser:"Schlüssel zum API-Browser hinzugefügt",clearLogsFailed:"Protokolle konnten nicht gelöscht werden",uiPreferencesReset:"UI-Einstellungen zurückgesetzt. Wird neu geladen...",authDisabled:"Authentifizierung erfolgreich deaktiviert",authDisableFailed:"Authentifizierung konnte nicht deaktiviert werden",apiKeyCreated:"API-Schlüssel erstellt",apiKeyDeleted:"API-Schlüssel gelöscht",userCreated:"Benutzer erfolgreich erstellt",userUpdated:"Benutzer erfolgreich aktualisiert",userDeleted:"Benutzer erfolgreich gelöscht",groupCreated:"Gruppe erfolgreich erstellt",groupUpdated:"Gruppe erfolgreich aktualisiert",groupDeleted:"Gruppe erfolgreich gelöscht",fillRequiredFields:"Bitte füllen Sie alle erforderlichen Felder aus",passwordsDoNotMatch:"Passwörter stimmen nicht überein",passwordTooShort:"Passwort muss mindestens 6 Zeichen lang sein",enterGroupName:"Bitte geben Sie einen Gruppennamen ein",settingsSaved:"Einstellungen gespeichert",cameraSettingsSaved:"Kamera-Einstellungen gespeichert",enterCameraUrl:"Bitte geben Sie eine Kamera-URL ein",passwordChanged:"Passwort erfolgreich geändert",connectionFailed:"Verbindung fehlgeschlagen",testFailed:"Test fehlgeschlagen",cameraConnected:"Kamera verbunden{{resolution}}"},testConnection:"Verbindung testen",catalog:{spoolCatalog:"Spulenkatalog",spoolCatalogDescription:"Leerspulengewichte nach Marke/Typ. Wird für die automatische Gewichtssuche beim Hinzufügen von Spulen verwendet.",searchCatalog:"Katalog durchsuchen...",addNewEntry:"Neuen Eintrag hinzufügen",namePlaceholder:"Name (z.B. Bambu Lab - Plastik)",weight:"Gewicht",type:"Typ",default:"Standard",custom:"Benutzerdefiniert",noMatch:"Keine Einträge entsprechen Ihrer Suche",empty:"Keine Einträge im Katalog",deleteEntry:"Eintrag löschen",deleteConfirm:'Möchten Sie "{{name}}" wirklich löschen?',resetCatalog:"Katalog zurücksetzen",resetConfirm:"Katalog auf Standardwerte zurücksetzen? Alle benutzerdefinierten Einträge werden entfernt.",loadFailed:"Spulenkatalog konnte nicht geladen werden",nameWeightRequired:"Name und Gewicht sind erforderlich",entryAdded:"Eintrag hinzugefügt",addFailed:"Eintrag konnte nicht hinzugefügt werden",entryUpdated:"Eintrag aktualisiert",updateFailed:"Eintrag konnte nicht aktualisiert werden",entryDeleted:"Eintrag gelöscht",deleteFailed:"Eintrag konnte nicht gelöscht werden",resetSuccess:"Katalog auf Standardwerte zurückgesetzt",resetFailed:"Katalog konnte nicht zurückgesetzt werden",exported:"{{count}} Einträge exportiert",imported:"{{added}} Einträge importiert ({{skipped}} übersprungen)",importFailed:"Import fehlgeschlagen: ungültiges JSON-Format",exportTooltip:"Katalog als JSON exportieren",importTooltip:"Katalog aus JSON importieren",resetTooltip:"Auf Standardwerte zurücksetzen",selectedCount:"{{count}} ausgewählt",deleteSelected:"Ausgewählte löschen",bulkDeleteConfirm:"Möchten Sie {{count}} Einträge wirklich löschen?",bulkDeleted:"{{count}} Einträge gelöscht",bulkDeleteFailed:"Fehler beim Löschen der Einträge"},colorCatalog:{title:"Farbkatalog",description:"Filamentfarben nach Hersteller/Material. Wird für die automatische Farbsuche beim Hinzufügen von Spulen verwendet.",searchColors:"Farben durchsuchen...",allManufacturers:"Alle Hersteller",addNewColor:"Neue Farbe hinzufügen",manufacturer:"Hersteller",colorName:"Farbname",hex:"Hex",materialOptional:"Material (optional)",showing:"{{filtered}} von {{total}} Farben angezeigt",noMatch:"Keine Farben entsprechen Ihrer Suche",empty:"Keine Farben im Katalog",deleteColor:"Farbe löschen",deleteConfirm:'Möchten Sie "{{name}}" wirklich löschen?',resetCatalog:"Farbkatalog zurücksetzen",resetConfirm:"Katalog auf Standardwerte zurücksetzen? Alle benutzerdefinierten Farben werden entfernt.",sync:"Sync",starting:"Starten...",syncTooltip:"Von FilamentColors.xyz synchronisieren (2000+ Farben)",loadFailed:"Farbkatalog konnte nicht geladen werden",fieldsRequired:"Hersteller, Farbname und Hex-Farbe sind erforderlich",colorAdded:"Farbe hinzugefügt",addFailed:"Farbe konnte nicht hinzugefügt werden",colorUpdated:"Farbe aktualisiert",updateFailed:"Farbe konnte nicht aktualisiert werden",colorDeleted:"Farbe gelöscht",deleteFailed:"Farbe konnte nicht gelöscht werden",resetSuccess:"Farbkatalog auf Standardwerte zurückgesetzt",resetFailed:"Katalog konnte nicht zurückgesetzt werden",syncUpToDate:"Bereits aktuell ({{count}} Farben geprüft)",syncComplete:"{{added}} neue Farben hinzugefügt ({{skipped}} bereits vorhanden)",syncError:"Sync-Fehler",syncFailed:"Synchronisierung von FilamentColors.xyz fehlgeschlagen",exported:"{{count}} Farben exportiert",imported:"{{added}} Farben importiert ({{skipped}} übersprungen)",importFailed:"Import fehlgeschlagen: ungültiges JSON-Format",selectedCount:"{{count}} ausgewählt",deleteSelected:"Ausgewählte löschen",bulkDeleteConfirm:"Möchten Sie {{count}} Farben wirklich löschen?",bulkDeleted:"{{count}} Farben gelöscht",bulkDeleteFailed:"Fehler beim Löschen der Farben"},dateFormat:"Datumsformat",dateFormatUs:"US (MM/TT/JJJJ)",dateFormatEu:"EU (TT/MM/JJJJ)",dateFormatIso:"ISO (JJJJ-MM-TT)",timeFormat:"Zeitformat",timeFormat12:"12-Stunden (3:30 PM)",timeFormat24:"24-Stunden (15:30)",defaultPrinter:"Standarddrucker",defaultPrinterDescription:"Diesen Drucker für Uploads, Nachdrucke und andere Vorgänge vorauswählen.",slicerBambuStudio:"Bambu Studio",slicerOrcaSlicer:"OrcaSlicer",sidebarOrderDescription:"Elemente in der Seitenleiste per Drag & Drop neu anordnen. Hier auf Standardreihenfolge zurücksetzen.",setDefault:"Standard setzen",sidebarOrderSetDefaultHint:"Standard setzen übernimmt die aktuelle Menüreihenfolge für Benutzer, die ihre noch nicht angepasst haben.",sidebarDefaultSet:"Standard-Menüreihenfolge wurde festgelegt.",sidebarDefaultCleared:"Standard-Menüreihenfolge gelöscht.",sidebarDefaultFailed:"Festlegen der Standard-Menüreihenfolge fehlgeschlagen.",reset:"Zurücksetzen",darkMode:"Dunkelmodus",lightMode:"Hellmodus",active:"(aktiv)",background:"Hintergrund",accent:"Akzent",style:"Stil",bgNeutral:"Neutral",bgWarm:"Warm",bgCool:"Kühl",bgOled:"OLED Schwarz",bgSlate:"Schieferblau",bgForest:"Waldgrün",accentGreen:"Grün",accentTeal:"Türkis",accentBlue:"Blau",accentOrange:"Orange",accentPurple:"Lila",accentRed:"Rot",styleClassic:"Klassisch",styleGlow:"Leuchtend",styleVibrant:"Lebendig",themeToggleHint:"Zwischen Hell- und Dunkelmodus mit dem Sonnen-/Mondsymbol in der Seitenleiste wechseln.",autoArchivePrints:"Drucke automatisch archivieren",autoArchiveDescription:"3MF-Dateien automatisch speichern, wenn Drucke abgeschlossen sind",saveThumbnailsDescription:"Vorschaubilder aus 3MF-Dateien extrahieren und speichern",captureFinishPhotoDescription:"Foto von der Druckerkamera aufnehmen, wenn der Druck abgeschlossen ist",ffmpegNotInstalled:"ffmpeg nicht installiert",ffmpegRequired:"Kameraaufnahme benötigt ffmpeg. Installieren über brew install ffmpeg (macOS) oder apt install ffmpeg (Linux).",camera:"Kamera",cameraViewMode:"Kamera-Ansichtsmodus",cameraOverlayDescription:"Kamera öffnet sich als größenveränderbares Overlay auf dem Hauptbildschirm",cameraWindowDescription:"Kamera öffnet sich in einem separaten Browserfenster",externalCamerasDescription:"Externe Kameras konfigurieren, um die eingebaute Druckerkamera zu ersetzen. Unterstützt MJPEG-Streams, RTSP, HTTP-Snapshots und USB-Kameras (V4L2). Wenn aktiviert, wird die externe Kamera für Live-Ansicht und Abschlussfotos verwendet.",cameraPlaceholderUsb:"Gerätepfad (/dev/video0)",cameraPlaceholderUrl:"Kamera-URL (rtsp://... oder http://...)",cameraTypeMjpeg:"MJPEG-Stream",cameraTypeRtsp:"RTSP-Stream",cameraTypeSnapshot:"HTTP-Snapshot",cameraTypeUsb:"USB-Kamera (V4L2)",cameraRotation:"Drehung",test:"Testen",connected:"Verbunden",disconnected:"Getrennt",currency:"Währung",defaultFilamentCost:"Standard-Filamentkosten (pro kg)",electricityCost:"Stromkosten pro kWh",energyDisplayMode:"Energieanzeige-Modus",energyModePrintDescription:"Dashboard zeigt Summe der während Drucken verbrauchten Energie",energyModeTotalDescription:"Dashboard zeigt Gesamtenergie der Smart Plugs",fileManager:"Dateimanager",createArchiveEntry:"Archiveintrag beim Drucken erstellen",createArchiveEntryDescription:"Beim Drucken aus dem Dateimanager optional einen Archiveintrag erstellen",lowDiskSpaceWarning:"Warnung bei wenig Speicherplatz",lowDiskSpaceDescription:"Warnung anzeigen, wenn freier Speicherplatz unter diesen Schwellenwert fällt",printerFirmware:"Drucker-Firmware",checkFirmwareDescription:"Nach Firmware-Updates von Bambu Lab suchen",bambuddySoftware:"Bambuddy Software",autoCheckDescription:"Automatisch beim Start nach neuen Versionen suchen",checkNow:"Jetzt prüfen",updateAvailableVersion:"Update verfügbar: v{{version}}",releaseNotes:"Versionshinweise",updateViaDocker:"Update über Docker Compose:",installUpdate:"Update installieren",latestVersionRunning:"Sie verwenden die neueste Version",failedToCheckUpdates:"Update-Prüfung fehlgeschlagen: {{error}}",backupRestore:"Sicherung & Wiederherstellung",backupRestoreDescription:"Einstellungen exportieren/importieren und GitHub-Backup konfigurieren",goToBackup:"Zur Sicherung",externalUrl:"Externe URL",externalUrlDescription:"Die externe URL, unter der Bambuddy erreichbar ist. Wird für Benachrichtigungsbilder und externe Integrationen verwendet.",bambuddyUrl:"Bambuddy-URL",externalUrlHint:"Protokoll und Port angeben (z.B. http://192.168.1.100:8000)",ftpRetry:"FTP-Wiederholung",ftpRetryDescription:"FTP-Operationen bei unzuverlässigem Drucker-WLAN wiederholen. Gilt für 3MF-Downloads, Druck-Uploads, Zeitraffer-Downloads und Firmware-Updates.",autoRetryDescription:"Fehlgeschlagene FTP-Operationen automatisch wiederholen",retryAttempts:"Wiederholungsversuche",retryDelay:"Wiederholungsverzögerung",connectionTimeout:"Verbindungs-Timeout",time_one:"{{count}} Mal",time_other:"{{count}} Mal",second_one:"{{count}} Sekunde",second_other:"{{count}} Sekunden",nSeconds:"{{count}} Sekunden",increaseForWeakWifi:"Erhöhen für Drucker mit schwachem WLAN",homeAssistant:"Home Assistant",homeAssistantFullDescription:"Mit Home Assistant verbinden, um Smart Plugs über die HA REST-API zu steuern. Unterstützt Switch-, Light-, Input_Boolean- und Script-Entitäten.",homeAssistantUrl:"Home Assistant URL",longLivedAccessToken:"Langlebiges Zugriffstoken",haTokenHint:"Token in HA erstellen: Profil → Langlebige Zugriffstoken → Token erstellen",connectionSuccessful:"Verbindung erfolgreich",connectionFailed:"Verbindung fehlgeschlagen",haConnectionSuccess:"Erfolgreich mit Home Assistant verbunden.",haConnectionFailed:"Verbindung zu Home Assistant fehlgeschlagen.",mqttPublishing:"MQTT-Veröffentlichung",mqttDescription:"BamBuddy-Ereignisse an einen externen MQTT-Broker zur Integration mit Node-RED, Home Assistant und anderen Automatisierungssystemen veröffentlichen.",mqttEnableDescription:"Ereignisse an externen MQTT-Broker veröffentlichen",brokerHostname:"Broker-Hostname",port:"Port",usernameOptional:"Benutzername (optional)",passwordOptional:"Passwort (optional)",topicPrefix:"Topic-Präfix",topicPrefixHint:"Topics werden sein: {{prefix}}/printers//status, etc.",prometheusMetrics:"Prometheus-Metriken",prometheusEndpointDescription:"Druckermetriken unter /api/v1/metrics für Prometheus/Grafana-Überwachung bereitstellen.",bearerTokenOptional:"Bearer-Token (optional)",bearerTokenHint:"Wenn gesetzt, müssen Anfragen Authorization: Bearer enthalten",metricsConnectionStatus:"Verbindungsstatus",metricsPrinterState:"Druckerstatus (idle/printing/etc)",metricsPrintProgress:"Druckfortschritt 0-100%",metricsBedTemp:"Betttemperatur",metricsNozzleTemp:"Düsentemperatur",metricsPrintsTotal:"Gesamtdrucke nach Ergebnis",metricsMore:"...und mehr (Schichten, Lüfter, Warteschlange, Filamentverbrauch)",smartPlugsDescription:"Smart Plugs (Tasmota oder Home Assistant) verbinden, um Stromsteuerung zu automatisieren und Energieverbrauch für Ihre Drucker zu verfolgen.",allOn:"Alle Ein",allOff:"Alle Aus",addSmartPlug:"Smart Plug hinzufügen",energySummary:"Energieübersicht",currentPower:"Aktuelle Leistung",plugsOnline:"{{reachable}}/{{total}} Plugs online",today:"Heute",yesterday:"Gestern",total:"Gesamt",enablePlugsForSummary:"Plugs aktivieren, um Energieübersicht zu sehen",addNotificationProvider:"Hinzufügen",systemBadge:"(System)",creating:"Erstellen...",changing:"Ändern...",deleteUserAndItems:"Benutzer UND dessen Elemente löschen",deleteUserKeepItems:"Benutzer löschen, Elemente behalten (werden herrenlos)",ok:"OK",twoFa:{totpTitle:"Authenticator-App (TOTP)",totpDesc:"Verwende eine Authenticator-App wie Google Authenticator, Aegis oder Authy.",emailOtpTitle:"E-Mail OTP",emailOtpDesc:"Sende einen Einmalcode an {{email}} beim Einloggen.",emailOtpNoEmail:"Füge eine E-Mail-Adresse zu deinem Konto hinzu, um diese Methode zu aktivieren.",addEmailFirst:"Dein Konto hat keine E-Mail-Adresse. Bitte einen Administrator, eine hinzuzufügen.",setupTotp:"Authenticator-App einrichten",setupAuthApp:"Authenticator-App einrichten",setupInstructions:"Scanne den QR-Code mit deiner Authenticator-App und bestätige mit einem Code.",manualEntry:"Kein Scanner? Gib dieses Secret manuell ein:",scannedContinue:"Code gescannt — weiter",enterCodeToConfirm:"Gib den 6-stelligen Code aus deiner Authenticator-App ein, um die Einrichtung zu bestätigen.",activate:"Aktivieren",disableTotp:"Authenticator deaktivieren",disableConfirmHint:"Gib einen gültigen TOTP-Code oder einen Backup-Code ein, um den Authenticator zu deaktivieren.",totpDisabled:"Authenticator-App deaktiviert.",emailOtpEnabled:"E-Mail OTP aktiviert.",emailOtpDisabled:"E-Mail OTP deaktiviert.",smtpRequired:"Bitte konfigurieren und testen Sie zuerst die SMTP-Einstellungen.",invalidCode:"Ungültiger Code. Bitte erneut versuchen.",enableEmailOtp:"E-Mail OTP aktivieren",disableEmailOtp:"E-Mail OTP deaktivieren",emailSetupEnterCode:"Ein Bestätigungscode wurde an Ihre E-Mail-Adresse gesendet. Geben Sie ihn unten ein, um zu bestätigen, dass Ihnen dieses Postfach gehört.",verifyAndEnable:"Verifizieren & Aktivieren",emailDisablePasswordHint:"Geben Sie Ihr Kontopasswort ein, um die Deaktivierung des E-Mail OTP zu bestätigen.",passwordPlaceholder:"Passwort eingeben",backupCodesTitle:"Backup-Codes sichern",backupCodesWarning:"Speichere diese Codes sicher. Jeder Code kann nur einmal verwendet werden und wird nicht erneut angezeigt.",backupCodesRemaining:"{{count}} Backup-Codes verbleibend",savedCodes:"Codes gespeichert",regenBackup:"Backup-Codes neu generieren",regenBackupHint:"Gib deinen aktuellen TOTP-Code ein, um 10 neue Backup-Codes zu generieren. Alle bestehenden Codes werden ungültig.",newBackupCodes:"Neue Backup-Codes",linkedAccounts:"Verknüpfte SSO-Konten",linkedAccountsDesc:"Diese externen Identitätsanbieter sind mit deinem Konto verknüpft.",oidcUnlinked:"Konto getrennt."},oidc:{title:"SSO / OIDC-Anbieter",desc:"Konfiguriere OpenID Connect-Anbieter für Single Sign-On.",addProvider:"Anbieter hinzufügen",newProvider:"Neuer Anbieter",empty:"Noch keine OIDC-Anbieter konfiguriert.",created:"Anbieter erstellt.",updated:"Anbieter aktualisiert.",deleted:"Anbieter gelöscht.",deleteTitle:"Anbieter löschen",deleteMessage:'"{{name}}" löschen? Alle verknüpften Benutzerkonten werden getrennt.',form:{name:"Anzeigename",issuerUrl:"Aussteller-URL",clientId:"Client-ID",clientSecret:"Client-Secret",scopes:"Scopes",iconUrl:"Symbol-URL (optional)",enabled:"Aktiviert",autoCreate:"Benutzer automatisch anlegen",autoCreateDesc:"Erstellt beim ersten Login automatisch ein lokales Konto.",autoLink:"Bestehende Konten automatisch verknüpfen",autoLinkDesc:"Verknüpft beim ersten Login vorhandene lokale Konten anhand der E-Mail-Adresse.",secretHint:"leer lassen zum Beibehalten",secretPlaceholder:"neues Secret"}}},notification:{printStarted:{title:"Druck gestartet",body:"{{printer}}: {{filename}} wird gedruckt"},printCompleted:{title:"Druck abgeschlossen",body:"{{printer}}: {{filename}} erfolgreich abgeschlossen"},printFailed:{title:"Druck fehlgeschlagen",body:"{{printer}}: {{filename}} ist fehlgeschlagen"},printStopped:{title:"Druck gestoppt",body:"{{printer}}: {{filename}} wurde gestoppt"},printProgress:{title:"Druckfortschritt",body:"{{printer}}: {{filename}} ist zu {{percent}}% abgeschlossen"},printerOffline:{title:"Drucker offline",body:"{{printer}} ist offline"},printerError:{title:"Druckerfehler",body:"{{printer}}: {{error}}"},filamentLow:{title:"Filament niedrig",body:"{{printer}}: Filament geht zur Neige"},maintenanceDue:{title:"Wartung fällig",body:"{{printer}}: {{items}} benötigen Aufmerksamkeit"}},errors:{generic:"Etwas ist schiefgelaufen",networkError:"Netzwerkfehler. Bitte überprüfen Sie Ihre Verbindung.",notFound:"Nicht gefunden",unauthorized:"Nicht autorisiert",serverError:"Serverfehler",validationError:"Bitte überprüfen Sie Ihre Eingabe",printerConnectionFailed:"Verbindung zum Drucker fehlgeschlagen",saveFailed:"Speichern fehlgeschlagen",deleteFailed:"Löschen fehlgeschlagen",loadFailed:"Laden der Daten fehlgeschlagen"},hmsErrors:{title:"Fehler - {{name}}",noErrors:"Keine Fehler",viewOnWiki:"Im Bambu Lab Wiki ansehen",clearInstructions:"Löschen Sie die Fehler am Drucker, um sie hier zu entfernen.",clearErrors:"Fehler löschen",clearSuccess:"HMS-Fehler gelöscht",clearFailed:"HMS-Fehler konnten nicht gelöscht werden"},mqttDebug:{title:"MQTT-Debug-Protokoll",searchPlaceholder:"Topic oder Payload suchen...",noMessages:"Noch keine Nachrichten protokolliert",startLoggingHint:'Klicken Sie auf "Protokollierung starten", um MQTT-Nachrichten aufzuzeichnen',noMessagesMatch:"Keine Nachrichten entsprechen Ihrem Filter",adjustFilterHint:"Versuchen Sie, Ihre Such- oder Filterkriterien anzupassen",incoming:"Eingehend",outgoing:"Ausgehend",loggingStopped:"Protokollierung gestoppt",loggingActive:"Protokollierung aktiv - Nachrichten werden automatisch aktualisiert",startLogging:"Protokollierung starten",stopLogging:"Protokollierung stoppen",clearLog:"Protokoll löschen",topic:"Topic",timestamp:"Zeitstempel",direction:"Richtung",all:"Alle"},printerFiles:{title:"Dateimanager",storageUsed:"Belegt:",storageFree:"Frei:",filterPlaceholder:"Dateien filtern...",deleteButton:"Löschen",deleteFiles:"{{count}} Dateien löschen",deleteFileConfirm:'"{{name}}" löschen? Dies kann nicht rückgängig gemacht werden.',deleteFilesConfirm:"{{count}} ausgewählte Dateien löschen? Dies kann nicht rückgängig gemacht werden.",noFiles:"Keine Dateien auf dem Drucker",loadingFiles:"Dateien werden geladen...",failedToLoad:"Dateien konnten nicht geladen werden",toast:{filesDeleted:"{{count}} Datei(en) gelöscht",deleteFailed:"Löschen fehlgeschlagen: {{error}}"}},confirm:{delete:"Möchten Sie dies wirklich löschen?",unsavedChanges:"Sie haben ungespeicherte Änderungen. Möchten Sie wirklich verlassen?",clearQueue:"Möchten Sie die Warteschlange wirklich leeren?"},login:{title:"Bambuddy Anmeldung",subtitle:"Melden Sie sich bei Ihrem Konto an",username:"Benutzername",usernamePlaceholder:"Benutzername eingeben",usernameOrEmail:"Benutzername oder E-Mail",usernameOrEmailPlaceholder:"Benutzername oder @ E-Mail",password:"Passwort",passwordPlaceholder:"Passwort eingeben",signIn:"Anmelden",signingIn:"Anmeldung läuft...",forgotPassword:"Passwort vergessen?",loginSuccess:"Erfolgreich angemeldet",loginFailed:"Anmeldung fehlgeschlagen",enterCredentials:"Bitte Benutzername und Passwort eingeben",enterEmail:"Bitte geben Sie Ihre E-Mail-Adresse ein",oidcLoginFailed:"OIDC-Anmeldung fehlgeschlagen",oidcErrors:{providerError:"Der Identity-Provider hat einen Fehler zurückgegeben",missingParameters:"Dem OIDC-Callback fehlen erforderliche Parameter",invalidState:"OIDC-State ist ungültig oder wurde bereits verwendet",stateExpired:"OIDC-Sitzung abgelaufen — bitte erneut versuchen",providerNotFound:"OIDC-Provider nicht gefunden",discoveryFailed:"OIDC-Discovery-Dokument konnte nicht abgerufen werden",invalidDiscovery:"OIDC-Discovery-Dokument ist ungültig",networkError:"Netzwerkfehler beim OIDC-Token-Austausch",badResponse:"Unerwartete Antwort beim OIDC-Token-Austausch",noIdToken:"OIDC-Provider hat kein ID-Token zurückgegeben",validationFailed:"OIDC-Token-Validierung fehlgeschlagen",nonceMismatch:"OIDC-Nonce stimmt nicht überein — möglicher Replay-Angriff",missingSubClaim:"OIDC-Token enthält keinen Sub-Claim",noLinkedAccount:"Kein lokales Konto mit dieser OIDC-Identität verknüpft",accountInactive:"Ihr Konto ist inaktiv",userResolutionFailed:"Ihr Konto konnte nicht aufgelöst werden",internalError:"Interner Fehler beim OIDC-Login",tokenExchangeFailed:"OIDC-Token-Austausch fehlgeschlagen"},forgotPasswordTitle:"Passwort vergessen",forgotPasswordMessage:"Wenn Sie Ihr Passwort vergessen haben, wenden Sie sich bitte an Ihren Systemadministrator.",forgotPasswordEmailMessage:"Geben Sie Ihre E-Mail-Adresse ein und wir senden Ihnen ein neues Passwort.",emailAddress:"E-Mail-Adresse",emailPlaceholder:"ihre.email@beispiel.de",cancel:"Abbrechen",sending:"Wird gesendet...",sendResetEmail:"Zurücksetzungs-E-Mail senden",howToReset:"So setzen Sie Ihr Passwort zurück:",resetStep1:"Kontaktieren Sie Ihren Bambuddy-Administrator",resetStep2:"Bitten Sie ihn, Ihr Passwort in der Benutzerverwaltung zurückzusetzen",resetStep3:"Er kann ein neues temporäres Passwort für Sie festlegen",resetStep4:"Melden Sie sich mit dem neuen Passwort an und ändern Sie es in den Einstellungen",gotIt:"Verstanden",resetPassword:{title:"Neues Passwort festlegen",subtitle:"Geben Sie unten Ihr neues Passwort ein und bestätigen Sie es.",newPassword:"Neues Passwort",newPasswordPlaceholder:"Mindestens 8 Zeichen",confirmPassword:"Passwort bestätigen",confirmPasswordPlaceholder:"Neues Passwort wiederholen",saving:"Wird gespeichert…",submit:"Neues Passwort festlegen",backToLogin:"Zurück zur Anmeldung",passwordsDoNotMatch:"Passwörter stimmen nicht überein",passwordTooShort:"Passwort muss mindestens 8 Zeichen lang sein",resetFailed:"Passwort zurücksetzen fehlgeschlagen. Der Link ist möglicherweise abgelaufen."},twoFA:{title:"Zwei-Faktor-Authentifizierung",subtitle:"Ihr Konto ist mit 2FA geschützt. Geben Sie unten den Bestätigungscode ein.",methodAuthenticator:"Authenticator-App",methodEmail:"E-Mail-Code",methodBackup:"Wiederherstellungscode",instructionsTotp:"Öffnen Sie Ihre Authenticator-App und geben Sie den 6-stelligen Code für Bambuddy ein.",instructionsEmail:"Ein 6-stelliger Code wurde an Ihre E-Mail-Adresse gesendet. Er ist 10 Minuten gültig.",instructionsEmailNotSent:"Klicken Sie unten, um einen Bestätigungscode per E-Mail zu erhalten.",instructionsBackup:"Geben Sie einen Ihrer 8-stelligen Wiederherstellungscodes ein. Jeder Code kann nur einmal verwendet werden.",sendCodeButton:"Code per E-Mail senden",sendingCode:"Wird gesendet...",resendCode:"Code erneut senden",codeLabel:"Bestätigungscode",backupCodeLabel:"Wiederherstellungscode",codePlaceholder:"000000",backupCodePlaceholder:"XXXXXXXX",verifyButton:"Bestätigen",verifyingButton:"Wird überprüft...",backToLogin:"← Zurück zur Anmeldung",orContinueWith:"oder anmelden mit",signInWith:"Anmelden mit {{provider}}",enterCode:"Bitte geben Sie den Bestätigungscode ein",sendCodeFailed:"Bestätigungscode konnte nicht gesendet werden",invalidCode:"Ungültiger Code. Bitte erneut versuchen."}},setup:{title:"Bambuddy Einrichtung",subtitle:"Konfigurieren Sie die Authentifizierung für Ihre Bambuddy-Instanz",enableAuth:"Authentifizierung aktivieren",adminAccount:"Admin-Konto",adminAccountDesc:"Wenn bereits Admin-Benutzer existieren, wird die Authentifizierung mit den vorhandenen Admin-Konten aktiviert. Lassen Sie die Felder unten leer, um vorhandene Admins zu verwenden, oder geben Sie neue Anmeldedaten ein, um einen neuen Admin-Benutzer zu erstellen.",adminUsername:"Admin-Benutzername",adminPassword:"Admin-Passwort",optionalIfAdminExists:"(optional, wenn Admin-Benutzer existieren)",adminUsernamePlaceholder:"Admin-Benutzernamen eingeben (optional)",adminPasswordPlaceholder:"Admin-Passwort eingeben (optional)",confirmPassword:"Passwort bestätigen",confirmPasswordPlaceholder:"Admin-Passwort bestätigen",settingUp:"Einrichtung läuft...",completeSetup:"Einrichtung abschließen",toast:{authEnabledAdminCreated:"Authentifizierung aktiviert und Admin-Benutzer erstellt",authEnabledExistingAdmins:"Authentifizierung mit vorhandenen Admin-Benutzern aktiviert",setupCompleted:"Einrichtung abgeschlossen",enterBothCredentials:"Bitte geben Sie sowohl Admin-Benutzernamen als auch Passwort ein, oder lassen Sie beide leer, um vorhandene Admin-Benutzer zu verwenden",passwordsDoNotMatch:"Passwörter stimmen nicht überein",passwordTooShort:"Passwort muss mindestens 6 Zeichen lang sein"}},changePassword:{title:"Passwort ändern",currentPassword:"Aktuelles Passwort",currentPasswordPlaceholder:"Aktuelles Passwort eingeben",newPassword:"Neues Passwort",newPasswordPlaceholder:"Neues Passwort eingeben (min. 6 Zeichen)",confirmPassword:"Neues Passwort bestätigen",confirmPasswordPlaceholder:"Neues Passwort bestätigen",passwordsDoNotMatch:"Passwörter stimmen nicht überein",passwordTooShort:"Passwort muss mindestens 6 Zeichen lang sein",changing:"Wird geändert...",success:"Passwort erfolgreich geändert",failed:"Passwortänderung fehlgeschlagen"},plateAlert:{title:"Druck pausiert!",message:"Objekte auf dem Druckbett erkannt. Der Druck wurde automatisch pausiert. Bitte räumen Sie das Druckbett und setzen Sie den Druck fort.",understand:"Verstanden"},camera:{title:"Kameraansicht",invalidPrinterId:"Ungültige Drucker-ID",live:"Live",snapshot:"Schnappschuss",restartStream:"Stream neu starten",refreshSnapshot:"Schnappschuss aktualisieren",fullscreen:"Vollbild",exitFullscreen:"Vollbild beenden",connectingToCamera:"Verbinde mit Kamera...",capturingSnapshot:"Schnappschuss wird aufgenommen...",connectionLost:"Verbindung verloren",connectionFailed:"Kameraverbindung fehlgeschlagen",reconnecting:"Neuverbindung in {{countdown}}s... (Versuch {{attempt}}/{{max}})",reconnectNow:"Jetzt verbinden",cameraUnavailable:"Kamera nicht verfügbar",cameraUnavailableDesc:"Stellen Sie sicher, dass der Drucker eingeschaltet und verbunden ist.",noCamera:"Keine Kamera verfügbar",retry:"Erneut versuchen",cameraStream:"Kamera-Stream",zoomOut:"Verkleinern",zoomIn:"Vergrößern",resetZoom:"Zoom zurücksetzen",recording:"Aufnahme",startRecording:"Aufnahme starten",stopRecording:"Aufnahme stoppen",chamberLight:"Kammerbeleuchtung umschalten"},groups:{title:"Gruppenverwaltung",subtitle:"Berechtigungsgruppen für Zugriffskontrolle verwalten",backToSettings:"Zurück zu Einstellungen",createGroup:"Gruppe erstellen",noPermission:"Sie haben keine Berechtigung, auf diese Seite zuzugreifen.",system:"System",noDescription:"Keine Beschreibung",usersCount:"{{count}} Benutzer",permissionsCount:"{{count}} Berechtigungen",edit:"Bearbeiten",delete:"Löschen",toast:{created:"Gruppe erfolgreich erstellt",updated:"Gruppe erfolgreich aktualisiert",deleted:"Gruppe erfolgreich gelöscht",enterGroupName:"Bitte geben Sie einen Gruppennamen ein"},modal:{editGroup:"Gruppe bearbeiten",createGroup:"Gruppe erstellen",cancel:"Abbrechen",saving:"Speichern...",creating:"Erstellen...",saveChanges:"Änderungen speichern"},form:{groupName:"Gruppenname",groupNamePlaceholder:"Gruppennamen eingeben",systemGroupWarning:"Systemgruppennamen können nicht geändert werden",description:"Beschreibung",descriptionPlaceholder:"Beschreibung eingeben (optional)",permissions:"Berechtigungen ({{count}} ausgewählt)"},deleteModal:{title:"Gruppe löschen",message:"Sind Sie sicher, dass Sie diese Gruppe löschen möchten? Benutzer in dieser Gruppe verlieren diese Berechtigungen.",confirm:"Gruppe löschen"},editor:{title:"Gruppe bearbeiten",createTitle:"Gruppe erstellen",search:"Berechtigungen suchen...",selectAll:"Alle auswählen",clearAll:"Alle abwählen",permissionsSelected:"{{count}} ausgewählt",noResults:"Keine Berechtigungen entsprechen Ihrer Suche"}},users:{title:"Benutzerverwaltung",subtitle:"Benutzer und deren Zugriff auf Ihre Bambuddy-Instanz verwalten",backToSettings:"Zurück zu Einstellungen",createUser:"Benutzer erstellen",noPermission:"Sie haben keine Berechtigung, auf diese Seite zuzugreifen.",admin:"Admin",noGroups:"Keine Gruppen",active:"Aktiv",inactive:"Inaktiv",edit:"Bearbeiten",delete:"Löschen",system:"System",noGroupsAvailable:"Keine Gruppen verfügbar",table:{username:"Benutzername",groups:"Gruppen",status:"Status",actions:"Aktionen"},toast:{created:"Benutzer erfolgreich erstellt",updated:"Benutzer erfolgreich aktualisiert",deleted:"Benutzer erfolgreich gelöscht",fillRequired:"Bitte füllen Sie alle Pflichtfelder aus",passwordsDoNotMatch:"Passwörter stimmen nicht überein",passwordTooShort:"Passwort muss mindestens 6 Zeichen lang sein"},modal:{createUser:"Benutzer erstellen",editUser:"Benutzer bearbeiten",cancel:"Abbrechen",creating:"Erstellen...",saving:"Speichern...",saveChanges:"Änderungen speichern",advancedAuthSubtitle:"mit erweiterter Authentifizierung"},form:{username:"Benutzername",usernamePlaceholder:"Benutzernamen eingeben",email:"E-Mail",emailPlaceholder:"benutzer@beispiel.de",password:"Passwort",passwordPlaceholder:"Passwort eingeben",confirmPassword:"Passwort bestätigen",confirmPasswordPlaceholder:"Passwort bestätigen",newPasswordPlaceholder:"Neues Passwort eingeben",confirmNewPasswordPlaceholder:"Neues Passwort bestätigen",leaveBlankToKeep:"leer lassen, um das aktuelle zu behalten",groups:"Gruppen",optional:"optional",autoGeneratedPassword:"Ein sicheres Passwort wird automatisch generiert und per E-Mail an den Benutzer gesendet.",passwordManagedByAdvancedAuth:'Das Passwort wird durch erweiterte Authentifizierung verwaltet. Verwenden Sie "Passwort zurücksetzen", um ein neues Passwort per E-Mail an den Benutzer zu senden.',resetPassword:"Passwort zurücksetzen",resettingPassword:"Passwort wird zurückgesetzt..."},deleteModal:{title:"Benutzer löschen",message:"Sind Sie sicher, dass Sie diesen Benutzer löschen möchten? Diese Aktion kann nicht rückgängig gemacht werden.",confirm:"Benutzer löschen"}},streamOverlay:{title:"Stream-Overlay",invalidPrinterId:"Ungültige Drucker-ID",cameraStream:"Kamera-Stream",progress:"Fortschritt",eta:"ETA",printerIdle:"Drucker ist inaktiv",printerOffline:"Drucker offline",status:{printing:"Druckt",paused:"Pausiert",finished:"Fertig",failed:"Fehlgeschlagen",idle:"Inaktiv",unknown:"Unbekannt"}},profiles:{title:"Profile",subtitle:"Verwalten Sie Ihre Slicer-Voreinstellungen und Druckvorschub-Kalibrierungen",tabs:{cloud:"Cloud-Profile",local:"Lokale Profile",kprofiles:"K-Profile"},localProfiles:{title:"Lokale Profile",subtitle:"Slicer-Voreinstellungen aus OrcaSlicer importieren und verwalten",import:"Profile importieren",importDesc:".bbscfg-, .bbsflmt-, .orca_filament-, .zip- oder .json-Dateien hier ablegen",importing:"Importiere...",search:"Lokale Voreinstellungen durchsuchen...",noPresets:"Noch keine lokalen Voreinstellungen",badge:"Lokal",edit:"Bearbeiten",delete:"Löschen",cancel:"Abbrechen",deleteConfirmTitle:"Voreinstellung löschen",deleteConfirm:"Möchten Sie diese Voreinstellung wirklich löschen? Dies kann nicht rückgängig gemacht werden.",source:"Quelle",inheritsFrom:"Erbt von",filamentType:"Typ",vendor:"Hersteller",compatiblePrinters:"Drucker",nozzleTemp:"Düsentemperatur",cost:"Kosten",density:"Dichte",pressureAdvance:"Druckvorschub",filament:"Filament",process:"Prozess",printer:"Drucker",toast:{importSuccess:"{{count}} Voreinstellung(en) importiert",importSkipped:"{{count}} Voreinstellung(en) übersprungen (Duplikate)",importError:"{{count}} Fehler beim Import",deleted:"Voreinstellung gelöscht",updated:"Voreinstellung aktualisiert"}},connectedAs:"Verbunden als",logout:"Abmelden",noLogoutPermission:"Sie haben keine Berechtigung zum Abmelden",failedToLoad:"Profile konnten nicht geladen werden",retry:"Erneut versuchen",time:{justNow:"Gerade eben",minsAgo:"vor {{count}}m",hoursAgo:"vor {{count}}h",daysAgo:"vor {{count}}d"},toast:{loggedOut:"Abgemeldet"},login:{title:"Mit Bambu Cloud verbinden",subtitle:"Synchronisieren Sie Ihre Slicer-Voreinstellungen geräteübergreifend",email:"E-Mail",password:"Passwort",region:"Region",regionGlobal:"Global",regionChina:"China",verificationCode:"Bestätigungscode",totpCode:"Authenticator-Code",checkEmail:"Prüfen Sie Ihre E-Mail ({{email}}) für einen 6-stelligen Code",enterTotpHint:"Geben Sie den 6-stelligen Code aus Ihrer Authenticator-App ein",accessToken:"Zugriffstoken",accessTokenHint:"Fügen Sie Ihr Bambu Lab Zugriffstoken ein (aus Bambu Studio)",back:"Zurück",loginButton:"Anmelden",verifyButton:"Bestätigen",setTokenButton:"Token setzen",useToken:"Stattdessen Zugriffstoken verwenden",useEmail:"Stattdessen mit E-Mail anmelden",toast:{loggedIn:"Erfolgreich angemeldet",codeSent:"Bestätigungscode an Ihre E-Mail gesendet",enterTotp:"Geben Sie den Code aus Ihrer Authenticator-App ein",tokenSet:"Token erfolgreich gesetzt"}},presets:{myPreset:"Mein Profil (bearbeitbar)",duplicate:"Duplizieren",editable:"Bearbeitbar",failedToLoadDetails:"Profil-Details konnten nicht geladen werden",deleteConfirm:"Dieses Profil löschen?",deleteWarning:'"{{name}}" wird dauerhaft aus Bambu Cloud gelöscht. Dies kann nicht rückgängig gemacht werden.',noDuplicatePermission:"Sie haben keine Berechtigung zum Duplizieren von Profilen",noEditPermission:"Sie haben keine Berechtigung zum Bearbeiten von Profilen",noDeletePermission:"Sie haben keine Berechtigung zum Löschen von Profilen",types:{filament:"Filament-Profil",printer:"Drucker-Profil",process:"Prozess-Profil"},toast:{deleted:"Profil gelöscht",created:"Profil erstellt",updated:"Profil aktualisiert",duplicated:"Profil dupliziert",fieldAdded:'Feld "{{key}}" hinzugefügt',exported:"Profil exportiert"},baseLabel:"Basis: {{name}}",currentLabel:"Aktuell: {{name}}",newPreset:"Neues Profil",editPreset:"Profil bearbeiten",duplicatePreset:"Profil duplizieren",createNewPreset:"Neues Profil erstellen",customizeSettings:"Passen Sie die Einstellungen für Ihr neues Profil an",compareWithBase:"Mit Basis-Profil vergleichen",compare:"Vergleichen",basePreset:"Basis-Profil",selectBasePreset:"Basis-Profil auswählen...",presetName:"Profilname",myCustomPreset:"Mein eigenes Profil",inheritsFrom:"Erbt von",dropJsonToImport:"JSON zum Importieren ablegen",tabs:{common:"Allgemein",allFields:"Alle Felder"},availableFields:"Verfügbare Felder",searchFieldsPlaceholder:"Felder suchen...",noMatchingFields:"Keine passenden Felder",allFieldsAdded:"Alle Felder hinzugefügt",addCustomField:"Eigenes Feld hinzufügen",yourOverrides:"Ihre Überschreibungen",noOverridesYet:"Noch keine Überschreibungen",clickFieldsToAdd:"Klicken Sie links auf Felder, um sie hinzuzufügen",saveAsTemplate:"Als Vorlage speichern",jsonTip:"Tipp: Ziehen Sie eine .json-Datei auf dieses Fenster, um Einstellungen zu importieren"},cloudView:{searchPlaceholder:"Profile suchen...",templates:"Vorlagen",refresh:"Aktualisieren",newPreset:"Neues Profil",clearFilters:"Filter zurücksetzen",compareMode:"Vergleichsmodus",selectAnotherPreset:"Wählen Sie ein weiteres {{type}}-Profil",clickTwoPresets:"Klicken Sie auf zwei Profile des gleichen Typs zum Vergleichen",selectFirst:"1. Erstes auswählen",selectSecond:"2. Zweites auswählen",compareNow:"Jetzt vergleichen",lastSynced:"Zuletzt synchronisiert:",showingCount:"{{showing}} von {{total}} Profilen",noPresetsFound:"Keine Profile gefunden",columns:{filament:"Filament",process:"Prozess",printer:"Drucker"},noFilamentPresets:"Keine Filament-Profile",noProcessPresets:"Keine Prozess-Profile",noPrinterPresets:"Keine Drucker-Profile",filters:{type:"Typ",owner:"Besitzer",printer:"Drucker",nozzle:"Düse",filament:"Filament",layer:"Schicht",all:"Alle",myPresets:"Meine Profile",builtIn:"Voreingestellt",process:"Prozess"},noTemplatesPermission:"Sie haben keine Berechtigung, Vorlagen zu verwalten",noRefreshPermission:"Sie haben keine Berechtigung, Profile zu aktualisieren",noCreatePermission:"Sie haben keine Berechtigung, Profile zu erstellen"},templates:{title:"Schnellvorlagen",noTemplates:"Noch keine Vorlagen",createFirst:"Erstellen Sie Vorlagen aus dem Preset-Editor",typeFilter:"Typ:",deleteTitle:"Vorlage löschen",deleteWarning:"Diese Aktion kann nicht rückgängig gemacht werden",deleteConfirm:'Möchten Sie "{{name}}" wirklich löschen?',namePlaceholder:"Vorlagenname",descriptionPlaceholder:"Beschreibung",settingsJson:"Einstellungen (JSON)",fieldsCount:"{{count}} Felder",shownInModals:"In Dialogen angezeigt",hiddenInModals:"In Dialogen ausgeblendet",apply:"Anwenden",toast:{deleted:"Vorlage gelöscht",updated:"Vorlage aktualisiert",created:"Vorlage erstellt",applied:"Vorlage angewendet"}}},support:{debugLoggingActive:"Debug-Protokollierung ist aktiv",manageLogs:"Verwalten",collectItem7:"Drucker-Verbindungsstatus und Firmware-Versionen",collectItem8:"Integrationsstatus (Spoolman, MQTT, HA)",collectItem9:"Netzwerkschnittstellen (nur Subnetze)",collectItem10:"Python-Paketversionen",collectItem11:"Datenbankzustandsprüfungen",collectItem12:"Docker-Umgebungsdetails"},fileManager:{title:"Dateimanager",subtitle:"Organisieren und verwalten Sie Ihre Druckdateien",uploadFiles:"Dateien hochladen",newFolder:"Neuer Ordner",folderName:"Ordnername",folderNamePlaceholder:"z.B. Funktionsteile",renameFile:"Datei umbenennen",renameFolder:"Ordner umbenennen",moveFiles:"{{count}} Datei(en) verschieben",rootNoFolder:"Stammverzeichnis (Kein Ordner)",current:"aktuell",linkFolder:"Ordner verknüpfen",linkFolderDescription:'"{{name}}" mit einem Projekt oder Archiv verknüpfen für schnellen Zugriff.',project:"Projekt",archive:"Archiv",noProjectsFound:"Keine Projekte gefunden",noArchivesFound:"Keine Archive gefunden",unlink:"Verknüpfung aufheben",link:"Verknüpfen",dragDropFiles:"Dateien hierher ziehen",dropFilesHere:"Dateien hier ablegen",orClickToBrowse:"oder klicken zum Durchsuchen",allFileTypesSupported:"Alle Dateitypen werden unterstützt. ZIP-Dateien werden extrahiert.",zipFilesDetected:"ZIP-Dateien erkannt",zipExtractOptions:"ZIP-Dateien werden extrahiert. Wählen Sie, wie die Ordnerstruktur behandelt werden soll:",preserveZipStructure:"Ordnerstruktur aus ZIP beibehalten",createFolderFromZip:"Ordner aus ZIP-Dateiname erstellen",stlThumbnailGeneration:"STL-Vorschaubildgenerierung",zipMayContainStl:"ZIP-Dateien können STL-Dateien enthalten. Vorschaubilder können während der Extraktion generiert werden.",thumbnailsCanBeGenerated:"Vorschaubilder können für STL-Dateien generiert werden. Große Modelle benötigen möglicherweise mehr Zeit.",generateThumbnailsForStl:"Vorschaubilder für STL-Dateien generieren",threemfDetected:"3MF-Dateien erkannt",threemfExtractionInfo:"Druckermodell, Material, Farbe und Druckeinstellungen werden automatisch aus 3MF-Dateien extrahiert.",willBeExtracted:"Wird extrahiert",filesExtracted:"{{count}} Dateien extrahiert",uploadComplete:"Upload abgeschlossen: {{succeeded}} erfolgreich",uploadFailed:"Hochladen fehlgeschlagen",zipFilesFailed:"{{count}} Dateien fehlgeschlagen",uploading:"Hochladen...",changeLink:"Verknüpfung ändern...",linkTo:"Verknüpfen mit...",linkToProjectOrArchive:"Mit Projekt oder Archiv verknüpfen",addToQueue:"Zur Warteschlange",schedulePrint:"Planen",generateThumbnail:"Vorschaubild generieren",generateThumbnails:"Vorschaubilder generieren",generateThumbnailsForMissing:"Vorschaubilder für STL-Dateien ohne Vorschau generieren",gridView:"Rasteransicht",listView:"Listenansicht",lowDiskSpaceWarning:"Warnung: Wenig Speicherplatz",lowDiskSpaceDetails:"Nur {{free}} frei von {{total}} gesamt. Schwellenwert ist auf {{threshold}} GB eingestellt.",files:"Dateien",folders:"Ordner",size:"Größe",free:"Frei",allFiles:"Alle Dateien",wrap:"Umbrechen",enableTextWrapping:"Textumbruch aktivieren",disableTextWrapping:"Textumbruch deaktivieren",collapse:"Einklappen",collapseFoldersByDefault:"Ordner standardmäßig einklappen",expandFoldersByDefault:"Ordner standardmäßig ausklappen",dragToResizeTooltip:"Ziehen zum Ändern der Größe, Doppelklick zum Zurücksetzen",searchFiles:"Dateien suchen...",allTypes:"Alle Typen",prints:"Drucke",ascending:"Aufsteigend",descending:"Absteigend",resultsCount:"{{showing}} von {{total}} Dateien",selectAll:"Alle auswählen",deselectAll:"Auswahl aufheben",selected:"{{count}} ausgewählt",adding:"Hinzufügen...",loadingFiles:"Dateien werden geladen...",folderIsEmpty:"Ordner ist leer",noFilesYet:"Noch keine Dateien",folderEmptyDescription:"Laden Sie Dateien hoch oder verschieben Sie Dateien in diesen Ordner.",noFilesDescription:"Laden Sie Dateien hoch, um Ihre Druckdateien zu organisieren.",noMatchingFiles:"Keine passenden Dateien",noMatchingFilesDescription:"Keine Dateien entsprechen Ihren aktuellen Such- oder Filterkriterien.",clearFilters:"Filter zurücksetzen",printedCount:"{{count}}x gedruckt",uploadedBy:"Hochgeladen von",deleteFolder:"Ordner löschen",deleteFile:"Datei löschen",deleteFilesCount:"{{count}} Dateien löschen",deleteFolderConfirm:"Möchten Sie diesen Ordner wirklich löschen? Alle Dateien darin werden ebenfalls gelöscht.",deleteFileConfirm:"Möchten Sie diese Datei wirklich löschen?",deleteFilesConfirm:"Möchten Sie {{count}} ausgewählte Dateien wirklich löschen? Diese Aktion kann nicht rückgängig gemacht werden.",deleting:"Wird gelöscht...",noPermissionRenameFolder:"Sie haben keine Berechtigung, Ordner umzubenennen",noPermissionLinkFolder:"Sie haben keine Berechtigung, Ordner zu verknüpfen",noPermissionDeleteFolder:"Sie haben keine Berechtigung, Ordner zu löschen",noPermissionPrint:"Sie haben keine Berechtigung zum Drucken",noPermissionAddToQueue:"Sie haben keine Berechtigung, zur Warteschlange hinzuzufügen",noPermissionDownload:"Sie haben keine Berechtigung, Dateien herunterzuladen",noPermissionRenameFile:"Sie haben keine Berechtigung, diese Datei umzubenennen",noPermissionGenerateThumbnail:"Sie haben keine Berechtigung, Vorschaubilder zu generieren",noPermissionDeleteFile:"Sie haben keine Berechtigung, diese Datei zu löschen",noPermissionCreateFolder:"Sie haben keine Berechtigung, Ordner zu erstellen",noPermissionUpload:"Sie haben keine Berechtigung, Dateien hochzuladen",noPermissionMoveFiles:"Sie haben keine Berechtigung, Dateien zu verschieben",noPermissionDeleteFiles:"Sie haben keine Berechtigung, Dateien zu löschen",linkExternal:"Extern verknüpfen",linkExternalFolder:"Externen Ordner verknüpfen",linkExternalFolderDescription:"Ein Host-Verzeichnis (NAS, USB, Netzlaufwerk) in den Dateimanager einbinden. Dateien werden nicht kopiert — sie werden direkt vom Originalpfad gelesen.",externalFolderNamePlaceholder:"z.B. NAS-Drucke",externalPath:"Host-Pfad",externalPathHelp:"Absoluter Pfad zum Verzeichnis auf dem Docker-Host. Muss als Bind-Mount in den Container eingebunden sein.",readOnly:"Nur Lesen",readOnlyHelp:"verhindert Uploads und Löschungen",showHiddenFiles:"Versteckte Dateien anzeigen (Punkt-Dateien)",externalFolder:"Externer Ordner",scanFolder:"Scannen",toast:{folderCreated:"Ordner erstellt",folderDeleted:"Ordner gelöscht",fileDeleted:"Datei gelöscht",filesDeleted:"{{count}} Dateien gelöscht",filesMoved:"Dateien verschoben",folderLinked:"Ordner verknüpft",folderUnlinked:"Ordnerverknüpfung aufgehoben",externalFolderLinked:"Externer Ordner verknüpft und gescannt",folderScanned:"Scan abgeschlossen: {{added}} hinzugefügt, {{removed}} entfernt",addedToQueue:"{{count}} Datei(en) zur Warteschlange hinzugefügt",addedToQueuePartial:"{{added}} Datei(en) hinzugefügt, {{failed}} fehlgeschlagen",failedToAddToQueue:"Fehler beim Hinzufügen: {{error}}",fileRenamed:"Datei umbenannt",folderRenamed:"Ordner umbenannt",thumbnailsGenerated:"{{count}} Vorschaubild(er) generiert",thumbnailsGeneratedPartial:"{{succeeded}} Vorschaubild(er) generiert, {{failed}} fehlgeschlagen",noStlMissingThumbnails:"Keine STL-Dateien ohne Vorschaubild",failedToGenerateThumbnails:"Fehler beim Generieren der Vorschaubilder: {{error}}",thumbnailGenerated:"Vorschaubild generiert",failedToGenerateThumbnail:"Fehler beim Generieren des Vorschaubildes: {{error}}"}},projects:{title:"Projekte",subtitle:"Organisieren und verfolgen Sie Ihre 3D-Druckprojekte",newProject:"Neues Projekt",editProject:"Projekt bearbeiten",deleteProject:"Projekt löschen",projectName:"Projektname",description:"Beschreibung",noProjects:"Noch keine Projekte",noProjectsFiltered:"Keine {{status}} Projekte",noProjectsFilteredHelp:"Sie haben keine {{status}} Projekte. Projekte werden hier angezeigt, wenn sich ihr Status ändert.",createFirst:"Erstellen Sie Ihr erstes Projekt, um verwandte Drucke zu organisieren, den Fortschritt zu verfolgen und Ihre Builds zu verwalten.",createFirstButton:"Erstes Projekt erstellen",create:"Erstellen",files:"Dateien",prints:"Drucke",plates:"Platten",parts:"Teile",lastModified:"Zuletzt geändert",deleteConfirm:"Möchten Sie dieses Projekt wirklich löschen? Archive und Warteschlangenelemente werden getrennt, aber nicht gelöscht.",addFiles:"Dateien hinzufügen",removeFile:"Datei entfernen",viewDetails:"Details anzeigen",namePlaceholder:"z.B. Voron 2.4 Build",descriptionPlaceholder:"Optionale Beschreibung...",color:"Farbe",targetPlates:"Ziel-Platten",targetPlatesPlaceholder:"z.B. 25",targetPlatesHelp:"Anzahl der Druckaufträge",targetParts:"Ziel-Teile",targetPartsPlaceholder:"z.B. 150",targetPartsHelp:"Benötigte Objekte insgesamt",tagsLabel:"Tags (kommagetrennt)",tagsPlaceholder:"z.B. voron, funktional, geschenk",dueDate:"Fälligkeitsdatum",priority:"Priorität",priorityLow:"Niedrig",priorityNormal:"Normal",priorityHigh:"Hoch",priorityUrgent:"Dringend",statusActive:"Aktiv",statusCompleted:"Abgeschlossen",statusArchived:"Archiviert",done:"Fertig",completed:"abgeschlossen",failed:"fehlgeschlagen",inQueue:"in Warteschlange",noPrintsYet:"Noch keine Drucke",printJobs:"Druckaufträge (Platten)",partsPrinted:"Gedruckte Teile",failedParts:"Fehlgeschlagene Teile",import:"Importieren",export:"Exportieren",importProject:"Projekt importieren",exportAll:"Alle Projekte exportieren",loading:"Projekte werden geladen...",noEditPermission:"Sie haben keine Berechtigung, Projekte zu bearbeiten",noDeletePermission:"Sie haben keine Berechtigung, Projekte zu löschen",noCreatePermission:"Sie haben keine Berechtigung, Projekte zu erstellen",noImportPermission:"Sie haben keine Berechtigung, Projekte zu importieren",noExportPermission:"Sie haben keine Berechtigung, Projekte zu exportieren",toast:{created:"Projekt erstellt",updated:"Projekt aktualisiert",deleted:"Projekt gelöscht",imported:"Projekt importiert",multipleImported:"{{count}} Projekte importiert",importFailed:"Import fehlgeschlagen",exported:"Projekte exportiert (nur Metadaten)"}},projectDetail:{notFound:"Projekt nicht gefunden",backToProjects:"Zurück zu Projekten",export:"Exportieren",exportProject:"Projekt exportieren",noExportPermission:"Sie haben keine Berechtigung, Projekte zu exportieren",noEditPermission:"Sie haben keine Berechtigung, Projekte zu bearbeiten",partOf:"Teil von:",priorityLabel:"Priorität:",noPrints:"Noch keine Drucke in diesem Projekt",status:{active:"Aktiv",completed:"Abgeschlossen",archived:"Archiviert"},priority:{low:"Niedrig",normal:"Normal",high:"Hoch",urgent:"Dringend"},dueDate:{overdue:"Überfällig",today:"Heute fällig",daysLeft:"{{count}} Tage übrig"},progress:{platesProgress:"Platten-Fortschritt",partsProgress:"Teile-Fortschritt",printJobs:"Druckaufträge",parts:"Teile",percentComplete:"{{percent}}% abgeschlossen",remaining:"{{count}} verbleibend"},stats:{printJobs:"Druckaufträge",total:"gesamt",failed:"{{count}} fehlgeschlagen",partsPrinted:"{{count}} Teile gedruckt",printTime:"Druckzeit",filamentUsed:"Filament verbraucht"},cost:{title:"Kostenverfolgung",filamentCost:"Filamentkosten",energy:"Energie",totalCost:"Gesamtkosten",total:"Gesamt",includesBom:"inkl. Stückliste",budget:"Budget",remaining:"Verbleibend"},subProjects:{title:"Unterprojekte ({{count}})"},notes:{title:"Notizen",noEditPermission:"Sie haben keine Berechtigung, Notizen zu bearbeiten",placeholder:"Notizen zu diesem Projekt hinzufügen...",empty:"Noch keine Notizen. Klicken Sie auf Bearbeiten, um Notizen hinzuzufügen."},files:{title:"Dateien",linkFolders:"Ordner aus dem Dateimanager verknüpfen",forQuickAccess:"für schnellen Zugriff auf dieses Projekt.",fileCount:"{{count}} Datei(en)",empty:"Keine Ordner verknüpft. Gehen Sie zum Dateimanager und verknüpfen Sie einen Ordner mit diesem Projekt.",noFiles:"Keine Dateien in diesem Ordner.",print:"Jetzt drucken",addToQueue:"Zur Warteschlange"},bom:{title:"Stückliste",acquired:"{{completed}}/{{total}} beschafft",showAll:"Alle anzeigen",hideDone:"Erledigte ausblenden",addPart:"Teil hinzufügen",noAddPermission:"Sie haben keine Berechtigung, Teile hinzuzufügen",partNamePlaceholder:"Teilename (z.B. M3x8 Schrauben)",partName:"Teilename",qty:"Menge",price:"Preis ({{currency}})",sourcingUrlPlaceholder:"Bezugsquelle-URL (optional)",remarksPlaceholder:"Bemerkungen (optional)",deletePart:"Teil löschen",deleteConfirm:'Möchten Sie "{{name}}" wirklich löschen?',noUpdatePermission:"Sie haben keine Berechtigung, Teile zu aktualisieren",noEditPermission:"Sie haben keine Berechtigung, Teile zu bearbeiten",noDeletePermission:"Sie haben keine Berechtigung, Teile zu löschen",totalCost:"Gesamtkosten:",empty:"Keine Teile in der Stückliste. Fügen Sie Hardware, Elektronik oder andere Komponenten hinzu, um zu verfolgen, was beschafft werden muss."},timeline:{title:"Aktivitätsverlauf",empty:"Noch keine Aktivität."},template:{saveAsTemplate:"Als Vorlage speichern",noCreatePermission:"Sie haben keine Berechtigung, Vorlagen zu erstellen"},queue:{title:"Warteschlange",viewAll:"Alle anzeigen",printing:"{{count}} druckend",queued:"{{count}} in Warteschlange"},prints:{title:"Drucke ({{count}})"},toast:{projectUpdated:"Projekt aktualisiert",partAdded:"Teil hinzugefügt",partRemoved:"Teil entfernt",exportFailed:"Export fehlgeschlagen",projectExported:"Projekt exportiert",templateCreated:"Vorlage erstellt"}},system:{title:"Systeminformationen",version:"Version",uptime:"Laufzeit",cpuUsage:"CPU-Auslastung",memoryUsage:"Speicherauslastung",diskUsage:"Festplattenauslastung",networkInfo:"Netzwerkinformationen",logs:"Protokolle",debugMode:"Debug-Modus",enableDebug:"Debug-Protokollierung aktivieren",disableDebug:"Debug-Protokollierung deaktivieren",downloadLogs:"Protokolle herunterladen",clearLogs:"Protokolle löschen",dockerInfo:"Docker-Info",containerName:"Container-Name",imageName:"Image-Name",platform:"Plattform",architecture:"Architektur"},library:{title:"Filament-Bibliothek",addFilament:"Filament hinzufügen",editFilament:"Filament bearbeiten",deleteFilament:"Filament löschen",vendor:"Hersteller",material:"Material",color:"Farbe",kFactor:"K-Faktor",temperature:"Temperatur",noFilaments:"Keine Filamente in der Bibliothek",deleteConfirm:"Möchten Sie dieses Filament wirklich löschen?",importFromPrinter:"Vom Drucker importieren",exportToFile:"In Datei exportieren"},spoolman:{title:"Spoolman-Integration",enabled:"Spoolman aktiviert",url:"Spoolman URL",connected:"Verbunden",disconnected:"Nicht verbunden",testConnection:"Verbindung testen",sync:"Synchronisieren",syncing:"Synchronisiert...",lastSync:"Letzte Synchronisierung",linkToSpoolman:"Mit Spoolman verknüpfen",openInSpoolman:"In Spoolman öffnen",unlinkSpool:"Spule trennen",unlinkConfirmTitle:"Spule entkoppeln?",unlinkConfirmMessage:"Dadurch wird die Spule von Spoolman getrennt. Die Spulendaten in Spoolman bleiben unverändert.",selectSpool:"Spule auswählen",noUnlinkedSpools:"Keine nicht verknüpften Spulen verfügbar",linkSuccess:"Spule erfolgreich mit Spoolman verknüpft",linkFailed:"Verknüpfung mit Spoolman fehlgeschlagen",unlinkSuccess:"Spule erfolgreich von Spoolman getrennt",unlinkFailed:"Trennen der Spule von Spoolman fehlgeschlagen",spoolId:"Spulen-ID",fillSourceLabel:"(Spoolman)",weight:"Gewicht",remaining:"Verbleibend",disableWeightSync:"AMS-Gewichtsschätzung deaktivieren",disableWeightSyncDesc:"Verbleibende Kapazität nicht aus AMS-Schätzungen aktualisieren. Verwenden Sie dies, wenn Sie die Verbrauchserfassung von Spoolman gegenüber den prozentualen AMS-Schätzungen bevorzugen. Neue Spulen verwenden weiterhin die AMS-Schätzung als Anfangsgewicht.",reportPartialUsage:"Teilverbrauch bei fehlgeschlagenen Drucken melden",reportPartialUsageDesc:"Wenn ein Druck fehlschlägt oder abgebrochen wird, den geschätzten Filamentverbrauch bis zu diesem Zeitpunkt basierend auf dem Schichtfortschritt melden."},inventory:{title:"Spulen-Inventar",addSpool:"Spule hinzufügen",editSpool:"Spule bearbeiten",material:"Material",selectMaterial:"Material auswählen...",subtype:"Untertyp",brand:"Marke",searchBrand:"Marke suchen...",useCustomBrand:'"{{brand}}" verwenden',useCustomMaterial:"Benutzerdefiniertes Material verwenden: {{material}}",colorName:"Farbname",colorNamePlaceholder:"Jade White, Fire Red...",color:"Farbe",hexColor:"Hex-Farbe",pickColor:"Benutzerdefinierte Farbe wählen",labelWeight:"Nenngewicht",coreWeight:"Leergewicht der Spule",searchSpoolWeight:"Spulengewicht suchen...",weightUsed:"Verbraucht",currentWeight:"Restgewicht",measuredWeight:"Gemessenes Gewicht",spoolName:"Spule",costPerKg:"Kosten pro kg",measuredWeightError:"Das gemessene Gewicht muss zwischen {{min}}g und {{max}}g liegen.",slicerFilament:"Slicer-Filament",slicerFilamentName:"Slicer-Preset-Name",slicerPreset:"Slicer-Preset",searchPresets:"Filament-Presets suchen...",selectedPreset:"Ausgewählt",noPresetsFound:"Keine Presets gefunden",tempOverrides:"Temperatur-Überschreibungen",note:"Notiz",notePlaceholder:"Zusätzliche Notizen zu dieser Spule...",archive:"Archivieren",restore:"Wiederherstellen",noSpools:"Noch keine Spulen. Fügen Sie Ihre erste Spule hinzu.",noManualSpools:"Keine manuell hinzugefügten Spulen verfügbar. Fügen Sie zuerst eine Spule zum Inventar hinzu.",kProfiles:"K-Profile",addKProfile:"K-Profil hinzufügen",assignSpool:"Spule zuweisen",unassignSpool:"Zuweisung aufheben",assignSuccess:"Spule zugewiesen und AMS-Slot konfiguriert",assignFailed:"Spulenzuweisung fehlgeschlagen",selectSpool:"Wählen Sie eine Spule für diesen Slot",assigned:"Zugewiesen",assigning:"Wird zugewiesen...",searchSpools:"Spulen suchen...",showAllSpools:"Alle Spulen anzeigen",allMaterials:"Alle Materialien",filterByBrand:"Nach Marke filtern...",showArchived:"Archivierte anzeigen",quickAdd:"Schnellerfassung (Lager)",quantity:"Menge",stock:"Lager",configured:"Konfiguriert",spoolsCreated:"{{count}} Spulen erstellt",spoolCreated:"Spule erstellt",spoolUpdated:"Spule aktualisiert",spoolDeleted:"Spule gelöscht",spoolArchived:"Spule archiviert",spoolRestored:"Spule wiederhergestellt",deleteConfirm:"Möchten Sie diese Spule wirklich löschen? Dies kann nicht rückgängig gemacht werden.",archiveConfirm:"Möchten Sie diese Spule wirklich archivieren?",advancedSettings:"Erweiterte Einstellungen",filamentInfoTab:"Filament-Info",paProfileTab:"PA-Profil",filamentInfo:"Filament",additional:"Zusätzlich",loadingPresets:"Cloud-Presets werden geladen...",cloudConnected:"Cloud verbunden",cloudNotConnected:"Cloud nicht verbunden (Standardwerte)",recentColors:"Zuletzt",searchColors:"Farben suchen...",searchResults:"Suchergebnisse",allColors:"Alle Farben",commonColors:"Häufige Farben",showLess:"Weniger",showAll:"Alle",noColorsFound:"Keine Farben gefunden",noResults:"Keine Ergebnisse",selectMaterialFirst:"Bitte zuerst ein Material im Filament-Info Tab auswählen.",noPrintersConfigured:"Keine Drucker konfiguriert. Fügen Sie Drucker hinzu.",matchingFilter:"Filter",anyBrand:"Jede Marke",anyVariant:"Jede Variante",autoSelect:"Auto-Auswahl",matches:"Treffer",match:"Treffer",noMatches:"Keine Treffer",connected:"Verbunden",offline:"Offline",printerOffline:"Drucker ist offline. Verbinden Sie ihn, um Kalibrierungsprofile anzuzeigen.",noKProfilesMatch:"Keine K-Profile stimmen mit dem gewählten Filament überein.",leftNozzle:"Linke Düse",rightNozzle:"Rechte Düse",profilesSelected:"Kalibrierungsprofil(e) ausgewählt",totalInventory:"Gesamtbestand",totalConsumed:"Gesamtverbrauch",byMaterial:"Nach Material",inPrinter:"Im Drucker",lowStock:"Niedriger Bestand",sinceTracking:"Seit Beginn der Erfassung",loadedInAms:"Im AMS/Ext geladen",remaining:"Verbleibend",weightCheck:"Gewichtskontrolle",lastWeighed:"Zuletzt gewogen",neverWeighed:"Nie gewogen",search:"Spulen suchen...",showing:"Zeige",to:"bis",of:"von",show:"Zeige",spools:"Spulen",spool:"Spule",page:"Seite",noSpoolsMatch:"Keine Ergebnisse",noSpoolsMatchDesc:"Versuchen Sie, Ihre Suche oder Filter anzupassen.",active:"Aktiv",archived:"Archiviert",all:"Alle",used:"Verwendet",new:"Neu",clearFilters:"Filter löschen",table:"Tabelle",cards:"Karten",net:"Netto",groupSimilar:"Gruppieren",groupedSpools:"{{count}} identische Spulen",groupedRows:"Zeilen",columns:"Spalten",configureColumns:"Spalten konfigurieren",configureColumnsDesc:"Ziehen zum Neuordnen oder Pfeile verwenden. Sichtbarkeit mit dem Augensymbol umschalten.",visible:"sichtbar",reset:"Zurücksetzen",cancel:"Abbrechen",applyChanges:"Änderungen anwenden",moveUp:"Nach oben",moveDown:"Nach unten",hideColumn:"Spalte ausblenden",showColumn:"Spalte einblenden",linkToSpool:"Mit Spule verknüpfen",tagLinked:"Tag mit Spule verknüpft",tagLinkFailed:"Tag-Verknüpfung fehlgeschlagen",tagAlreadyLinked:"Tag bereits mit anderer Spule verknüpft",unknownTag:"Unbekannter RFID-Tag erkannt",usageHistory:"Verbrauchshistorie",noUsageHistory:"Noch kein Verbrauch erfasst",printName:"Druckname",weightConsumed:"Verbrauchtes Gewicht",clearHistory:"Löschen",historyCleared:"Verbrauchshistorie gelöscht",fillSourceLabel:"(Inv)",lowStockThresholdError:"Der Schwellenwert muss zwischen 0.1 und 99.9 liegen",assignMismatchTitle:"Material stimmt nicht überein",assignMismatchMessage:'Das ausgewählte Spulenmaterial "{{spoolMaterial}}" stimmt nicht mit dem Tray-Material "{{trayMaterial}}" für {{location}} überein. Trotzdem zuweisen?',assignMismatchConfirm:"Trotzdem zuweisen",assignPartialMismatchMessage:'Das Spulenmaterial "{{spoolMaterial}}" ist ähnlich, stimmt aber nicht genau mit "{{trayMaterial}}" in {{location}} überein. Möchten Sie fortfahren?',assignProfileMismatchMessage:'Das Spulenprofil "{{spoolProfile}}" stimmt nicht mit dem Fachprofil "{{trayProfile}}" in {{location}} überein. Möchten Sie fortfahren?'},timelapse:{title:"Zeitraffer",create:"Zeitraffer erstellen",download:"Herunterladen",delete:"Löschen",preview:"Vorschau",frameRate:"Bildrate",quality:"Qualität",processing:"Wird verarbeitet...",noTimelapses:"Keine Zeitraffer verfügbar"},ams:{title:"AMS",slot:"Slot",empty:"Leer",emptySlot:"Leerer Slot",unknown:"Unbekannt",humidity:"Luftfeuchtigkeit",temperature:"Temperatur",filamentType:"Filamenttyp",filamentColor:"Farbe",remaining:"Verbleibend",history:"AMS-Verlauf",noHistory:"Kein Verlauf verfügbar",configureSlot:"Slot konfigurieren",externalSpool:"Externe Spule",profile:"Profil",kFactor:"K-Faktor",fill:"Füllstand",configure:"Konfigurieren",used:"verwendet",remainingUnit:"verbleibend"},printModal:{title:"Druck starten",selectPrinter:"Drucker auswählen",selectPlate:"Platte auswählen",filamentMapping:"Filamentzuordnung",totalCost:"Gesamtkosten:",slotRemainingShort:" - {{grams}}g übrig",printSettings:"Druckeinstellungen",bedLeveling:"Bett-Nivellierung",flowCalibration:"Fluss-Kalibrierung",vibrationCalibration:"Vibrations-Kalibrierung",layerInspection:"Erste-Schicht-Prüfung",timelapse:"Zeitraffer",startPrint:"Druck starten",addToQueue:"Zur Warteschlange hinzufügen",cancel:"Abbrechen",noPrintersAvailable:"Keine Drucker verfügbar",printerBusy:"Drucker ist beschäftigt",printerOffline:"Drucker ist offline",sameTypeDifferentColor:"Gleicher Typ, andere Farbe",filamentTypeNotLoaded:"Filamenttyp nicht geladen",openCalendar:"Kalender öffnen",leftNozzle:"L",rightNozzle:"R",leftNozzleTooltip:"Linke Düse",rightNozzleTooltip:"Rechte Düse",filamentOverride:"Filament-Überschreibung",filamentOverrideHint:"Filamente für modellbasierte Zuweisung optional überschreiben. Der Planer wird gegen die ausgewählten Filamente statt der ursprünglichen 3MF-Werte abgleichen.",originalFilament:"Original",overrideWith:"Ersetzen mit",resetToOriginal:"Auf Original zurücksetzen",insufficientFilamentTitle:"Nicht genug Filament",insufficientFilamentMessage:"Einige zugewiesene Spulen haben weniger Filament als dieser Druck benötigt:",insufficientFilamentLine:"{{printer}} - {{slot}}: benötigt {{required}}g, verbleibend {{remaining}}g",printAnyway:"Trotzdem drucken",forceColorMatch:"Farbe erzwingen",staggerPrinterStarts:"Stagger printer starts",staggerGroupSize:"Group size",staggerInterval:"Interval (min)",staggerPreview:"{{printers}} printers → {{groups}} groups of {{size}}, starting every {{interval}} min",staggerLastGroup:"last group: {{count}}",staggerTotal:"total: {{minutes}} min",staggerToPrinters:"Gestaffelt an {{count}} Drucker senden",gcodeInjection:"Auto-Print G-code einfügen"},backup:{title:"Sichern & Wiederherstellen",createBackup:"Sicherung erstellen",restoreBackup:"Sicherung wiederherstellen",restoreDescription:"Alle Daten aus einer Sicherungsdatei ersetzen",downloadBackup:"Sicherung herunterladen",uploadBackup:"Sicherung hochladen",lastBackup:"Letzte Sicherung",autoBackup:"Automatische Sicherung",backupNow:"Jetzt sichern",restoreWarning:"Warnung: Das Wiederherstellen einer Sicherung überschreibt alle aktuellen Daten.",includeArchives:"Archive einschließen",includeSettings:"Einstellungen einschließen",includeProfiles:"Profile einschließen",backupSuccess:"Sicherung erfolgreich erstellt",restoreSuccess:"Sicherung erfolgreich wiederhergestellt",backupFailed:"Sicherung fehlgeschlagen",restoreFailed:"Wiederherstellung fehlgeschlagen",restoreNote:"Virtueller Drucker wird während der Wiederherstellung gestoppt",githubBackup:"GitHub Backup",enabled:"Aktiviert",cloudLoginRequired:"Bambu Cloud Login erforderlich. Melden Sie sich unter Profile → Cloud-Profile an, um GitHub-Backup zu aktivieren.",cloudLoginRequiredShort:"Cloud-Login erforderlich",githubDescription:"Synchronisieren Sie Ihre Profile automatisch mit einem privaten GitHub-Repository für Backup und Versionsverlauf.",repositoryUrl:"Repository-URL",personalAccessToken:"Persönlicher Zugriffstoken",tokenSaved:"(gespeichert)",enterNewToken:"Neuen Token eingeben zum Aktualisieren",tokenHint:"Feingranularer Token mit Lese-/Schreibberechtigung für Inhalte",branch:"Branch",manualOnly:"Nur manuell",hourly:"Stündlich",daily:"Täglich",weekly:"Wöchentlich",includeInBackup:"In Sicherung einschließen",kProfiles:"K-Profile",kProfilesDescription:"Druckvorschub-Kalibrierung von verbundenen Druckern",noPrintersConnected:"Keine Drucker verbunden",printersConnected:"{{connected}}/{{total}} verbunden",cloudProfiles:"Cloud-Profile",cloudProfilesDescription:"Filament-, Drucker- und Prozessprofile aus der Bambu Cloud",appSettings:"App-Einstellungen",appSettingsDescription:"Bambuddy-Konfiguration (komplette Datenbank)",spoolInventory:"Spulenbestand",spoolInventoryDescription:"Filamentspulen, Nutzungsverlauf und Kostenverfolgung",printArchives:"Druckarchive",printArchivesDescription:"Druckverlauf-Metadaten (keine GCode/3MF-Dateien)",lastBackupAt:"Letzte Sicherung:",noBackupsYet:"Noch keine Sicherungen",next:"Nächste:",startingBackup:"Sicherung wird gestartet...",test:"Test",enableBackup:"Sicherung aktivieren",testConnection:"Verbindung testen",enterRepoUrl:"Repository-URL eingeben",enterRepoAndToken:"Repository-URL und Zugriffstoken eingeben",repoRequired:"Repository-URL ist erforderlich",tokenRequired:"Zugriffstoken ist erforderlich",githubBackupEnabled:"GitHub-Backup aktiviert",tokenUpdated:"Token aktualisiert",settingsSaved:"Einstellungen gespeichert",failedToSave:"Speichern fehlgeschlagen: {{message}}",backupCompleteFiles:"Sicherung abgeschlossen - {{count}} Dateien aktualisiert",backupSkippedNoChanges:"Sicherung übersprungen - keine Änderungen",backupFailed2:"Sicherung fehlgeschlagen: {{message}}",clearedLogs:"{{count}} Protokolle gelöscht",failedToClearLogs:"Protokolle löschen fehlgeschlagen: {{message}}",history:"Verlauf",clear:"Löschen",date:"Datum",status:"Status",commit:"Commit",localBackup:"Lokale Sicherung",localBackupDescription:"Erstellen Sie eine vollständige Sicherung Ihrer Bambuddy-Daten einschließlich Datenbank, Archive, Uploads und aller Dateien.",downloadBackupLabel:"Sicherung herunterladen",completeBackupZip:"Vollständige Sicherung: Datenbank + alle Dateien (ZIP)",download:"Herunterladen",preparingBackup:"Sicherung wird vorbereitet...",creatingArchive:"Sicherungsarchiv wird erstellt... Dies kann bei großen Archiven eine Weile dauern.",downloadingFile:"Sicherungsdatei wird heruntergeladen...",backupDownloaded:"Sicherung erfolgreich heruntergeladen",failedToCreateBackup:"Sicherung erstellen fehlgeschlagen: {{message}}",restore:"Wiederherstellen",restoreReplacesAll:"Wiederherstellung ersetzt alle Daten.",restoreReplacesAllDetail:"Ihre aktuelle Datenbank und Dateien werden vollständig ersetzt. Nach der Wiederherstellung ist ein Neustart erforderlich.",restoreConfirmTitle:"Sicherung wiederherstellen",restoreConfirmMessage:'Sind Sie sicher, dass Sie von "{{filename}}" wiederherstellen möchten? Dies ersetzt Ihre aktuelle Datenbank und alle Dateien vollständig. Die Anwendung muss nach der Wiederherstellung neu gestartet werden.',restoreConfirmButton:"Sicherung wiederherstellen",uploadingFile:"Sicherungsdatei wird hochgeladen...",backupRestoredRestart:"Sicherung wiederhergestellt. Bitte starten Sie Bambuddy neu.",failedToRestore:"Sicherung wiederherstellen fehlgeschlagen. Bitte überprüfen Sie das Dateiformat.",reloadNow:"Jetzt neu laden",creatingBackup:"Sicherung erstellen",restoringBackup:"Sicherung wiederherstellen",preparing:"Vorbereiten...",processing:"Verarbeiten...",doNotClosePage:"Bitte schließen Sie diese Seite nicht und navigieren Sie nicht weg. Dieser Vorgang kann bei großen Sicherungen mehrere Minuten dauern.",restoring:"Wiederherstellen...",restoreComplete:"Wiederherstellung abgeschlossen",restoreFailed2:"Wiederherstellung fehlgeschlagen",importSettings:"Einstellungen aus einer Sicherungsdatei importieren",pleaseWaitRestoring:"Bitte warten Sie, während Ihre Daten wiederhergestellt werden",selectBackupFile:"Klicken Sie, um eine Sicherungsdatei auszuwählen (.json oder .zip)",duplicateHandling:"So funktioniert die Duplikatbehandlung:",matchPrinters:"Drucker",matchPrintersBy:"abgeglichen nach Seriennummer",matchSmartPlugs:"Smart Plugs",matchSmartPlugsBy:"abgeglichen nach IP-Adresse",matchNotificationProviders:"Benachrichtigungsanbieter",matchNotificationProvidersBy:"abgeglichen nach Name",matchFilaments:"Filamente",matchFilamentsBy:"abgeglichen nach Name + Typ + Marke",matchArchives:"Archive",matchArchivesBy:"abgeglichen nach Inhaltshash (immer übersprungen)",matchPendingUploads:"Ausstehende Uploads",matchPendingUploadsBy:"abgeglichen nach Dateiname",matchSettingsTemplates:"Einstellungen & Vorlagen",matchSettingsTemplatesBy:"immer überschrieben",replaceExisting:"Vorhandene Daten ersetzen",keepExisting:"Vorhandene Daten behalten",overwriteDescription:"Bereits vorhandene Elemente mit Sicherungsdaten überschreiben",keepDescription:"Nur Elemente wiederherstellen, die noch nicht vorhanden sind",overwriteCaution:"Achtung:",overwriteWarning:"Das Überschreiben ersetzt Ihre aktuellen Konfigurationen durch Daten aus der Sicherung. Drucker-Zugangscodes werden aus Sicherheitsgründen nie überschrieben.",cancel:"Abbrechen",processingBackup:"Sicherungsdatei wird verarbeitet...",itemsRestored:"Wiederhergestellt",itemsSkipped:"Übersprungen",restored:"Wiederhergestellt",skippedAlreadyExist:"Übersprungen (bereits vorhanden)",filesCategory:"Dateien (3MF, Thumbnails, etc.)",andMore:"...und {{count}} weitere",newApiKeysGenerated:"Neue API-Schlüssel generiert",keysShownOnce:"Diese Schlüssel werden nur einmal angezeigt. Kopieren Sie sie jetzt!",copy:"Kopieren",noDataFound:"In der Sicherungsdatei wurden keine Daten zur Wiederherstellung gefunden.",close:"Schließen",scheduledBackup:"Scheduled Backups",scheduledBackupDescription:"Automatically create backup snapshots on a schedule. Output directory can be mounted to a NAS or external storage.",frequency:"Frequency",backupTime:"Time",retention:"Retention",retentionDescription:"Number of backups to keep",outputPath:"Output Path",outputPathPlaceholder:"Default: {{path}}",outputPathDescription:"Leave empty for default location",runNow:"Run Now",backupFiles:"Backup Files",noScheduledBackups:"No backups yet",deleteBackup:"Delete",deleteBackupConfirm:"Delete this backup file?",backupRunning:"Backup in progress...",scheduledBackupComplete:"Backup completed successfully",scheduledBackupFailed:"Backup failed",nextBackup:"Next backup",backupSize:"Size",utc:"UTC",defaultPathLabel:"Default:",categories:{settings:"Einstellungen",notification_providers:"Benachrichtigungsanbieter",notification_templates:"Benachrichtigungsvorlagen",smart_plugs:"Smart Plugs",printers:"Drucker",filaments:"Filamente",maintenance_types:"Wartungstypen",archives:"Archive",projects:"Projekte",pending_uploads:"Ausstehende Uploads",external_links:"Externe Links",api_keys:"API-Schlüssel"}},tags:{title:"Tags",addTag:"Tag hinzufügen",editTag:"Tag bearbeiten",deleteTag:"Tag löschen",tagName:"Tag-Name",tagColor:"Tag-Farbe",noTags:"Keine Tags",deleteConfirm:"Möchten Sie diesen Tag wirklich löschen?",manageTags:"Tags verwalten"},uploadModal:{title:"3MF-Dateien hochladen",dragDrop:"3MF-Dateien hierher ziehen",or:"oder",browseFiles:"Dateien durchsuchen",extractionInfo:"Das Druckermodell wird automatisch aus den 3MF-Datei-Metadaten extrahiert.",uploaded:"hochgeladen",failed:"fehlgeschlagen",uploading:"Wird hochgeladen...",upload:"Hochladen",uploadFailed:"Hochladen fehlgeschlagen"},editArchive:{title:"Archiv bearbeiten",name:"Name",namePlaceholder:"Druckname",printer:"Drucker",noPrinter:"Kein Drucker",project:"Projekt",noProject:"Kein Projekt",itemsPrinted:"Gedruckte Teile",itemsPrintedHelp:"Anzahl der in diesem Druckauftrag produzierten Teile",notes:"Notizen",notesPlaceholder:"Notizen zu diesem Druck hinzufügen...",externalLink:"Externer Link",externalLinkPlaceholder:"https://printables.com/model/...",externalLinkHelp:"Link zu Printables, Thingiverse oder anderer Quelle",tags:"Tags",tagsPlaceholder:"Tags hinzufügen...",addMoreTags:"Weitere Tags hinzufügen...",matchingTags:'Übereinstimmend mit "{{query}}"',existingTags:"Vorhandene Tags",clickToAdd:"(zum Hinzufügen klicken)",status:"Status",failureReason:"Fehlergrund",selectReason:"Grund auswählen...",photos:"Fotos des Druckergebnisses",photosHelp:"Klicken Sie auf + um Fotos Ihres Druckergebnisses hinzuzufügen",printResult:"Druckergebnis",saving:"Wird gespeichert...",failureReasons:{adhesionFailure:"Haftungsfehler",spaghettiDetached:"Spaghetti / Abgelöst",layerShift:"Schichtversatz",cloggedNozzle:"Verstopfte Düse",filamentRunout:"Filament aufgebraucht",warping:"Verformung",stringing:"Fadenziehen",underExtrusion:"Unterextrusion",powerFailure:"Stromausfall",userCancelled:"Vom Benutzer abgebrochen",other:"Sonstiges"},statuses:{completed:"Abgeschlossen",failed:"Fehlgeschlagen",aborted:"Abgebrochen",printing:"Druckt"}},kProfiles:{title:"K-Profile",noPrintersConfigured:"Keine Drucker konfiguriert",addPrinterInSettings:"Fügen Sie einen Drucker in den Einstellungen hinzu, um K-Profile zu verwalten",noActivePrinters:"Keine aktiven Drucker",enablePrinterConnection:"Aktivieren Sie eine Druckerverbindung, um K-Profile anzuzeigen",loadingProfiles:"Lade K-Profile...",printerOffline:"Drucker offline",printerOfflineDesc:"Der ausgewählte Drucker ist nicht verbunden. Schalten Sie ihn ein, um K-Profile anzuzeigen.",noMatchingProfiles:"Keine passenden Profile",noMatchingProfilesDesc:"Keine Profile entsprechen Ihren Suchkriterien",noKProfiles:"Keine K-Profile",noKProfilesDesc:"Keine Druckvorschub-Profile für {{diameter}}mm Düse gefunden",createFirstProfile:"Erstes Profil erstellen",printer:"Drucker",nozzle:"Düse",refresh:"Aktualisieren",addProfile:"Profil hinzufügen",export:"Exportieren",import:"Importieren",select:"Auswählen",selectAll:"Alle auswählen",delete:"Löschen",searchPlaceholder:"Nach Name oder Filament suchen...",allExtruders:"Alle Extruder",leftOnly:"Nur links",rightOnly:"Nur rechts",allFlow:"Alle Flusstypen",hfOnly:"Nur HF",sOnly:"Nur S",sortName:"Sortieren: Name",sortKValue:"Sortieren: K-Wert",sortFilament:"Sortieren: Filament",leftExtruder:"Linker Extruder",rightExtruder:"Rechter Extruder",modal:{addTitle:"K-Profil hinzufügen",editTitle:"K-Profil bearbeiten",profileName:"Profilname",profileNamePlaceholder:"Mein PLA-Profil",kValue:"K-Wert",kValuePlaceholder:"0,020",kValueHelp:"Typischer Bereich: 0,01 - 0,06 für PLA, 0,02 - 0,10 für PETG",filament:"Filament",selectFilament:"Filament auswählen...",noFilamentsHelp:"Keine Filamente gefunden. Erstellen Sie zuerst ein K-Profil in Bambu Studio.",flowType:"Flusstyp",highFlow:"High Flow",standard:"Standard",nozzleSize:"Düsengröße",extruder:"Extruder",extruders:"Extruder",left:"Links",right:"Rechts",notes:"Notizen (lokal gespeichert)",notesPlaceholder:"Notizen zu diesem Profil hinzufügen...",notesHelp:"Notizen werden in Bambuddy gespeichert, nicht auf dem Drucker",syncing:"Synchronisiert mit Drucker...",savingExtruder:"Speichern auf Extruder {{current}}/{{total}}...",pleaseWait:"Bitte warten"},deleteConfirm:{title:"Profil löschen",cannotUndo:"Dies kann nicht rückgängig gemacht werden",message:'Möchten Sie "{{name}}" wirklich vom Drucker löschen?'},bulkDelete:{title:"Profile löschen",cannotUndo:"Dies kann nicht rückgängig gemacht werden",message:"Möchten Sie wirklich {{count}} ausgewählte Profile vom Drucker löschen?"},toast:{profileSaved:"K-Profil gespeichert",profilesSaved:"K-Profil auf {{count}} Extrudern gespeichert",selectAtLeastOneExtruder:"Bitte wählen Sie mindestens einen Extruder aus",profileDeleted:"K-Profil gelöscht",profilesDeleted:"{{count}} Profile gelöscht",exportedProfiles:"{{count}} Profile exportiert",importedProfiles:"{{count}} von {{total}} Profilen importiert",noProfilesToExport:"Keine Profile zum Exportieren",invalidFileFormat:"Ungültiges Dateiformat",failedToParseImport:"Import-Datei konnte nicht gelesen werden",failedToSaveBatch:"K-Profile konnten nicht gespeichert werden",noteSaved:"Notiz gespeichert",failedToSaveNote:"Notiz konnte nicht gespeichert werden"},permission:{noRead:"Sie haben keine Berechtigung, Profile zu aktualisieren",noCreate:"Sie haben keine Berechtigung, Profile hinzuzufügen",noUpdate:"Sie haben keine Berechtigung, K-Profile zu aktualisieren",noDelete:"Sie haben keine Berechtigung, K-Profile zu löschen",noExport:"Sie haben keine Berechtigung, Profile zu exportieren",noImport:"Sie haben keine Berechtigung, Profile zu importieren"}},virtualPrinter:{title:"Virtueller Drucker",running:"Läuft",stopped:"Gestoppt",description:{default:"Aktiviere einen virtuellen Drucker, der in Bambu Studio und OrcaSlicer erscheint. Dateien, die an diesen Drucker gesendet werden, werden direkt archiviert ohne zu drucken.",proxy:"Aktiviere einen Proxy, der Slicer-Datenverkehr an einen echten Drucker weiterleitet, um Ferndruck über jedes Netzwerk zu ermöglichen."},enable:{title:"Virtuellen Drucker aktivieren",visibleInSlicer:'Sichtbar als "Bambuddy" in der Slicer-Erkennung',proxyingTo:"Proxy zu {{name}}",notActive:"Nicht aktiv"},model:{title:"Druckermodell",description:"Wähle welches Druckermodell emuliert werden soll.",restartWarning:"Das Ändern des Modells startet den virtuellen Drucker neu"},accessCode:{title:"Zugangscode",isSet:"Zugangscode ist gesetzt",notSet:"Kein Zugangscode gesetzt - erforderlich zum Aktivieren",placeholder:"8-Zeichen-Code eingeben",placeholderChange:"Neuen Code eingeben zum Ändern",hint:"Muss genau 8 Zeichen lang sein. Wird von Slicern zur Authentifizierung verwendet.",charCount:"({{count}}/8)"},targetPrinter:{title:"Zieldrucker",configured:"Proxy-Ziel konfiguriert",notConfigured:"Kein Zieldrucker ausgewählt - erforderlich für Proxy-Modus",placeholder:"Drucker auswählen...",hint:"Wähle den Drucker aus, an den der Slicer-Datenverkehr weitergeleitet werden soll. Der Drucker muss im LAN-Modus sein.",noPrinters:"Keine Drucker konfiguriert. Füge zuerst einen Drucker hinzu, um den Proxy-Modus zu verwenden."},remoteInterface:{title:"Netzwerkschnittstelle überschreiben",configured:"Schnittstellenüberschreibung aktiv",optional:"Optional - verwenden wenn die automatisch erkannte IP falsch ist (z.B. mehrere NICs, Docker, VPN)",placeholder:"Automatisch erkennen (Standard)...",hint:"Überschreibt die per SSDP beworbene und im TLS-Zertifikat verwendete IP-Adresse. Nützlich wenn Bambuddy mehrere Netzwerkschnittstellen hat."},mode:{title:"Modus",archive:"Archivieren",archiveDesc:"Dateien sofort archivieren",review:"Überprüfen",reviewDesc:"Vor dem Archivieren überprüfen",queue:"Warteschlange",queueDesc:"Archivieren und zur Warteschlange hinzufügen",proxy:"Proxy",proxyDesc:"An echten Drucker weiterleiten"},autoDispatch:{title:"Automatisch starten",description:"Drucke automatisch starten, wenn sie zur Warteschlange hinzugefügt werden. Wenn deaktiviert, warten Drucke auf manuellen Start."},setupRequired:{title:"Einrichtung erforderlich",description:"Die virtuelle Druckerfunktion erfordert zusätzliche Systemkonfiguration, bevor sie funktioniert. Dies beinhaltet Portweiterleitung, Firewall-Regeln und plattformspezifische Einstellungen.",readGuide:"Lese die Einrichtungsanleitung vor dem Aktivieren"},howItWorks:{title:"So funktioniert es",step1:"Im selben LAN erscheinen virtuelle Drucker automatisch in deinem Slicer (Bambu Studio / OrcaSlicer). Aus anderen Netzwerken füge sie manuell per IP-Adresse und Zugangscode hinzu.",step2:'Im Archiv-, Überprüfungs- und Warteschlangen-Modus verwende die "Senden"-Funktion im Slicer, um 3MF-Dateien an Bambuddy zu senden. Der Slicer zeigt "Druck erfolgreich" — die Datei wird gespeichert, nicht gedruckt.',step3:"Im Proxy-Modus leitet der virtuelle Drucker den gesamten Datenverkehr an einen echten Drucker weiter — Drucke starten sofort wie bei einer direkten Verbindung."},status:{title:"Status-Details",printerName:"Druckername",model:"Modell",serialNumber:"Seriennummer",mode:"Modus",pendingFiles:"Ausstehende Dateien",targetPrinter:"Zieldrucker",ftpPort:"FTP-Port",mqttPort:"MQTT-Port",ftpConnections:"FTP-Verbindungen",mqttConnections:"MQTT-Verbindungen"},toast:{updated:"Virtuelle Druckereinstellungen aktualisiert",failedToUpdate:"Einstellungen konnten nicht aktualisiert werden",accessCodeRequired:"Bitte zuerst einen Zugangscode setzen",targetPrinterRequired:"Bitte zuerst einen Zieldrucker auswählen",bindIpRequired:"Bitte zuerst eine Bind-IP setzen",accessCodeEmpty:"Zugangscode darf nicht leer sein",accessCodeLength:"Zugangscode muss genau 8 Zeichen lang sein",created:"Virtueller Drucker erstellt",failedToCreate:"Virtueller Drucker konnte nicht erstellt werden",deleted:"Virtueller Drucker gelöscht",failedToDelete:"Virtueller Drucker konnte nicht gelöscht werden"},list:{title:"Virtuelle Drucker",add:"Hinzufügen",addFirst:"Virtuellen Drucker hinzufügen",empty:"Keine virtuellen Drucker konfiguriert. Fügen Sie einen hinzu, um zu beginnen."},bindIp:{title:"Bind-Interface",placeholder:"Interface auswählen...",hint:"Netzwerkinterface, an das dieser virtuelle Drucker gebunden wird. Muss pro Drucker eindeutig sein."},proxy:{accessCodeHint:"Im Proxy-Modus den Zugangscode des Zieldruckers im Slicer verwenden. Die Verbindung wird transparent zum echten Drucker weitergeleitet."},addDialog:{title:"Virtuellen Drucker hinzufügen",name:"Name",hint:"Sie können Zugangscode, Zieldrucker und andere Einstellungen nach dem Erstellen konfigurieren.",create:"Erstellen"},deleteConfirm:{title:"Virtuellen Drucker löschen",message:'Möchten Sie "{{name}}" wirklich löschen? Dies stoppt alle Dienste für diesen Drucker.'}},modelViewer:{openInSlicer:"Im Slicer öffnen",tabs:{model:"3D-Modell",gcode:"G-Code Vorschau"},notAvailable:"nicht verfügbar",notSliced:"nicht geslicet",plates:"Platten",allPlates:"Alle Platten",plateNumber:"Platte {{number}}",plateCount:"{{count}} Platte",plateCount_other:"{{count}} Platten",objectCount:"{{count}} Objekt",objectCount_other:"{{count}} Objekte",filamentCount:"{{count}} Filament",filamentCount_other:"{{count}} Filamente",eta:"ETA {{minutes}} Min",noPreview:"Keine Vorschau für diese Datei verfügbar",pagination:{pageOf:"Seite {{current}} von {{total}}",prev:"Zurück",next:"Weiter"},errors:{failedToLoad:"Datei konnte nicht geladen werden",noMeshes:"Keine Meshes in 3MF-Datei gefunden",unsupportedFormat:"Nicht unterstütztes Dateiformat"}},maintenanceDescriptions:{lubricateCarbonRods:"Schmiermittel auf Karbonstäbe für sanfte Bewegung auftragen",lubricateRails:"Schmiermittel auf Linearschienen für sanfte Bewegung auftragen",cleanNozzle:"Hotend und Düse reinigen, um Verstopfungen zu verhindern",checkBelts:"Riemenspannung für präzise Drucke überprüfen",cleanBuildPlate:"Druckplatte für bessere Haftung reinigen",checkExtruder:"Extruderzahnräder auf Verschleiß prüfen",checkCooling:"Sicherstellen, dass Lüfter ordnungsgemäß funktionieren",generalInspection:"Allgemeine Druckerinspektion",cleanCarbonRods:"Karbonstäbe reinigen, um Reibung zu reduzieren",lubricateSteelRods:"Schmiermittel auf Stahlstangen für sanfte Bewegung auftragen",cleanSteelRods:"Stahlstangen reinigen, um Reibung zu reduzieren",cleanLinearRails:"Linearschienen abwischen, um Staub und Schmutz zu entfernen",checkPtfeTube:"PTFE-Schlauch auf Verschleiß oder Beschädigung prüfen",replaceHepaFilter:"HEPA-Filter für Luftqualität ersetzen",replaceCarbonFilter:"Aktivkohlefilter ersetzen",lubricateLeftNozzleRail:"Linke Düsenschiene schmieren (H2-Serie)"},smartPlugs:{offline:"Offline",admin:"Admin",openPlugAdminPage:"Plug-Admin-Seite öffnen",deleteSmartPlug:"Smart Plug löschen",turnOnSmartPlug:"Smart Plug einschalten",turnOffSmartPlug:"Smart Plug ausschalten",turnOn:"Einschalten",turnOff:"Ausschalten",addSmartPlug:{scanningNetwork:"Netzwerk wird durchsucht...",chooseEntity:"Entität auswählen...",connectionFailed:"Verbindung fehlgeschlagen",searchEntities:"Entitäten suchen...",searchPowerSensors:"Leistungssensoren suchen...",searchEnergySensors:"Energiesensoren suchen...",placeholders:{plugName:"Wohnzimmer Steckdose",mqttStateOnValue:"ON, true, 1",mqttSameAsPower:"Gleich wie Leistungs-Topic oder anders"}},linkedTo:"Verbunden mit:",monitorOnly:"Nur Überwachung",alerts:"Alarme",scheduleOn:"Ein {{time}}",scheduleOff:"Aus {{time}}",on:"Ein",off:"Aus",power:"Leistung",kwhToday:"kWh Heute",settings:"Einstellungen",automationSettings:"Automatisierungseinstellungen",showInSwitchbar:"In Schaltleiste anzeigen",quickAccessSidebar:"Schnellzugriff über Seitenleiste",enabled:"Aktiviert",enableAutomation:"Automatisierung für diesen Stecker aktivieren",autoOn:"Auto Ein",autoOnDescription:"Einschalten wenn Druck startet",autoOff:"Auto Aus",autoOffDescription:"Ausschalten wenn Druck abgeschlossen (einmalig)",autoOffPersistent:"Aktiviert lassen",autoOffPersistentDescription:"Zwischen Drucken aktiviert bleiben statt einmalig",turnOffDelayMode:"Ausschaltverzögerungsmodus",time:"Zeit",temp:"Temp",delayMinutes:"Verzögerung (Minuten)",tempThreshold:"Temperaturschwelle (°C)",tempThresholdDescription:"Schaltet aus wenn die Düse unter diese Temperatur abkühlt",edit:"Bearbeiten",deleteConfirm:'Möchten Sie "{{name}}" wirklich löschen? Dies kann nicht rückgängig gemacht werden.',turnOnConfirm:'Möchten Sie "{{name}}" wirklich einschalten?',turnOffConfirm:'Möchten Sie "{{name}}" wirklich ausschalten? Dies unterbricht die Stromversorgung des angeschlossenen Geräts.',failedToTurn:'{{name}}" konnte nicht {{action}} werden',unknown:"Unbekannt",addTitle:"Smart Plug hinzufügen",editTitle:"Smart Plug bearbeiten",stopScanning:"Suche beenden",discoverTasmota:"Tasmota Geräte suchen",foundDevices:"{{count}} Gerät(e) gefunden - zum Auswählen klicken:",noDevicesFound:"Keine Tasmota Geräte in Ihrem Netzwerk gefunden",haNotConfigured:"Home Assistant ist nicht konfiguriert. Einrichtung unter",haSettingsPath:"Einstellungen → Netzwerk → Home Assistant",selectEntity:"Entität auswählen *",ipAddress:"IP-Adresse *",nameLabel:"Name *",username:"Benutzername",password:"Passwort",authHint:"Leer lassen, wenn Ihr Tasmota-Gerät keine Authentifizierung benötigt",linkToPrinter:"Mit Drucker verbinden",noPrinter:"Kein Drucker (nur manuelle Steuerung)",linkingDescription:"Verknüpfung ermöglicht automatisches Ein-/Ausschalten bei Druckstart/-ende",powerAlerts:"Leistungsalarme",alertAbove:"Alarm wenn über (W)",alertBelow:"Alarm wenn unter (W)",alertDescription:"Benachrichtigung wenn der Stromverbrauch diese Schwellenwerte überschreitet. Leer lassen um diese Richtung zu deaktivieren.",dailySchedule:"Tagesplan",turnOnAt:"Einschalten um",turnOffAt:"Ausschalten um",scheduleDescription:"Den Stecker automatisch täglich zu diesen Zeiten ein-/ausschalten. Leer lassen um diese Aktion zu überspringen.",showOnPrinterCard:"Auf Druckerkarte anzeigen",displayOnPrinterCard:"Schaltfläche auf Druckerkarte anzeigen",connectedResult:"Verbunden!",deviceLabel:"Gerät: {{name}} - ",stateLabel:"Status: {{state}}",test:"Test",delete:"Löschen",save:"Speichern",add:"Hinzufügen",cancel:"Abbrechen",failedToStartScan:"Suche konnte nicht gestartet werden",nameRequired:"Name ist erforderlich",entityRequired:"Entität ist für Home Assistant Stecker erforderlich",mqttTopicRequired:"Mindestens ein MQTT-Topic muss für Leistung, Energie oder Statusüberwachung konfiguriert sein",loadingEntities:"Entitäten werden geladen...",loading:"Laden...",failedToLoadEntities:"Entitäten konnten nicht geladen werden: {{error}}",noEntitiesMatching:'Keine Entitäten gefunden die "{{search}}" entsprechen',noEntitiesAvailable:"Keine Entitäten verfügbar",searchingEntities:"Alle Entitäten durchsuchen ({{count}} gefunden)",showingEntities:"Zeige switch, light, input_boolean ({{count}} verfügbar)",energyMonitoringOptional:"Energieüberwachung (Optional)",energyMonitoringHint:"Sensoren suchen und auswählen, die Leistungs-/Energiedaten liefern.",powerSensorW:"Leistungssensor (W)",energyTodayKwh:"Energie Heute (kWh)",totalEnergyKwh:"Gesamtenergie (kWh)",noMatchingSensors:"Keine passenden Sensoren",none:"Keine",mqttNotConfigured:"MQTT-Broker nicht konfiguriert. Broker-Adresse einstellen unter",mqttSettingsPath:"Einstellungen → Netzwerk → MQTT-Veröffentlichung",mqttNotConfiguredSuffix:"(Sie müssen die Veröffentlichung nicht aktivieren, nur die Broker-Details ausfüllen).",mqttMonitorOnlyDescription:"MQTT-Stecker empfangen Leistungs-/Energiedaten über MQTT-Abonnement. Ein-/Ausschalten ist nicht verfügbar - verwenden Sie Ihren MQTT-Broker oder Ihr Home-Automation-System.",powerMonitoring:"Leistungsüberwachung",energyMonitoring:"Energieüberwachung",stateMonitoring:"Statusüberwachung",optional:"optional",topic:"Topic",jsonPath:"JSON-Pfad",multiplier:"Multiplikator",onValue:"EIN-Wert",mqttPowerHint:`JSON-Pfad extrahiert Wert aus JSON-Payload (z.B. "power_l1"). Leer lassen wenn Topic rohe numerische Werte sendet. @@ -30,13 +30,13 @@ Valor ON: a string exata que significa "ON". Deixe vazio para detecção automá ON值:表示"ON"的确切字符串。留空以自动检测(ON、true、1)。`,restControl:"Control",restOnUrl:"Turn ON URL",restOffUrl:"Turn OFF URL",restOnBody:"ON Request Body",restOffBody:"OFF Request Body",restMethod:"HTTP Method",restHeaders:"Custom Headers (JSON)",restStatusUrl:"Status URL",restStatusPath:"State JSON Path",restStatusOnValue:"ON Value",restPowerUrl:"功率URL",restPowerPath:"Power JSON Path",restPowerMultiplier:"功率乘数",restEnergyUrl:"能耗URL",restEnergyPath:"Energy JSON Path",restEnergyMultiplier:"能耗乘数",restUrlRequired:"At least one URL (ON or OFF) is required for REST plugs",restHeadersHint:'e.g. {"Authorization": "Bearer your-token"}',restBodyHint:'e.g. ON, {"state": "on"}',restStatusHint:"URL to poll for current state",restPathHint:"e.g. state or data.power.status",restPowerUrlHint:"功率数据的独立URL(留空则使用状态URL)",restEnergyUrlHint:"能耗数据的独立URL(留空则使用状态URL)",restEnergyHint:"每个值可以使用独立的URL,或回退到状态URL。使用乘数进行单位转换(例如:0.001 将 Wh 转换为 kWh)。",testConnection:"Test Connection",connectionSuccess:"Connection successful",noSwitchesInSwitchbar:"开关栏中没有开关",enableSwitchbarHint:'在设置 > 智能插座中启用"在开关栏显示"'},notifications:{providerTypes:{callmebot:"CallMeBot/WhatsApp",ntfy:"ntfy",pushover:"Pushover",telegram:"Telegram",email:"电子邮件",discord:"Discord",webhook:"Webhook",homeassistant:"Home Assistant"},providerDescriptions:{email:"SMTP 电子邮件通知",telegram:"通过 Telegram 机器人发送通知",discord:"通过 Webhook 发送到 Discord 频道",ntfy:"免费、可自托管的推送通知",pushover:"简单、可靠的推送通知",callmebot:"通过 CallMeBot 免费发送 WhatsApp 通知",webhook:"通用 HTTP POST 到任意 URL",homeassistant:"Home Assistant 仪表板中的持久通知"},lastSuccess:"上次:{{date}}",error:"错误",printer:"打印机:",allPrinters:"所有打印机",sendTestNotification:"发送测试通知",eventSettings:"事件设置",enabled:"已启用",sendFromProvider:"从此提供商发送通知",printEvents:"打印事件",printerStatus:"打印机状态",amsAlarms:"AMS 警报",amsHtAlarms:"AMS-HT 警报",printQueue:"打印队列",start:"开始",plateCheck:"热床检测",complete:"完成",failed:"失败",stopped:"已停止",progress:"进度",offline:"离线",lowFilament:"耗材不足",maintenance:"维护",amsHumidity:"AMS 湿度",amsTemp:"AMS 温度",amsHtHumidity:"AMS-HT 湿度",amsHtTemp:"AMS-HT 温度",bedCooled:"热床已冷却",firstLayer:"首层完成",quiet:"免打扰",digest:"摘要 {{time}}",printStarted:"打印已开始",plateNotEmpty:"热床非空",plateNotEmptyDescription:"打印前检测到物体",printCompleted:"打印已完成",bedCooledLabel:"热床已冷却",bedCooledDescription:"打印后热床温度降至阈值以下",firstLayerCompleteLabel:"首层打印完成",firstLayerCompleteDescription:"首层完成时发送带照片的通知",missingSpoolAssignmentLabel:"缺少料卷分配",missingSpoolAssignmentDescription:"当打印开始且所需料盘没有分配料卷时发送通知",printFailed:"打印失败",printStopped:"打印已停止",progressMilestones:"进度里程碑",progressMilestonesDescription:"在 25%、50%、75% 时通知",printerOffline:"打印机离线",printerError:"打印机错误",lowFilamentLabel:"耗材不足",maintenanceDue:"需要维护",maintenanceDueDescription:"需要维护时通知",amsHumidityHigh:"AMS 湿度过高",amsHumidityHighDescription:"普通 AMS 湿度超过阈值",amsTemperatureHigh:"AMS 温度过高",amsTemperatureHighDescription:"普通 AMS 温度超过阈值",amsHtHumidityHigh:"AMS-HT 湿度过高",amsHtHumidityHighDescription:"AMS-HT 湿度超过阈值",amsHtTemperatureHigh:"AMS-HT 温度过高",amsHtTemperatureHighDescription:"AMS-HT 温度超过阈值",jobAdded:"任务已添加",jobAddedDescription:"任务已添加到队列",jobAssigned:"任务已分配",jobAssignedDescription:"基于模型的任务已分配给打印机",jobStarted:"任务已开始",jobStartedDescription:"队列任务已开始打印",jobWaiting:"任务等待中",jobWaitingDescription:"任务正在等待耗材或打印机",jobSkipped:"任务已跳过",jobSkippedDescription:"任务已跳过(上一个失败)",jobFailed:"任务失败",jobFailedDescription:"任务启动失败",queueComplete:"队列已完成",queueCompleteDescription:"所有队列任务已完成",quietHours:"免打扰时段",noNotificationsDuring:"在此时段内不发送通知",editProviderToChangeQuietHours:"编辑提供商以更改免打扰时段",dailyDigest:"每日摘要",batchNotifications:"将通知汇总为每日摘要",sendAt:"发送于 {{time}}",editProviderToChangeDigestTime:"编辑提供商以更改摘要时间",edit:"编辑",deleteProvider:"删除通知提供商",deleteConfirm:'确定要删除"{{name}}"吗?此操作无法撤销。',delete:"删除",addTitle:"添加通知提供商",editTitle:"编辑通知提供商",nameLabel:"名称 *",namePlaceholder:"我的通知",providerTypeLabel:"提供商类型 *",configuration:"配置",testConfiguration:"测试配置",printerFilter:"打印机筛选",onlyFromPrinter:"仅发送来自此打印机的事件通知",quietHoursDnd:"免打扰时段",quietStart:"开始",quietEnd:"结束",dailyDigestLabel:"每日摘要",sendDigestAt:"发送摘要于",digestCollected:"事件将被收集并在此时间作为单条摘要发送",notificationEvents:"通知事件",progressPercent:"(25%、50%、75%)",bedCooledAfterPrint:"(打印完成后)",cancel:"取消",save:"保存",add:"添加",nameRequired:"名称为必填项",fieldRequired:"{{field}}为必填项",phoneNumber:"电话号码",apiKey:"API 密钥",serverUrl:"服务器 URL",topic:"主题",authToken:"认证令牌",userKey:"用户密钥",appToken:"应用令牌",priority:"优先级",botToken:"机器人令牌",chatId:"聊天 ID",smtpServer:"SMTP 服务器",smtpPort:"SMTP 端口",security:"安全",authentication:"认证",username:"用户名",password:"密码",fromEmail:"发件人邮箱",toEmail:"收件人邮箱",webhookUrl:"Webhook URL",payloadFormat:"负载格式",authorization:"授权",titleFieldName:"标题字段名",messageFieldName:"消息字段名",editTemplate:"编辑模板:{{name}}",titleLabel:"标题",bodyLabel:"正文",titlePlaceholder:"通知标题...",bodyPlaceholder:"通知正文...",availableVariables:"可用变量",clickToInsert:"点击插入到正文光标位置",livePreview:"实时预览",hide:"隐藏",show:"显示",loadingPreview:"加载预览中...",enterTemplateContent:"输入模板内容以查看预览",titlePreview:"标题:",bodyPreview:"正文:",resetToDefault:"恢复默认",titleRequired:"标题为必填项",bodyRequired:"正文为必填项",notificationLog:"通知日志",showFailedOnly:"仅显示失败",last24Hours:"最近 24 小时",last7Days:"最近 7 天",last30Days:"最近 30 天",last90Days:"最近 90 天",justNow:"刚刚",noFailedNotifications:"没有失败的通知",noNotificationsLogged:"没有通知记录",unknownProvider:"未知提供商",logTitle:"标题",logMessage:"消息",logError:"错误",logProvider:"提供商:{{type}}",logTime:"时间:{{time}}",refresh:"刷新",clearOld:"清除旧记录",statsSummary:"最近 {{days}} 天:",statsNotifications:"条通知",statsSent:"{{count}} 条已发送",statsFailed:"{{count}} 条失败",eventTypes:{print_start:"打印已开始",print_complete:"打印完成",print_failed:"打印失败",print_stopped:"打印已停止",print_progress:"进度",printer_offline:"打印机离线",printer_error:"打印机错误",filament_low:"耗材不足",maintenance_due:"需要维护",test:"测试"},userEmail:{title:"通知",emailNotifications:"邮件通知",emailNotificationsDesc:"接收您自己打印任务的邮件通知。邮件将通过高级身份验证中配置的 SMTP 设置发送。",sendingTo:"通知将发送至",noEmailWarning:"您的账户没有邮件地址。请联系管理员添加。",printJobNotifications:"打印任务通知",printJobNotificationsDesc:"选择哪些事件会触发您提交的打印任务的邮件通知。",printJobStarts:"打印任务开始",printJobStartsDesc:"当您的打印任务开始时收到通知。",printJobFinishes:"打印任务完成",printJobFinishesDesc:"当您的打印任务成功完成时收到通知。",printErrors:"打印错误",printErrorsDesc:"当您的打印任务失败或遇到错误时收到通知。",printJobStops:"打印任务停止",printJobStopsDesc:"当您的打印任务被取消或停止时收到通知。",saveSuccess:"通知偏好设置已保存。",saveError:"保存通知偏好设置失败。"}},richTextEditor:{bold:"粗体",italic:"斜体",underline:"下划线",bulletList:"无序列表",numberedList:"有序列表",alignLeft:"左对齐",alignCenter:"居中对齐",alignRight:"右对齐",addLink:"添加链接",removeLink:"移除链接"},externalLinks:{noLinksConfigured:"未配置外部链接",deleteLink:"删除链接",removeCustomIcon:"移除自定义图标",openInNewTab:"在新标签页中打开",placeholders:{linkName:"我的链接"}},keyboardShortcuts:{title:"键盘快捷键",navigation:"导航",archivesSection:"归档",kProfilesSection:"K 值配置",generalSection:"通用",shortcuts:{goToPrinters:"前往打印机",goToArchives:"前往归档",goToQueue:"前往队列",goToStats:"前往统计",goToProfiles:"前往云端配置",goToSettings:"前往设置",focusSearch:"聚焦搜索",openUploadModal:"打开上传对话框",clearSelection:"清除选择 / 取消焦点",contextMenu:"卡片右键菜单",refreshProfiles:"刷新配置",newProfile:"新建配置",exitSelectionMode:"退出选择模式",showHelp:"显示此帮助"},footer:"按 Esc 或点击外部关闭"},notificationLog:{title:"通知日志",events:{printStarted:"打印开始",printComplete:"打印完成",printFailed:"打印失败",printStopped:"打印停止",progress:"进度",printerOffline:"打印机离线",printerError:"打印机错误",lowFilament:"耗材不足",maintenanceDue:"维护到期",test:"测试"},timeAgo:{justNow:"刚刚",minutesAgo:"{{minutes}} 分钟前",hoursAgo:"{{hours}} 小时前"}},restoreBackup:{title:"恢复备份",restoring:"恢复中...",restoreComplete:"恢复完成",restoreFailed:"恢复失败",importSettings:"从备份文件导入设置",pleaseWait:"请稍候,正在恢复您的数据",clickToSelect:"点击选择备份文件(.json 或 .zip)",howDuplicateHandling:"重复处理方式:",categories:{printers:"打印机",smartPlugs:"智能插座",notificationProviders:"通知提供商",filaments:"耗材",archives:"归档",pendingUploads:"待处理上传",settingsTemplates:"设置和模板"},matchingInfo:{printers:"按序列号匹配",smartPlugs:"按 IP 地址匹配",notificationProviders:"按名称匹配",filaments:"按名称 + 类型 + 品牌匹配",archives:"按内容哈希匹配",pendingUploads:"按文件名匹配",settingsTemplates:"始终覆盖"},replaceExisting:"替换现有数据",keepExisting:"保留现有数据",replaceDescription:"用备份数据覆盖已存在的项目",keepDescription:"仅恢复不存在的项目",caution:"注意:",cautionText:"覆盖将用备份数据替换您当前的配置。出于安全考虑,打印机访问码永远不会被覆盖。",itemsRestored:"已恢复项目",itemsSkipped:"已跳过项目",restored:"已恢复",skipped:"已跳过(已存在)",filesLabel:"文件(3MF、缩略图等)",newApiKeysGenerated:"已生成新 API 密钥",newApiKeysWarning:"这些密钥仅显示一次。请立即复制!",processingBackup:"处理备份文件中...",noDataFound:"备份文件中未找到可恢复的数据。",failedToRestore:"恢复备份失败。请检查文件格式。"},backupExport:{title:"导出备份",selectData:"选择要包含的数据",selectAll:"全选",selectNone:"全不选",categoryDescriptions:{settings:"语言、主题、更新偏好",notifications:"ntfy、Pushover、Discord 等",templates:"自定义消息模板",smartPlugs:"Tasmota 插座配置",externalLinks:"侧边栏外部服务链接",printers:"打印机信息(不含访问码)",plateDetection:"空打印板参考图像",filaments:"耗材类型和成本",maintenance:"自定义维护计划",archives:"所有打印数据 + 文件(3MF、缩略图、照片)",projects:"项目、材料清单和附件",pendingUploads:"虚拟打印机待审核的上传",apiKeys:"Webhook API 密钥(导入时生成新密钥)"},requiresPrinters:"需要选择打印机",zipFileWarning:"将创建 ZIP 文件。",zipFileDescription:"包括所有 3MF 文件、缩略图、延时摄影和照片。这可能需要一些时间并生成较大的文件。",includeAccessCodes:"包含访问码",includeAccessCodesDescription:"用于转移到另一台机器",includeAccessCodesWarning:"访问码将以明文形式包含。请妥善保管此备份文件!",categoriesSelected:"已选择 {{selectedCount}} 个类别"},pendingUploads:{placeholders:{notes:"添加关于此打印的备注..."},discardUpload:"丢弃上传",archiveAllUploads:"归档所有上传",discardAllUploads:"丢弃所有上传",archive:"归档",timeAgo:{justNow:"刚刚",minutesAgo:"{{minutes}} 分钟前",hoursAgo:"{{hours}} 小时前",daysAgo:"{{days}} 天前"}},apiBrowser:{placeholders:{requestBody:"JSON 请求体...",searchEndpoints:"搜索端点..."}},configureAmsSlot:{title:"配置 AMS 槽位",slotConfigured:"槽位已配置!",configuringSlot:"正在配置槽位:",slotLabel:"{{ams}} 槽位 {{slot}}",searchPresets:"搜索预设...",colorPlaceholder:"颜色名称或十六进制(例如:棕色、FF8800)",clearCustomColor:"清除自定义颜色",noCloudPresets:"无云端预设。登录拓竹云以同步。",noPresetsAvailable:"无可用预设。登录拓竹云或导入本地配置。",noMatchingPresets:"未找到匹配的预设。",custom:"自定义",builtin:"内置",settingsSentToPrinter:"设置已发送到打印机",filamentProfile:"耗材配置",kProfileLabel:"K 值配置(压力推进)",filteringFor:"筛选:{{material}}",noKProfile:"无 K 值配置(使用默认值 0.020)",noMatchingKProfiles:"未找到匹配的 K 值配置。将使用默认 K=0.020。",selectFilamentFirst:"请先选择耗材配置",kFromCalibration:"K={{value}}(来自打印机校准)",customColorLabel:"自定义颜色(可选)",presetColors:"{{name}} 颜色:",showLessColors:"显示更少颜色",showMoreColors:"显示更多颜色",clear:"清除",hexLabel:"十六进制:#{{hex}}",resetting:"重置中...",resetSlot:"重置槽位",cancel:"取消",configuring:"配置中...",configureSlot:"配置槽位"},githubBackup:{title:"GitHub 备份",history:"历史",downloadBackup:"下载备份",restoreBackup:"恢复备份",noBackupsYet:"暂无备份"},emailSettings:{placeholders:{fromName:"BamBuddy"}},tagManagement:{searchTags:"搜索标签...",renameTag:"重命名标签",deleteTag:"删除标签"},notificationTemplates:{placeholders:{title:"通知标题...",body:"通知正文..."}},batchTag:{placeholders:{newTag:"输入新标签..."}},photoGallery:{deletePhoto:"删除照片"},filamentHoverCard:{copySpoolUuid:"复制耗材 UUID"},kProfilesView:{hasNote:"有备注",copyProfile:"复制配置"},layout:{openMenu:"打开菜单",noPermissionSystemInfo:"您没有查看系统信息的权限"},dashboard:{dragToReorder:"拖动以重新排列",hideWidget:"隐藏小部件"},notificationProviderCard:{deleteNotificationProvider:"删除通知提供商"},fileManagerModal:{closeFileManager:"关闭文件管理器",sortFiles:"排序文件",goToParentFolder:"返回上级文件夹",threeView:"3D 视图"},embeddedCameraViewer:{refreshStream:"刷新流",close:"关闭",zoomOut:"缩小",resetZoom:"重置缩放",zoomIn:"放大",dragToResize:"拖动调整大小"},timelapseViewer:{skipBack5s:"后退 5 秒",skipForward5s:"前进 5 秒"},notificationProviders:{descriptions:{email:"SMTP 邮件通知",telegram:"通过 Telegram 机器人通知",discord:"通过 Webhook 发送到 Discord 频道",ntfy:"免费、可自托管的推送通知",pushover:"简单、可靠的推送通知",callmebot:"通过 CallMeBot 的免费 WhatsApp 通知",webhook:"通用 HTTP POST 到任意 URL"}},logViewer:{searchPlaceholder:"搜索消息或日志名称...",noLogEntries:"未找到日志条目"},switchbarPopover:{noSwitchesInSwitchbar:"切换栏中没有开关"},projectPageModal:{placeholders:{title:"标题",designer:"设计师",license:"许可证",description:"输入描述...",profileTitle:"配置标题",profileDescription:"配置描述..."}},spoolmanSettings:{},time:{unknown:"-",waiting:"等待中",justNow:"刚刚",now:"现在",minsAgo:"{{count}} 分钟前",inMins:"{{count}} 分钟后",hoursAgo:"{{count}} 小时前",inHours:"{{count}} 小时后",daysAgo:"{{count}} 天前",inDays:"{{count}} 天后"},spoolbuddy:{nav:{dashboard:"仪表板",ams:"AMS",inventory:"库存",writeTag:"写入",settings:"设置"},status:{nfcReady:"NFC 就绪",nfcOff:"NFC 关闭",offline:"离线",online:"在线",noPrinters:"无打印机",deviceOffline:"设备离线",waitingConnection:"等待设备连接...",systemReady:"系统就绪",status:"状态"},dashboard:{readyToScan:"准备扫描",idleMessage:"将耗材放在秤上以识别",nfcHint:"NFC 标签将自动读取",device:"设备",syncWeight:"同步重量",weightSynced:"已同步!",unknownTag:"未知标签",newTag:"检测到新标签",onScale:"在秤上",linkSpool:"链接到耗材",linkTagTitle:"将标签链接到耗材",linkTag:"链接标签",selectSpool:"选择要链接此标签的耗材:",noUntagged:"未找到没有标签的耗材",tagDetected:"检测到标签",noTag:"无标签",tagId:"标签",grossWeight:"毛重",spoolSize:"耗材盘尺寸",close:"关闭",currentSpool:"当前耗材"},modal:{spoolDetected:"检测到耗材",assignToAms:"分配到 AMS",syncWeight:"同步重量",weightSynced:"已同步!",syncing:"同步中...",newTagDetected:"检测到新标签",addToInventory:"添加到库存",assignToAmsTitle:"分配到 AMS",selectSlot:"选择槽位",assign:"分配",assigning:"分配中...",assignSuccess:"已分配!",assignError:"分配耗材失败。请重试。",noPrinterSelected:"选择打印机...",noAmsDetected:"此打印机未检测到 AMS",slot:"槽位"},weight:{noReading:"无读数",stable:"稳定",measuring:"测量中...",tare:"去皮",calibrate:"校准"},spool:{remaining:"剩余",material:"材料",brand:"品牌",color:"颜色",coreWeight:"空盘",labelWeight:"标签",scaleWeight:"秤重",netWeight:"净重",lastUsed:"上次使用"},ams:{noData:"未检测到 AMS",connectAms:"连接 AMS 以查看耗材槽位",noPrinter:"未选择打印机",selectPrinter:"从顶部栏选择打印机",printerDisconnected:"打印机已断开",humidity:"湿度",level:"余量",active:"活跃",slot:"槽位",empty:"空"},inventory:{search:"搜索耗材...",empty:"库存中没有耗材",noResults:"没有匹配的耗材",spools:"个耗材",addSpool:"添加耗材"},settings:{tabDevice:"设备",tabDisplay:"显示",tabScale:"秤",tabUpdates:"更新",nfcReader:"NFC 读卡器",type:"类型",connection:"连接",notConnected:"不适用",deviceInfo:"设备信息",hostname:"主机",uptime:"运行时间",systemConfig:"后端与认证",backendUrl:"Bambuddy 后端 URL",apiToken:"API 令牌",apiTokenPlaceholder:"输入 API 令牌",saveConfig:"保存配置",systemQueued:"配置已加入队列。",nfcDiagnostic:"NFC 诊断",scaleDiagnostic:"秤诊断",readTagDiagnostic:"读取标签诊断",testNfc:"测试读卡器",testScale:"测试精度",testReadTag:"读取标签",systemFieldsRequired:"后端 URL 为必填项。",brightness:"亮度",saved:"已保存",noBacklight:"未检测到 DSI 背光。亮度控制需要 DSI 显示屏。",screenBlank:"屏幕熄灭超时",screenBlankDesc:"不活动后屏幕关闭。触摸唤醒。",displayNote:"亮度作为软件滤镜应用。",scaleCalibration:"秤校准",currentWeight:"当前重量",tareOffset:"去皮",calFactor:"系数",knownWeight:"已知重量",calStep1:"移除秤上所有物品并按设置零点。",calStep2:"将已知重量放在秤上。",setZero:"设置零点",calibrateNow:"校准",calibrated:"已校准",tareSet:"去皮命令已发送。等待设备响应...",tareFailed:"发送去皮命令失败",zeroSet:"零点已设置。将已知重量放在秤上。",calibrationDone:"校准完成!",calibrationFailed:"校准失败",lastCalibrated:"上次校准",stable:"稳定",settling:"稳定中...",firmware:"固件",scale:"秤",noDevice:"未找到 SpoolBuddy 设备",daemonVersion:"守护进程版本",currentVersion:"当前",versionPending:"等待守护进程...",checking:"检查中...",checkUpdates:"检查更新",updateAvailable:"有可用更新",updateInstructions:"通过 SSH 更新:运行 SpoolBuddy 安装脚本进行升级。",upToDate:"已是最新",includeBeta:"包含测试版本"},writeTag:{tabExisting:"现有耗材",tabNew:"新耗材",tabReplace:"替换标签",searchPlaceholder:"按材料、颜色、品牌搜索...",noUntaggedSpools:"没有无标签的耗材",noTaggedSpools:"没有有标签的耗材",selectSpool:"选择一个耗材,然后将空白 NTAG 放在读卡器上",placeTag:"将 NTAG 放在读卡器上",tagReady:"检测到标签 — 准备写入",writeTag:"写入标签",replaceTag:"替换标签",writing:"写入标签中...",waiting:"等待 SpoolBuddy...",writeSuccess:"标签写入成功!",writeFailed:"写入失败",queueFailed:"排队写入命令失败",tryAgain:"重试",cancel:"取消",replaceWarning:"旧标签将被取消链接。新标签将替换它。",deviceOffline:"SpoolBuddy 离线",material:"材料",colorName:"颜色名称",color:"颜色",brand:"品牌",weight:"重量 (g)",createSpool:"创建耗材",creating:"创建中...",spoolCreated:"耗材已创建!准备写入。",createFailed:"创建耗材失败"},quickMenu:{printerPower:"打印机电源",systemControls:"系统",restartDaemon:"重启守护进程",restartBrowser:"重启浏览器",reboot:"重启",shutdown:"关机",swipeToClose:"向下滑动关闭",confirmTitle:"确认",confirmShutdown:"确定要关闭SpoolBuddy吗?您需要物理访问才能重新开启。",confirmReboot:"确定要重启SpoolBuddy吗?",confirmRestartDaemon:"重启SpoolBuddy守护进程?NFC和秤将暂时不可用。",confirmRestartBrowser:"重启kiosk浏览器?屏幕将短暂变黑。",confirm:"确认",confirmPlugOn:"开启 {{name}}?",confirmPlugOff:"关闭 {{name}}?",turnOn:"开启",turnOff:"关闭"}},bugReport:{title:"报告错误",description:"描述",descriptionPlaceholder:"出了什么问题?请描述问题...",email:"邮箱(可选)",emailPlaceholder:"your@email.com",emailPrivacy:"如果提供,您的邮箱将包含在GitHub Issue的折叠部分中,以便维护者后续跟进。",screenshot:"截图",uploadOrPaste:"上传、粘贴或拖拽图片",dataCollectedSummary:"报告中包含哪些数据?",dataIncluded:"包含:",dataIncludedList:"应用版本、操作系统、架构、Python版本、数据库统计(仅计数)、打印机型号、喷嘴数量、固件版本、连接状态、集成状态(Spoolman、MQTT、HA)、非敏感设置、网络接口数量、Docker详情、依赖版本。",dataNeverIncluded:"绝不包含:",dataNeverIncludedList:"打印机名称、序列号、访问代码、密码、IP地址、邮箱地址、API密钥、令牌、Webhook URL、主机名或用户名。",submit:"提交",startLogging:"开始调试日志",stepEnableLogging:"调试日志已启用",stepReproduce:"请现在重现问题",stepStopLogging:"停止并提交报告",stopAndSubmit:"停止并提交",maxDuration:"{{minutes}}分钟后自动停止",stoppingLogs:"正在收集日志并提交...",submitting:"正在提交错误报告...",submitSuccess:"错误报告提交成功!",submitFailed:"提交错误报告失败",thankYou:"谢谢!",submitted:"您的错误报告已提交。",viewIssue:"查看Issue",unexpectedError:"发生了意外错误"},failureDetection:{title:"AI 故障检测",description:"通过自托管的 Obico ML API 监控打印,并对检测到的故障自动采取行动。",mlUrl:"Obico ML API 地址",mlUrlHint:"您自托管的 Obico ml_api 容器的基础 URL(例如 http://192.168.1.10:3333)。",test:"测试",testSuccess:"ML API 可访问且正常。",testFailed:"无法访问 ML API。",sensitivity:"灵敏度",sensitivityLow:"低(减少误报)",sensitivityMedium:"中(平衡)",sensitivityHigh:"高(更早检测,更多误报)",sensitivityHint:"调整触发警告和故障的置信度阈值。",action:"检测到故障时的操作",actionNotify:"仅通知",actionPause:"暂停打印",actionPauseOff:"暂停并切断电源",pollInterval:"检查间隔(秒)",pollIntervalHint:"打印过程中每台打印机的检查频率。最小 5 秒,最大 120 秒。",externalUrlMissing:"External URL is not set.",externalUrlHint:"The ML API fetches the camera snapshot by URL. Set the External URL in General settings so the ML API container can reach Bambuddy.",perPrinterTitle:"监控的打印机",perPrinterHint:"选择检测服务要监视哪些打印机。",monitorAll:"监控所有已连接的打印机",statusTitle:"状态",serviceRunning:"服务运行中",thresholds:"低 / 高阈值",activePrinters:"活动打印",noActivePrints:"当前没有正在进行的打印。",historyTitle:"最近检测",noHistory:"暂无检测记录。"}},Que={nav:{printers:"印表機",archives:"歸檔",queue:"佇列",stats:"統計",profiles:"設定檔案",maintenance:"維護",projects:"專案",inventory:"耗材",files:"檔案管理器",notifications:"通知",settings:"設定",system:"系統",collapseSidebar:"收起側邊欄",expandSidebar:"展開側邊欄",update:"更新",updateAvailable:"有可用更新:v{{version}}",updateAvailableBanner:"版本 {{version}} 已發布!",viewUpdate:"檢視更新",viewOnGithub:"在 GitHub 上檢視",keyboardShortcuts:"鍵盤快捷鍵 (?)",switchToLight:"切換到淺色模式",switchToDark:"切換到深色模式",smartSwitches:"智慧開關",logout:"登出"},common:{save:"儲存",saving:"儲存中...",cancel:"取消",delete:"刪除",edit:"編輯",add:"新增",close:"關閉",confirm:"確認",loading:"載入中...",error:"錯誤",success:"成功",warning:"警告",enabled:"已啟用",disabled:"已停用",yes:"是",no:"否",on:"開",off:"關",all:"全部",none:"無",search:"搜尋",filter:"篩選",sort:"排序",refresh:"重新整理",download:"下載",upload:"上傳",uploading:"上傳中...",uploadFailed:"上傳失敗",actions:"操作",status:"狀態",name:"名稱",description:"描述",date:"日期",time:"時間",hours:"小時",minutes:"分鐘",seconds:"秒",days:"天",enable:"啟用",disable:"停用",permissions:"權限",noPrinters:"未設定印表機",noData:"尚無資料",linkNotFound:"未找到連結",required:"必填",optional:"可選",dismiss:"關閉",apply:"套用",reset:"重設",export:"匯出",import:"匯入",clear:"清除",selectAll:"全選",deselectAll:"取消全選",noChange:"— 不更改 —",unchanged:"未更改",unassigned:"未分配",unknown:"未知",unknownError:"未知錯誤",today:"今天",tomorrow:"明天",asap:"儘快",overdue:"已逾期",now:"現在",collapse:"收起",expand:"展開",viewArchive:"檢視歸檔",viewInFileManager:"在檔案管理器中檢視",addedBy:"由 {{username}} 新增",prints:"次列印",more:"還有 {{count}} 個",ascending:"升序",descending:"降序",back:"返回",copy:"複製",copied:"已複製!",printer:"印表機",remove:"移除",type:"類型",print:"列印",rename:"重新命名",move:"移動",create:"建立",duplicate:"複製",left:"左",right:"右"},printers:{title:"印表機",addPrinter:"新增印表機",editPrinter:"編輯印表機",deletePrinter:"刪除印表機",printerName:"印表機名稱",serialNumber:"序列號",ipAddress:"IP 位址 / 主機名稱",accessCode:"存取碼",model:"型號",nozzleCount:"噴嘴數量",autoArchive:"自動歸檔",status:{available:"可用",idle:"空閒",printing:"列印中",paused:"已暫停",offline:"離線",problem:"故障",error:"錯誤",finished:"已完成",unknown:"未知"},temperatures:{nozzle:"噴嘴",bed:"熱床",chamber:"腔室"},progress:"{{percent}}% 完成",timeRemaining:"剩餘 {{time}}",deleteConfirm:'確定要刪除"{{name}}"嗎?',maintenanceOk:"維護正常",maintenanceWarning:"{{count}} 個警告",maintenanceWarning_plural:"{{count}} 個警告",maintenanceDue:"{{count}} 個到期",maintenanceDue_plural:"{{count}} 個到期",sort:{name:"名稱",status:"狀態",model:"型號",location:"位置",ascending:"升序排列",descending:"降序排列"},cardSize:{small:"小卡片",medium:"中卡片",large:"大卡片",extraLarge:"超大卡片"},hideOffline:"隱藏離線",nextAvailable:"下一個可用",powerOn:"開機",offlinePrintersWithPlugs:"帶智慧插座的離線印表機",noPrintersConfigured:"尚未設定印表機",search:"搜尋印表機...",noSearchResults:"沒有印表機符合您的搜尋或篩選條件",filter:{allStatuses:"所有狀態",allLocations:"所有位置"},readyToPrint:"準備列印",external:"外部",extL:"外接左",extR:"外接右",deleteArchives:"刪除列印歸檔",noLabel:"無標籤",printPreview:"列印預覽",width:"寬度",height:"高度",noObjectsFound:"未找到物件",objectsLoadedOnPrintStart:"物件在列印開始時載入",willBeSkipped:"將被跳過",name:"名稱",serialCannotBeChanged:"序列號無法更改",locationHelp:"用於分組印表機和篩選佇列任務",wifiSignal:{veryWeak:"非常弱",weak:"弱",fair:"一般",good:"良好",excellent:"優秀"},maintenanceUpToDate:"所有維護均已完成 - 點選檢視",chamberLightOn:"開啟腔室燈",chamberLightOff:"關閉腔室燈",files:"檔案",browseFiles:"瀏覽印表機檔案",autoOffAfterPrint:"列印後自動關機",autoOffExecuted:"已執行自動關機 - 開啟印表機以重設",hmsErrors:"HMS 錯誤",viewHmsErrors:"檢視 {{count}} 個 HMS 錯誤",resume:"繼續",pause:"暫停",stop:"停止",camera:"攝影機",skipObject:"跳過物件",reconnect:"重新連線",forceRefresh:"強制重新整理",forceRefreshSuccess:"已請求重新整理",mqttDebug:"MQTT 偵錯",printerInformation:"印表機資訊",copyToClipboard:"複製",copied:"已複製!",state:"狀態",wifiSignalLabel:"Wi-Fi 訊號",developerMode:"開發者模式",enabled:"已啟用",disabled:"已停用",addedOn:"新增日期",sdCard:"SD 卡",inserted:"已插入",notInserted:"未插入",totalPrintHours:"列印時長",activeNozzle:"目前:{{nozzle}} 噴嘴",nozzleRack:"噴嘴架",nozzleDocked:"已停靠",nozzleMounted:"已安裝",nozzleActive:"使用中",nozzleIdle:"空閒",nozzleDiameter:"直徑",nozzleType:"類型",nozzleStatus:"狀態",nozzleFilament:"耗材",nozzleWear:"磨損",nozzleMaxTemp:"最高溫度",nozzleSerial:"序列號",nozzleHardenedSteel:"硬化鋼",nozzleStainlessSteel:"不鏽鋼",nozzleTungstenCarbide:"碳化鎢",nozzleFlow:"流量",nozzleHighFlow:"高流量",nozzleStandardFlow:"標準",firmwareUpdate:"韌體更新",firmwareInstructions:"在印表機觸控式螢幕上,前往",firmwareNav:"導航到",settings:"設定",firmware:"韌體",discoverPrinters:"發現印表機",searching:"搜尋中...",manualEntry:"手動輸入",addFromCloud:"從雲端新增",toast:{printerDeleted:"印表機已刪除",missingSpoolAssignment:"已在{{printer}}上開始列印。以下料槽未分配耗材: {{slots}}",printerAdded:"印表機已新增",printerUpdated:"印表機已更新",failedToDelete:"刪除印表機失敗",failedToAdd:"新增印表機失敗",failedToUpdate:"更新印表機失敗",commandSent:"命令已傳送",failedToSendCommand:"傳送命令失敗",turnedOn:"{{name}} 已開啟",failedToPowerOn:"開啟 {{name}} 失敗",scriptTriggered:"腳本已觸發",printStopped:"列印已停止",printPaused:"列印已暫停",printResumed:"列印已繼續",referenceDeleted:"參考已刪除",detectionAreaSaved:"檢測區域已儲存",failedToRunScript:"執行腳本失敗",failedToStopPrint:"停止列印失敗",failedToPausePrint:"暫停列印失敗",failedToResumePrint:"繼續列印失敗",failedToControlChamberLight:"控制腔室燈失敗",failedToSetSpeed:"設定列印速度失敗",failedToUpdateSetting:"更新設定失敗",failedToSkipObjects:"跳過物件失敗",failedToRereadRfid:"重新讀取 RFID 失敗",failedToCheckPlate:"檢查列印板失敗",failedToUpdateLabel:"更新標籤失敗",failedToDeleteReference:"刪除參考失敗",failedToSaveDetectionArea:"儲存檢測區域失敗",plateCheckEnabled:"列印板檢查已啟用",plateCheckDisabled:"列印板檢查已停用",calibrationSaved:"校準已儲存!",calibrationFailed:"校準失敗",rfidRereadInitiated:"已發起 RFID 重新讀取"},connection:{connected:"已連線",offline:"離線"},plateStatus:{markCleared:"將列印板標記為已清理",cleared:"列印板已清理",notCleared:"列印板未清理",inUse:"列印板使用中"},queue:{inQueue:"佇列中有 {{count}} 個列印任務",inQueue_plural:"佇列中有 {{count}} 個列印任務"},controls:"控制",rfid:{reread:"重新讀取 RFID"},bedJog:{title:"移動熱床",bed:"熱床",step:"步長 (mm)",up:"熱床上移",down:"熱床下移",disabledWhilePrinting:"列印中已停用",notHomedTitle:"印表機未歸零",notHomedMessage:"印表機自上次列印以來尚未歸零。請先執行自動歸零以確保安全定位(先停放噴頭,然後歸零 X、Y 和 Z),或者直接移動 — 軟限位將被繞過。",homeZ:"自動歸零",moveAnyway:"強制移動",homingStarted:"印表機自動歸零中…"},permission:{noAdd:"您沒有新增印表機的權限",noEdit:"您沒有編輯印表機的權限",noDelete:"您沒有刪除印表機的權限",noControl:"您沒有控制印表機的權限",noFiles:"您沒有存取印表機檔案的權限",noAmsRfid:"您沒有重新讀取 AMS RFID 的權限",noSmartPlugControl:"您沒有控制智慧插座的權限",noCamera:"您沒有檢視攝影機的權限"},modal:{addTitle:"新增印表機",editTitle:"編輯印表機",myPrinter:"我的印表機",selectModel:"選擇型號...",locationGroup:"位置 / 分組(可選)",locationPlaceholder:"例如:工作室、辦公室、地下室",autoArchiveLabel:"自動歸檔已完成的列印",fromPrinterSettings:"來自印表機設定",modelOptional:"型號(可選)",saveChanges:"儲存更改"},skipObjects:{tooltip:"跳過物件",onlyWhilePrinting:"跳過物件(僅在列印時)",requiresMultiple:"跳過物件(需要2個以上物件)",title:"跳過物件",matchIdsInfo:"將 ID 與印表機螢幕上的 ID 進行對照",printerShowsIds:"印表機螢幕上顯示列印板上物件的 ID",skipSelected:"跳過所選",skipping:"跳過中...",noObjectsSelected:"未選擇物件",selectObjectsToSkip:"選擇要從目前列印中跳過的物件",skipped:"已跳過",objectsSkipped:"物件已跳過",activeCount:"{{count}} 個活躍",waitForLayer:"等待第2層以上才能跳過物件(目前第 {{layer}} 層)",skip:"跳過",confirmTitle:"跳過物件?",confirmMessage:'確定要跳過"{{name}}"嗎?此操作無法復原。'},confirm:{deleteTitle:"刪除印表機",deleteMessage:'確定要刪除"{{name}}"嗎?這將移除所有連線設定。',deleteArchivesNote:"此印表機的所有列印歷史將被永久刪除。",keepArchivesNote:"列印歷史將保留,但不再與此印表機連結。",stopTitle:"停止列印",stopMessage:'確定要停止"{{name}}"上的目前列印嗎?這將取消列印任務。',stopButton:"停止列印",pauseTitle:"暫停列印",pauseMessage:'確定要暫停"{{name}}"上的目前列印嗎?',pauseButton:"暫停列印",resumeTitle:"繼續列印",resumeMessage:'確定要繼續"{{name}}"上的列印嗎?',resumeButton:"繼續列印",powerOnTitle:"開啟印表機",powerOnMessage:'確定要開啟"{{name}}"的電源嗎?',powerOnButton:"開機",powerOffTitle:"關閉印表機",powerOffMessage:'確定要關閉"{{name}}"的電源嗎?',powerOffWarning:'警告:"{{name}}"正在列印中!確定要關閉電源嗎?這將中斷列印並可能損壞印表機。',powerOffButton:"關機"},bulk:{select:"選擇",selectAll:"全選",selectByLocation:"按位置選擇",selected:"已選擇{{count}}臺",actions:{stop:"停止",pause:"暫停",resume:"繼續",clearPlate:"清除列印床",clearHMS:"清除通知"},confirm:{stopTitle:"停止{{count}}個列印任務",stopMessage:"這將取消{{count}}臺印表機上的活動列印任務。此操作無法復原。",stopButton:"全部停止",pauseTitle:"暫停{{count}}個列印任務",pauseMessage:"這將暫停{{count}}臺印表機上的活動列印任務。",pauseButton:"全部暫停",clearPlateTitle:"清除{{count}}個列印床",clearPlateMessage:"這將清除{{count}}臺印表機的列印床,可能會觸發佇列中的任務。",clearPlateButton:"全部清除"},success:"{{action}}已在{{count}}臺印表機上完成",partial:"{{succeeded}}成功,{{failed}}失敗",noneApplicable:"沒有選中的印表機處於適合此操作的狀態",selectByState:"按狀態選擇"},discovery:{title:"發現印表機",searching:"搜尋中...",scanning:"掃描中...",scanProgress:"掃描中... {{scanned}}/{{total}}",foundPrinters:"發現 {{count}} 臺印表機",noPrintersFound:"未找到印表機",noPrintersFoundSubnet:"在指定子網中未找到印表機。",noPrintersFoundNetwork:"在網路上未找到印表機。",allConfigured:"所有發現的印表機已設定完畢。",alreadyAdded:"已新增",select:"選擇",manualEntry:"手動輸入",addFromCloud:"從雲端新增",subnetToScan:"要掃描的子網",dockerNote:"偵測到 Docker 環境。請以 CIDR 格式輸入印表機所在子網。需要在 docker-compose.yml 中設定 network_mode: host。",scanSubnet:"掃描子網查詢印表機",discoverNetwork:"在網路上發現印表機",scanningSubnet:"正在掃描子網查詢拓竹印表機...",scanningNetwork:"正在掃描網路...",serialRequired:"需要序列號",unknown:"未知",failedToStart:"啟動發現失敗"},drying:{start:"開始乾燥",stop:"停止乾燥",temperature:"溫度",duration:"時長",hours:"小時",timeRemaining:"剩餘 {{time}}",active:"乾燥中",notSupported:"不支援乾燥",powerRequired:"連線AMS電源介面卡以啟用乾燥",startingDrying:"正在啟動乾燥...",stoppingDrying:"正在停止乾燥...",rotateTray:"乾燥時旋轉料盤"},filaments:"耗材",openCameraOverlay:"開啟攝影機疊加層",openCameraWindow:"在新視窗中開啟攝影機",firmwareUpdateAvailable:"韌體更新可用:{{current}} → {{latest}}",firmwareUpToDate:"韌體 {{version}} — 已是最新",firmwareUpdateButton:"更新",plateDetection:{noPermission:"您沒有更新印表機的權限",enabledClick:"列印板檢查已啟用 - 點選停用",disabledClick:"列印板檢查已停用 - 點選啟用",manageCalibration:"管理列印板檢測校準",calibrationRequired:"需要校準",calibrationInstructions:"請確保列印板完全空置,然後點選校準。",calibrationDescription:"校準會拍攝空置列印板的參考影像。後續檢查將與此參考進行比較以檢測物體。",calibrationTip:"提示:您最多可以為不同的列印板儲存5個校準。系統會在檢查時自動使用最佳匹配。",plateEmpty:"列印板似乎是空的",objectsDetected:"在列印板上偵測到物體",confidence:"置信度",difference:"差異",analysisPreview:"分析預覽:",analysisLegend:"綠色框 = 檢測區域,紅色覆蓋 = 與校準的差異",savedReferences:"已儲存的參考 ({{count}}/{{max}})",deleteReference:"刪除參考",labelPlaceholder:"標籤...",clickToEdit:"{{label}} - 點選編輯",clickToAddLabel:"點選新增標籤"},speed:{title:"列印速度",silent:"靜音 (50%)",standard:"標準 (100%)",sport:"運動 (124%)",ludicrous:"瘋狂 (166%)"},airduct:{title:"風道模式",cooling:"製冷",heating:"加熱"},noSdCard:"無SD",door:{open:"開",closed:"關"},fans:{partCooling:"零件冷卻風扇",auxiliary:"輔助風扇",chamber:"腔室風扇"},clickToViewHmsErrors:"點選檢視 HMS 錯誤",estimatedCompletion:"預計完成時間",plateNumber:"板 {{number}}",slotOptions:"槽位選項",amsPopup:{friendlyName:"AMS 名稱",friendlyNamePlaceholder:"例如 AMS 友好名稱",serialNumber:"序列號",firmwareVersion:"韌體",save:"儲存",clear:"清除",noEditPermission:"您沒有重新命名 AMS 單元的權限"},firmwareModal:{title:"韌體更新",titleUpToDate:"韌體資訊",currentVersion:"目前版本:",latestVersion:"最新版本:",releaseNotes:"發布說明",checkingPrereqs:"正在檢查前提條件...",sdCardReady:"SD 卡已就緒。點選下方上傳韌體。",uploadedSuccess:"韌體已上傳到 SD 卡!",applyInstructions:"在印表機上套用更新:",step1:"在印表機觸控式螢幕上,前往設定",step2:"導航到韌體",step3:"選擇從 SD 卡更新",step4:"更新將需要 10-20 分鐘",done:"完成",starting:"啟動中...",uploadFirmware:"上傳韌體",uploadFailed:"上傳啟動失敗:{{error}}",uploadedToast:"韌體已上傳!請在印表機螢幕上觸發更新。",availableVersions:"可用版本",usable:"可用",unavailable:"不可用",installed:"已安裝",newerBadge:"較新",olderBadge:"較舊",currentBadge:"目前"},accessCodePlaceholder:"留空以保持目前值",roi:{title:"檢測區域 (ROI)",xStart:"X 起點",yStart:"Y 起點",width:"寬度",height:"高度",instruction:"調整檢測區域以聚焦到列印板。預覽中的綠色框顯示目前區域。"},developerModeWarning:"以下印表機未啟用開發者區域網路模式:{{names}}。某些功能可能無法使用。",howToEnable:"如何啟用",incompatibleFile:"此檔案是為 {{slicedFor}} 切片的,但該印表機是 {{printerModel}}",dropNotPrintable:"只能列印 .gcode 和 .gcode.3mf 檔案",dropToPrint:"拖放以列印",cannotPrint:"印表機忙碌"},archives:{title:"列印歸檔",searchPlaceholder:"搜尋歸檔...",filterByPrinter:"按印表機篩選",filterByStatus:"按狀態篩選",sortBy:"排序方式",sortNewest:"最新優先",sortOldest:"最舊優先",sortName:"名稱",sortDuration:"時長",sortLargest:"最大優先",sortSmallest:"最小優先",sortSize:"大小",noArchives:"未找到歸檔",noArchivesSearch:"沒有匹配搜尋的歸檔",originalPrintNotVisible:"原始列印不可見 - 請嘗試清除篩選條件",noArchivesYet:"尚無歸檔",prints:"條列印",pagination:{showing:"顯示",to:"至",of:"共",show:"每頁",page:"頁",all:"全部"},loadingArchives:"載入歸檔中...",releaseToUpload:"放開以上傳",showAll:"顯示全部",showFavoritesOnly:"僅顯示收藏",gridView:"網格檢視",listView:"列表檢視",calendarView:"日曆檢視",logView:"列印日誌",manageTags:"管理標籤",showFailedPrints:"顯示失敗的列印",hideFailedPrints:"隱藏失敗的列印",hideDuplicates:"隱藏重複項",viewOriginalPrint:"點選檢視原始列印 (#{{id}})",printTime:"列印時間",filamentUsed:"耗材用量",cost:"成本",reprint:"重新列印",preview:"預覽",deleteArchive:"刪除歸檔",deleteConfirm:"確定要刪除此歸檔嗎?",favorite:"收藏",unfavorite:"取消收藏",viewDetails:"檢視詳情",status:{completed:"已完成",failed:"失敗",stopped:"已停止"},toast:{source3mfAttached:"源 3MF 已附加:{{filename}}",failedUploadSource3mf:"上傳源 3MF 失敗",source3mfRemoved:"源 3MF 已移除",failedRemoveSource3mf:"移除源 3MF 失敗",f3dAttached:"F3D 已附加:{{filename}}",failedUploadF3d:"上傳 F3D 失敗",f3dRemoved:"F3D 已移除",failedRemoveF3d:"移除 F3D 失敗",timelapseAttached:"縮時攝影已附加:{{filename}}",timelapseAlreadyAttached:"縮時攝影已附加",noMatchingTimelapse:"未找到匹配的縮時攝影",failedScanTimelapse:"掃描縮時攝影失敗",failedAttachTimelapse:"附加縮時攝影失敗",timelapseRemoved:"縮時攝影已移除",failedRemoveTimelapse:"移除縮時攝影失敗",timelapseUploaded:"縮時攝影已上傳:{{filename}}",failedUploadTimelapse:"上傳縮時攝影失敗",archiveDeleted:"歸檔已刪除",failedDeleteArchive:"刪除歸檔失敗",addedToFavorites:"已新增到收藏",removedFromFavorites:"已從收藏中移除",projectUpdated:"專案已更新",failedUpdateProject:"更新項目失敗",linkCopied:"連結已複製到剪貼簿",failedCopyLink:"複製連結失敗",photoDeleted:"照片已刪除",failedDeletePhoto:"刪除照片失敗",failedDeleteArchives:"刪除歸檔失敗",failedUpdateFavorites:"更新收藏失敗",exportDownloaded:"匯出已下載",exportFailed:"匯出失敗"},menu:{print:"列印",schedule:"排程",openInBambuStudio:"在切片軟體中開啟",slice:"切片",externalLink:"外部連結",viewOnMakerWorld:"在 MakerWorld 上檢視",preview3d:"3D 預覽",viewTimelapse:"檢視縮時攝影",scanForTimelapse:"掃描縮時攝影",uploadTimelapse:"上傳縮時攝影",removeTimelapse:"移除縮時攝影",downloadSource3mf:"下載源 3MF",uploadSource3mf:"上傳源 3MF",replaceSource3mf:"替換源 3MF",removeSource3mf:"移除源 3MF",uploadF3d:"上傳 F3D",replaceF3d:"替換 F3D",downloadF3d:"下載 F3D",removeF3d:"移除 F3D",download:"下載",copyDownloadLink:"複製下載連結",qrCode:"QR Code",viewPhotos:"檢視照片",viewPhotosCount:"檢視照片 ({{count}})",projectPage:"專案頁面",addToFavorites:"新增到收藏",removeFromFavorites:"從收藏中移除",edit:"編輯",goToProject:"前往專案:{{name}}",addToProject:"新增到專案",removeFromProject:"從專案中移除",loading:"載入中...",noProjectsAvailable:"無可用專案",select:"選擇",deselect:"取消選擇",delete:"刪除"},permission:{noReprint:"您沒有重新列印此歸檔的權限",noAddToQueue:"您沒有新增到佇列的權限",noUpdateArchives:"您沒有更新歸檔的權限",noUploadFiles:"您沒有上傳檔案的權限",noDownload:"您沒有下載歸檔的權限",noCopyLink:"您沒有複製下載連結的權限",noDelete:"您沒有刪除此歸檔的權限",noCreate:"您沒有建立歸檔的權限"},card:{previousPlate:"上一個板",nextPlate:"下一個板",plateNumber:"板 {{index}}",moreOptions:"右鍵檢視更多選項",addToFavorites:"新增到收藏",removeFromFavorites:"從收藏中移除",cancelled:"已取消",failed:"失敗",duplicate:"重複",duplicateTitle:"此模型之前已列印過",openSource3mf:"在 Bambu Studio 中開啟源 3MF(右鍵檢視更多選項)",downloadF3d:"下載 Fusion 360 設計檔案",viewTimelapse:"檢視縮時攝影",viewPhoto:"檢視 1 張照片",viewPhotos:"檢視 {{count}} 張照片",openFolder:"開啟資料夾:{{name}}",slicedFile:"已切片檔案 - 可以列印",sourceFile:"僅原始檔 - 無 AMS 對應可用",gcode:"GCODE",source:"原始檔",project:"專案:{{name}}",estimated:"預計:{{time}}",actual:"實際:{{time}}",accuracy:"準確度:{{percent}}%",filament:"{{weight}}g",layer:"{{count}} 層",layers:"{{count}} 層",object:"{{count}} 個物件",objects:"{{count}} 個物件",slicedFor:"為 {{model}} 切片",uploadedBy:"上傳者",noPermissionReprint:"您沒有重新列印的權限",noFileForReprint:"無可用的 3MF 檔案 — 列印紀錄時無法從印表機下載該檔案",noPermissionEdit:"您沒有編輯歸檔的權限",noPermissionDelete:"您沒有刪除歸檔的權限",reprint:"重新列印",schedulePrint:"排程列印",schedule:"排程",openInBambuStudio:"在切片軟體中開啟",openInBambuStudioToSlice:"在切片軟體中開啟進行切片",slice:"切片",externalLink:"外部連結",makerWorld:"MakerWorld:{{designer}}",viewProject:"檢視專案",noExternalLink:"無外部連結",preview3d:"3D 預覽",download:"下載",edit:"編輯",delete:"刪除"},modal:{deleteArchive:"刪除歸檔",deleteConfirm:'確定要刪除"{{name}}"嗎?此操作無法復原。',deleteButton:"刪除",removeSource3mf:"移除源 3MF",removeSource3mfConfirm:'確定要從"{{name}}"中移除源 3MF 檔案嗎?這將刪除原始切片專案檔案。',removeButton:"移除",removeF3d:"移除 F3D",removeF3dConfirm:'確定要從"{{name}}"中移除 Fusion 360 設計檔案嗎?',removeTimelapse:"移除縮時攝影",removeTimelapseConfirm:'確定要從"{{name}}"中移除縮時攝影影片嗎?',timelapse:"{{name}} - 縮時攝影",selectTimelapse:"選擇縮時攝影",selectTimelapseDesc:"未找到自動匹配。請選擇此列印的縮時攝影:",deleteArchives:"刪除歸檔",deleteArchivesConfirm:"確定要刪除 {{count}} 個歸檔嗎?此操作無法復原。",deleteCount:"刪除 {{count}} 個"},page:{title:"歸檔",printsCount:"{{filtered}} / {{total}} 次列印",dropFilesHere:"將 .3mf 檔案拖放到此處",releaseToUpload:"放開以上傳",only3mfSupported:"僅支援 .3mf 檔案",close:"關閉",selected:"已選擇 {{count}} 個",selectAll:"全選",tags:"標籤",project:"專案",favorite:"收藏",delete:"刪除",toggledFavorites:"已切換 {{count}} 個歸檔的收藏狀態",failedUpdateFavorites:"更新收藏失敗",archivesDeleted:"已刪除 {{count}} 個歸檔",failedDeleteArchives:"刪除歸檔失敗",photoDeleted:"照片已刪除",failedDeletePhoto:"刪除照片失敗"},list:{name:"名稱",printer:"印表機",date:"日期",size:"大小",actions:"操作",hasTimelapse:"有縮時攝影"},log:{date:"日期",printName:"列印名稱",printer:"印表機",user:"使用者",status:"狀態",duration:"時長",filament:"耗材",allPrinters:"所有印表機",allUsers:"所有使用者",allStatuses:"所有狀態",cancelled:"已取消",skipped:"已跳過",dateFrom:"從",dateTo:"到",noEntries:"未找到列印日誌條目",showing:"顯示 {{count}} / {{total}} 條",rowsPerPage:"行數",page:"頁",prev:"上一頁",next:"下一頁",clearLog:"清除日誌",clearLogTitle:"清除列印日誌",clearLogConfirm:"所有列印日誌條目將被永久刪除。歸檔和佇列項目不受影響。此操作無法復原。確定要繼續嗎?",clearLogButton:"全部清除",cleared:"已清除 {{count}} 條日誌",clearFailed:"清除列印日誌失敗"}},queue:{title:"列印佇列",subtitle:"排程和管理您的列印任務",addToQueue:"新增到佇列",print:"列印",reprint:"重新列印",schedulePrint:"排程列印",editQueueItem:"編輯佇列項目",printToPrinters:"列印到 {{count}} 臺印表機",queueToPrinters:"佇列到 {{count}} 臺印表機",queueSelectedPlates:"將 {{count}} 個熱床加入佇列",selectAllPlates:"選擇全部 {{count}} 個熱床",deselectAll:"取消全選",printQueued:"已加入列印佇列",itemsQueued:"{{count}} 個任務已加入佇列",sending:"傳送中...",sendingProgress:"傳送中 {{current}}/{{total}}...",adding:"新增中...",addingProgress:"新增中 {{current}}/{{total}}...",savingProgress:"儲存中 {{current}}/{{total}}...",clearQueue:"清空佇列",clearHistory:"清除歷史",emptyQueue:"佇列為空",position:"位置",scheduledTime:"排程時間",moveUp:"上移",moveDown:"下移",startNow:"立即開始",printingInProgress:"列印進行中...",viewArchive:"檢視歸檔",viewInFileManager:"在檔案管理器中檢視",itemCount:"{{count}} 個項目",itemCount_plural:"{{count}} 個項目",dragToReorder:"拖曳以重新排序(僅限盡快)",reorderHint:'位置僅影響"儘快"項目。排程項目按設定時間執行。',sjf:{label:"SJF",tooltip:"最短任務優先 — 排程器優先處理較短的列印任務"},addedBy:"由 {{name}} 新增",nextInQueue:"佇列中的下一個",clearPlate:"清理列印板並開始下一個",clearPlateSuccess:"列印板已清理 — 準備進行下一個列印",plateReady:"列印板已清理 — 準備進行下一個列印",plateNumber:"板 {{index}}",quantity:"數量",quantityHint:"建立 {{count}} 個佇列項目",activeBatches:"活躍批次",batchProgress:"已完成 {{completed}}/{{total}}",cancelBatch:"取消剩餘",batchCancelled:"已取消剩餘批次項目",cancelBatchConfirmTitle:"取消批次",cancelBatchConfirmMessage:"取消此批次中所有剩餘的待處理項目?",batch:"批次",sections:{currentlyPrinting:"正在列印",queued:"佇列中",history:"歷史"},status:{pending:"等待中",waiting:"等待中",printing:"列印中",paused:"已暫停",completed:"已完成",failed:"失敗",skipped:"已跳過",cancelled:"已取消"},summary:{printing:"列印中",queued:"佇列中",totalTime:"總佇列時間",totalWeight:"總佇列重量",history:"歷史"},filter:{allPrinters:"所有印表機",unassigned:"未分配",allStatus:"所有狀態",allLocations:"所有位置",any:"任意"},sort:{byPosition:"按位置排序",byName:"按名稱排序",byPrinter:"按印表機排序",bySchedule:"按排程排序",byDate:"按日期排序",ascendingOldest:"升序(最舊優先)",descendingNewest:"降序(最新優先)"},badges:{staged:"已暫存",requiresPrevious:"需要前一個成功",autoPowerOff:"自動關機",gcodeInjection:"G-code"},empty:{title:"沒有排程的列印",description:'從歸檔頁面使用右鍵選單中的"排程"選項來排程列印,或拖放檔案開始。'},time:{asap:"儘快",overdue:"已逾期",now:"現在",lessThanMinute:"不到一分鐘",inMinutes:"{{count}} 分鐘後",inHours:"{{count}} 小時後"},actions:{stopPrint:"停止列印",startPrint:"開始列印",requeue:"重新佇列"},bulkEdit:{title:"編輯 {{count}} 個項目",title_plural:"編輯 {{count}} 個項目",description:"僅更改的設定將套用於所選項目。",printer:"印表機",noChange:"— 不更改 —",queueOptions:"佇列選項",staged:"暫存(手動開始)",autoPowerOff:"列印後自動關機",requirePrevious:"要求前一個成功",printOptions:"列印選項",bedLevelling:"熱床調平",flowCalibration:"流量校準",vibrationCalibration:"振動校準",layerInspection:"首層檢查",timelapse:"縮時攝影",useAms:"使用 AMS",applyChanges:"套用更改",selectAll:"全選",deselectAll:"取消全選",selected:"已選擇 {{count}} 個",editSelected:"編輯所選",cancelSelected:"取消所選"},confirm:{cancelTitle:"取消排程列印",cancelMessage:'確定要取消"{{name}}"嗎?',stopTitle:"停止列印",stopMessage:'確定要停止目前列印"{{name}}"嗎?這將取消印表機上的列印任務。',removeTitle:"從歷史中移除",removeMessage:'確定要從佇列歷史中移除"{{name}}"嗎?',clearHistoryTitle:"清除歷史",clearHistoryMessage:"確定要從歷史中移除所有 {{count}} 個項目嗎?",cancelButton:"取消列印",stopButton:"停止列印",thisPrint:"此列印",thisItem:"此項目"},toast:{cancelled:"佇列項目已取消",cancelFailed:"取消項目失敗",removed:"佇列項目已移除",removeFailed:"移除項目失敗",stopped:"列印已停止",stopFailed:"停止列印失敗",released:"列印已加入佇列",startFailed:"開始列印失敗",reorderFailed:"重新排序佇列失敗",historyCleared:"已清除 {{count}} 條歷史紀錄",clearHistoryFailed:"清除歷史失敗",updateFailed:"更新項目失敗",bulkCancelled:"已取消 {{count}} 個項目",bulkCancelFailed:"批次取消項目失敗"},timeline:{listView:"列表",timelineView:"時間線",unassigned:"未分配",noData:"當天沒有計畫的列印任務",allDoneBy:"所有列印預計在 {{time}} 前完成",staged:"暫存",filterAll:"全部顯示",filterPrinting:"列印中",filterQueued:"佇列中",time:{anyMoment:"即將完成",minutesLeft:"剩餘{{minutes}} 分鐘",hoursLeft:"剩餘{{hours}}小時",hoursMinutesLeft:"剩餘{{hours}}小時{{minutes}} 分鐘"},day:{previous:"前一天",next:"後一天",today:"今天"}},permissions:{noStopPrint:"您沒有停止列印的權限",noStartPrint:"您沒有開始列印的權限",noEdit:"您沒有編輯此佇列項目的權限",noCancel:"您沒有取消此佇列項目的權限",noRequeue:"您沒有重新佇列的權限",noRemove:"您沒有移除此佇列項目的權限",noClearHistory:"您沒有清除所有歷史的權限",noEditItems:"您沒有編輯佇列項目的權限",noCancelItems:"您沒有取消佇列項目的權限"}},backgroundDispatch:{unknownFile:"未知檔案",unknownPrinter:"未知印表機",startingPrints:"正在開始列印",progressSummary:"{{complete}}/{{total}} 完成 • 已分發:{{dispatched}} • 處理中:{{processing}}",expandDetails:"展開分發詳情",collapseDetails:"收起分發詳情",dismissToast:"關閉分發通知",cancelDispatchJob:"取消分發任務",cancel:"取消",cancelling:"取消中…",status:{dispatched:"已分發",processing:"處理中",completed:"已完成",failed:"失敗",cancelled:"已取消"},toast:{cancellingUpload:"取消上傳中...",cancelled:"分發已取消",cancelFailed:"取消分發失敗",completeWithFailures:"後台分發完成:{{completed}} 成功,{{failed}} 失敗",completeSuccess:"後台分發完成:{{completed}} 成功",printStartedRemaining:"{{completed}} 個列印已開始,{{remaining}} 個正在傳送..."}},stats:{title:"儀表板",subtitle:"拖曳小工具以重新排列。點選眼睛圖示隱藏。",overview:"概覽",totalPrints:"總列印次數",successRate:"成功率",totalPrintTime:"總列印時間",printTime:"列印時間",totalFilament:"總耗材用量",filamentUsed:"耗材用量",filamentCost:"耗材成本",totalCost:"總成本",energyUsed:"能耗",energyCost:"能源成本",energyWarmingUpTooltip:"能耗追蹤正在收集每小時快照。當所選範圍之前至少存在一個快照時,時間段合計將變得準確。早期數值可能偏低。",averagePrintTime:"平均列印時間",printsPerDay:"每日列印次數",byPrinter:"按印表機",printsByPrinter:"各印表機列印次數",byMaterial:"按材料",byMonth:"按月份",last7Days:"最近 7 天",last30Days:"最近 30 天",last90Days:"最近 90 天",allTime:"全部時間",quickStats:"快速統計",printActivity:"列印活動",filamentTypes:"耗材類型",filamentTrends:"耗材趨勢",failureAnalysis:"失敗分析",timeAccuracy:"時間準確度",successful:"成功:",failed:"失敗:",perfectEstimate:"100% = 完美估計",noTimeAccuracyData:"尚無時間準確度資料",noFilamentData:"尚無耗材資料",noPrinterData:"尚無印表機資料",noPrintData:"尚無列印資料",noPrintDataLast30Days:"最近 30 天無列印資料",failureReasons:"失敗原因",topFailureReasons:"主要失敗原因",failedPrintsCount:"{{failed}} / {{total}} 次列印失敗",lastWeekRate:"上週:{{rate}}%",resetLayout:"重設佈局",recalculateCosts:"重新計算成本",recalculateCostsHint:"使用目前耗材價格重新計算所有歸檔成本",exportStats:"匯出統計",exportAsCsv:"匯出為 CSV",exportAsExcel:"匯出為 Excel",hiddenCount:"{{count}} 個已隱藏",exportDownloaded:"匯出已下載",exportFailed:"匯出失敗",layoutReset:"佈局已重設",recalculatedCosts:"已為 {{count}} 個歸檔重新計算成本",recalculateFailed:"重新計算成本失敗",loadingStats:"載入統計資料中...",noPermissionResetLayout:"您沒有重設佈局的權限",noPermissionRecalculate:"您沒有重新計算成本的權限",noPrintDataInRange:"所選範圍內無列印資料",periodFilament:"期間耗材",periodCost:"期間成本",avgPerPrint:"每次列印平均",usageOverTime:"隨時間的使用量",filamentByWeight:"重量",printDuration:"列印時長",printerUtilization:"印表機利用率",filamentSuccess:"按材料成功率",printHabits:"列印習慣",printTimeOfDay:"列印時段",colorDistribution:"顏色分佈",noColorData:"尚無顏色資料",records:"紀錄",longestPrint:"最長列印",heaviestPrint:"最重列印",mostExpensivePrint:"最貴列印",busiestDay:"最忙碌的一天",successStreak:"連續成功",streakPrint:"連續列印",streakPrints:"{{count}} 次連續列印",printerStats:"印表機統計",hours:"小時",avgPrints:"平均列印",noArchiveData:"尚無列印資料",filamentByTime:"時間",avgWeight:"平均重量",avgTime:"平均時間",filamentByPrints:"列印次數",timeframe:{today:"今天","this-week":"本週","this-month":"本月","last-7":"最近 7 天","last-30":"最近 30 天","last-90":"最近 90 天","this-year":"今年","all-time":"全部時間",custom:"自訂範圍",from:"從",to:"到"},allUsers:"所有使用者",noUser:"無使用者(系統)",filterByUser:"按使用者篩選"},maintenance:{title:"維護",overview:"概覽",allOk:"所有維護均已完成",dueCount:"{{count}} 項到期",dueCount_plural:"{{count}} 項到期",warningCount:"{{count}} 個警告",warningCount_plural:"{{count}} 個警告",totalPrintTime:"總列印時間",nextMaintenance:"下次維護",nothingDue:"無到期項目",tasks:"任務",lastPerformed:"上次執行",interval:"間隔",hoursRemaining:"剩餘 {{hours}} 小時",hoursOverdue:"逾期 {{hours}} 小時",markDone:"標記為完成",performMaintenance:"執行維護",history:"歷史",noHistory:"無維護歷史",editPrintHours:"編輯列印時間",currentHours:"目前小時數",statusTab:"狀態",settingsTab:"設定",overdueCount:"{{count}} 個逾期",dueSoonCount:"{{count}} 個即將到期",dueSoon:"即將到期",allGood:"一切正常",overdueBy:"逾期 {{duration}}",dueIn:"{{duration}} 後到期",timeLeft:"剩餘 {{duration}}",day:"1 天",days:"{{count}} 天",week:"1 週",weeks:"{{count}} 週",month:"1 個月",months:"{{count}} 個月",year:"1 年",maintenanceTypes:"維護類型",maintenanceTypesDescription:"系統類型和您的自訂維護任務",addCustomType:"新增自訂類型",restoreDefaults:"恢復預設任務",intervalType:"間隔類型",intervalValue:"間隔 ({{type}})",icon:"圖示",documentationLink:"說明文件連結(可選)",assignToPrinters:"分配給印表機",selectAtLeastOnePrinter:"至少選擇一臺印表機",addType:"新增類型",custom:"自訂",printHours:"列印小時數",calendarDays:"日曆天數",exampleName:"例如:更換 HEPA 過濾器",viewDocumentation:"檢視說明文件",timeBasedInterval:"基於時間的間隔",intervalOverrides:"間隔覆蓋",intervalOverridesDescription:"為特定印表機自訂間隔",assignedToPrinters:"已分配給印表機:",noPrintersAssigned:"未分配印表機",addPrinterShort:"新增:",printersAssignedClick:"已分配 {{count}} 臺印表機 - 點選管理",removeFromPrinter:"從此印表機移除",types:{lubricateCarbonRods:"潤滑碳纖維杆",lubricateRails:"潤滑線性導軌",cleanNozzle:"清潔噴嘴/熱端",checkBelts:"檢查皮帶張力",cleanBuildPlate:"清潔列印板",checkExtruder:"檢查擠出機齒輪",checkCooling:"檢查冷卻風扇",generalInspection:"綜合檢查",cleanCarbonRods:"清潔碳纖維杆",lubricateSteelRods:"潤滑鋼杆",cleanSteelRods:"清潔鋼杆",cleanLinearRails:"清潔線性導軌",checkPtfeTube:"檢查 PTFE 管",replaceHepaFilter:"更換 HEPA 過濾器",replaceCarbonFilter:"更換活性炭過濾器",lubricateLeftNozzleRail:"潤滑左噴嘴導軌"},maintenanceComplete:"維護已標記為完成",typeUpdated:"維護類型已更新",typeDeleted:"維護類型已刪除",defaultsRestored:"已恢復 {{count}} 個預設任務",printHoursUpdated:"列印小時數已更新",printerAssigned:"印表機已分配",printerRemoved:"印表機已移除",deleteTypeConfirm:'刪除"{{name}}"?',deleteSystemTypeTitle:"刪除預設維護任務?",deleteSystemTypeMessage:'確定要刪除預設維護任務"{{name}}"嗎?',noPermissionUpdate:"您沒有更新維護項目的權限",noPermissionPerform:"您沒有執行維護的權限",noPermissionEditTypes:"您沒有編輯維護類型的權限",noPermissionDeleteTypes:"您沒有刪除維護類型的權限",noPermissionEditHours:"您沒有編輯列印時間的權限",noPermissionRemovePrinter:"您沒有移除印表機分配的權限",noPermissionAssignPrinter:"您沒有分配印表機的權限",noPermissionEditIntervals:"您沒有編輯間隔的權限",configureSettings:"設定維護類型和間隔"},settings:{title:"設定",general:"通用",tabs:{general:"通用",smartPlugs:"智慧插座",notifications:"通知",queue:"工作流程",filament:"耗材",network:"網路",apiKeys:"API 金鑰",virtualPrinter:"虛擬印表機",spoolbuddy:"SpoolBuddy",failureDetection:"故障檢測",users:"身份驗證",backup:"備份",emailAuth:"信箱認證",ldap:"LDAP",twoFa:"雙因素認證",oidc:"SSO / OIDC"},spoolbuddy:{infoTitle:"SpoolBuddy 裝置",infoBody:"SpoolBuddy kiosk 透過心跳自動註冊。如果裝置不再使用,或守護程式當機遺留了過時的重複項,可在此取消註冊。",duplicatesTitle:"已註冊 {{count}} 台裝置",duplicatesBody:"kiosk 介面只使用最先註冊的裝置。如果其中有因當機遺留的過時重複項,請取消註冊它——線上裝置會在下次心跳時重新註冊自己。",empty:"尚未註冊任何 SpoolBuddy 裝置。",online:"線上",offline:"離線",unregister:"取消註冊",unregisterSuccess:"裝置已取消註冊",unregisterError:"取消註冊裝置失敗",confirmTitle:"取消註冊 SpoolBuddy 裝置?",confirmBody:'將從資料庫中移除 "{{hostname}}" ({{deviceId}})。如果裝置線上,會在下次心跳時重新註冊自己。',ipAddress:"IP 位址",firmware:"韌體",lastSeen:"上次線上",daemonUptime:"守護程式執行時間",systemUptime:"系統執行時間",never:"從未",nfc:"NFC",scale:"磅秤",cpuTemp:"CPU 溫度",memory:"記憶體",disk:"磁碟",update:"更新",updateConfirmTitle:"更新 SpoolBuddy 守護程式?",updateConfirmBody:'對 "{{hostname}}" 觸發軟體更新?更新完成後守護程式將重新啟動。',restartBrowser:"重新啟動瀏覽器",restartBrowserConfirmTitle:"重新啟動 kiosk 瀏覽器?",restartBrowserConfirmBody:'在 "{{hostname}}" 上重新啟動 kiosk 瀏覽器?顯示將短暫黑屏。',restartDaemon:"重新啟動守護程式",restartDaemonConfirmTitle:"重新啟動 SpoolBuddy 守護程式?",restartDaemonConfirmBody:'在 "{{hostname}}" 上重新啟動 SpoolBuddy 守護程式?裝置將離線幾秒鐘。',reboot:"重新開機",rebootConfirmTitle:"重新開機?",rebootConfirmBody:'重新開機 "{{hostname}}"?裝置將離線約一分鐘。',shutdown:"關機",shutdownConfirmTitle:"關閉裝置?",shutdownConfirmBody:'關閉 "{{hostname}}"?您需要實體存取才能重新開機。',commandConfirm:"確認",commandQueued:"命令已加入佇列",commandError:"傳送命令失敗"},ldap:{title:"LDAP 認證",enabledDesc:"LDAP 認證已啟用",disabledDesc:"LDAP 認證已停用",disabledHint:"在下方設定並儲存 LDAP 設定,然後啟用。",enabled:"LDAP 認證已啟用",disabled:"LDAP 認證已停用",feature1:"使用者可以使用 LDAP 憑據登入",feature2:"本機管理員帳戶作為後備保留",feature3:"登入時 LDAP 群組對應到 BamBuddy 群組",serverConfig:"LDAP 伺服器設定",serverUrl:"伺服器 URL",serverUrlHint:"使用 ldap:// 進行標準連線或 ldaps:// 進行 SSL 連線",security:"安全",securityHint:"StartTLS 將普通連線升級為 TLS。LDAPS 從一開始就使用 TLS。",bindDn:"繫結 DN(服務帳戶)",bindPassword:"繫結密碼",searchBase:"搜尋基礎 DN",userFilter:"使用者搜尋過濾器",userFilterHint:"{username} 替換為登入使用者名稱。OpenLDAP 使用 (uid={username})。",autoProvision:"自動建立使用者",autoProvisionHint:"首次 LDAP 登入時自動建立 BamBuddy 帳戶",defaultGroup:"預設群組",defaultGroupNone:"— 無(無復原)—",defaultGroupHint:"當 LDAP 使用者透過身份驗證但不在任何已對應的 LDAP 群組中時分配的備援群組。留空以使未對應的使用者沒有權限。",groupMapping:"群組對應(JSON)",groupMappingHint:"將 LDAP 群組 DN 對應到 BamBuddy 群組。可用群組:",testConnection:"測試連線",settingsSaved:"LDAP 設定已儲存",errors:{serverRequired:"LDAP 伺服器 URL 為必填項",searchBaseRequired:"搜尋基礎 DN 為必填項",enableAuthFirst:"請先啟用認證",configureLdapFirst:"請先儲存 LDAP 設定"}},email:{smtpSettings:"SMTP 設定",smtpHost:"SMTP 伺服器",smtpPort:"SMTP 連接埠",security:"安全",authentication:"認證",username:"使用者名稱",password:"密碼",fromEmail:"寄件信箱",fromName:"寄件人名稱",testConnection:"測試 SMTP 連線",testRecipient:"測試收件信箱",sendTest:"傳送測試郵件",sending:"傳送中...",save:"儲存設定",saving:"儲存中...",advancedAuth:"進階認證",advancedAuthEnabled:"進階認證已啟用",advancedAuthEnabledDesc:"基於信箱的使用者管理功能已啟用。新使用者將透過郵件收到自動產生的密碼,使用者可以透過忘記密碼功能重設密碼。",advancedAuthDisabled:"進階認證已停用",advancedAuthDisabledDesc:"啟用進階認證以啟用基於信箱的使用者管理功能。",enable:"啟用",disable:"停用",feature1:"密碼自動產生並透過郵件傳送給新使用者",feature2:"使用者可以使用使用者名稱或信箱登入",feature3:"忘記密碼功能可用",feature4:"管理員可以透過郵件重設使用者密碼",errors:{requiredFields:"請填寫所有必填欄位",usernameRequired:"啟用認證時需要使用者名稱",enterTestEmail:"請輸入測試信箱地址",smtpServerAndEmail:"測試前請填寫 SMTP 伺服器和寄件信箱",usernamePasswordRequired:"啟用認證時需要使用者名稱和密碼",configureSmtpFirst:"請先設定並測試 SMTP 設定",enableAuthFirst:"請先啟用身份驗證才能使用基於電子郵件的功能。"},success:{settingsSaved:"SMTP 設定儲存成功"},securityOptions:{starttls:"STARTTLS(連接埠 587)",ssl:"SSL/TLS(連接埠 465)",none:"無(連接埠 25)"},authOptions:{enabled:"已啟用",disabled:"已停用"}},appearance:"外觀",notifications:"通知",smartPlugs:"智慧插座",spoolman:"Spoolman",updates:"更新",language:"語言",languageDescription:"選擇您的首選語言",theme:"主題",themeLight:"淺色",themeDark:"深色",themeSystem:"跟隨系統",defaultView:"預設檢視",defaultViewDescription:"開啟應用程式時顯示的頁面",checkForUpdates:"檢查更新",autoUpdate:"自動更新",currentVersion:"目前版本",latestVersion:"最新版本",upToDate:"已是最新版本",updateAvailable:"有可用更新",notificationLanguage:"通知語言",notificationLanguageDescription:"推送通知的語言",bedCooledThreshold:"熱床冷卻閾值",bedCooledThresholdDescription:"列印後熱床被視為已冷卻的溫度",userNotificationsEnabled:"使用者通知",userNotificationsEnabledDescription:"啟用使用者通知選單和列印任務事件的郵件通知。需要進階身份驗證。",userNotificationsDisabledHint:"請啟用進階身份驗證以使用使用者通知。",notificationProviders:"通知提供者",addProvider:"新增提供者",editProvider:"編輯提供者",providerType:"提供者類型",testNotification:"測試通知",testSuccess:"測試通知傳送成功",testFailed:"傳送測試通知失敗",quietHours:"免打擾時間",quietHoursDescription:"在此時間段內不傳送通知",quietHoursStart:"開始",quietHoursEnd:"結束",events:{title:"通知事件",printStart:"列印開始",printComplete:"列印完成",printFailed:"列印失敗",printStopped:"列印停止",printProgress:"進度里程碑",printProgressDescription:"在 25%、50%、75% 時通知",printerOffline:"印表機離線",printerError:"印表機錯誤",filamentLow:"耗材不足",maintenanceDue:"維護到期",maintenanceDueDescription:"需要維護時通知"},smartPlug:{title:"智慧插座",add:"新增智慧插座",edit:"編輯智慧插座",name:"名稱",ipAddress:"IP 位址",linkedPrinter:"連結印表機",autoOn:"自動開啟",autoOnDescription:"列印開始時開啟",autoOff:"自動關閉",autoOffDescription:"列印完成後關閉",offDelay:"關閉延遲",offDelayMinutes:"列印後分鐘數",offDelayTemp:"當噴嘴溫度低於",currentState:"目前狀態",turnOn:"開啟",turnOff:"關閉"},filamentTracking:"耗材追蹤",filamentTrackingDesc:"選擇如何追蹤您的耗材。您可以使用內建庫存或連線外部 Spoolman 伺服器。",filamentChecks:"耗材檢查",disableFilamentWarnings:"停用耗材警告",disableFilamentWarningsDesc:"在列印或加入佇列時不顯示耗材不足警告",preferLowestFilament:"優先使用剩餘最少的耗材",preferLowestFilamentDesc:"當多個料盤匹配時,使用剩餘耗材最少的那個",trackingModeBuiltIn:"內建庫存",trackingModeBuiltInDesc:"包含 RFID 自動匹配和用量追蹤",trackingModeSpoolmanDesc:"外部耗材管理伺服器",builtInFeatureRfid:"自動檢測 AMS 中的拓竹 RFID 耗材",builtInFeatureUsage:"追蹤每次列印的耗材消耗",builtInFeatureCatalog:"管理耗材、顏色和 K 值設定檔案",builtInFeatureThirdParty:"第三方耗材可分配到庫存耗材",amsSyncButton:"從 AMS 同步重量",amsSyncTitle:"從 AMS 同步耗材重量",amsSyncMessage:"這將使用已連線印表機的目前 AMS 剩餘百分比值覆蓋所有庫存耗材重量。用於從損壞的重量資料中恢復。印表機必須線上。",amsSyncing:"同步中...",amsSyncSuccess:"已同步 {{synced}} 個耗材,跳過 {{skipped}} 個",amsSyncError:"從 AMS 同步重量失敗",spoolmanUrl:"Spoolman URL",spoolmanUrlHint:"Spoolman 伺服器的 URL(例如 http://localhost:7912)",spoolmanConnected:"已連線",spoolmanDisconnected:"未連線",status:"狀態",connect:"連線",disconnect:"斷開",howSyncWorks:"同步工作原理",syncInfoRfidOnly:"僅同步帶有 RFID 的官方拓竹耗材",syncInfoAutoCreate:"首次同步時自動在 Spoolman 中建立新耗材",syncInfoThirdPartySkipped:"非拓竹耗材(第三方、重新填充的)將被跳過",linkingExistingSpools:"連結現有耗材",linkingExistingSpoolsDesc:'要將現有的 Spoolman 耗材連結到您的 AMS,請將滑鼠懸停在 AMS 槽位上並點選"連結到 Spoolman"。',syncMode:"同步模式",syncModeAuto:"自動",syncModeManual:"僅手動",syncModeAutoDesc:"偵測到更改時自動同步 AMS 資料",syncModeManualDesc:"僅在手動觸發時同步",syncAmsData:"同步 AMS 資料",syncAmsDataDesc:"手動將印表機 AMS 資料同步到 Spoolman",allPrinters:"所有印表機",noDefaultPrinter:"無預設(每次詢問)",sidebarOrder:"側邊欄順序",saveThumbnails:"儲存縮圖",captureFinishPhoto:"拍攝完成照片",noPrintersConfigured:"未設定印表機",archiveMode:{always:"始終建立歸檔條目",never:"從不建立歸檔條目",ask:"每次詢問"},checkForUpdatesLabel:"檢查更新",checkPrinterFirmware:"檢查印表機韌體",includeBetaUpdates:"包含測試版本",includeBetaUpdatesDesc:"檢查更新時通知測試版和預發布版本",enableRetry:"啟用重試",homeAssistantDescription:"透過 Home Assistant 控制智慧插座",environmentManagedLabel:"(環境變數管理)",autoEnabledViaEnv:"透過環境變數自動啟用",urlFromEnvReadOnly:"值由 HA_URL 環境變數設定(只讀)",tokenFromEnvReadOnly:"值由 HA_TOKEN 環境變數設定(只讀)",mqttConnectedTo:"已連線到",prometheusDescription:"以 Prometheus 格式暴露印表機資料",noSmartPlugsTitle:"未設定智慧插座",noSmartPlugsDescription:"新增基於 Tasmota 的智慧插座以追蹤能耗並自動化電源控制。",noProvidersTitle:"未設定提供者",noProvidersDescription:"新增提供者以接收警報。",noTemplatesAvailable:"無可用範本。重新啟動後端以載入預設範本。",apiPermissionView:"檢視印表機狀態和佇列",apiPermissionEdit:"新增和移除列印佇列中的項目",apiKeysEmptyTitle:"無 API 金鑰",apiKeysEmptyDescription:"建立 API 金鑰以與外部服務整合。",noUsersFound:"未找到使用者",noGroupsFound:"未找到群組",noGroupsAvailable:"無可用群組",passwordsDoNotMatch:"密碼不符",systemGroupWarning:"系統群組名稱不可更改",authDisabledTitle:"身份驗證已停用",authDisabledFeature1:"需要登入才能存取系統",authDisabledFeature2:"建立多個使用者並基於群組的權限管理",authDisabledFeature3:"使用 50+ 個細粒度權限控制存取",userHasCreated:"此使用者已建立:",userItemsQuestion:"您想如何處理這些項目?",deleteUserConfirm:"確定要刪除此使用者嗎?",actionCannotBeUndone:"此操作無法復原。",addFirstSmartPlug:"新增您的第一個智慧插座",providers:"提供者",log:"日誌",testAll:"全部測試",testResults:"測試結果",testPassedCount:"{{count}} 個透過",testFailedCount:"{{count}} 個失敗",messageTemplates:"訊息範本",messageTemplatesDescription:"自訂每個事件的通知訊息。",apiKeys:"API 金鑰",apiKeysDescription:"建立 API 金鑰用於外部整合和 Webhook。",createKey:"建立金鑰",apiKeyCreated:"API 金鑰建立成功",apiKeyCopyWarning:"請立即複製此金鑰 - 它不會再次顯示!",useInApiBrowser:"在 API 瀏覽器中使用",createNewApiKey:"建立新 API 金鑰",keyName:"金鑰名稱",keyNamePlaceholder:"例如:Home Assistant、OctoPrint",readStatus:"讀取狀態",readStatusDescription:"檢視印表機狀態和佇列",manageQueue:"管理佇列",manageQueueDescription:"新增和移除列印佇列中的項目",controlPrinter:"控制印表機",controlPrinterDescription:"暫停、繼續和停止列印",unnamedKey:"未命名金鑰",lastUsed:"上次使用",read:"讀取",control:"控制",createFirstKey:"建立您的第一個金鑰",webhookEndpoints:"Webhook 端點",webhookApiKeyHint:"在 X-API-Key 請求頭中使用您的 API 金鑰。",webhook:{getAllStatus:"獲取所有印表機狀態",getSpecificStatus:"獲取特定印表機狀態",addToQueue:"新增到列印佇列",pausePrint:"暫停列印",resumePrint:"繼續列印",stopPrint:"停止列印"},apiBrowser:"API 瀏覽器",apiBrowserDescription:"瀏覽和測試所有可用的 API 端點。",apiKeyForTesting:"測試用 API 金鑰",apiKeyPlaceholder:"在此貼上您的 API 金鑰以測試需要認證的端點...",apiKeyHint:"此金鑰將作為 X-API-Key 請求頭隨請求傳送。",deleteApiKeyTitle:"刪除 API 金鑰",deleteApiKeyMessage:"確定要刪除此 API 金鑰嗎?使用此金鑰的所有整合將停止工作。",deleteKey:"刪除金鑰",amsDisplayThresholds:"AMS 顯示閾值",amsThresholdsDescription:"設定 AMS 濕度和溫度指示器的顏色閾值。",humidity:"濕度",goodGreen:"良好(綠色)",fairOrange:"一般(橙色)",aboveFairBad:"超過一般閾值顯示為紅色(差)",fairAlsoDryingThreshold:"此閾值也用於觸發自動乾燥",temperature:"溫度",goodBlue:"良好(藍色)",aboveFairHot:"超過一般閾值顯示為紅色(熱)",historyRetention:"歷史保留",keepSensorHistory:"保留感測器歷史",historyRetentionDescription:"較舊的濕度和溫度資料將被自動刪除",defaultPrintOptions:"預設列印選項",defaultPrintOptionsDescription:"設定新列印的預設選項值。可在列印對話方塊中逐次覆蓋。",defaultBedLevelling:"熱床調平",defaultBedLevellingDesc:"列印前自動調平熱床",defaultFlowCali:"流量校準",defaultFlowCaliDesc:"校準擠出流量",defaultVibrationCali:"振動校準",defaultVibrationCaliDesc:"減少振紋偽影",defaultLayerInspect:"首層檢測",defaultLayerInspectDesc:"AI首層檢測",defaultTimelapse:"縮時攝影",defaultTimelapseDesc:"錄製縮時攝影影片",staggeredStart:"錯開啟動",staggeredStartDescription:"多台印表機批次啟動時的預設群組大小與間隔。可在列印對話框中逐批覆寫。",plateClear:"熱床清空確認",requirePlateClear:"需要熱床清空確認",requirePlateClearDescription:"啟用後,排程器會在已完成列印的印表機上啟動佇列列印之前,等待每臺印表機的熱床清空確認。停用後,也會隱藏印表機卡片上的列印板狀態標記和「將列印板標記為已清理」按鈕。",gcodeInjection:"G-code注入",gcodeInjectionDescription:'為Farmloop、SwapMod、AutoClear和Printflow 3D等自動列印系統設定自訂G-code,在列印開始和/或結束時注入。程式碼片段按印表機型號設定,在佇列項目上啟用"注入G-code"時套用。',gcodeInjectionNoPrinters:"未找到印表機。新增印表機以設定G-code程式碼片段。",gcodeStartLabel:"開始G-code",gcodeEndLabel:"結束G-code",gcodeStartPlaceholder:"在列印開始前插入的G-code...",gcodeEndPlaceholder:"在列印結束後追加的G-code...",staggerGroupSize:"群組大小",staggerGroupSizeHelp:"每個群組要同時啟動的印表機數量",staggerInterval:"間隔(分鐘)",staggerIntervalHelp:"每個群組啟動之間的延遲",queueDrying:"自動乾燥",queueDryingDescription:"在佇列列印之間,印表機空閒時自動乾燥AMS耗材。使用上方的濕度閾值觸發乾燥。",queueDryingEnabled:"啟用自動乾燥",queueDryingEnabledDescription:"當印表機空閒且濕度超過閾值時,自動啟動AMS乾燥",queueDryingBlock:"等待乾燥完成",queueDryingBlockDescription:"阻止列印佇列直到乾燥完成。關閉時,列印優先於乾燥。",ambientDryingEnabled:"環境乾燥",ambientDryingEnabledDescription:"當空閒印表機的濕度超過閾值時自動乾燥耗材,無需佇列列印。",dryingPresets:"乾燥預設",dryingPresetsDescription:"每種耗材類型的溫度和時長。AMS 2 Pro使用較低溫度,AMS-HT支援較高溫度。",dryingFilament:"耗材",printModal:"列印對話方塊",expandCustomMapping:"預設展開自訂對應",expandCustomMappingDescription:"列印到多臺印表機時,預設展開顯示每臺印表機的 AMS 對應",authentication:"身份驗證",authEnabledDescription:"您的實例已透過使用者身份驗證保護",authDisabledDescription:"啟用以要求登入並管理使用者存取",authDisabledMessage:"啟用身份驗證以建立使用者帳戶、管理權限並保護您的 Bambuddy 實例。",enableAuthentication:"啟用身份驗證",currentUser:"目前使用者",changePassword:"修改密碼",admin:"管理員",users:"使用者",addUser:"新增使用者",groups:"群組",addGroup:"新增群組",system:"系統",noDescription:"無描述",userCount:"{{count}} 個使用者",permissionCount:"{{count}} 個權限",createUser:"建立使用者",username:"使用者名稱",enterUsername:"輸入使用者名稱",password:"密碼",enterPassword:"輸入密碼(至少 6 個字元)",confirmPassword:"確認密碼",confirmPasswordPlaceholder:"確認密碼",viewReleaseOnGitHub:"在 GitHub 上檢視發布",turnAllPlugsOn:"開啟所有插座",turnAllPlugsOff:"關閉所有插座",clearNotificationLogs:"清除通知日誌",clearLogsMessage:"這將永久刪除所有 30 天前的通知日誌。此操作無法復原。",clearLogs:"清除日誌",resetUiPreferences:"重設 UI 偏好",resetUiPreferencesMessage:"這將重設所有 UI 偏好為預設值:側邊欄順序、主題、儀表板佈局、檢視模式和排序偏好。您的印表機、歸檔和伺服器設定不會受到影響。清除後頁面將重新載入。",resetPreferences:"重設偏好",deleteGroupTitle:"刪除群組",deleteGroupMessage:"確定要刪除此群組嗎?此群組中的使用者將失去這些權限。",deleteGroup:"刪除群組",disableAuthenticationTitle:"停用身份驗證",disableAuthenticationMessage:"確定要停用身份驗證嗎?這將使您的 Bambuddy 實例無需登入即可存取。所有使用者將保留在資料庫中但身份驗證將被停用。",disableAuthentication:"停用身份驗證",configureBambuddy:"設定 Bambuddy",systemDefault:"系統預設",archiveSettings:"歸檔設定",newWindow:"新視窗",embeddedOverlay:"嵌入式疊加層",preferredSlicer:"首選切片軟體",preferredSlicerDescription:"選擇要用於開啟檔案的切片軟體",externalCameras:"外部攝影機",costTracking:"成本追蹤",printsOnly:"僅列印",totalConsumption:"總消耗",dataManagement:"資料管理",storageUsage:"儲存使用情況",storageUsageDescription:"按類別的資料使用情況明細",storageUsageTotal:"總計",storageUsageErrors:"錯誤",storageUsageOtherBreakdown:"其他(包括靜態資源、腳本和設定檔案)",storageUsageSystem:"系統",storageUsageData:"資料",storageUsageUnavailable:"儲存使用資訊不可用",clearNotificationLogsDescription:"刪除 30 天前的通知日誌",resetUiPreferencesDescription:"重設側邊欄順序、主題、檢視模式和佈局偏好。印表機、歸檔和設定不受影響。",enableHomeAssistant:"啟用 Home Assistant",enableMqtt:"啟用 MQTT",useTls:"使用 TLS",enableMetricsEndpoint:"啟用指標端點",availableMetrics:"可用指標",editUser:"編輯使用者",deleteUserTitle:"刪除使用者",groupName:"群組名稱",leaveEmptyForAnonymous:"留空為匿名",leaveEmptyForNoAuth:"留空為無認證",enterNewPassword:"輸入新密碼",confirmNewPassword:"確認新密碼",enterGroupName:"輸入群組名稱",enterDescriptionOptional:"輸入描述(可選)",enterCurrentPassword:"輸入目前密碼",enterNewPasswordMin6:"輸入新密碼(至少 6 個字元)",toast:{keyCopied:"金鑰已複製到剪貼簿",copyFailed:"複製金鑰失敗",keyAddedToBrowser:"金鑰已新增到 API 瀏覽器",clearLogsFailed:"清除日誌失敗",uiPreferencesReset:"UI 偏好已重設。重新整理中...",authDisabled:"身份驗證已成功停用",authDisableFailed:"停用身份驗證失敗",apiKeyCreated:"API 金鑰已建立",apiKeyDeleted:"API 金鑰已刪除",userCreated:"使用者建立成功",userUpdated:"使用者更新成功",userDeleted:"使用者刪除成功",groupCreated:"群組建立成功",groupUpdated:"群組更新成功",groupDeleted:"群組刪除成功",fillRequiredFields:"請填寫所有必填欄位",passwordsDoNotMatch:"密碼不符",passwordTooShort:"密碼至少需要 6 個字元",enterGroupName:"請輸入群組名稱",settingsSaved:"設定已儲存",cameraSettingsSaved:"攝影機設定已儲存",enterCameraUrl:"請輸入攝影機 URL",passwordChanged:"密碼修改成功",connectionFailed:"連線失敗",testFailed:"測試失敗",cameraConnected:"攝影機已連線{{resolution}}"},testConnection:"測試連線",catalog:{spoolCatalog:"耗材目錄",spoolCatalogDescription:"按品牌/類型的空耗材重量。用於新增耗材時的自動重量查詢。",searchCatalog:"搜尋目錄...",addNewEntry:"新增新條目",namePlaceholder:"名稱(例如:Bambu Lab - 塑膠)",weight:"重量",type:"類型",default:"預設",custom:"自訂",noMatch:"沒有條目匹配您的搜尋",empty:"目錄中沒有條目",deleteEntry:"刪除條目",deleteConfirm:'確定要刪除"{{name}}"嗎?',resetCatalog:"重設目錄",resetConfirm:"重設目錄為預設值?這將移除所有自訂條目。",loadFailed:"載入耗材目錄失敗",nameWeightRequired:"名稱和重量為必填項",entryAdded:"條目已新增",addFailed:"新增條目失敗",entryUpdated:"條目已更新",updateFailed:"更新條目失敗",entryDeleted:"條目已刪除",deleteFailed:"刪除條目失敗",resetSuccess:"目錄已重設為預設值",resetFailed:"重設目錄失敗",exported:"已匯出 {{count}} 條",imported:"已匯入 {{added}} 條(跳過 {{skipped}} 條)",importFailed:"匯入失敗:無效的 JSON 格式",exportTooltip:"匯出目錄為 JSON",importTooltip:"從 JSON 匯入目錄",resetTooltip:"重設為預設值",selectedCount:"已選擇 {{count}} 項",deleteSelected:"刪除所選",bulkDeleteConfirm:"確定要刪除 {{count}} 個條目嗎?",bulkDeleted:"已刪除 {{count}} 個條目",bulkDeleteFailed:"刪除條目失敗"},colorCatalog:{title:"顏色目錄",description:"按製造商/材料的耗材顏色。用於新增耗材時的自動顏色查詢。",searchColors:"搜尋顏色...",allManufacturers:"所有製造商",addNewColor:"新增新顏色",manufacturer:"製造商",colorName:"顏色名稱",hex:"十六進位",materialOptional:"材料(可選)",showing:"顯示 {{filtered}} / {{total}} 種顏色",noMatch:"沒有顏色匹配您的搜尋",empty:"目錄中沒有顏色",deleteColor:"刪除顏色",deleteConfirm:'確定要刪除"{{name}}"嗎?',resetCatalog:"重設顏色目錄",resetConfirm:"重設目錄為預設值?這將移除所有自訂顏色。",sync:"同步",starting:"啟動中...",syncTooltip:"從 FilamentColors.xyz 同步(2000+ 種顏色,可能需要一分鐘)",loadFailed:"載入顏色目錄失敗",fieldsRequired:"製造商、顏色名稱和十六進位顏色為必填項",colorAdded:"顏色已新增",addFailed:"新增顏色失敗",colorUpdated:"顏色已更新",updateFailed:"更新顏色失敗",colorDeleted:"顏色已刪除",deleteFailed:"刪除顏色失敗",resetSuccess:"顏色目錄已重設為預設值",resetFailed:"重設目錄失敗",syncUpToDate:"已是最新(檢查了 {{count}} 種顏色)",syncComplete:"新增了 {{added}} 種新顏色({{skipped}} 種已存在)",syncError:"同步錯誤",syncFailed:"從 FilamentColors.xyz 同步失敗",exported:"已匯出 {{count}} 種顏色",imported:"已匯入 {{added}} 種顏色(跳過 {{skipped}} 種)",importFailed:"匯入失敗:無效的 JSON 格式",selectedCount:"已選擇 {{count}} 項",deleteSelected:"刪除所選",bulkDeleteConfirm:"確定要刪除 {{count}} 種顏色嗎?",bulkDeleted:"已刪除 {{count}} 種顏色",bulkDeleteFailed:"刪除顏色失敗"},dateFormat:"日期格式",dateFormatUs:"美式 (MM/DD/YYYY)",dateFormatEu:"歐式 (DD/MM/YYYY)",dateFormatIso:"ISO (YYYY-MM-DD)",timeFormat:"時間格式",timeFormat12:"12小時制 (3:30 PM)",timeFormat24:"24小時制 (15:30)",defaultPrinter:"預設印表機",defaultPrinterDescription:"為上傳、重印和其他操作預選此印表機。",slicerBambuStudio:"Bambu Studio",slicerOrcaSlicer:"OrcaSlicer",sidebarOrderDescription:"拖曳側邊欄項目以重新排序。在此處重設為預設順序。",setDefault:"設為預設",sidebarOrderSetDefaultHint:"設為預設將目前選單順序套用於尚未自訂的使用者。",sidebarDefaultSet:"已設定預設選單順序。",sidebarDefaultCleared:"已清除預設選單順序。",sidebarDefaultFailed:"設定預設選單順序失敗。",reset:"重設",darkMode:"深色模式",lightMode:"淺色模式",active:"(目前)",background:"背景",accent:"強調色",style:"樣式",bgNeutral:"中性",bgWarm:"暖色",bgCool:"冷色",bgOled:"OLED 純黑",bgSlate:"石板藍",bgForest:"森林綠",accentGreen:"綠色",accentTeal:"青色",accentBlue:"藍色",accentOrange:"橙色",accentPurple:"紫色",accentRed:"紅色",styleClassic:"經典",styleGlow:"發光",styleVibrant:"鮮豔",themeToggleHint:"使用側邊欄中的太陽/月亮圖示在深色和淺色模式之間切換。",autoArchivePrints:"自動歸檔列印",autoArchiveDescription:"列印完成時自動儲存3MF檔案",saveThumbnailsDescription:"從3MF檔案中提取並儲存預覽影像",captureFinishPhotoDescription:"列印完成時從印表機攝影機拍照",ffmpegNotInstalled:"未安裝ffmpeg",ffmpegRequired:"攝影機捕獲需要ffmpeg。透過 brew install ffmpeg(macOS)或 apt install ffmpeg(Linux)安裝。",camera:"攝影機",cameraViewMode:"攝影機檢視模式",cameraOverlayDescription:"攝影機在主螢幕上以可調大小的覆蓋層開啟",cameraWindowDescription:"攝影機在單獨的瀏覽器視窗中開啟",externalCamerasDescription:"設定外部攝影機以替換內建印表機攝影機。支援MJPEG流、RTSP、HTTP快照和USB攝影機(V4L2)。啟用後,外部攝影機將用於即時檢視和完成照片。",cameraPlaceholderUsb:"裝置路徑 (/dev/video0)",cameraPlaceholderUrl:"攝影機URL (rtsp://... 或 http://...)",cameraTypeMjpeg:"MJPEG 流",cameraTypeRtsp:"RTSP 流",cameraTypeSnapshot:"HTTP 快照",cameraTypeUsb:"USB 攝影機 (V4L2)",cameraRotation:"旋轉",test:"測試",connected:"已連線",disconnected:"未連線",currency:"貨幣",defaultFilamentCost:"預設耗材成本(每公斤)",electricityCost:"每千瓦時電費",energyDisplayMode:"能源顯示模式",energyModePrintDescription:"儀表板顯示列印期間使用的能源總和",energyModeTotalDescription:"儀表板顯示智慧插座的累計能源",fileManager:"檔案管理器",createArchiveEntry:"列印時建立歸檔條目",createArchiveEntryDescription:"從檔案管理器列印時,可選擇建立歸檔條目",lowDiskSpaceWarning:"磁碟空間不足警告",lowDiskSpaceDescription:"當可用磁碟空間低於此閾值時顯示警告",printerFirmware:"印表機韌體",checkFirmwareDescription:"檢查Bambu Lab的印表機韌體更新",bambuddySoftware:"Bambuddy 軟體",autoCheckDescription:"啟動時自動檢查新版本",checkNow:"立即檢查",updateAvailableVersion:"可用更新:v{{version}}",releaseNotes:"發布說明",updateViaDocker:"透過 Docker Compose 更新:",installUpdate:"安裝更新",latestVersionRunning:"您正在執行最新版本",failedToCheckUpdates:"檢查更新失敗:{{error}}",backupRestore:"備份與恢復",backupRestoreDescription:"匯出/匯入設定並設定GitHub 備份",goToBackup:"前往備份",externalUrl:"外部URL",externalUrlDescription:"Bambuddy可存取的外部URL。用於通知影像和外部整合。",bambuddyUrl:"Bambuddy URL",externalUrlHint:"包含協定和連接埠(例如:http://192.168.1.100:8000)",ftpRetry:"FTP重試",ftpRetryDescription:"當印表機Wi-Fi 不穩定時重試FTP操作。適用於3MF下載、列印上傳、縮時攝影下載和韌體更新。",autoRetryDescription:"自動重試失敗的FTP操作",retryAttempts:"重試次數",retryDelay:"重試延遲",connectionTimeout:"連線超時",time_one:"{{count}} 次",time_other:"{{count}} 次",second_one:"{{count}} 秒",second_other:"{{count}} 秒",nSeconds:"{{count}} 秒",increaseForWeakWifi:"對Wi-Fi 訊號弱的印表機增加此值",homeAssistant:"Home Assistant",homeAssistantFullDescription:"連線到Home Assistant,透過HA REST API控制智慧插座。支援switch、light、input_boolean和script實體。",homeAssistantUrl:"Home Assistant URL",longLivedAccessToken:"長期存取權杖",haTokenHint:"在HA中建立權杖:個人資料 → 長期存取權杖 → 建立權杖",connectionSuccessful:"連線成功",connectionFailed:"連線失敗",haConnectionSuccess:"已成功連線到Home Assistant。",haConnectionFailed:"連線Home Assistant失敗。",mqttPublishing:"MQTT發布",mqttDescription:"將BamBuddy事件發布到外部MQTT代理,用於與Node-RED、Home Assistant和其他自動化系統整合。",mqttEnableDescription:"向外部MQTT代理發布事件",brokerHostname:"代理主機名稱",port:"連接埠",usernameOptional:"使用者名稱(可選)",passwordOptional:"密碼(可選)",topicPrefix:"主題前綴",topicPrefixHint:"主題格式:{{prefix}}/printers//status 等",prometheusMetrics:"Prometheus 指標",prometheusEndpointDescription:"在 /api/v1/metrics 公開印表機指標,用於Prometheus/Grafana監控。",bearerTokenOptional:"Bearer權杖(可選)",bearerTokenHint:"設定後,請求必須包含 Authorization: Bearer ",metricsConnectionStatus:"連線狀態",metricsPrinterState:"印表機狀態(空閒/列印中等)",metricsPrintProgress:"列印進度 0-100%",metricsBedTemp:"熱床溫度",metricsNozzleTemp:"噴嘴溫度",metricsPrintsTotal:"按結果分類的總列印數",metricsMore:"...以及更多(層數、風扇、佇列、耗材用量)",smartPlugsDescription:"連線智慧插座(Tasmota或Home Assistant)以自動化電源控制並追蹤印表機的能源使用情況。",allOn:"全部開啟",allOff:"全部關閉",addSmartPlug:"新增智慧插座",energySummary:"能源概要",currentPower:"目前功率",plugsOnline:"{{reachable}}/{{total}} 個插座線上",today:"今天",yesterday:"昨天",total:"總計",enablePlugsForSummary:"啟用插座以檢視能源概要",addNotificationProvider:"新增",systemBadge:"(系統)",creating:"建立中...",changing:"修改中...",deleteUserAndItems:"刪除使用者及其所有項目",deleteUserKeepItems:"刪除使用者,保留項目(將變為無主項目)",ok:"確定",twoFa:{totpTitle:"身份驗證器 App (TOTP)",totpDesc:"使用 Google Authenticator、Aegis 或 Authy 等 App。",emailOtpTitle:"郵件 OTP",emailOtpDesc:"登入時向 {{email}} 傳送一次性驗證碼。",emailOtpNoEmail:"請先為帳戶新增信箱地址以啟用此方式。",addEmailFirst:"您的帳戶沒有信箱地址,請聯絡管理員新增。",setupTotp:"設定身份驗證器 App",setupAuthApp:"設定身份驗證器 App",setupInstructions:"使用身份驗證器 App 掃描QR Code,然後輸入驗證碼確認。",manualEntry:"無法掃描?請手動輸入此金鑰:",scannedContinue:"已掃描 — 繼續",enterCodeToConfirm:"請輸入身份驗證器 App 中的6位驗證碼以確認設定。",activate:"啟用",disableTotp:"停用身份驗證器",disableConfirmHint:"請輸入有效的 TOTP 碼或備用碼來停用身份驗證器。",totpDisabled:"身份驗證器 App 已停用。",emailOtpEnabled:"郵件 OTP 已啟用。",emailOtpDisabled:"郵件 OTP 已停用。",smtpRequired:"請先設定並測試SMTP設定。",invalidCode:"無效驗證碼,請重試。",enableEmailOtp:"啟用郵件 OTP",disableEmailOtp:"停用郵件 OTP",emailSetupEnterCode:"驗證碼已傳送至您的信箱地址。請在下方輸入以確認您擁有此信箱。",verifyAndEnable:"驗證並啟用",emailDisablePasswordHint:"請輸入您的帳戶密碼以確認停用郵件 OTP。",passwordPlaceholder:"輸入您的密碼",backupCodesTitle:"儲存備用碼",backupCodesWarning:"請將這些碼儲存在安全的地方。每個碼只能使用一次,且不會再次顯示。",backupCodesRemaining:"剩餘 {{count}} 個備用碼",savedCodes:"已儲存",regenBackup:"重新產生備用碼",regenBackupHint:"輸入目前 TOTP 碼以產生 10 個新備用碼,所有現有備用碼將失效。",newBackupCodes:"新備用碼",linkedAccounts:"已連結的 SSO 帳戶",linkedAccountsDesc:"以下外部身份提供者已與您的帳戶連結。",oidcUnlinked:"帳戶已解除連結。"},oidc:{title:"SSO / OIDC 提供者",desc:"設定 OpenID Connect 提供者以實現單點登入。",addProvider:"新增提供者",newProvider:"新提供者",empty:"尚未設定 OIDC 提供者。",created:"提供者已建立。",updated:"提供者已更新。",deleted:"提供者已刪除。",deleteTitle:"刪除提供者",deleteMessage:'刪除"{{name}}"?所有連結帳戶將斷開連線。',form:{name:"顯示名稱",issuerUrl:"頒發者 URL",clientId:"客戶端 ID",clientSecret:"客戶端金鑰",scopes:"作用域",iconUrl:"圖示 URL(可選)",enabled:"已啟用",autoCreate:"自動建立使用者",autoCreateDesc:"首次登入時自動建立本機帳戶。",autoLink:"自動連結已有帳戶",autoLinkDesc:"首次登入時透過信箱匹配現有本機帳戶並自動連結。",secretHint:"留空以保留目前",secretPlaceholder:"新金鑰"}}},notification:{printStarted:{title:"列印已開始",body:"{{printer}}:{{filename}} 已開始列印"},printCompleted:{title:"列印已完成",body:"{{printer}}:{{filename}} 已成功完成"},printFailed:{title:"列印失敗",body:"{{printer}}:{{filename}} 列印失敗"},printStopped:{title:"列印已停止",body:"{{printer}}:{{filename}} 已停止"},printProgress:{title:"列印進度",body:"{{printer}}:{{filename}} 已完成 {{percent}}%"},printerOffline:{title:"印表機離線",body:"{{printer}} 已離線"},printerError:{title:"印表機錯誤",body:"{{printer}}:{{error}}"},filamentLow:{title:"耗材不足",body:"{{printer}}:耗材即將用完"},maintenanceDue:{title:"維護到期",body:"{{printer}}:{{items}} 需要關注"}},errors:{generic:"出了點問題",networkError:"網路錯誤。請檢查您的連線。",notFound:"未找到",unauthorized:"未授權",serverError:"伺服器錯誤",validationError:"請檢查您的輸入",printerConnectionFailed:"連線印表機失敗",saveFailed:"儲存更改失敗",deleteFailed:"刪除失敗",loadFailed:"載入資料失敗"},hmsErrors:{title:"錯誤 - {{name}}",noErrors:"無錯誤",viewOnWiki:"在拓竹 Wiki 上檢視",clearInstructions:"在印表機上清除錯誤以在此處消除它們。",clearErrors:"清除錯誤",clearSuccess:"HMS 錯誤已清除",clearFailed:"清除 HMS 錯誤失敗"},mqttDebug:{title:"MQTT 偵錯日誌",searchPlaceholder:"搜尋主題或負載...",noMessages:"尚未紀錄訊息",startLoggingHint:'點選"開始紀錄"以開始捕獲 MQTT 訊息',noMessagesMatch:"沒有訊息匹配您的篩選條件",adjustFilterHint:"嘗試調整您的搜尋或篩選條件",incoming:"傳入",outgoing:"傳出",loggingStopped:"紀錄已停止",loggingActive:"紀錄中 - 訊息將自動重新整理",startLogging:"開始紀錄",stopLogging:"停止紀錄",clearLog:"清除日誌",topic:"主題",timestamp:"時間戳",direction:"方向",all:"全部"},printerFiles:{title:"檔案管理器",storageUsed:"已用:",storageFree:"剩餘:",filterPlaceholder:"篩選檔案...",deleteButton:"刪除",deleteFiles:"刪除 {{count}} 個檔案",deleteFileConfirm:'刪除"{{name}}"?此操作無法復原。',deleteFilesConfirm:"刪除 {{count}} 個選中的檔案?此操作無法復原。",noFiles:"印表機上沒有檔案",loadingFiles:"載入檔案中...",failedToLoad:"載入檔案失敗",toast:{filesDeleted:"已刪除 {{count}} 個檔案",deleteFailed:"刪除失敗:{{error}}"}},confirm:{delete:"確定要刪除嗎?",unsavedChanges:"您有未儲存的更改。確定要離開嗎?",clearQueue:"確定要清空佇列嗎?"},login:{title:"Bambuddy 登入",subtitle:"登入您的帳戶",username:"使用者名稱",usernamePlaceholder:"輸入您的使用者名稱",usernameOrEmail:"使用者名稱或信箱",usernameOrEmailPlaceholder:"使用者名稱或 @ 信箱",password:"密碼",passwordPlaceholder:"輸入您的密碼",signIn:"登入",signingIn:"登入中...",forgotPassword:"忘記密碼?",loginSuccess:"登入成功",loginFailed:"登入失敗",enterCredentials:"請輸入使用者名稱和密碼",enterEmail:"請輸入您的電子郵件地址",oidcLoginFailed:"OIDC 登入失敗",oidcErrors:{providerError:"身份提供者返回了一個錯誤",missingParameters:"OIDC 回呼缺少必要引數",invalidState:"OIDC 狀態無效或已被使用",stateExpired:"OIDC 登入會話已過期,請重試",providerNotFound:"未找到 OIDC 提供者",discoveryFailed:"無法獲取 OIDC 探索文件",invalidDiscovery:"OIDC 探索文件無效",networkError:"OIDC 權杖交換時出現網路錯誤",badResponse:"OIDC 權杖交換時收到意外回應",noIdToken:"OIDC 提供者未返回 ID 權杖",validationFailed:"OIDC 權杖驗證失敗",nonceMismatch:"OIDC nonce 不符,可能存在重放攻擊",missingSubClaim:"OIDC 權杖缺少 sub 宣告",noLinkedAccount:"沒有與此 OIDC 身份連結的本機帳戶",accountInactive:"您的帳戶已被停用",userResolutionFailed:"無法解析您的帳戶",internalError:"OIDC 登入過程中發生內部錯誤",tokenExchangeFailed:"OIDC 權杖交換失敗"},forgotPasswordTitle:"忘記密碼",forgotPasswordMessage:"如果您忘記了密碼,請聯絡系統管理員進行重設。",forgotPasswordEmailMessage:"輸入您的信箱地址,我們將向您傳送新密碼。",emailAddress:"信箱地址",emailPlaceholder:"your.email@example.com",cancel:"取消",sending:"傳送中...",sendResetEmail:"傳送重設郵件",howToReset:"如何重設密碼:",resetStep1:"聯絡您的 Bambuddy 管理員",resetStep2:"請他們在使用者管理中重設您的密碼",resetStep3:"他們可以為您設定一個臨時密碼",resetStep4:"使用新密碼登入並在設定中修改密碼",gotIt:"知道了",resetPassword:{title:"設定新密碼",subtitle:"請在下方輸入並確認您的新密碼。",newPassword:"新密碼",newPasswordPlaceholder:"至少 8 個字元",confirmPassword:"確認密碼",confirmPasswordPlaceholder:"重複輸入新密碼",saving:"儲存中…",submit:"設定新密碼",backToLogin:"回到登入",passwordsDoNotMatch:"密碼不符",passwordTooShort:"密碼至少需要 8 個字元",resetFailed:"密碼重設失敗。連結可能已過期。"},twoFA:{title:"兩步驗證",subtitle:"您的帳戶已啟用兩步驗證。請在下方輸入驗證碼。",methodAuthenticator:"身份驗證器 App",methodEmail:"信箱驗證碼",methodBackup:"備用恢復碼",instructionsTotp:"請開啟您的身份驗證器 App,輸入 Bambuddy 的 6 位驗證碼。",instructionsEmail:"6 位驗證碼已傳送至您的信箱,有效期為 10 分鐘。",instructionsEmailNotSent:"點選下方按鈕,透過郵件獲取驗證碼。",instructionsBackup:"請輸入您的一個 8 位備用恢復碼。每個恢復碼只能使用一次。",sendCodeButton:"傳送信箱驗證碼",sendingCode:"傳送中...",resendCode:"重新傳送驗證碼",codeLabel:"驗證碼",backupCodeLabel:"備用恢復碼",codePlaceholder:"000000",backupCodePlaceholder:"XXXXXXXX",verifyButton:"驗證",verifyingButton:"驗證中...",backToLogin:"← 回到登入頁面",orContinueWith:"或透過以下方式登入",signInWith:"使用 {{provider}} 登入",enterCode:"請輸入驗證碼",sendCodeFailed:"驗證碼傳送失敗",invalidCode:"無效驗證碼,請重試。"}},setup:{title:"Bambuddy 設定",subtitle:"為您的 Bambuddy 實例設定身份驗證",enableAuth:"啟用身份驗證",adminAccount:"管理員帳戶",adminAccountDesc:"如果管理員使用者已存在,將使用現有管理員帳戶啟用身份驗證。如需使用現有管理員,請將下方欄位留空,或輸入新憑據建立新管理員使用者。",adminUsername:"管理員使用者名稱",adminPassword:"管理員密碼",optionalIfAdminExists:"(如管理員使用者已存在則為可選)",adminUsernamePlaceholder:"輸入管理員使用者名稱(可選)",adminPasswordPlaceholder:"輸入管理員密碼(可選)",confirmPassword:"確認密碼",confirmPasswordPlaceholder:"確認管理員密碼",settingUp:"設定中...",completeSetup:"完成設定",toast:{authEnabledAdminCreated:"身份驗證已啟用並建立了管理員使用者",authEnabledExistingAdmins:"使用現有管理員使用者啟用了身份驗證",setupCompleted:"設定完成",enterBothCredentials:"請輸入管理員使用者名稱和密碼,或將兩者留空以使用現有管理員使用者",passwordsDoNotMatch:"密碼不符",passwordTooShort:"密碼至少需要 6 個字元"}},changePassword:{title:"修改密碼",currentPassword:"目前密碼",currentPasswordPlaceholder:"輸入目前密碼",newPassword:"新密碼",newPasswordPlaceholder:"輸入新密碼(至少 6 個字元)",confirmPassword:"確認新密碼",confirmPasswordPlaceholder:"確認新密碼",passwordsDoNotMatch:"密碼不符",passwordTooShort:"密碼至少需要 6 個字元",changing:"修改中...",success:"密碼修改成功",failed:"密碼修改失敗"},plateAlert:{title:"列印已暫停!",message:"在列印板上偵測到物體。列印已自動暫停。請清理列印板並繼續列印。",understand:"我知道了"},camera:{title:"攝影機檢視",invalidPrinterId:"無效的印表機 ID",live:"即時",snapshot:"快照",restartStream:"重新啟動流",refreshSnapshot:"重新整理快照",fullscreen:"全螢幕",exitFullscreen:"離開全螢幕",connectingToCamera:"連線攝影機中...",capturingSnapshot:"拍攝快照中...",connectionLost:"連線已斷開",connectionFailed:"攝影機連線失敗",reconnecting:"{{countdown}} 秒後重新連線...(第 {{attempt}}/{{max}} 次嘗試)",reconnectNow:"立即重新連線",cameraUnavailable:"攝影機不可用",cameraUnavailableDesc:"請確保印表機已通電並已連線。",noCamera:"無可用攝影機",retry:"重試",cameraStream:"攝影機流",zoomOut:"縮小",zoomIn:"放大",resetZoom:"重設縮放",recording:"錄製中",startRecording:"開始錄製",stopRecording:"停止錄製",chamberLight:"切換腔室燈"},groups:{title:"群組管理",subtitle:"管理存取控制的權限群組",backToSettings:"返回設定",createGroup:"建立群組",noPermission:"您沒有存取此頁面的權限。",system:"系統",noDescription:"無描述",usersCount:"{{count}} 個使用者",permissionsCount:"{{count}} 個權限",edit:"編輯",delete:"刪除",toast:{created:"群組建立成功",updated:"群組更新成功",deleted:"群組刪除成功",enterGroupName:"請輸入群組名稱"},modal:{editGroup:"編輯群組",createGroup:"建立群組",cancel:"取消",saving:"儲存中...",creating:"建立中...",saveChanges:"儲存更改"},form:{groupName:"群組名稱",groupNamePlaceholder:"輸入群組名稱",systemGroupWarning:"系統群組名稱不可更改",description:"描述",descriptionPlaceholder:"輸入描述(可選)",permissions:"權限(已選 {{count}} 個)"},deleteModal:{title:"刪除群組",message:"確定要刪除此群組嗎?此群組中的使用者將失去這些權限。",confirm:"刪除群組"},editor:{title:"編輯群組",createTitle:"建立群組",search:"搜尋權限...",selectAll:"全選",clearAll:"清除全部",permissionsSelected:"已選 {{count}} 個",noResults:"沒有權限匹配您的搜尋"}},users:{title:"使用者管理",subtitle:"管理使用者及其對 Bambuddy 實例的存取",backToSettings:"返回設定",createUser:"建立使用者",noPermission:"您沒有存取此頁面的權限。",admin:"管理員",noGroups:"無群組",active:"活躍",inactive:"非活躍",edit:"編輯",delete:"刪除",system:"系統",noGroupsAvailable:"無可用群組",table:{username:"使用者名稱",groups:"群組",status:"狀態",actions:"操作"},toast:{created:"使用者建立成功",updated:"使用者更新成功",deleted:"使用者刪除成功",fillRequired:"請填寫所有必填欄位",passwordsDoNotMatch:"密碼不符",passwordTooShort:"密碼至少需要 6 個字元"},modal:{createUser:"建立使用者",editUser:"編輯使用者",cancel:"取消",creating:"建立中...",saving:"儲存中...",saveChanges:"儲存更改",advancedAuthSubtitle:"使用進階認證"},form:{username:"使用者名稱",usernamePlaceholder:"輸入使用者名稱",email:"信箱",emailPlaceholder:"user@example.com",password:"密碼",passwordPlaceholder:"輸入密碼",confirmPassword:"確認密碼",confirmPasswordPlaceholder:"確認密碼",newPasswordPlaceholder:"輸入新密碼",confirmNewPasswordPlaceholder:"確認新密碼",leaveBlankToKeep:"留空以保持目前值",groups:"群組",optional:"可選",autoGeneratedPassword:"將自動產生安全密碼並透過郵件傳送給使用者。",passwordManagedByAdvancedAuth:'密碼由進階認證管理。使用"重設密碼"透過郵件向使用者傳送新密碼。',resetPassword:"重設密碼",resettingPassword:"重設密碼中..."},deleteModal:{title:"刪除使用者",message:"確定要刪除此使用者嗎?此操作無法復原。",confirm:"刪除使用者"}},streamOverlay:{title:"流疊加層",invalidPrinterId:"無效的印表機 ID",cameraStream:"攝影機流",progress:"進度",eta:"預計完成時間",printerIdle:"印表機空閒",printerOffline:"印表機離線",status:{printing:"列印中",paused:"已暫停",finished:"已完成",failed:"失敗",idle:"空閒",unknown:"未知"}},profiles:{title:"設定檔案",subtitle:"管理您的切片預設和壓力推進校準",tabs:{cloud:"雲端設定檔案",local:"本機設定檔案",kprofiles:"K 值設定"},localProfiles:{title:"本機設定檔案",subtitle:"從 OrcaSlicer 匯入和管理切片預設",import:"匯入設定檔案",importDesc:"將 .bbscfg、.bbsflmt、.orca_filament、.zip 或 .json 檔案拖放到此處",importing:"匯入中...",search:"搜尋本機預設...",noPresets:"尚無本機預設",badge:"本機",edit:"編輯",delete:"刪除",cancel:"取消",deleteConfirmTitle:"刪除預設",deleteConfirm:"確定要刪除此預設嗎?此操作無法復原。",source:"來源",inheritsFrom:"繼承自",filamentType:"類型",vendor:"廠商",compatiblePrinters:"相容印表機",nozzleTemp:"噴嘴溫度",cost:"成本",density:"密度",pressureAdvance:"壓力推進",filament:"耗材",process:"工藝",printer:"印表機",toast:{importSuccess:"已匯入 {{count}} 個預設",importSkipped:"跳過 {{count}} 個預設(重複)",importError:"匯入時出現 {{count}} 個錯誤",deleted:"預設已刪除",updated:"預設已更新"}},connectedAs:"已連線為",logout:"登出",noLogoutPermission:"您沒有登出的權限",failedToLoad:"載入設定檔案失敗",retry:"重試",time:{justNow:"剛剛",minsAgo:"{{count}} 分鐘前",hoursAgo:"{{count}} 小時前",daysAgo:"{{count}} 天前"},toast:{loggedOut:"已登出"},login:{title:"連線到拓竹雲",subtitle:"跨裝置同步您的切片預設",email:"信箱",password:"密碼",region:"地區",regionGlobal:"全球",regionChina:"中國",verificationCode:"驗證碼",totpCode:"驗證器驗證碼",checkEmail:"檢查您的信箱 ({{email}}) 獲取 6 位驗證碼",enterTotpHint:"輸入驗證器 App 中的 6 位驗證碼",accessToken:"存取權杖",accessTokenHint:"貼上您的拓竹存取權杖(來自 Bambu Studio)",back:"返回",loginButton:"登入",verifyButton:"驗證",setTokenButton:"設定權杖",useToken:"改用存取權杖",useEmail:"改用信箱登入",toast:{loggedIn:"登入成功",codeSent:"驗證碼已傳送到您的信箱",enterTotp:"輸入驗證器 App 中的程式碼",tokenSet:"權杖設定成功"}},presets:{myPreset:"我的預設(可編輯)",duplicate:"複製",editable:"可編輯",failedToLoadDetails:"載入預設詳情失敗",deleteConfirm:"刪除此預設?",deleteWarning:'這將從拓竹雲中永久刪除"{{name}}"。此操作無法復原。',noDuplicatePermission:"您沒有複製預設的權限",noEditPermission:"您沒有編輯預設的權限",noDeletePermission:"您沒有刪除預設的權限",types:{filament:"耗材預設",printer:"印表機預設",process:"工藝預設"},toast:{deleted:"預設已刪除",created:"預設已建立",updated:"預設已更新",duplicated:"預設已複製",fieldAdded:'欄位"{{key}}"已新增',exported:"預設已匯出"},baseLabel:"基礎:{{name}}",currentLabel:"目前:{{name}}",newPreset:"新增預設",editPreset:"編輯預設",duplicatePreset:"複製預設",createNewPreset:"建立新預設",customizeSettings:"自訂新預設的設定",compareWithBase:"與基礎預設比較",compare:"比較",basePreset:"基礎預設",selectBasePreset:"選擇基礎預設...",presetName:"預設名稱",myCustomPreset:"我的自訂預設",inheritsFrom:"繼承自",dropJsonToImport:"拖放 JSON 以匯入",tabs:{common:"常用",allFields:"所有欄位"},availableFields:"可用欄位",searchFieldsPlaceholder:"搜尋欄位...",noMatchingFields:"沒有匹配的欄位",allFieldsAdded:"所有欄位已新增",addCustomField:"新增自訂欄位",yourOverrides:"您的覆蓋值",noOverridesYet:"尚無覆蓋值",clickFieldsToAdd:"點選左側的欄位進行新增",saveAsTemplate:"儲存為範本",jsonTip:"提示:將 .json 檔案拖放到此對話方塊的任意位置以匯入設定"},cloudView:{searchPlaceholder:"搜尋預設...",templates:"範本",refresh:"重新整理",newPreset:"新增預設",clearFilters:"清除篩選",compareMode:"比較模式",selectAnotherPreset:"選擇另一個 {{type}} 預設",clickTwoPresets:"點選兩個相同類型的預設進行比較",selectFirst:"1. 選擇第一個",selectSecond:"2. 選擇第二個",compareNow:"立即比較",lastSynced:"上次同步:",showingCount:"顯示 {{showing}} / {{total}} 個預設",noPresetsFound:"未找到預設",columns:{filament:"耗材",process:"工藝",printer:"印表機"},noFilamentPresets:"無耗材預設",noProcessPresets:"無工藝預設",noPrinterPresets:"無印表機預設",filters:{type:"類型",owner:"所有者",printer:"印表機",nozzle:"噴嘴",filament:"耗材",layer:"層",all:"全部",myPresets:"我的預設",builtIn:"內建",process:"工藝"},noTemplatesPermission:"您沒有管理範本的權限",noRefreshPermission:"您沒有重新整理設定檔案的權限",noCreatePermission:"您沒有建立預設的權限"},templates:{title:"快速範本",noTemplates:"尚無範本",createFirst:"從預設編輯器建立範本",typeFilter:"類型:",deleteTitle:"刪除範本",deleteWarning:"此操作無法復原",deleteConfirm:'確定要刪除"{{name}}"嗎?',namePlaceholder:"範本名稱",descriptionPlaceholder:"描述",settingsJson:"設定 (JSON)",fieldsCount:"{{count}} 個欄位",shownInModals:"在對話方塊中顯示",hiddenInModals:"在對話方塊中隱藏",apply:"套用",toast:{deleted:"範本已刪除",updated:"範本已更新",created:"範本已建立",applied:"範本已套用"}}},support:{debugLoggingActive:"偵錯日誌紀錄已啟用",manageLogs:"管理",collectItem7:"印表機連線和韌體版本",collectItem8:"整合狀態(Spoolman、MQTT、HA)",collectItem9:"網路介面(僅子網)",collectItem10:"Python 套件版本",collectItem11:"資料庫健康檢查",collectItem12:"Docker 環境詳情"},fileManager:{title:"檔案管理器",subtitle:"組織和管理您的列印檔案",uploadFiles:"上傳檔案",newFolder:"新增資料夾",folderName:"資料夾名稱",folderNamePlaceholder:"例如:功能零件",renameFile:"重新命名檔案",renameFolder:"重新命名資料夾",moveFiles:"移動 {{count}} 個檔案",rootNoFolder:"根目錄(無資料夾)",current:"目前",linkFolder:"連結資料夾",linkFolderDescription:'將"{{name}}"連結到專案或歸檔以便快速存取。',project:"專案",archive:"歸檔",noProjectsFound:"未找到專案",noArchivesFound:"未找到歸檔",unlink:"取消連結",link:"連結",dragDropFiles:"將檔案拖放到此處",dropFilesHere:"將檔案放在此處",orClickToBrowse:"或點選瀏覽",allFileTypesSupported:"支援所有檔案類型。ZIP 檔案將被解壓。",zipFilesDetected:"偵測到 ZIP 檔案",zipExtractOptions:"ZIP 檔案將被解壓。選擇如何處理資料夾結構:",preserveZipStructure:"保留 ZIP 中的資料夾結構",createFolderFromZip:"從 ZIP 檔名建立資料夾",stlThumbnailGeneration:"STL 縮圖產生",zipMayContainStl:"ZIP 檔案可能包含 STL 檔案。可以在解壓時產生縮圖。",thumbnailsCanBeGenerated:"可以為 STL 檔案產生縮圖。大型模型可能需要更長時間處理。",generateThumbnailsForStl:"為 STL 檔案產生縮圖",threemfDetected:"偵測到 3MF 檔案",threemfExtractionInfo:"將自動從 3MF 檔案中提取印表機型號、材料、顏色和列印設定。",willBeExtracted:"將被解壓",filesExtracted:"已解壓 {{count}} 個檔案",uploadComplete:"上傳完成:{{succeeded}} 個成功",uploadFailed:"上傳失敗",zipFilesFailed:"{{count}} 個檔案失敗",uploading:"上傳中...",changeLink:"更改連結...",linkTo:"連結到...",linkToProjectOrArchive:"連結到專案或歸檔",addToQueue:"新增到佇列",schedulePrint:"排程",generateThumbnail:"產生縮圖",generateThumbnails:"產生縮圖",generateThumbnailsForMissing:"為缺少縮圖的 STL 檔案產生縮圖",gridView:"網格檢視",listView:"列表檢視",lowDiskSpaceWarning:"磁碟空間不足警告",lowDiskSpaceDetails:"僅剩 {{free}}(總共 {{total}})。閾值設定為 {{threshold}} GB。",files:"檔案",folders:"資料夾",size:"大小",free:"剩餘",allFiles:"所有檔案",wrap:"換行",enableTextWrapping:"啟用文字換行",disableTextWrapping:"停用文字換行",collapse:"折疊",collapseFoldersByDefault:"預設折疊資料夾",expandFoldersByDefault:"預設展開資料夾",dragToResizeTooltip:"拖曳調整大小,雙擊重設",searchFiles:"搜尋檔案...",allTypes:"所有類型",prints:"列印",ascending:"升序",descending:"降序",resultsCount:"{{showing}} / {{total}} 個檔案",selectAll:"全選",deselectAll:"取消全選",selected:"已選擇 {{count}} 個",adding:"新增中...",loadingFiles:"載入檔案中...",folderIsEmpty:"資料夾為空",noFilesYet:"尚無檔案",folderEmptyDescription:"上傳檔案或將檔案移入此資料夾以開始使用。",noFilesDescription:"上傳檔案以開始組織您的列印相關檔案。",noMatchingFiles:"沒有匹配的檔案",noMatchingFilesDescription:"沒有檔案匹配您目前的搜尋或篩選條件。",clearFilters:"清除篩選",printedCount:"已列印 {{count}} 次",uploadedBy:"上傳者",deleteFolder:"刪除資料夾",deleteFile:"刪除檔案",deleteFilesCount:"刪除 {{count}} 個檔案",deleteFolderConfirm:"確定要刪除此資料夾嗎?其中的所有檔案也將被刪除。",deleteFileConfirm:"確定要刪除此檔案嗎?",deleteFilesConfirm:"確定要刪除 {{count}} 個選中的檔案嗎?此操作無法復原。",deleting:"刪除中...",noPermissionRenameFolder:"您沒有重新命名資料夾的權限",noPermissionLinkFolder:"您沒有連結資料夾的權限",noPermissionDeleteFolder:"您沒有刪除資料夾的權限",noPermissionPrint:"您沒有列印的權限",noPermissionAddToQueue:"您沒有新增到佇列的權限",noPermissionDownload:"您沒有下載檔案的權限",noPermissionRenameFile:"您沒有重新命名此檔案的權限",noPermissionGenerateThumbnail:"您沒有產生縮圖的權限",noPermissionDeleteFile:"您沒有刪除此檔案的權限",noPermissionCreateFolder:"您沒有建立資料夾的權限",noPermissionUpload:"您沒有上傳檔案的權限",noPermissionMoveFiles:"您沒有移動檔案的權限",noPermissionDeleteFiles:"您沒有刪除檔案的權限",linkExternal:"連結外部",linkExternalFolder:"連結外部資料夾",linkExternalFolderDescription:"將主機目錄(NAS、USB、網路共享)掛載到檔案管理器中。檔案不會被複制——直接從原始路徑存取。",externalFolderNamePlaceholder:"例如:NAS列印檔案",externalPath:"主機路徑",externalPathHelp:"Docker主機上目錄的絕對路徑。必須以繫結掛載方式掛載到容器中。",readOnly:"只讀",readOnlyHelp:"防止上傳和刪除",showHiddenFiles:"顯示隱藏檔案(點檔案)",externalFolder:"外部資料夾",scanFolder:"掃描",toast:{folderCreated:"資料夾已建立",folderDeleted:"資料夾已刪除",fileDeleted:"檔案已刪除",filesDeleted:"已刪除 {{count}} 個檔案",filesMoved:"檔案已移動",folderLinked:"資料夾已連結",folderUnlinked:"資料夾已取消連結",externalFolderLinked:"外部資料夾已連結並掃描",folderScanned:"掃描完成:新增 {{added}} 個,移除 {{removed}} 個",addedToQueue:"已將 {{count}} 個檔案新增到佇列",addedToQueuePartial:"已新增 {{added}} 個檔案,{{failed}} 個失敗",failedToAddToQueue:"新增檔案失敗:{{error}}",fileRenamed:"檔案已重新命名",folderRenamed:"資料夾已重新命名",thumbnailsGenerated:"已產生 {{count}} 個縮圖",thumbnailsGeneratedPartial:"已產生 {{succeeded}} 個縮圖,{{failed}} 個失敗",noStlMissingThumbnails:"沒有缺少縮圖的 STL 檔案",failedToGenerateThumbnails:"產生縮圖失敗:{{error}}",thumbnailGenerated:"縮圖已產生",failedToGenerateThumbnail:"產生縮圖失敗:{{error}}"}},projects:{title:"專案",subtitle:"組織和追蹤您的 3D 列印專案",newProject:"新增專案",editProject:"編輯專案",deleteProject:"刪除專案",projectName:"專案名稱",description:"描述",noProjects:"尚無專案",noProjectsFiltered:"沒有{{status}}專案",noProjectsFilteredHelp:"您沒有任何{{status}}專案。當專案狀態更改時,它們將出現在這裡。",createFirst:"建立您的第一個項目以開始組織相關列印、追蹤進度和管理構建。",createFirstButton:"建立您的第一個項目",create:"建立",files:"檔案",prints:"列印",plates:"板",parts:"零件",lastModified:"最後修改",deleteConfirm:"確定要刪除此項目嗎?歸檔和佇列項目將被取消連結但不會被刪除。",addFiles:"新增檔案",removeFile:"移除檔案",viewDetails:"檢視詳情",namePlaceholder:"例如:Voron 2.4 構建",descriptionPlaceholder:"可選描述...",color:"顏色",targetPlates:"目標板數",targetPlatesPlaceholder:"例如:25",targetPlatesHelp:"列印任務數量",targetParts:"目標零件數",targetPartsPlaceholder:"例如:150",targetPartsHelp:"所需零件總數",tagsLabel:"標籤(逗號分隔)",tagsPlaceholder:"例如:voron、功能件、禮物",dueDate:"截止日期",priority:"優先順序",priorityLow:"低",priorityNormal:"普通",priorityHigh:"高",priorityUrgent:"緊急",statusActive:"進行中",statusCompleted:"已完成",statusArchived:"已歸檔",done:"完成",completed:"已完成",failed:"失敗",inQueue:"佇列中",noPrintsYet:"尚無列印",printJobs:"列印任務(板)",partsPrinted:"已列印零件",failedParts:"失敗零件",import:"匯入",export:"匯出",importProject:"匯入專案",exportAll:"匯出所有專案",loading:"載入專案中...",noEditPermission:"您沒有編輯專案的權限",noDeletePermission:"您沒有刪除專案的權限",noCreatePermission:"您沒有建立專案的權限",noImportPermission:"您沒有匯入專案的權限",noExportPermission:"您沒有匯出專案的權限",toast:{created:"專案已建立",updated:"專案已更新",deleted:"專案已刪除",imported:"專案已匯入",multipleImported:"已匯入 {{count}} 個項目",importFailed:"匯入失敗",exported:"專案已匯出(僅中繼資料)"}},projectDetail:{notFound:"未找到專案",backToProjects:"返回專案",export:"匯出",exportProject:"匯出專案",noExportPermission:"您沒有匯出專案的權限",noEditPermission:"您沒有編輯專案的權限",partOf:"屬於:",priorityLabel:"優先順序:",noPrints:"此項目尚無列印",status:{active:"進行中",completed:"已完成",archived:"已歸檔"},priority:{low:"低",normal:"普通",high:"高",urgent:"緊急"},dueDate:{overdue:"已逾期",today:"今天到期",daysLeft:"還有 {{count}} 天"},progress:{platesProgress:"板進度",partsProgress:"零件進度",printJobs:"列印任務",parts:"零件",percentComplete:"{{percent}}% 完成",remaining:"剩餘 {{count}} 個"},stats:{printJobs:"列印任務",total:"總計",failed:"{{count}} 個失敗",partsPrinted:"已列印 {{count}} 個零件",printTime:"列印時間",filamentUsed:"耗材用量"},cost:{title:"成本追蹤",filamentCost:"耗材成本",energy:"能源",totalCost:"總成本",total:"總計",includesBom:"含物料清單",budget:"預算",remaining:"剩餘"},subProjects:{title:"子專案 ({{count}})"},notes:{title:"備註",noEditPermission:"您沒有編輯備註的權限",placeholder:"新增關於此項目的備註...",empty:"尚無備註。點選編輯新增備註。"},files:{title:"檔案",linkFolders:"從檔案管理器連結資料夾",forQuickAccess:"到此項目以便快速存取。",fileCount:"{{count}} 個檔案",empty:"未連結資料夾。前往檔案管理器將資料夾連結到此項目。",noFiles:"此資料夾中沒有檔案。",print:"立即列印",addToQueue:"加入佇列"},bom:{title:"材料清單",acquired:"已獲取 {{completed}}/{{total}}",showAll:"顯示全部",hideDone:"隱藏已完成",addPart:"新增零件",noAddPermission:"您沒有新增零件的權限",partNamePlaceholder:"零件名稱(例如:M3x8 螺絲)",partName:"零件名稱",qty:"數量",price:"價格 ({{currency}})",sourcingUrlPlaceholder:"採購連結(可選)",remarksPlaceholder:"備註(可選)",deletePart:"刪除零件",deleteConfirm:'確定要刪除"{{name}}"嗎?',noUpdatePermission:"您沒有更新零件的權限",noEditPermission:"您沒有編輯零件的權限",noDeletePermission:"您沒有刪除零件的權限",totalCost:"總成本:",empty:"材料清單中沒有零件。新增硬體、電子元件或其他元件以追蹤需要採購的物品。"},timeline:{title:"活動時間線",empty:"尚無活動。"},template:{saveAsTemplate:"儲存為範本",noCreatePermission:"您沒有建立範本的權限"},queue:{title:"佇列",viewAll:"檢視全部",printing:"{{count}} 個列印中",queued:"{{count}} 個佇列中"},prints:{title:"列印 ({{count}})"},toast:{projectUpdated:"專案已更新",partAdded:"零件已新增",partRemoved:"零件已移除",exportFailed:"匯出失敗",projectExported:"專案已匯出",templateCreated:"範本已建立"}},system:{title:"系統資訊",version:"版本",uptime:"執行時間",cpuUsage:"CPU 使用率",memoryUsage:"記憶體使用率",diskUsage:"磁碟使用率",networkInfo:"網路資訊",logs:"日誌",debugMode:"偵錯模式",enableDebug:"啟用偵錯日誌",disableDebug:"停用偵錯日誌",downloadLogs:"下載日誌",clearLogs:"清除日誌",dockerInfo:"Docker 資訊",containerName:"容器名稱",imageName:"映象名稱",platform:"平臺",architecture:"架構"},library:{title:"耗材庫",addFilament:"新增耗材",editFilament:"編輯耗材",deleteFilament:"刪除耗材",vendor:"廠商",material:"材料",color:"顏色",kFactor:"K 值",temperature:"溫度",noFilaments:"耗材庫中沒有耗材",deleteConfirm:"確定要刪除此耗材嗎?",importFromPrinter:"從印表機匯入",exportToFile:"匯出到檔案"},spoolman:{title:"Spoolman 整合",enabled:"Spoolman 已啟用",url:"Spoolman URL",connected:"已連線",disconnected:"未連線",testConnection:"測試連線",sync:"同步",syncing:"同步中...",lastSync:"上次同步",linkToSpoolman:"連結到 Spoolman",openInSpoolman:"在 Spoolman 中開啟",unlinkSpool:"取消連結耗材",unlinkConfirmTitle:"解開料盤?",unlinkConfirmMessage:"這將斷開卷軸與 Spoolman 的連線。Spoolman 中的卷軸資料將保持不變。",selectSpool:"選擇耗材",noUnlinkedSpools:"無未連結的耗材",linkSuccess:"耗材已成功連結到 Spoolman",linkFailed:"連結耗材失敗",unlinkSuccess:"已成功從 Spoolman 取消連結耗材",unlinkFailed:"取消連結耗材失敗",spoolId:"耗材 ID",fillSourceLabel:"(Spoolman)",weight:"重量",remaining:"剩餘",disableWeightSync:"停用 AMS 估計重量同步",disableWeightSyncDesc:"不從 AMS 估計值更新剩餘容量。如果您更喜歡 Spoolman 的用量追蹤而非 AMS 百分比估計,請使用此選項。新耗材仍將使用 AMS 估計值作為初始重量。",reportPartialUsage:"報告失敗列印的部分用量",reportPartialUsageDesc:"當列印失敗或被取消時,根據層進度報告估計的耗材使用量。"},inventory:{title:"耗材庫存",addSpool:"新增耗材",editSpool:"編輯耗材",material:"材料",selectMaterial:"選擇材料...",subtype:"子類型",brand:"品牌",searchBrand:"搜尋品牌...",useCustomBrand:'使用"{{brand}}"',useCustomMaterial:"使用自訂材料:{{material}}",colorName:"顏色名稱",colorNamePlaceholder:"翡翠白、烈焰紅...",color:"顏色",hexColor:"十六進位顏色",pickColor:"選擇自訂顏色",labelWeight:"標籤重量",coreWeight:"空盤重量",searchSpoolWeight:"搜尋耗材重量...",weightUsed:"已使用",currentWeight:"剩餘重量",measuredWeight:"稱量重量",spoolName:"料盤",costPerKg:"每公斤成本",measuredWeightError:"稱量重量必須在 {{min}}g 到 {{max}}g 之間。",slicerFilament:"切片耗材",slicerFilamentName:"切片預設名稱",slicerPreset:"切片預設",searchPresets:"搜尋耗材預設...",selectedPreset:"已選擇",noPresetsFound:"未找到預設",tempOverrides:"溫度覆蓋",note:"備註",notePlaceholder:"關於此耗材的任何備註...",archive:"歸檔",restore:"恢復",noSpools:"尚無耗材。新增您的第一個耗材開始使用。",noManualSpools:"沒有手動新增的耗材。請先向庫存中新增耗材。",kProfiles:"K 值設定",addKProfile:"新增 K 值設定",assignSpool:"分配耗材",unassignSpool:"取消分配",assignSuccess:"耗材已分配,AMS 槽位已設定",assignFailed:"分配耗材失敗",assignMismatchTitle:"材料不符",assignMismatchMessage:'所選料盤材料 "{{spoolMaterial}}" 與 {{location}} 的料槽材料 "{{trayMaterial}}" 不符。仍要分配嗎?',assignMismatchConfirm:"仍然分配",assignPartialMismatchMessage:'料盤材料 "{{spoolMaterial}}" 與 {{location}} 的 "{{trayMaterial}}" 相近但不完全一致。是否繼續?',assignProfileMismatchMessage:'料盤設定 "{{spoolProfile}}" 與 {{location}} 的料槽設定 "{{trayProfile}}" 不一致。是否繼續?',selectSpool:"選擇要分配到此槽位的耗材",assigned:"已分配",assigning:"分配中...",searchSpools:"搜尋耗材...",showAllSpools:"顯示所有耗材",allMaterials:"所有材料",filterByBrand:"按品牌篩選...",showArchived:"顯示已歸檔",quickAdd:"快速新增(庫存)",quantity:"數量",stock:"庫存",configured:"已設定",spoolsCreated:"已建立 {{count}} 個耗材",spoolCreated:"耗材已建立",spoolUpdated:"耗材已更新",spoolDeleted:"耗材已刪除",spoolArchived:"耗材已歸檔",spoolRestored:"耗材已恢復",deleteConfirm:"確定要刪除此耗材嗎?此操作無法復原。",archiveConfirm:"確定要歸檔此耗材嗎?",advancedSettings:"進階設定",filamentInfoTab:"耗材資訊",paProfileTab:"PA 設定",filamentInfo:"耗材",additional:"附加",loadingPresets:"載入雲端預設中...",cloudConnected:"雲端已連線",cloudNotConnected:"雲端未連線(使用預設值)",recentColors:"最近",searchColors:"搜尋顏色...",searchResults:"搜尋結果",allColors:"所有顏色",commonColors:"常用顏色",showLess:"顯示更少",showAll:"顯示全部",noColorsFound:"沒有顏色匹配您的搜尋",noResults:"未找到匹配項",selectMaterialFirst:"請先在耗材資訊分頁中選擇材料。",noPrintersConfigured:"未設定印表機。新增印表機以使用 PA 設定。",matchingFilter:"匹配",anyBrand:"任何品牌",anyVariant:"任何變體",autoSelect:"自動選擇",matches:"匹配",match:"匹配",noMatches:"無匹配",connected:"已連線",offline:"離線",printerOffline:"印表機離線。連線後檢視校準設定。",noKProfilesMatch:"沒有 K 值設定匹配所選耗材。",leftNozzle:"左噴嘴",rightNozzle:"右噴嘴",profilesSelected:"個校準設定已選擇",totalInventory:"總庫存",totalConsumed:"總消耗",byMaterial:"按材料",inPrinter:"在印表機中",lowStock:"庫存不足",sinceTracking:"自開始追蹤",loadedInAms:"已裝載到 AMS/外接",remaining:"剩餘",weightCheck:"重量檢查",lastWeighed:"上次稱量",neverWeighed:"從未稱量",search:"搜尋耗材...",showing:"顯示",to:"到",of:"共",show:"顯示",spools:"個耗材",spool:"個耗材",page:"頁",noSpoolsMatch:"未找到結果",noSpoolsMatchDesc:"嘗試調整您的搜尋或篩選條件。",active:"活躍",archived:"已歸檔",all:"全部",used:"已使用",new:"新的",clearFilters:"清除篩選",table:"表格",cards:"卡片",net:"淨重",groupSimilar:"分組",groupedSpools:"{{count}} 個相同耗材",groupedRows:"行",columns:"列",configureColumns:"設定列",configureColumnsDesc:"拖曳以重新排序列或使用箭頭。使用眼睛圖示切換可見性。",visible:"可見",reset:"重設",cancel:"取消",applyChanges:"套用更改",moveUp:"上移",moveDown:"下移",hideColumn:"隱藏列",showColumn:"顯示列",linkToSpool:"連結到耗材",tagLinked:"標籤已連結到耗材",tagLinkFailed:"連結標籤失敗",tagAlreadyLinked:"標籤已連結到其他耗材",unknownTag:"偵測到未知 RFID 標籤",usageHistory:"使用歷史",noUsageHistory:"尚無使用紀錄",printName:"列印名稱",weightConsumed:"消耗重量",clearHistory:"清除",historyCleared:"使用歷史已清除",fillSourceLabel:"(庫存)",lowStockThresholdError:"閾值必須在 0.1 到 99.9 之間"},timelapse:{title:"縮時攝影",create:"建立縮時攝影",download:"下載",delete:"刪除",preview:"預覽",frameRate:"幀率",quality:"品質",processing:"處理中...",noTimelapses:"無可用縮時攝影"},ams:{title:"AMS",slot:"槽位",empty:"空",emptySlot:"空槽位",unknown:"未知",humidity:"濕度",temperature:"溫度",filamentType:"耗材類型",filamentColor:"顏色",remaining:"剩餘",history:"AMS 歷史",noHistory:"無可用歷史",configureSlot:"設定槽位",externalSpool:"外接耗材",profile:"設定",kFactor:"K 值",fill:"填充",configure:"設定",used:"已使用",remainingUnit:"剩餘"},printModal:{title:"開始列印",selectPrinter:"選擇印表機",selectPlate:"選擇板",filamentMapping:"耗材對應",totalCost:"總成本:",slotRemainingShort:" - 剩餘 {{grams}}g",printSettings:"列印設定",bedLeveling:"熱床調平",flowCalibration:"流量校準",vibrationCalibration:"振動校準",layerInspection:"首層檢查",timelapse:"縮時攝影",startPrint:"開始列印",addToQueue:"新增到佇列",cancel:"取消",noPrintersAvailable:"無可用印表機",printerBusy:"印表機忙碌",printerOffline:"印表機離線",sameTypeDifferentColor:"相同類型,不同顏色",filamentTypeNotLoaded:"耗材類型未裝載",openCalendar:"開啟日曆",leftNozzle:"左",rightNozzle:"右",leftNozzleTooltip:"左噴嘴",rightNozzleTooltip:"右噴嘴",filamentOverride:"耗材覆蓋",filamentOverrideHint:"可選覆蓋用於基於模型的耗材分配。排程器將使用您選擇的耗材而不是原始 3MF 值進行匹配。",originalFilament:"原始",overrideWith:"覆蓋為",resetToOriginal:"恢復為原始",insufficientFilamentTitle:"耗材不足",insufficientFilamentMessage:"部分已分配料盤的剩餘耗材少於本次列印所需:",insufficientFilamentLine:"{{printer}} - {{slot}}:需要 {{required}}g,剩餘 {{remaining}}g",printAnyway:"仍然列印",forceColorMatch:"強制顏色匹配",staggerPrinterStarts:"錯開印表機啟動",staggerGroupSize:"群組大小",staggerInterval:"間隔(分鐘)",staggerPreview:"{{printers}} 台印表機 → 分成 {{groups}} 組,每組 {{size}} 台,每 {{interval}} 分鐘啟動一組",staggerLastGroup:"最後一組:{{count}}",staggerTotal:"總計:{{minutes}} 分鐘",staggerToPrinters:"分批傳送到 {{count}} 臺印表機",gcodeInjection:"注入自動列印G-code"},backup:{title:"備份與恢復",createBackup:"建立備份",restoreBackup:"恢復備份",restoreDescription:"從備份檔案替換所有資料",downloadBackup:"下載備份",uploadBackup:"上傳備份",lastBackup:"上次備份",autoBackup:"自動備份",backupNow:"立即備份",restoreWarning:"警告:恢復備份將覆蓋所有目前資料。",includeArchives:"包含歸檔",includeSettings:"包含設定",includeProfiles:"包含設定檔案",backupSuccess:"備份建立成功",restoreSuccess:"備份恢復成功",backupFailed:"備份失敗",restoreFailed:"恢復失敗",restoreNote:"恢復期間虛擬印表機將停止",githubBackup:"GitHub 備份",enabled:"已啟用",cloudLoginRequired:"需要登入 Bambu Cloud。請在 設定檔案 → 雲設定檔案 中登入以啟用 GitHub 備份。",cloudLoginRequiredShort:"需要雲端登入",githubDescription:"自動將您的設定檔案同步到私有 GitHub 倉庫以進行備份和版本歷史紀錄。",repositoryUrl:"倉庫 URL",personalAccessToken:"個人存取權杖",tokenSaved:"(已儲存)",enterNewToken:"輸入新權杖以更新",tokenHint:"具有內容讀寫權限的細粒度權杖",branch:"分支",manualOnly:"僅手動",hourly:"每小時",daily:"每天",weekly:"每週",includeInBackup:"包含在備份中",kProfiles:"K 設定檔案",kProfilesDescription:"來自已連線印表機的壓力推進校準",noPrintersConnected:"沒有印表機連線",printersConnected:"{{connected}}/{{total}} 已連線",cloudProfiles:"雲設定檔案",cloudProfilesDescription:"來自 Bambu Cloud 的耗材、印表機和工藝預設",appSettings:"應用程式設定",appSettingsDescription:"Bambuddy 設定(完整資料庫)",spoolInventory:"耗材庫存",spoolInventoryDescription:"耗材卷軸、使用紀錄和成本追蹤",printArchives:"列印檔案",printArchivesDescription:"列印歷史中繼資料(不含 gcode/3MF 檔案)",lastBackupAt:"上次備份:",noBackupsYet:"尚無備份",next:"下次:",startingBackup:"正在啟動備份...",test:"測試",enableBackup:"啟用備份",testConnection:"測試連線",enterRepoUrl:"請輸入倉庫 URL",enterRepoAndToken:"請輸入倉庫 URL 和存取權杖",repoRequired:"倉庫 URL 為必填項",tokenRequired:"存取權杖為必填項",githubBackupEnabled:"GitHub 備份已啟用",tokenUpdated:"權杖已更新",settingsSaved:"設定已儲存",failedToSave:"儲存失敗:{{message}}",backupCompleteFiles:"備份完成 - {{count}} 個檔案已更新",backupSkippedNoChanges:"備份已跳過 - 無更改",backupFailed2:"備份失敗:{{message}}",clearedLogs:"已清除 {{count}} 條日誌",failedToClearLogs:"清除日誌失敗:{{message}}",history:"歷史紀錄",clear:"清除",date:"日期",status:"狀態",commit:"提交",localBackup:"本機備份",localBackupDescription:"建立 Bambuddy 資料的完整備份,包括資料庫、檔案、上傳和所有檔案。",downloadBackupLabel:"下載備份",completeBackupZip:"完整備份:資料庫 + 所有檔案(ZIP)",download:"下載",preparingBackup:"正在準備備份...",creatingArchive:"正在建立備份歸檔...對於大型歸檔可能需要一些時間。",downloadingFile:"正在下載備份檔案...",backupDownloaded:"備份下載成功",failedToCreateBackup:"建立備份失敗:{{message}}",restore:"恢復",restoreReplacesAll:"恢復將替換所有資料。",restoreReplacesAllDetail:"您目前的資料庫和檔案將被完全替換。恢復後需要重新啟動。",restoreConfirmTitle:"恢復備份",restoreConfirmMessage:'您確定要從"{{filename}}"恢復嗎?這將完全替換您目前的資料庫和所有檔案。恢復後需要重新啟動應用程式。',restoreConfirmButton:"恢復備份",uploadingFile:"正在上傳備份檔案...",backupRestoredRestart:"備份已恢復。請重新啟動 Bambuddy。",failedToRestore:"恢復備份失敗。請檢查檔案格式。",reloadNow:"立即重新載入",creatingBackup:"正在建立備份",restoringBackup:"正在恢復備份",preparing:"準備中...",processing:"處理中...",doNotClosePage:"請不要關閉此頁面或離開頁面。對於大型備份,此操作可能需要幾分鐘。",restoring:"恢復中...",restoreComplete:"恢復完成",restoreFailed2:"恢復失敗",importSettings:"從備份檔案匯入設定",pleaseWaitRestoring:"請等待資料恢復中",selectBackupFile:"點選選擇備份檔案(.json 或 .zip)",duplicateHandling:"重複項處理方式:",matchPrinters:"印表機",matchPrintersBy:"按序列號匹配",matchSmartPlugs:"智慧插座",matchSmartPlugsBy:"按 IP 位址匹配",matchNotificationProviders:"通知提供者",matchNotificationProvidersBy:"按名稱匹配",matchFilaments:"耗材",matchFilamentsBy:"按名稱 + 類型 + 品牌匹配",matchArchives:"檔案",matchArchivesBy:"按內容雜湊匹配(始終跳過)",matchPendingUploads:"待上傳",matchPendingUploadsBy:"按檔名匹配",matchSettingsTemplates:"設定和範本",matchSettingsTemplatesBy:"始終覆蓋",replaceExisting:"替換現有資料",keepExisting:"保留現有資料",overwriteDescription:"用備份資料覆蓋已存在的項目",keepDescription:"僅恢復尚不存在的項目",overwriteCaution:"注意:",overwriteWarning:"覆蓋將用備份資料替換您目前的設定。出於安全考慮,印表機存取碼永遠不會被覆蓋。",cancel:"取消",processingBackup:"正在處理備份檔案...",itemsRestored:"已恢復項目",itemsSkipped:"已跳過項目",restored:"已恢復",skippedAlreadyExist:"已跳過(已存在)",filesCategory:"檔案(3MF、縮圖等)",andMore:"...還有 {{count}} 項",newApiKeysGenerated:"已產生新的 API 金鑰",keysShownOnce:"這些金鑰僅顯示一次。請立即複製!",copy:"複製",noDataFound:"在備份檔案中未找到可恢復的資料。",close:"關閉",scheduledBackup:"排程備份",scheduledBackupDescription:"依排程自動建立備份快照。輸出目錄可掛載到 NAS 或外部儲存。",frequency:"頻率",backupTime:"時間",retention:"保留",retentionDescription:"保留的備份數量",outputPath:"輸出路徑",outputPathPlaceholder:"預設:{{path}}",outputPathDescription:"留空以使用預設位置",runNow:"立即執行",backupFiles:"備份檔案",noScheduledBackups:"尚無備份",deleteBackup:"刪除",deleteBackupConfirm:"要刪除此備份檔案嗎?",backupRunning:"備份進行中…",scheduledBackupComplete:"備份已成功完成",scheduledBackupFailed:"備份失敗",nextBackup:"下次備份",backupSize:"大小",utc:"UTC",defaultPathLabel:"預設:",categories:{settings:"設定",notification_providers:"通知提供者",notification_templates:"通知範本",smart_plugs:"智慧插座",printers:"印表機",filaments:"耗材",maintenance_types:"維護類型",archives:"檔案",projects:"專案",pending_uploads:"待上傳",external_links:"外部連結",api_keys:"API 金鑰"}},tags:{title:"標籤",addTag:"新增標籤",editTag:"編輯標籤",deleteTag:"刪除標籤",tagName:"標籤名稱",tagColor:"標籤顏色",noTags:"無標籤",deleteConfirm:"確定要刪除此標籤嗎?",manageTags:"管理標籤"},uploadModal:{title:"上傳 3MF 檔案",dragDrop:"將 .3mf 檔案拖放到此處",or:"或",browseFiles:"瀏覽檔案",extractionInfo:"將從 3MF 檔案中繼資料中自動提取印表機型號。",uploaded:"已上傳",failed:"失敗",uploading:"上傳中...",upload:"上傳",uploadFailed:"上傳失敗"},editArchive:{title:"編輯歸檔",name:"名稱",namePlaceholder:"列印名稱",printer:"印表機",noPrinter:"無印表機",project:"專案",noProject:"無專案",itemsPrinted:"列印數量",itemsPrintedHelp:"此列印任務中生產的物品數量",notes:"備註",notesPlaceholder:"新增關於此列印的備註...",externalLink:"外部連結",externalLinkPlaceholder:"https://printables.com/model/...",externalLinkHelp:"連結到 Printables、Thingiverse 或其他來源",tags:"標籤",tagsPlaceholder:"新增標籤...",addMoreTags:"新增更多標籤...",matchingTags:'匹配"{{query}}"',existingTags:"現有標籤",clickToAdd:"(點選新增)",status:"狀態",failureReason:"失敗原因",selectReason:"選擇原因...",photos:"列印成品照片",photosHelp:"點選 + 新增列印成品照片",printResult:"列印成品",saving:"儲存中...",failureReasons:{adhesionFailure:"附著力失敗",spaghettiDetached:"拉絲 / 脫落",layerShift:"層偏移",cloggedNozzle:"噴嘴堵塞",filamentRunout:"耗材用完",warping:"翹曲",stringing:"拉絲",underExtrusion:"擠出不足",powerFailure:"斷電",userCancelled:"使用者取消",other:"其他"},statuses:{completed:"已完成",failed:"失敗",aborted:"已取消",printing:"列印中"}},kProfiles:{title:"K 值設定",noPrintersConfigured:"未設定印表機",addPrinterInSettings:"在設定中新增印表機以管理 K 值設定",noActivePrinters:"無活躍印表機",enablePrinterConnection:"啟用印表機連線以檢視其 K 值設定",loadingProfiles:"載入 K 值設定中...",printerOffline:"印表機離線",printerOfflineDesc:"所選印表機未連線。開啟電源以檢視 K 值設定。",noMatchingProfiles:"無匹配的設定",noMatchingProfilesDesc:"沒有設定匹配您的搜尋條件",noKProfiles:"無 K 值設定",noKProfilesDesc:"未找到 {{diameter}}mm 噴嘴的壓力推進設定",createFirstProfile:"建立第一個設定",printer:"印表機",nozzle:"噴嘴",refresh:"重新整理",addProfile:"新增設定",export:"匯出",import:"匯入",select:"選擇",selectAll:"全選",delete:"刪除",searchPlaceholder:"按名稱或耗材搜尋...",allExtruders:"所有擠出機",leftOnly:"僅左側",rightOnly:"僅右側",allFlow:"所有流量",hfOnly:"僅高流量",sOnly:"僅標準",sortName:"排序:名稱",sortKValue:"排序:K 值",sortFilament:"排序:耗材",leftExtruder:"左擠出機",rightExtruder:"右擠出機",modal:{addTitle:"新增 K 值設定",editTitle:"編輯 K 值設定",profileName:"設定名稱",profileNamePlaceholder:"我的 PLA 設定",kValue:"K 值",kValuePlaceholder:"0.020",kValueHelp:"典型範圍:PLA 0.01 - 0.06,PETG 0.02 - 0.10",filament:"耗材",selectFilament:"選擇耗材...",noFilamentsHelp:"未找到耗材。請先在 Bambu Studio 中建立 K 值設定。",flowType:"流量類型",highFlow:"高流量",standard:"標準",nozzleSize:"噴嘴尺寸",extruder:"擠出機",extruders:"擠出機",left:"左",right:"右",notes:"備註(本機儲存)",notesPlaceholder:"新增關於此設定的備註...",notesHelp:"備註儲存在 Bambuddy 中,不在印表機上",syncing:"與印表機同步中...",savingExtruder:"儲存到擠出機 {{current}}/{{total}}...",pleaseWait:"請稍候"},deleteConfirm:{title:"刪除設定",cannotUndo:"此操作無法復原",message:'確定要從印表機刪除"{{name}}"嗎?'},bulkDelete:{title:"刪除設定",cannotUndo:"此操作無法復原",message:"確定要從印表機刪除 {{count}} 個選中的設定嗎?"},toast:{profileSaved:"K 值設定已儲存",profilesSaved:"K 值設定已儲存到 {{count}} 個擠出機",selectAtLeastOneExtruder:"請至少選擇一個擠出機",profileDeleted:"K 值設定已刪除",profilesDeleted:"已刪除 {{count}} 個設定",exportedProfiles:"已匯出 {{count}} 個設定",importedProfiles:"已匯入 {{count}} / {{total}} 個設定",noProfilesToExport:"無可匯出的設定",invalidFileFormat:"無效的檔案格式",failedToParseImport:"解析匯入檔案失敗",failedToSaveBatch:"批次儲存 K 值設定失敗",noteSaved:"備註已儲存",failedToSaveNote:"儲存備註失敗"},permission:{noRead:"您沒有重新整理設定的權限",noCreate:"您沒有新增設定的權限",noUpdate:"您沒有更新 K 值設定的權限",noDelete:"您沒有刪除 K 值設定的權限",noExport:"您沒有匯出設定的權限",noImport:"您沒有匯入設定的權限"}},virtualPrinter:{title:"虛擬印表機",running:"執行中",stopped:"已停止",description:{default:"啟用虛擬印表機,使其在 Bambu Studio 和 OrcaSlicer 中可見。傳送到此印表機的檔案將直接歸檔而不列印。",proxy:"啟用代理,將切片軟體流量中繼到真實印表機,允許在任何網路上遠端列印。"},enable:{title:"啟用虛擬印表機",visibleInSlicer:'在切片軟體發現中顯示為"Bambuddy"',proxyingTo:"代理到 {{name}}",notActive:"未啟用"},model:{title:"印表機型號",description:"選擇要模擬的印表機型號。",restartWarning:"更改型號將重新啟動虛擬印表機"},accessCode:{title:"存取碼",isSet:"存取碼已設定",notSet:"未設定存取碼 - 需要設定才能啟用",placeholder:"輸入 8 位字元程式碼",placeholderChange:"輸入新程式碼以更改",hint:"必須恰好 8 個字元。切片軟體使用此程式碼進行認證。",charCount:"({{count}}/8)"},targetPrinter:{title:"目標印表機",configured:"代理目標已設定",notConfigured:"未選擇目標印表機 - 代理模式需要設定",placeholder:"選擇印表機...",hint:"選擇要將切片軟體流量代理到的印表機。印表機必須處於區域網路模式。",noPrinters:"未設定印表機。請先新增印表機以使用代理模式。"},remoteInterface:{title:"網路介面覆蓋",configured:"介面覆蓋已啟用",optional:"可選 - 當自動檢測的 IP 不正確時使用(例如多網路卡、Docker、VPN)",placeholder:"自動檢測(預設)...",hint:"覆蓋透過 SSDP 廣播並在 TLS 憑證中使用的 IP 位址。在 Bambuddy 有多個網路介面時很有用。"},mode:{title:"模式",archive:"歸檔",archiveDesc:"立即歸檔檔案",review:"審核",reviewDesc:"歸檔前審核",queue:"佇列",queueDesc:"歸檔並新增到佇列",proxy:"代理",proxyDesc:"中繼到真實印表機"},autoDispatch:{title:"自動派發",description:"新增到佇列時自動開始列印。關閉後,列印任務等待手動派發。"},setupRequired:{title:"需要設定",description:"虛擬印表機功能需要額外的系統設定才能工作。包括埠轉發、防火牆規則和平臺特定設定。",readGuide:"啟用前請閱讀設定指南"},howItWorks:{title:"工作原理",step1:"在同一區域網路中,虛擬印表機會透過發現機制自動出現在您的切片軟體(Bambu Studio / OrcaSlicer)中。從其他網路,透過 IP 位址和存取碼手動新增。",step2:'在歸檔、審核和佇列模式下,使用切片軟體中的"傳送"按鈕將 3MF 檔案上傳到 Bambuddy。切片軟體會顯示"列印成功"— 檔案已儲存,未列印。',step3:"在代理模式下,虛擬印表機將所有流量中繼到真實印表機 — 列印會立即開始,就像直接連線一樣。"},status:{title:"狀態詳情",printerName:"印表機名稱",model:"型號",serialNumber:"序列號",mode:"模式",pendingFiles:"待處理檔案",targetPrinter:"目標印表機",ftpPort:"FTP 連接埠",mqttPort:"MQTT 連接埠",ftpConnections:"FTP 連線",mqttConnections:"MQTT 連線"},toast:{updated:"虛擬印表機設定已更新",failedToUpdate:"更新設定失敗",accessCodeRequired:"請先設定存取碼",targetPrinterRequired:"請先選擇目標印表機",bindIpRequired:"請先設定繫結 IP",accessCodeEmpty:"存取碼不能為空",accessCodeLength:"存取碼必須恰好 8 個字元",created:"虛擬印表機已建立",failedToCreate:"建立虛擬印表機失敗",deleted:"虛擬印表機已刪除",failedToDelete:"刪除虛擬印表機失敗"},list:{title:"虛擬印表機",add:"新增",addFirst:"新增虛擬印表機",empty:"未設定虛擬印表機。新增一個以開始使用。"},bindIp:{title:"繫結介面",placeholder:"選擇介面...",hint:"此虛擬印表機繫結的網路介面。每臺印表機必須唯一。"},proxy:{accessCodeHint:"在代理模式下,在切片軟體中使用目標印表機的存取碼。連線會透明轉發到真實印表機。"},addDialog:{title:"新增虛擬印表機",name:"名稱",hint:"建立後可以設定存取碼、目標印表機和其他設定。",create:"建立"},deleteConfirm:{title:"刪除虛擬印表機",message:'確定要刪除"{{name}}"嗎?這將停止此印表機的所有服務。'}},modelViewer:{openInSlicer:"在切片軟體中開啟",tabs:{model:"3D 模型",gcode:"G-code 預覽"},notAvailable:"不可用",notSliced:"未切片",plates:"板",allPlates:"所有板",plateNumber:"板 {{number}}",plateCount:"{{count}} 個板",plateCount_other:"{{count}} 個板",objectCount:"{{count}} 個物件",objectCount_other:"{{count}} 個物件",filamentCount:"{{count}} 種耗材",filamentCount_other:"{{count}} 種耗材",eta:"預計 {{minutes}} 分鐘",noPreview:"此檔案無可用預覽",pagination:{pageOf:"第 {{current}} / {{total}} 頁",prev:"上一頁",next:"下一頁"},errors:{failedToLoad:"載入檔案失敗",noMeshes:"3MF 檔案中未找到網格",unsupportedFormat:"不支援的檔案格式"}},maintenanceDescriptions:{lubricateCarbonRods:"在碳纖維杆上塗抹潤滑劑以確保順暢運動",lubricateRails:"在線性導軌上塗抹潤滑劑以確保順暢運動",cleanNozzle:"清潔熱端和噴嘴以防止堵塞",checkBelts:"檢查皮帶張力以確保列印精度",cleanBuildPlate:"清潔列印板以獲得更好的附著力",checkExtruder:"檢查擠出機齒輪磨損情況",checkCooling:"確保冷卻風扇正常工作",generalInspection:"印表機綜合檢查",cleanCarbonRods:"清潔碳纖維杆以減少摩擦",lubricateSteelRods:"在鋼杆上塗抹潤滑劑以確保順暢運動",cleanSteelRods:"清潔鋼杆以減少摩擦",cleanLinearRails:"擦拭線性導軌以清除灰塵和碎屑",checkPtfeTube:"檢查 PTFE 管的磨損或損壞",replaceHepaFilter:"更換 HEPA 過濾器以保證空氣品質",replaceCarbonFilter:"更換活性炭過濾器",lubricateLeftNozzleRail:"潤滑左噴嘴導軌(H2 系列)"},smartPlugs:{offline:"離線",admin:"管理",openPlugAdminPage:"開啟插座管理頁面",deleteSmartPlug:"刪除智慧插座",turnOnSmartPlug:"開啟智慧插座",turnOffSmartPlug:"關閉智慧插座",turnOn:"開啟",turnOff:"關閉",addSmartPlug:{scanningNetwork:"掃描網路中...",chooseEntity:"選擇實體...",connectionFailed:"連線失敗",searchEntities:"搜尋實體...",searchPowerSensors:"搜尋功率感測器...",searchEnergySensors:"搜尋能量感測器...",placeholders:{plugName:"客廳插座",mqttStateOnValue:"ON、true、1",mqttSameAsPower:"與功率主題相同,或不同"}},linkedTo:"連結到:",monitorOnly:"僅監控",alerts:"警報",scheduleOn:"開啟 {{time}}",scheduleOff:"關閉 {{time}}",on:"開啟",off:"關閉",power:"功率",kwhToday:"今日kWh",settings:"設定",automationSettings:"自動化設定",showInSwitchbar:"在開關欄顯示",quickAccessSidebar:"從側邊欄快速存取",enabled:"已啟用",enableAutomation:"為此插座啟用自動化",autoOn:"自動開啟",autoOnDescription:"列印開始時開啟",autoOff:"自動關閉",autoOffDescription:"列印完成時關閉(一次性)",autoOffPersistent:"保持啟用",autoOffPersistentDescription:"在列印之間保持啟用而非一次性",turnOffDelayMode:"關閉延遲模式",time:"時間",temp:"溫度",delayMinutes:"延遲(分鐘)",tempThreshold:"溫度閾值(°C)",tempThresholdDescription:"當噴嘴冷卻到此溫度以下時關閉",edit:"編輯",deleteConfirm:'確定要刪除"{{name}}"嗎?此操作無法復原。',turnOnConfirm:'確定要開啟"{{name}}"嗎?',turnOffConfirm:'確定要關閉"{{name}}"嗎?這將切斷連線裝置的電源。',failedToTurn:'無法{{action}}"{{name}}"',unknown:"未知",addTitle:"新增智慧插座",editTitle:"編輯智慧插座",stopScanning:"停止掃描",discoverTasmota:"發現Tasmota裝置",foundDevices:"找到{{count}}個裝置 - 點選選擇:",noDevicesFound:"未在您的網路中找到Tasmota裝置",haNotConfigured:"Home Assistant未設定。請在以下位置設定",haSettingsPath:"設定 → 網路 → Home Assistant",selectEntity:"選擇實體 *",ipAddress:"IP 位址 *",nameLabel:"名稱 *",username:"使用者名稱",password:"密碼",authHint:"如果您的Tasmota裝置不需要認證,請留空",linkToPrinter:"連結印表機",noPrinter:"無印表機(僅手動控制)",linkingDescription:"連結後可在列印開始/完成時自動開關",powerAlerts:"功率警報",alertAbove:"高於時警報(W)",alertBelow:"低於時警報(W)",alertDescription:"當電力消耗超過這些閾值時收到通知。留空以停用該方向。",dailySchedule:"每日計畫",turnOnAt:"開啟時間",turnOffAt:"關閉時間",scheduleDescription:"每天在這些時間自動開關插座。留空以跳過該操作。",showOnPrinterCard:"在印表機卡片上顯示",displayOnPrinterCard:"在印表機卡片上顯示按鈕",connectedResult:"已連線!",deviceLabel:"裝置:{{name}} - ",stateLabel:"狀態:{{state}}",test:"測試",delete:"刪除",save:"儲存",add:"新增",cancel:"取消",failedToStartScan:"無法開始掃描",nameRequired:"名稱為必填項",entityRequired:"Home Assistant插座需要實體",mqttTopicRequired:"必須為功率、能源或狀態監控設定至少一個MQTT主題",loadingEntities:"正在載入實體...",loading:"載入中...",failedToLoadEntities:"載入實體失敗:{{error}}",noEntitiesMatching:'未找到匹配"{{search}}"的實體',noEntitiesAvailable:"無可用實體",searchingEntities:"搜尋所有實體(找到{{count}}個)",showingEntities:"顯示 switch、light、input_boolean({{count}}個可用)",energyMonitoringOptional:"能源監控(可選)",energyMonitoringHint:"搜尋並選擇提供功率/能源資料的感測器。",powerSensorW:"功率感測器(W)",energyTodayKwh:"今日能源(kWh)",totalEnergyKwh:"總能源(kWh)",noMatchingSensors:"無匹配的感測器",none:"無",mqttNotConfigured:"MQTT代理未設定。請在以下位置設定代理地址",mqttSettingsPath:"設定 → 網路 → MQTT發布",mqttNotConfiguredSuffix:"(您不需要啟用發布,只需填寫代理詳細資訊)。",mqttMonitorOnlyDescription:"MQTT插座透過MQTT訂閱接收功率/能源資料。開關控制不可用 - 請使用您的MQTT代理或家庭自動化系統。",powerMonitoring:"功率監控",energyMonitoring:"能源監控",stateMonitoring:"狀態監控",optional:"可選",topic:"主題",jsonPath:"JSON路徑",multiplier:"乘數",onValue:"ON值",mqttPowerHint:`JSON路徑從JSON負載中提取值(例如"power_l1")。如果主題發布原始數值,請留空。 乘數:mW→W使用0.001,kW→W使用1000。`,mqttEnergyHint:`JSON路徑從JSON負載中提取值。原始值請留空。 乘數:Wh→kWh使用0.001,MWh→kWh使用1000。`,mqttStateHint:`JSON路徑從JSON負載中提取值。原始值請留空。 -ON值:表示"ON"的確切字串。留空以自動檢測(ON、true、1)。`,restControl:"控制",restOnUrl:"開啟 URL",restOffUrl:"關閉 URL",restOnBody:"開啟請求內容",restOffBody:"關閉請求內容",restMethod:"HTTP 方法",restHeaders:"自訂標頭(JSON)",restStatusUrl:"狀態 URL",restStatusPath:"狀態 JSON 路徑",restStatusOnValue:"ON 值",restPowerUrl:"功率URL",restPowerPath:"功率 JSON 路徑",restPowerMultiplier:"功率乘數",restEnergyUrl:"能耗URL",restEnergyPath:"能耗 JSON 路徑",restEnergyMultiplier:"能耗乘數",restUrlRequired:"REST 插座至少需要一個 URL(ON 或 OFF)",restHeadersHint:'例如:{"Authorization": "Bearer your-token"}',restBodyHint:'例如:ON、{"state": "on"}',restStatusHint:"用於輪詢目前狀態的 URL",restPathHint:"例如:state 或 data.power.status",restPowerUrlHint:"功率資料的獨立URL(留空則使用狀態URL)",restEnergyUrlHint:"能耗資料的獨立URL(留空則使用狀態URL)",restEnergyHint:"每個值可以使用獨立的URL,或回退到狀態 URL。使用乘數進行單位轉換(例如:0.001 將 Wh 轉換為 kWh)。",testConnection:"測試連線",connectionSuccess:"連線成功",noSwitchesInSwitchbar:"開關欄中沒有開關",enableSwitchbarHint:'在設定 > 智慧插座中啟用"在開關欄顯示"'},notifications:{providerTypes:{callmebot:"CallMeBot/WhatsApp",ntfy:"ntfy",pushover:"Pushover",telegram:"Telegram",email:"電子郵件",discord:"Discord",webhook:"Webhook",homeassistant:"Home Assistant"},providerDescriptions:{email:"SMTP 電子郵件通知",telegram:"透過 Telegram 機器人傳送通知",discord:"透過 Webhook 傳送到 Discord 頻道",ntfy:"免費、可自託管的推送通知",pushover:"簡單、可靠的推送通知",callmebot:"透過 CallMeBot 免費傳送 WhatsApp 通知",webhook:"通用 HTTP POST 到任意 URL",homeassistant:"Home Assistant 儀表板中的持久通知"},lastSuccess:"上次:{{date}}",error:"錯誤",printer:"印表機:",allPrinters:"所有印表機",sendTestNotification:"傳送測試通知",eventSettings:"事件設定",enabled:"已啟用",sendFromProvider:"從此提供者傳送通知",printEvents:"列印事件",printerStatus:"印表機狀態",amsAlarms:"AMS 警報",amsHtAlarms:"AMS-HT 警報",printQueue:"列印佇列",start:"開始",plateCheck:"熱床檢測",complete:"完成",failed:"失敗",stopped:"已停止",progress:"進度",offline:"離線",lowFilament:"耗材不足",maintenance:"維護",amsHumidity:"AMS 濕度",amsTemp:"AMS 溫度",amsHtHumidity:"AMS-HT 濕度",amsHtTemp:"AMS-HT 溫度",bedCooled:"熱床已冷卻",firstLayer:"首層完成",quiet:"免打擾",digest:"摘要 {{time}}",printStarted:"列印已開始",plateNotEmpty:"熱床非空",plateNotEmptyDescription:"列印前偵測到物體",printCompleted:"列印已完成",bedCooledLabel:"熱床已冷卻",bedCooledDescription:"列印後熱床溫度降至閾值以下",firstLayerCompleteLabel:"首層列印完成",firstLayerCompleteDescription:"首層完成時傳送帶照片的通知",missingSpoolAssignmentLabel:"缺少料卷分配",missingSpoolAssignmentDescription:"當列印開始且所需料盤沒有分配料卷時傳送通知",printFailed:"列印失敗",printStopped:"列印已停止",progressMilestones:"進度里程碑",progressMilestonesDescription:"在 25%、50%、75% 時通知",printerOffline:"印表機離線",printerError:"印表機錯誤",lowFilamentLabel:"耗材不足",maintenanceDue:"需要維護",maintenanceDueDescription:"需要維護時通知",amsHumidityHigh:"AMS 濕度過高",amsHumidityHighDescription:"普通 AMS 濕度超過閾值",amsTemperatureHigh:"AMS 溫度過高",amsTemperatureHighDescription:"普通 AMS 溫度超過閾值",amsHtHumidityHigh:"AMS-HT 濕度過高",amsHtHumidityHighDescription:"AMS-HT 濕度超過閾值",amsHtTemperatureHigh:"AMS-HT 溫度過高",amsHtTemperatureHighDescription:"AMS-HT 溫度超過閾值",jobAdded:"任務已新增",jobAddedDescription:"任務已新增到佇列",jobAssigned:"任務已分配",jobAssignedDescription:"基於模型的任務已分配給印表機",jobStarted:"任務已開始",jobStartedDescription:"佇列任務已開始列印",jobWaiting:"任務等待中",jobWaitingDescription:"任務正在等待耗材或印表機",jobSkipped:"任務已跳過",jobSkippedDescription:"任務已跳過(上一個失敗)",jobFailed:"任務失敗",jobFailedDescription:"任務啟動失敗",queueComplete:"佇列已完成",queueCompleteDescription:"所有佇列任務已完成",quietHours:"免打擾時段",noNotificationsDuring:"在此時段內不傳送通知",editProviderToChangeQuietHours:"編輯提供者以更改免打擾時段",dailyDigest:"每日摘要",batchNotifications:"將通知彙總為每日摘要",sendAt:"傳送於 {{time}}",editProviderToChangeDigestTime:"編輯提供者以更改摘要時間",edit:"編輯",deleteProvider:"刪除通知提供者",deleteConfirm:'確定要刪除"{{name}}"嗎?此操作無法復原。',delete:"刪除",addTitle:"新增通知提供者",editTitle:"編輯通知提供者",nameLabel:"名稱 *",namePlaceholder:"我的通知",providerTypeLabel:"提供者類型 *",configuration:"設定",testConfiguration:"測試設定",printerFilter:"印表機篩選",onlyFromPrinter:"僅傳送來自此印表機的事件通知",quietHoursDnd:"免打擾時段",quietStart:"開始",quietEnd:"結束",dailyDigestLabel:"每日摘要",sendDigestAt:"傳送摘要於",digestCollected:"事件將被收集並在此時間作為單條摘要傳送",notificationEvents:"通知事件",progressPercent:"(25%、50%、75%)",bedCooledAfterPrint:"(列印完成後)",cancel:"取消",save:"儲存",add:"新增",nameRequired:"名稱為必填項",fieldRequired:"{{field}}為必填項",phoneNumber:"電話號碼",apiKey:"API 金鑰",serverUrl:"伺服器 URL",topic:"主題",authToken:"認證權杖",userKey:"使用者金鑰",appToken:"應用程式權杖",priority:"優先順序",botToken:"機器人權杖",chatId:"聊天 ID",smtpServer:"SMTP 伺服器",smtpPort:"SMTP 連接埠",security:"安全",authentication:"認證",username:"使用者名稱",password:"密碼",fromEmail:"寄件人信箱",toEmail:"收件人信箱",webhookUrl:"Webhook URL",payloadFormat:"負載格式",authorization:"授權",titleFieldName:"標題欄位名",messageFieldName:"訊息欄位名",editTemplate:"編輯範本:{{name}}",titleLabel:"標題",bodyLabel:"正文",titlePlaceholder:"通知標題...",bodyPlaceholder:"通知正文...",availableVariables:"可用變數",clickToInsert:"點選插入到正文游標位置",livePreview:"即時預覽",hide:"隱藏",show:"顯示",loadingPreview:"載入預覽中...",enterTemplateContent:"輸入範本內容以檢視預覽",titlePreview:"標題:",bodyPreview:"正文:",resetToDefault:"恢復預設",titleRequired:"標題為必填項",bodyRequired:"正文為必填項",notificationLog:"通知日誌",showFailedOnly:"僅顯示失敗",last24Hours:"最近 24 小時",last7Days:"最近 7 天",last30Days:"最近 30 天",last90Days:"最近 90 天",justNow:"剛剛",noFailedNotifications:"沒有失敗的通知",noNotificationsLogged:"沒有通知紀錄",unknownProvider:"未知提供者",logTitle:"標題",logMessage:"訊息",logError:"錯誤",logProvider:"提供者:{{type}}",logTime:"時間:{{time}}",refresh:"重新整理",clearOld:"清除舊紀錄",statsSummary:"最近 {{days}} 天:",statsNotifications:"條通知",statsSent:"{{count}} 條已傳送",statsFailed:"{{count}} 條失敗",eventTypes:{print_start:"列印已開始",print_complete:"列印完成",print_failed:"列印失敗",print_stopped:"列印已停止",print_progress:"進度",printer_offline:"印表機離線",printer_error:"印表機錯誤",filament_low:"耗材不足",maintenance_due:"需要維護",test:"測試"},userEmail:{title:"通知",emailNotifications:"郵件通知",emailNotificationsDesc:"接收您自己列印任務的郵件通知。郵件將透過進階身份驗證中設定的 SMTP 設定傳送。",sendingTo:"通知將傳送至",noEmailWarning:"您的帳戶沒有郵件地址。請聯絡管理員新增。",printJobNotifications:"列印任務通知",printJobNotificationsDesc:"選擇哪些事件會觸發您提交的列印任務的郵件通知。",printJobStarts:"列印任務開始",printJobStartsDesc:"當您的列印任務開始時收到通知。",printJobFinishes:"列印任務完成",printJobFinishesDesc:"當您的列印任務成功完成時收到通知。",printErrors:"列印錯誤",printErrorsDesc:"當您的列印任務失敗或遇到錯誤時收到通知。",printJobStops:"列印任務停止",printJobStopsDesc:"當您的列印任務被取消或停止時收到通知。",saveSuccess:"通知偏好設定已儲存。",saveError:"儲存通知偏好設定失敗。"}},richTextEditor:{bold:"粗體",italic:"斜體",underline:"底線",bulletList:"無序列表",numberedList:"有序列表",alignLeft:"左對齊",alignCenter:"居中對齊",alignRight:"右對齊",addLink:"新增連結",removeLink:"移除連結"},externalLinks:{noLinksConfigured:"未設定外部連結",deleteLink:"刪除連結",removeCustomIcon:"移除自訂圖示",openInNewTab:"在新標籤頁中開啟",placeholders:{linkName:"我的連結"}},keyboardShortcuts:{title:"鍵盤快捷鍵",navigation:"導航",archivesSection:"歸檔",kProfilesSection:"K 值設定",generalSection:"通用",shortcuts:{goToPrinters:"前往印表機",goToArchives:"前往歸檔",goToQueue:"前往佇列",goToStats:"前往統計",goToProfiles:"前往雲端設定",goToSettings:"前往設定",focusSearch:"聚焦搜尋",openUploadModal:"開啟上傳對話方塊",clearSelection:"清除選擇 / 取消焦點",contextMenu:"卡片右鍵選單",refreshProfiles:"重新整理設定",newProfile:"新增設定",exitSelectionMode:"結束選擇模式",showHelp:"顯示此協助"},footer:"按 Esc 或點選外部關閉"},notificationLog:{title:"通知日誌",events:{printStarted:"列印開始",printComplete:"列印完成",printFailed:"列印失敗",printStopped:"列印停止",progress:"進度",printerOffline:"印表機離線",printerError:"印表機錯誤",lowFilament:"耗材不足",maintenanceDue:"維護到期",test:"測試"},timeAgo:{justNow:"剛剛",minutesAgo:"{{minutes}} 分鐘前",hoursAgo:"{{hours}} 小時前"}},restoreBackup:{title:"恢復備份",restoring:"恢復中...",restoreComplete:"恢復完成",restoreFailed:"恢復失敗",importSettings:"從備份檔案匯入設定",pleaseWait:"請稍候,正在恢復您的資料",clickToSelect:"點選選擇備份檔案(.json 或 .zip)",howDuplicateHandling:"重複處理方式:",categories:{printers:"印表機",smartPlugs:"智慧插座",notificationProviders:"通知提供者",filaments:"耗材",archives:"歸檔",pendingUploads:"待處理上傳",settingsTemplates:"設定和範本"},matchingInfo:{printers:"按序列號匹配",smartPlugs:"按 IP 位址匹配",notificationProviders:"按名稱匹配",filaments:"按名稱 + 類型 + 品牌匹配",archives:"按內容雜湊匹配",pendingUploads:"按檔名匹配",settingsTemplates:"始終覆蓋"},replaceExisting:"替換現有資料",keepExisting:"保留現有資料",replaceDescription:"用備份資料覆蓋已存在的項目",keepDescription:"僅恢復不存在的項目",caution:"注意:",cautionText:"覆蓋將用備份資料替換您目前的設定。出於安全考慮,印表機存取碼永遠不會被覆蓋。",itemsRestored:"已恢復項目",itemsSkipped:"已跳過項目",restored:"已恢復",skipped:"已跳過(已存在)",filesLabel:"檔案(3MF、縮圖等)",newApiKeysGenerated:"已產生新 API 金鑰",newApiKeysWarning:"這些金鑰僅顯示一次。請立即複製!",processingBackup:"處理備份檔案中...",noDataFound:"備份檔案中未找到可恢復的資料。",failedToRestore:"恢復備份失敗。請檢查檔案格式。"},backupExport:{title:"匯出備份",selectData:"選擇要包含的資料",selectAll:"全選",selectNone:"全不選",categoryDescriptions:{settings:"語言、主題、更新偏好",notifications:"ntfy、Pushover、Discord 等",templates:"自訂訊息範本",smartPlugs:"Tasmota 插座設定",externalLinks:"側邊欄外部服務連結",printers:"印表機資訊(不含存取碼)",plateDetection:"空列印板參考影像",filaments:"耗材類型和成本",maintenance:"自訂維護計畫",archives:"所有列印資料 + 檔案(3MF、縮圖、照片)",projects:"專案、材料清單和附件",pendingUploads:"虛擬印表機待審核的上傳",apiKeys:"Webhook API 金鑰(匯入時產生新金鑰)"},requiresPrinters:"需要選擇印表機",zipFileWarning:"將建立 ZIP 檔案。",zipFileDescription:"包括所有 3MF 檔案、縮圖、縮時攝影和照片。這可能需要一些時間並產生較大的檔案。",includeAccessCodes:"包含存取碼",includeAccessCodesDescription:"用於轉移到另一臺機器",includeAccessCodesWarning:"存取碼將以明文形式包含。請妥善保管此備份檔案!",categoriesSelected:"已選擇 {{selectedCount}} 個類別"},pendingUploads:{placeholders:{notes:"新增關於此列印的備註..."},discardUpload:"丟棄上傳",archiveAllUploads:"歸檔所有上傳",discardAllUploads:"丟棄所有上傳",archive:"歸檔",timeAgo:{justNow:"剛剛",minutesAgo:"{{minutes}} 分鐘前",hoursAgo:"{{hours}} 小時前",daysAgo:"{{days}} 天前"}},apiBrowser:{placeholders:{requestBody:"JSON 請求體...",searchEndpoints:"搜尋端點..."}},configureAmsSlot:{title:"設定 AMS 槽位",slotConfigured:"槽位已設定!",configuringSlot:"正在設定槽位:",slotLabel:"{{ams}} 槽位 {{slot}}",searchPresets:"搜尋預設...",colorPlaceholder:"顏色名稱或十六進位(例如:棕色、FF8800)",clearCustomColor:"清除自訂顏色",noCloudPresets:"無雲端預設。登入拓竹雲以同步。",noPresetsAvailable:"無可用預設。登入拓竹雲或匯入本機設定。",noMatchingPresets:"未找到匹配的預設。",custom:"自訂",builtin:"內建",settingsSentToPrinter:"設定已傳送到印表機",filamentProfile:"耗材設定",kProfileLabel:"K 值設定(壓力推進)",filteringFor:"篩選:{{material}}",noKProfile:"無 K 值設定(使用預設值 0.020)",noMatchingKProfiles:"未找到匹配的 K 值設定。將使用預設 K=0.020。",selectFilamentFirst:"請先選擇耗材設定",kFromCalibration:"K={{value}}(來自印表機校準)",customColorLabel:"自訂顏色(可選)",presetColors:"{{name}} 顏色:",showLessColors:"顯示更少顏色",showMoreColors:"顯示更多顏色",clear:"清除",hexLabel:"十六進位:#{{hex}}",resetting:"重設中...",resetSlot:"重設槽位",cancel:"取消",configuring:"設定中...",configureSlot:"設定槽位"},githubBackup:{title:"GitHub 備份",history:"歷史",downloadBackup:"下載備份",restoreBackup:"恢復備份",noBackupsYet:"尚無備份"},emailSettings:{placeholders:{fromName:"BamBuddy"}},tagManagement:{searchTags:"搜尋標籤...",renameTag:"重新命名標籤",deleteTag:"刪除標籤"},notificationTemplates:{placeholders:{title:"通知標題...",body:"通知正文..."}},batchTag:{placeholders:{newTag:"輸入新標籤..."}},photoGallery:{deletePhoto:"刪除照片"},filamentHoverCard:{copySpoolUuid:"複製耗材 UUID"},kProfilesView:{hasNote:"有備註",copyProfile:"複製設定"},layout:{openMenu:"開啟選單",noPermissionSystemInfo:"您沒有檢視系統資訊的權限"},dashboard:{dragToReorder:"拖曳以重新排列",hideWidget:"隱藏小工具"},notificationProviderCard:{deleteNotificationProvider:"刪除通知提供者"},fileManagerModal:{closeFileManager:"關閉檔案管理器",sortFiles:"排序檔案",goToParentFolder:"返回上級資料夾",threeView:"3D 檢視"},embeddedCameraViewer:{refreshStream:"重新整理流",close:"關閉",zoomOut:"縮小",resetZoom:"重設縮放",zoomIn:"放大",dragToResize:"拖曳調整大小"},timelapseViewer:{skipBack5s:"後退 5 秒",skipForward5s:"前進 5 秒"},notificationProviders:{descriptions:{email:"SMTP 郵件通知",telegram:"透過 Telegram 機器人通知",discord:"透過 Webhook 傳送到 Discord 頻道",ntfy:"免費、可自託管的推送通知",pushover:"簡單、可靠的推送通知",callmebot:"透過 CallMeBot 的免費 WhatsApp 通知",webhook:"通用 HTTP POST 到任意 URL"}},logViewer:{searchPlaceholder:"搜尋訊息或日誌名稱...",noLogEntries:"未找到日誌條目"},switchbarPopover:{noSwitchesInSwitchbar:"切換欄中沒有開關"},projectPageModal:{placeholders:{title:"標題",designer:"設計師",license:"許可證",description:"輸入描述...",profileTitle:"設定標題",profileDescription:"設定描述..."}},spoolmanSettings:{},time:{unknown:"-",waiting:"等待中",justNow:"剛剛",now:"現在",minsAgo:"{{count}} 分鐘前",inMins:"{{count}} 分鐘後",hoursAgo:"{{count}} 小時前",inHours:"{{count}} 小時後",daysAgo:"{{count}} 天前",inDays:"{{count}} 天後"},spoolbuddy:{nav:{dashboard:"儀表板",ams:"AMS",inventory:"庫存",writeTag:"寫入",settings:"設定"},status:{nfcReady:"NFC 就緒",nfcOff:"NFC 關閉",offline:"離線",online:"線上",noPrinters:"無印表機",deviceOffline:"裝置離線",waitingConnection:"等待裝置連線...",systemReady:"系統就緒",status:"狀態"},dashboard:{readyToScan:"準備掃描",idleMessage:"將耗材放在磅秤上以識別",nfcHint:"NFC 標籤將自動讀取",device:"裝置",syncWeight:"同步重量",weightSynced:"已同步!",unknownTag:"未知標籤",newTag:"偵測到新標籤",onScale:"在磅秤上",linkSpool:"連結到耗材",linkTagTitle:"將標籤連結到耗材",linkTag:"連結標籤",selectSpool:"選擇要連結此標籤的耗材:",noUntagged:"未找到沒有標籤的耗材",tagDetected:"偵測到標籤",noTag:"無標籤",tagId:"標籤",grossWeight:"毛重",spoolSize:"耗材盤尺寸",close:"關閉",currentSpool:"目前耗材"},modal:{spoolDetected:"偵測到耗材",assignToAms:"分配到 AMS",syncWeight:"同步重量",weightSynced:"已同步!",syncing:"同步中...",newTagDetected:"偵測到新標籤",addToInventory:"新增到庫存",assignToAmsTitle:"分配到 AMS",selectSlot:"選擇槽位",assign:"分配",assigning:"分配中...",assignSuccess:"已分配!",assignError:"分配耗材失敗。請重試。",noPrinterSelected:"選擇印表機...",noAmsDetected:"此印表機未偵測到 AMS",slot:"槽位"},weight:{noReading:"無讀數",stable:"穩定",measuring:"測量中...",tare:"去皮",calibrate:"校準"},spool:{remaining:"剩餘",material:"材料",brand:"品牌",color:"顏色",coreWeight:"軸心重",labelWeight:"標籤重",scaleWeight:"磅秤重",netWeight:"淨重",lastUsed:"上次使用"},ams:{noData:"未偵測到 AMS",connectAms:"連線 AMS 以檢視耗材槽位",noPrinter:"未選擇印表機",selectPrinter:"從頂部欄選擇印表機",printerDisconnected:"印表機已斷開",humidity:"濕度",level:"餘量",active:"活躍",slot:"槽位",empty:"空"},inventory:{search:"搜尋耗材...",empty:"庫存中沒有耗材",noResults:"沒有匹配的耗材",spools:"個耗材",addSpool:"新增耗材"},settings:{tabDevice:"裝置",tabDisplay:"顯示",tabScale:"磅秤",tabUpdates:"更新",nfcReader:"NFC 讀卡器",type:"類型",connection:"連線",notConnected:"不適用",deviceInfo:"裝置資訊",hostname:"主機",uptime:"執行時間",systemConfig:"後端與認證",backendUrl:"Bambuddy 後端 URL",apiToken:"API 權杖",apiTokenPlaceholder:"輸入 API 權杖",saveConfig:"儲存設定",systemQueued:"設定已加入佇列。",nfcDiagnostic:"NFC 診斷",scaleDiagnostic:"磅秤診斷",readTagDiagnostic:"讀取標籤診斷",testNfc:"測試讀卡器",testScale:"測試精度",testReadTag:"讀取標籤",systemFieldsRequired:"後端 URL 為必填項。",brightness:"亮度",saved:"已儲存",noBacklight:"未偵測到 DSI 背光。亮度控制需要 DSI 螢幕。",screenBlank:"螢幕熄滅超時",screenBlankDesc:"不活動後螢幕關閉。觸控喚醒。",displayNote:"亮度作為軟體濾鏡套用。",scaleCalibration:"磅秤校準",currentWeight:"目前重量",tareOffset:"去皮",calFactor:"係數",knownWeight:"已知重量",calStep1:"移除磅秤上所有物品並按設定零點。",calStep2:"將已知重量放在磅秤上。",setZero:"設定零點",calibrateNow:"校準",calibrated:"已校準",tareSet:"去皮命令已傳送。等待裝置回應...",tareFailed:"傳送去皮命令失敗",zeroSet:"零點已設定。將已知重量放在磅秤上。",calibrationDone:"校準完成!",calibrationFailed:"校準失敗",lastCalibrated:"上次校準",stable:"穩定",settling:"穩定中...",firmware:"韌體",scale:"磅秤",noDevice:"未找到 SpoolBuddy 裝置",daemonVersion:"守護程式版本",currentVersion:"目前",versionPending:"等待守護程式...",checking:"檢查中...",checkUpdates:"檢查更新",updateAvailable:"有可用更新",updateInstructions:"透過 SSH 更新:執行 SpoolBuddy 安裝腳本進行升級。",upToDate:"已是最新",includeBeta:"包含測試版本"},writeTag:{tabExisting:"現有耗材",tabNew:"新耗材",tabReplace:"替換標籤",searchPlaceholder:"按材料、顏色、品牌搜尋...",noUntaggedSpools:"沒有無標籤的耗材",noTaggedSpools:"沒有有標籤的耗材",selectSpool:"選擇一個耗材,然後將空白 NTAG 放在讀卡器上",placeTag:"將 NTAG 放在讀卡器上",tagReady:"偵測到標籤 — 準備寫入",writeTag:"寫入標籤",replaceTag:"替換標籤",writing:"寫入標籤中...",waiting:"等待 SpoolBuddy...",writeSuccess:"標籤寫入成功!",writeFailed:"寫入失敗",queueFailed:"佇列寫入命令失敗",tryAgain:"重試",cancel:"取消",replaceWarning:"舊標籤將被取消連結。新標籤將替換它。",deviceOffline:"SpoolBuddy 離線",material:"材料",colorName:"顏色名稱",color:"顏色",brand:"品牌",weight:"重量 (g)",createSpool:"建立耗材",creating:"建立中...",spoolCreated:"耗材已建立!準備寫入。",createFailed:"建立耗材失敗"},quickMenu:{printerPower:"印表機電源",systemControls:"系統",restartDaemon:"重新啟動守護程式",restartBrowser:"重新啟動瀏覽器",reboot:"重新開機",shutdown:"關機",swipeToClose:"向下滑動關閉",confirmTitle:"確認",confirmShutdown:"確定要關閉 SpoolBuddy 嗎?您需要實體存取才能重新開啟。",confirmReboot:"確定要重新開機 SpoolBuddy 嗎?",confirmRestartDaemon:"重新啟動 SpoolBuddy 守護程式?NFC 和磅秤將暫時不可用。",confirmRestartBrowser:"重新啟動kiosk瀏覽器?螢幕將短暫變黑。",confirm:"確認",confirmPlugOn:"開啟 {{name}}?",confirmPlugOff:"關閉 {{name}}?",turnOn:"開啟",turnOff:"關閉"}},bugReport:{title:"報告錯誤",description:"描述",descriptionPlaceholder:"出了什麼問題?請描述問題...",email:"信箱(可選)",emailPlaceholder:"your@email.com",emailPrivacy:"如果提供,您的信箱將包含在GitHub Issue的摺疊部分中,以便維護者後續跟進。",screenshot:"截圖",uploadOrPaste:"上傳、貼上或拖曳圖片",dataCollectedSummary:"報告中包含哪些資料?",dataIncluded:"包含:",dataIncludedList:"應用程式版本、作業系統、架構、Python版本、資料庫統計(僅計數)、印表機型號、噴嘴數量、韌體版本、連線狀態、整合狀態(Spoolman、MQTT、HA)、非敏感設定、網路介面數量、Docker詳情、依賴版本。",dataNeverIncluded:"絕不包含:",dataNeverIncludedList:"印表機名稱、序列號、存取碼、密碼、IP 位址、信箱地址、API金鑰、權杖、Webhook URL、主機名稱或使用者名稱。",submit:"提交",startLogging:"開始偵錯日誌",stepEnableLogging:"偵錯日誌已啟用",stepReproduce:"請現在重現問題",stepStopLogging:"停止並提交報告",stopAndSubmit:"停止並提交",maxDuration:"{{minutes}} 分鐘後自動停止",stoppingLogs:"正在收集日誌並提交...",submitting:"正在提交錯誤報告...",submitSuccess:"錯誤報告提交成功!",submitFailed:"提交錯誤報告失敗",thankYou:"謝謝!",submitted:"您的錯誤報告已提交。",viewIssue:"檢視 Issue",unexpectedError:"發生了意外錯誤"},failureDetection:{title:"AI 故障檢測",description:"透過自託管的 Obico ML API 監控列印,並對偵測到的故障自動採取行動。",mlUrl:"Obico ML API 地址",mlUrlHint:"您自託管的 Obico ml_api 容器的基礎 URL(例如 http://192.168.1.10:3333)。",test:"測試",testSuccess:"ML API 可存取且正常。",testFailed:"無法存取 ML API。",sensitivity:"靈敏度",sensitivityLow:"低(減少誤報)",sensitivityMedium:"中(平衡)",sensitivityHigh:"高(更早檢測,更多誤報)",sensitivityHint:"調整觸發警告和故障的置信度閾值。",action:"偵測到故障時的操作",actionNotify:"僅通知",actionPause:"暫停列印",actionPauseOff:"暫停並切斷電源",pollInterval:"檢查間隔(秒)",pollIntervalHint:"列印過程中每臺印表機的檢查頻率。最小 5 秒,最大 120 秒。",externalUrlMissing:"尚未設定外部 URL。",externalUrlHint:"ML API 透過 URL 擷取攝影機快照。請在一般設定中設定外部 URL,讓 ML API 容器可以連線到 Bambuddy。",perPrinterTitle:"監控的印表機",perPrinterHint:"選擇檢測服務要監視哪些印表機。",monitorAll:"監控所有已連線的印表機",statusTitle:"狀態",serviceRunning:"服務執行中",thresholds:"低 / 高閾值",activePrinters:"活動列印",noActivePrints:"目前沒有正在進行的列印。",historyTitle:"最近檢測",noHistory:"尚無檢測紀錄。"}},Zue={en:{translation:$ue},de:{translation:Vue},fr:{translation:Gue},ja:{translation:Wue},it:{translation:Kue},"pt-BR":{translation:Xue},"zh-CN":{translation:Yue},"zh-TW":{translation:Que}};xo.use(YY).use(Sue).init({resources:Zue,fallbackLng:"en",supportedLngs:["en","de","fr","ja","it","pt-BR","zh-CN","zh-TW"],detection:{order:["localStorage","navigator","htmlTag"],lookupLocalStorage:"bambutrack_language",caches:["localStorage"]},interpolation:{escapeValue:!1},react:{useSuspense:!1}});const iH=[{code:"en",name:"English",nativeName:"English"},{code:"de",name:"German",nativeName:"Deutsch"},{code:"fr",name:"French",nativeName:"Français"},{code:"ja",name:"Japanese",nativeName:"日本語"},{code:"it",name:"Italian",nativeName:"Italiano"},{code:"pt-BR",name:"Portuguese (Brazil)",nativeName:"Português (Brasil)"},{code:"zh-CN",name:"Chinese (Simplified)",nativeName:"简体中文"},{code:"zh-TW",name:"Chinese (Traditional)",nativeName:"繁體中文"}];var sH="popstate";function Jue(t={}){function e(r,i){let{pathname:s,search:o,hash:l}=r.location;return H3("",{pathname:s,search:o,hash:l},i.state&&i.state.usr||null,i.state&&i.state.key||"default")}function n(r,i){return typeof i=="string"?i:Q0(i)}return tme(e,n,null,t)}function Aa(t,e){if(t===!1||t===null||typeof t>"u")throw new Error(e)}function lc(t,e){if(!t){typeof console<"u"&&console.warn(e);try{throw new Error(e)}catch{}}}function eme(){return Math.random().toString(36).substring(2,10)}function oH(t,e){return{usr:t.state,key:t.key,idx:e}}function H3(t,e,n=null,r){return{pathname:typeof t=="string"?t:t.pathname,search:"",hash:"",...typeof e=="string"?by(e):e,state:n,key:e&&e.key||r||eme()}}function Q0({pathname:t="/",search:e="",hash:n=""}){return e&&e!=="?"&&(t+=e.charAt(0)==="?"?e:"?"+e),n&&n!=="#"&&(t+=n.charAt(0)==="#"?n:"#"+n),t}function by(t){let e={};if(t){let n=t.indexOf("#");n>=0&&(e.hash=t.substring(n),t=t.substring(0,n));let r=t.indexOf("?");r>=0&&(e.search=t.substring(r),t=t.substring(0,r)),t&&(e.pathname=t)}return e}function tme(t,e,n,r={}){let{window:i=document.defaultView,v5Compat:s=!1}=r,o=i.history,l="POP",c=null,d=u();d==null&&(d=0,o.replaceState({...o.state,idx:d},""));function u(){return(o.state||{idx:null}).idx}function m(){l="POP";let b=u(),g=b==null?null:b-d;d=b,c&&c({action:l,location:v.location,delta:g})}function p(b,g){l="PUSH";let _=H3(v.location,b,g);d=u()+1;let C=oH(_,d),P=v.createHref(_);try{o.pushState(C,"",P)}catch(N){if(N instanceof DOMException&&N.name==="DataCloneError")throw N;i.location.assign(P)}s&&c&&c({action:l,location:v.location,delta:1})}function f(b,g){l="REPLACE";let _=H3(v.location,b,g);d=u();let C=oH(_,d),P=v.createHref(_);o.replaceState(C,"",P),s&&c&&c({action:l,location:v.location,delta:0})}function x(b){return nme(b)}let v={get action(){return l},get location(){return t(i,o)},listen(b){if(c)throw new Error("A history only accepts one active listener");return i.addEventListener(sH,m),c=b,()=>{i.removeEventListener(sH,m),c=null}},createHref(b){return e(i,b)},createURL:x,encodeLocation(b){let g=x(b);return{pathname:g.pathname,search:g.search,hash:g.hash}},push:p,replace:f,go(b){return o.go(b)}};return v}function nme(t,e=!1){let n="http://localhost";typeof window<"u"&&(n=window.location.origin!=="null"?window.location.origin:window.location.href),Aa(n,"No window.location.(origin|href) available to create URL");let r=typeof t=="string"?t:Q0(t);return r=r.replace(/ $/,"%20"),!e&&r.startsWith("//")&&(r=n+r),new URL(r,n)}function QY(t,e,n="/"){return rme(t,e,n,!1)}function rme(t,e,n,r){let i=typeof e=="string"?by(e):e,s=pm(i.pathname||"/",n);if(s==null)return null;let o=ZY(t);ame(o);let l=null;for(let c=0;l==null&&c{let u={relativePath:d===void 0?o.path||"":d,caseSensitive:o.caseSensitive===!0,childrenIndex:l,route:o};if(u.relativePath.startsWith("/")){if(!u.relativePath.startsWith(r)&&c)return;Aa(u.relativePath.startsWith(r),`Absolute route path "${u.relativePath}" nested under path "${r}" is not valid. An absolute child route path must start with the combined path of all its parent routes.`),u.relativePath=u.relativePath.slice(r.length)}let m=am([r,u.relativePath]),p=n.concat(u);o.children&&o.children.length>0&&(Aa(o.index!==!0,`Index routes must not have child routes. Please remove all child routes from route path "${m}".`),ZY(o.children,e,p,m,c)),!(o.path==null&&!o.index)&&e.push({path:m,score:ume(m,o.index),routesMeta:p})};return t.forEach((o,l)=>{if(o.path===""||!o.path?.includes("?"))s(o,l);else for(let c of JY(o.path))s(o,l,!0,c)}),e}function JY(t){let e=t.split("/");if(e.length===0)return[];let[n,...r]=e,i=n.endsWith("?"),s=n.replace(/\?$/,"");if(r.length===0)return i?[s,""]:[s];let o=JY(r.join("/")),l=[];return l.push(...o.map(c=>c===""?s:[s,c].join("/"))),i&&l.push(...o),l.map(c=>t.startsWith("/")&&c===""?"/":c)}function ame(t){t.sort((e,n)=>e.score!==n.score?n.score-e.score:mme(e.routesMeta.map(r=>r.childrenIndex),n.routesMeta.map(r=>r.childrenIndex)))}var ime=/^:[\w-]+$/,sme=3,ome=2,lme=1,cme=10,dme=-2,lH=t=>t==="*";function ume(t,e){let n=t.split("/"),r=n.length;return n.some(lH)&&(r+=dme),e&&(r+=ome),n.filter(i=>!lH(i)).reduce((i,s)=>i+(ime.test(s)?sme:s===""?lme:cme),r)}function mme(t,e){return t.length===e.length&&t.slice(0,-1).every((r,i)=>r===e[i])?t[t.length-1]-e[e.length-1]:0}function hme(t,e,n=!1){let{routesMeta:r}=t,i={},s="/",o=[];for(let l=0;l{if(u==="*"){let x=l[p]||"";o=s.slice(0,s.length-x.length).replace(/(.)\/+$/,"$1")}const f=l[p];return m&&!f?d[u]=void 0:d[u]=(f||"").replace(/%2F/g,"/"),d},{}),pathname:s,pathnameBase:o,pattern:t}}function pme(t,e=!1,n=!0){lc(t==="*"||!t.endsWith("*")||t.endsWith("/*"),`Route path "${t}" will be treated as if it were "${t.replace(/\*$/,"/*")}" because the \`*\` character must always follow a \`/\` in the pattern. To get rid of this warning, please change the route path to "${t.replace(/\*$/,"/*")}".`);let r=[],i="^"+t.replace(/\/*\*?$/,"").replace(/^\/*/,"/").replace(/[\\.*+^${}|()[\]]/g,"\\$&").replace(/\/:([\w-]+)(\?)?/g,(o,l,c)=>(r.push({paramName:l,isOptional:c!=null}),c?"/?([^\\/]+)?":"/([^\\/]+)")).replace(/\/([\w-]+)\?(\/|$)/g,"(/$1)?$2");return t.endsWith("*")?(r.push({paramName:"*"}),i+=t==="*"||t==="/*"?"(.*)$":"(?:\\/(.+)|\\/*)$"):n?i+="\\/*$":t!==""&&t!=="/"&&(i+="(?:(?=\\/|$))"),[new RegExp(i,e?void 0:"i"),r]}function fme(t){try{return t.split("/").map(e=>decodeURIComponent(e).replace(/\//g,"%2F")).join("/")}catch(e){return lc(!1,`The URL path "${t}" could not be decoded because it is a malformed URL segment. This is probably due to a bad percent encoding (${e}).`),t}}function pm(t,e){if(e==="/")return t;if(!t.toLowerCase().startsWith(e.toLowerCase()))return null;let n=e.endsWith("/")?e.length-1:e.length,r=t.charAt(n);return r&&r!=="/"?null:t.slice(n)||"/"}var gme=/^(?:[a-z][a-z0-9+.-]*:|\/\/)/i;function bme(t,e="/"){let{pathname:n,search:r="",hash:i=""}=typeof t=="string"?by(t):t,s;return n?(n=n.replace(/\/\/+/g,"/"),n.startsWith("/")?s=cH(n.substring(1),"/"):s=cH(n,e)):s=e,{pathname:s,search:vme(r),hash:wme(i)}}function cH(t,e){let n=e.replace(/\/+$/,"").split("/");return t.split("/").forEach(i=>{i===".."?n.length>1&&n.pop():i!=="."&&n.push(i)}),n.length>1?n.join("/"):"/"}function SM(t,e,n,r){return`Cannot include a '${t}' character in a manually specified \`to.${e}\` field [${JSON.stringify(r)}]. Please separate it out to the \`to.${n}\` field. Alternatively you may provide the full path as a string in and the router will parse it for you.`}function xme(t){return t.filter((e,n)=>n===0||e.route.path&&e.route.path.length>0)}function dO(t){let e=xme(t);return e.map((n,r)=>r===e.length-1?n.pathname:n.pathnameBase)}function uO(t,e,n,r=!1){let i;typeof t=="string"?i=by(t):(i={...t},Aa(!i.pathname||!i.pathname.includes("?"),SM("?","pathname","search",i)),Aa(!i.pathname||!i.pathname.includes("#"),SM("#","pathname","hash",i)),Aa(!i.search||!i.search.includes("#"),SM("#","search","hash",i)));let s=t===""||i.pathname==="",o=s?"/":i.pathname,l;if(o==null)l=n;else{let m=e.length-1;if(!r&&o.startsWith("..")){let p=o.split("/");for(;p[0]==="..";)p.shift(),m-=1;i.pathname=p.join("/")}l=m>=0?e[m]:"/"}let c=bme(i,l),d=o&&o!=="/"&&o.endsWith("/"),u=(s||o===".")&&n.endsWith("/");return!c.pathname.endsWith("/")&&(d||u)&&(c.pathname+="/"),c}var am=t=>t.join("/").replace(/\/\/+/g,"/"),yme=t=>t.replace(/\/+$/,"").replace(/^\/*/,"/"),vme=t=>!t||t==="?"?"":t.startsWith("?")?t:"?"+t,wme=t=>!t||t==="#"?"":t.startsWith("#")?t:"#"+t,Sme=class{constructor(t,e,n,r=!1){this.status=t,this.statusText=e||"",this.internal=r,n instanceof Error?(this.data=n.toString(),this.error=n):this.data=n}};function _me(t){return t!=null&&typeof t.status=="number"&&typeof t.statusText=="string"&&typeof t.internal=="boolean"&&"data"in t}function kme(t){return t.map(e=>e.route.path).filter(Boolean).join("/").replace(/\/\/*/g,"/")||"/"}var eQ=typeof window<"u"&&typeof window.document<"u"&&typeof window.document.createElement<"u";function tQ(t,e){let n=t;if(typeof n!="string"||!gme.test(n))return{absoluteURL:void 0,isExternal:!1,to:n};let r=n,i=!1;if(eQ)try{let s=new URL(window.location.href),o=n.startsWith("//")?new URL(s.protocol+n):new URL(n),l=pm(o.pathname,e);o.origin===s.origin&&l!=null?n=l+o.search+o.hash:i=!0}catch{lc(!1,` contains an invalid URL which will probably break when clicked - please update to a valid URL path.`)}return{absoluteURL:r,isExternal:i,to:n}}Object.getOwnPropertyNames(Object.prototype).sort().join("\0");var nQ=["POST","PUT","PATCH","DELETE"];new Set(nQ);var Nme=["GET",...nQ];new Set(Nme);var xy=w.createContext(null);xy.displayName="DataRouter";var OP=w.createContext(null);OP.displayName="DataRouterState";var Cme=w.createContext(!1),rQ=w.createContext({isTransitioning:!1});rQ.displayName="ViewTransition";var Pme=w.createContext(new Map);Pme.displayName="Fetchers";var Tme=w.createContext(null);Tme.displayName="Await";var Sl=w.createContext(null);Sl.displayName="Navigation";var Vw=w.createContext(null);Vw.displayName="Location";var xc=w.createContext({outlet:null,matches:[],isDataRoute:!1});xc.displayName="Route";var mO=w.createContext(null);mO.displayName="RouteError";var aQ="REACT_ROUTER_ERROR",Ame="REDIRECT",jme="ROUTE_ERROR_RESPONSE";function Mme(t){if(t.startsWith(`${aQ}:${Ame}:{`))try{let e=JSON.parse(t.slice(28));if(typeof e=="object"&&e&&typeof e.status=="number"&&typeof e.statusText=="string"&&typeof e.location=="string"&&typeof e.reloadDocument=="boolean"&&typeof e.replace=="boolean")return e}catch{}}function Eme(t){if(t.startsWith(`${aQ}:${jme}:{`))try{let e=JSON.parse(t.slice(40));if(typeof e=="object"&&e&&typeof e.status=="number"&&typeof e.statusText=="string")return new Sme(e.status,e.statusText,e.data)}catch{}}function Dme(t,{relative:e}={}){Aa(yy(),"useHref() may be used only in the context of a component.");let{basename:n,navigator:r}=w.useContext(Sl),{hash:i,pathname:s,search:o}=Ww(t,{relative:e}),l=s;return n!=="/"&&(l=s==="/"?n:am([n,s])),r.createHref({pathname:l,search:o,hash:i})}function yy(){return w.useContext(Vw)!=null}function ed(){return Aa(yy(),"useLocation() may be used only in the context of a component."),w.useContext(Vw).location}var iQ="You should call navigate() in a React.useEffect(), not when your component is first rendered.";function sQ(t){w.useContext(Sl).static||w.useLayoutEffect(t)}function Uo(){let{isDataRoute:t}=w.useContext(xc);return t?Wme():Fme()}function Fme(){Aa(yy(),"useNavigate() may be used only in the context of a component.");let t=w.useContext(xy),{basename:e,navigator:n}=w.useContext(Sl),{matches:r}=w.useContext(xc),{pathname:i}=ed(),s=JSON.stringify(dO(r)),o=w.useRef(!1);return sQ(()=>{o.current=!0}),w.useCallback((c,d={})=>{if(lc(o.current,iQ),!o.current)return;if(typeof c=="number"){n.go(c);return}let u=uO(c,JSON.parse(s),i,d.relative==="path");t==null&&e!=="/"&&(u.pathname=u.pathname==="/"?e:am([e,u.pathname])),(d.replace?n.replace:n.push)(u,d.state,d)},[e,n,s,i,t])}var oQ=w.createContext(null);function vy(){return w.useContext(oQ)}function Rme(t){let e=w.useContext(xc).outlet;return w.useMemo(()=>e&&w.createElement(oQ.Provider,{value:t},e),[e,t])}function Gw(){let{matches:t}=w.useContext(xc),e=t[t.length-1];return e?e.params:{}}function Ww(t,{relative:e}={}){let{matches:n}=w.useContext(xc),{pathname:r}=ed(),i=JSON.stringify(dO(n));return w.useMemo(()=>uO(t,JSON.parse(i),r,e==="path"),[t,i,r,e])}function Lme(t,e){return lQ(t,e)}function lQ(t,e,n,r,i){Aa(yy(),"useRoutes() may be used only in the context of a component.");let{navigator:s}=w.useContext(Sl),{matches:o}=w.useContext(xc),l=o[o.length-1],c=l?l.params:{},d=l?l.pathname:"/",u=l?l.pathnameBase:"/",m=l&&l.route;{let _=m&&m.path||"";dQ(d,!m||_.endsWith("*")||_.endsWith("*?"),`You rendered descendant (or called \`useRoutes()\`) at "${d}" (under ) but the parent route path has no trailing "*". This means if you navigate deeper, the parent won't match anymore and therefore the child routes will never render. +ON值:表示"ON"的確切字串。留空以自動檢測(ON、true、1)。`,restControl:"控制",restOnUrl:"開啟 URL",restOffUrl:"關閉 URL",restOnBody:"開啟請求內容",restOffBody:"關閉請求內容",restMethod:"HTTP 方法",restHeaders:"自訂標頭(JSON)",restStatusUrl:"狀態 URL",restStatusPath:"狀態 JSON 路徑",restStatusOnValue:"ON 值",restPowerUrl:"功率URL",restPowerPath:"功率 JSON 路徑",restPowerMultiplier:"功率乘數",restEnergyUrl:"能耗URL",restEnergyPath:"能耗 JSON 路徑",restEnergyMultiplier:"能耗乘數",restUrlRequired:"REST 插座至少需要一個 URL(ON 或 OFF)",restHeadersHint:'例如:{"Authorization": "Bearer your-token"}',restBodyHint:'例如:ON、{"state": "on"}',restStatusHint:"用於輪詢目前狀態的 URL",restPathHint:"例如:state 或 data.power.status",restPowerUrlHint:"功率資料的獨立URL(留空則使用狀態URL)",restEnergyUrlHint:"能耗資料的獨立URL(留空則使用狀態URL)",restEnergyHint:"每個值可以使用獨立的URL,或回退到狀態 URL。使用乘數進行單位轉換(例如:0.001 將 Wh 轉換為 kWh)。",testConnection:"測試連線",connectionSuccess:"連線成功",noSwitchesInSwitchbar:"開關欄中沒有開關",enableSwitchbarHint:'在設定 > 智慧插座中啟用"在開關欄顯示"'},notifications:{providerTypes:{callmebot:"CallMeBot/WhatsApp",ntfy:"ntfy",pushover:"Pushover",telegram:"Telegram",email:"電子郵件",discord:"Discord",webhook:"Webhook",homeassistant:"Home Assistant"},providerDescriptions:{email:"SMTP 電子郵件通知",telegram:"透過 Telegram 機器人傳送通知",discord:"透過 Webhook 傳送到 Discord 頻道",ntfy:"免費、可自託管的推送通知",pushover:"簡單、可靠的推送通知",callmebot:"透過 CallMeBot 免費傳送 WhatsApp 通知",webhook:"通用 HTTP POST 到任意 URL",homeassistant:"Home Assistant 儀表板中的持久通知"},lastSuccess:"上次:{{date}}",error:"錯誤",printer:"印表機:",allPrinters:"所有印表機",sendTestNotification:"傳送測試通知",eventSettings:"事件設定",enabled:"已啟用",sendFromProvider:"從此提供者傳送通知",printEvents:"列印事件",printerStatus:"印表機狀態",amsAlarms:"AMS 警報",amsHtAlarms:"AMS-HT 警報",printQueue:"列印佇列",start:"開始",plateCheck:"熱床檢測",complete:"完成",failed:"失敗",stopped:"已停止",progress:"進度",offline:"離線",lowFilament:"耗材不足",maintenance:"維護",amsHumidity:"AMS 濕度",amsTemp:"AMS 溫度",amsHtHumidity:"AMS-HT 濕度",amsHtTemp:"AMS-HT 溫度",bedCooled:"熱床已冷卻",firstLayer:"首層完成",quiet:"免打擾",digest:"摘要 {{time}}",printStarted:"列印已開始",plateNotEmpty:"熱床非空",plateNotEmptyDescription:"列印前偵測到物體",printCompleted:"列印已完成",bedCooledLabel:"熱床已冷卻",bedCooledDescription:"列印後熱床溫度降至閾值以下",firstLayerCompleteLabel:"首層列印完成",firstLayerCompleteDescription:"首層完成時傳送帶照片的通知",missingSpoolAssignmentLabel:"缺少料卷分配",missingSpoolAssignmentDescription:"當列印開始且所需料盤沒有分配料卷時傳送通知",printFailed:"列印失敗",printStopped:"列印已停止",progressMilestones:"進度里程碑",progressMilestonesDescription:"在 25%、50%、75% 時通知",printerOffline:"印表機離線",printerError:"印表機錯誤",lowFilamentLabel:"耗材不足",maintenanceDue:"需要維護",maintenanceDueDescription:"需要維護時通知",amsHumidityHigh:"AMS 濕度過高",amsHumidityHighDescription:"普通 AMS 濕度超過閾值",amsTemperatureHigh:"AMS 溫度過高",amsTemperatureHighDescription:"普通 AMS 溫度超過閾值",amsHtHumidityHigh:"AMS-HT 濕度過高",amsHtHumidityHighDescription:"AMS-HT 濕度超過閾值",amsHtTemperatureHigh:"AMS-HT 溫度過高",amsHtTemperatureHighDescription:"AMS-HT 溫度超過閾值",jobAdded:"任務已新增",jobAddedDescription:"任務已新增到佇列",jobAssigned:"任務已分配",jobAssignedDescription:"基於模型的任務已分配給印表機",jobStarted:"任務已開始",jobStartedDescription:"佇列任務已開始列印",jobWaiting:"任務等待中",jobWaitingDescription:"任務正在等待耗材或印表機",jobSkipped:"任務已跳過",jobSkippedDescription:"任務已跳過(上一個失敗)",jobFailed:"任務失敗",jobFailedDescription:"任務啟動失敗",queueComplete:"佇列已完成",queueCompleteDescription:"所有佇列任務已完成",quietHours:"免打擾時段",noNotificationsDuring:"在此時段內不傳送通知",editProviderToChangeQuietHours:"編輯提供者以更改免打擾時段",dailyDigest:"每日摘要",batchNotifications:"將通知彙總為每日摘要",sendAt:"傳送於 {{time}}",editProviderToChangeDigestTime:"編輯提供者以更改摘要時間",edit:"編輯",deleteProvider:"刪除通知提供者",deleteConfirm:'確定要刪除"{{name}}"嗎?此操作無法復原。',delete:"刪除",addTitle:"新增通知提供者",editTitle:"編輯通知提供者",nameLabel:"名稱 *",namePlaceholder:"我的通知",providerTypeLabel:"提供者類型 *",configuration:"設定",testConfiguration:"測試設定",printerFilter:"印表機篩選",onlyFromPrinter:"僅傳送來自此印表機的事件通知",quietHoursDnd:"免打擾時段",quietStart:"開始",quietEnd:"結束",dailyDigestLabel:"每日摘要",sendDigestAt:"傳送摘要於",digestCollected:"事件將被收集並在此時間作為單條摘要傳送",notificationEvents:"通知事件",progressPercent:"(25%、50%、75%)",bedCooledAfterPrint:"(列印完成後)",cancel:"取消",save:"儲存",add:"新增",nameRequired:"名稱為必填項",fieldRequired:"{{field}}為必填項",phoneNumber:"電話號碼",apiKey:"API 金鑰",serverUrl:"伺服器 URL",topic:"主題",authToken:"認證權杖",userKey:"使用者金鑰",appToken:"應用程式權杖",priority:"優先順序",botToken:"機器人權杖",chatId:"聊天 ID",smtpServer:"SMTP 伺服器",smtpPort:"SMTP 連接埠",security:"安全",authentication:"認證",username:"使用者名稱",password:"密碼",fromEmail:"寄件人信箱",toEmail:"收件人信箱",webhookUrl:"Webhook URL",payloadFormat:"負載格式",authorization:"授權",titleFieldName:"標題欄位名",messageFieldName:"訊息欄位名",editTemplate:"編輯範本:{{name}}",titleLabel:"標題",bodyLabel:"正文",titlePlaceholder:"通知標題...",bodyPlaceholder:"通知正文...",availableVariables:"可用變數",clickToInsert:"點選插入到正文游標位置",livePreview:"即時預覽",hide:"隱藏",show:"顯示",loadingPreview:"載入預覽中...",enterTemplateContent:"輸入範本內容以檢視預覽",titlePreview:"標題:",bodyPreview:"正文:",resetToDefault:"恢復預設",titleRequired:"標題為必填項",bodyRequired:"正文為必填項",notificationLog:"通知日誌",showFailedOnly:"僅顯示失敗",last24Hours:"最近 24 小時",last7Days:"最近 7 天",last30Days:"最近 30 天",last90Days:"最近 90 天",justNow:"剛剛",noFailedNotifications:"沒有失敗的通知",noNotificationsLogged:"沒有通知紀錄",unknownProvider:"未知提供者",logTitle:"標題",logMessage:"訊息",logError:"錯誤",logProvider:"提供者:{{type}}",logTime:"時間:{{time}}",refresh:"重新整理",clearOld:"清除舊紀錄",statsSummary:"最近 {{days}} 天:",statsNotifications:"條通知",statsSent:"{{count}} 條已傳送",statsFailed:"{{count}} 條失敗",eventTypes:{print_start:"列印已開始",print_complete:"列印完成",print_failed:"列印失敗",print_stopped:"列印已停止",print_progress:"進度",printer_offline:"印表機離線",printer_error:"印表機錯誤",filament_low:"耗材不足",maintenance_due:"需要維護",test:"測試"},userEmail:{title:"通知",emailNotifications:"郵件通知",emailNotificationsDesc:"接收您自己列印任務的郵件通知。郵件將透過進階身份驗證中設定的 SMTP 設定傳送。",sendingTo:"通知將傳送至",noEmailWarning:"您的帳戶沒有郵件地址。請聯絡管理員新增。",printJobNotifications:"列印任務通知",printJobNotificationsDesc:"選擇哪些事件會觸發您提交的列印任務的郵件通知。",printJobStarts:"列印任務開始",printJobStartsDesc:"當您的列印任務開始時收到通知。",printJobFinishes:"列印任務完成",printJobFinishesDesc:"當您的列印任務成功完成時收到通知。",printErrors:"列印錯誤",printErrorsDesc:"當您的列印任務失敗或遇到錯誤時收到通知。",printJobStops:"列印任務停止",printJobStopsDesc:"當您的列印任務被取消或停止時收到通知。",saveSuccess:"通知偏好設定已儲存。",saveError:"儲存通知偏好設定失敗。"}},richTextEditor:{bold:"粗體",italic:"斜體",underline:"底線",bulletList:"無序列表",numberedList:"有序列表",alignLeft:"左對齊",alignCenter:"居中對齊",alignRight:"右對齊",addLink:"新增連結",removeLink:"移除連結"},externalLinks:{noLinksConfigured:"未設定外部連結",deleteLink:"刪除連結",removeCustomIcon:"移除自訂圖示",openInNewTab:"在新標籤頁中開啟",placeholders:{linkName:"我的連結"}},keyboardShortcuts:{title:"鍵盤快捷鍵",navigation:"導航",archivesSection:"歸檔",kProfilesSection:"K 值設定",generalSection:"通用",shortcuts:{goToPrinters:"前往印表機",goToArchives:"前往歸檔",goToQueue:"前往佇列",goToStats:"前往統計",goToProfiles:"前往雲端設定",goToSettings:"前往設定",focusSearch:"聚焦搜尋",openUploadModal:"開啟上傳對話方塊",clearSelection:"清除選擇 / 取消焦點",contextMenu:"卡片右鍵選單",refreshProfiles:"重新整理設定",newProfile:"新增設定",exitSelectionMode:"結束選擇模式",showHelp:"顯示此協助"},footer:"按 Esc 或點選外部關閉"},notificationLog:{title:"通知日誌",events:{printStarted:"列印開始",printComplete:"列印完成",printFailed:"列印失敗",printStopped:"列印停止",progress:"進度",printerOffline:"印表機離線",printerError:"印表機錯誤",lowFilament:"耗材不足",maintenanceDue:"維護到期",test:"測試"},timeAgo:{justNow:"剛剛",minutesAgo:"{{minutes}} 分鐘前",hoursAgo:"{{hours}} 小時前"}},restoreBackup:{title:"恢復備份",restoring:"恢復中...",restoreComplete:"恢復完成",restoreFailed:"恢復失敗",importSettings:"從備份檔案匯入設定",pleaseWait:"請稍候,正在恢復您的資料",clickToSelect:"點選選擇備份檔案(.json 或 .zip)",howDuplicateHandling:"重複處理方式:",categories:{printers:"印表機",smartPlugs:"智慧插座",notificationProviders:"通知提供者",filaments:"耗材",archives:"歸檔",pendingUploads:"待處理上傳",settingsTemplates:"設定和範本"},matchingInfo:{printers:"按序列號匹配",smartPlugs:"按 IP 位址匹配",notificationProviders:"按名稱匹配",filaments:"按名稱 + 類型 + 品牌匹配",archives:"按內容雜湊匹配",pendingUploads:"按檔名匹配",settingsTemplates:"始終覆蓋"},replaceExisting:"替換現有資料",keepExisting:"保留現有資料",replaceDescription:"用備份資料覆蓋已存在的項目",keepDescription:"僅恢復不存在的項目",caution:"注意:",cautionText:"覆蓋將用備份資料替換您目前的設定。出於安全考慮,印表機存取碼永遠不會被覆蓋。",itemsRestored:"已恢復項目",itemsSkipped:"已跳過項目",restored:"已恢復",skipped:"已跳過(已存在)",filesLabel:"檔案(3MF、縮圖等)",newApiKeysGenerated:"已產生新 API 金鑰",newApiKeysWarning:"這些金鑰僅顯示一次。請立即複製!",processingBackup:"處理備份檔案中...",noDataFound:"備份檔案中未找到可恢復的資料。",failedToRestore:"恢復備份失敗。請檢查檔案格式。"},backupExport:{title:"匯出備份",selectData:"選擇要包含的資料",selectAll:"全選",selectNone:"全不選",categoryDescriptions:{settings:"語言、主題、更新偏好",notifications:"ntfy、Pushover、Discord 等",templates:"自訂訊息範本",smartPlugs:"Tasmota 插座設定",externalLinks:"側邊欄外部服務連結",printers:"印表機資訊(不含存取碼)",plateDetection:"空列印板參考影像",filaments:"耗材類型和成本",maintenance:"自訂維護計畫",archives:"所有列印資料 + 檔案(3MF、縮圖、照片)",projects:"專案、材料清單和附件",pendingUploads:"虛擬印表機待審核的上傳",apiKeys:"Webhook API 金鑰(匯入時產生新金鑰)"},requiresPrinters:"需要選擇印表機",zipFileWarning:"將建立 ZIP 檔案。",zipFileDescription:"包括所有 3MF 檔案、縮圖、縮時攝影和照片。這可能需要一些時間並產生較大的檔案。",includeAccessCodes:"包含存取碼",includeAccessCodesDescription:"用於轉移到另一臺機器",includeAccessCodesWarning:"存取碼將以明文形式包含。請妥善保管此備份檔案!",categoriesSelected:"已選擇 {{selectedCount}} 個類別"},pendingUploads:{placeholders:{notes:"新增關於此列印的備註..."},discardUpload:"丟棄上傳",archiveAllUploads:"歸檔所有上傳",discardAllUploads:"丟棄所有上傳",archive:"歸檔",timeAgo:{justNow:"剛剛",minutesAgo:"{{minutes}} 分鐘前",hoursAgo:"{{hours}} 小時前",daysAgo:"{{days}} 天前"}},apiBrowser:{placeholders:{requestBody:"JSON 請求體...",searchEndpoints:"搜尋端點..."}},configureAmsSlot:{title:"設定 AMS 槽位",slotConfigured:"槽位已設定!",configuringSlot:"正在設定槽位:",slotLabel:"{{ams}} 槽位 {{slot}}",searchPresets:"搜尋預設...",colorPlaceholder:"顏色名稱或十六進位(例如:棕色、FF8800)",clearCustomColor:"清除自訂顏色",noCloudPresets:"無雲端預設。登入拓竹雲以同步。",noPresetsAvailable:"無可用預設。登入拓竹雲或匯入本機設定。",noMatchingPresets:"未找到匹配的預設。",custom:"自訂",builtin:"內建",settingsSentToPrinter:"設定已傳送到印表機",filamentProfile:"耗材設定",kProfileLabel:"K 值設定(壓力推進)",filteringFor:"篩選:{{material}}",noKProfile:"無 K 值設定(使用預設值 0.020)",noMatchingKProfiles:"未找到匹配的 K 值設定。將使用預設 K=0.020。",selectFilamentFirst:"請先選擇耗材設定",kFromCalibration:"K={{value}}(來自印表機校準)",customColorLabel:"自訂顏色(可選)",presetColors:"{{name}} 顏色:",showLessColors:"顯示更少顏色",showMoreColors:"顯示更多顏色",clear:"清除",hexLabel:"十六進位:#{{hex}}",resetting:"重設中...",resetSlot:"重設槽位",cancel:"取消",configuring:"設定中...",configureSlot:"設定槽位"},githubBackup:{title:"GitHub 備份",history:"歷史",downloadBackup:"下載備份",restoreBackup:"恢復備份",noBackupsYet:"尚無備份"},emailSettings:{placeholders:{fromName:"BamBuddy"}},tagManagement:{searchTags:"搜尋標籤...",renameTag:"重新命名標籤",deleteTag:"刪除標籤"},notificationTemplates:{placeholders:{title:"通知標題...",body:"通知正文..."}},batchTag:{placeholders:{newTag:"輸入新標籤..."}},photoGallery:{deletePhoto:"刪除照片"},filamentHoverCard:{copySpoolUuid:"複製耗材 UUID"},kProfilesView:{hasNote:"有備註",copyProfile:"複製設定"},layout:{openMenu:"開啟選單",noPermissionSystemInfo:"您沒有檢視系統資訊的權限"},dashboard:{dragToReorder:"拖曳以重新排列",hideWidget:"隱藏小工具"},notificationProviderCard:{deleteNotificationProvider:"刪除通知提供者"},fileManagerModal:{closeFileManager:"關閉檔案管理器",sortFiles:"排序檔案",goToParentFolder:"返回上級資料夾",threeView:"3D 檢視"},embeddedCameraViewer:{refreshStream:"重新整理流",close:"關閉",zoomOut:"縮小",resetZoom:"重設縮放",zoomIn:"放大",dragToResize:"拖曳調整大小"},timelapseViewer:{skipBack5s:"後退 5 秒",skipForward5s:"前進 5 秒"},notificationProviders:{descriptions:{email:"SMTP 郵件通知",telegram:"透過 Telegram 機器人通知",discord:"透過 Webhook 傳送到 Discord 頻道",ntfy:"免費、可自託管的推送通知",pushover:"簡單、可靠的推送通知",callmebot:"透過 CallMeBot 的免費 WhatsApp 通知",webhook:"通用 HTTP POST 到任意 URL"}},logViewer:{searchPlaceholder:"搜尋訊息或日誌名稱...",noLogEntries:"未找到日誌條目"},switchbarPopover:{noSwitchesInSwitchbar:"切換欄中沒有開關"},projectPageModal:{placeholders:{title:"標題",designer:"設計師",license:"許可證",description:"輸入描述...",profileTitle:"設定標題",profileDescription:"設定描述..."}},spoolmanSettings:{},time:{unknown:"-",waiting:"等待中",justNow:"剛剛",now:"現在",minsAgo:"{{count}} 分鐘前",inMins:"{{count}} 分鐘後",hoursAgo:"{{count}} 小時前",inHours:"{{count}} 小時後",daysAgo:"{{count}} 天前",inDays:"{{count}} 天後"},spoolbuddy:{nav:{dashboard:"儀表板",ams:"AMS",inventory:"庫存",writeTag:"寫入",settings:"設定"},status:{nfcReady:"NFC 就緒",nfcOff:"NFC 關閉",offline:"離線",online:"線上",noPrinters:"無印表機",deviceOffline:"裝置離線",waitingConnection:"等待裝置連線...",systemReady:"系統就緒",status:"狀態"},dashboard:{readyToScan:"準備掃描",idleMessage:"將耗材放在磅秤上以識別",nfcHint:"NFC 標籤將自動讀取",device:"裝置",syncWeight:"同步重量",weightSynced:"已同步!",unknownTag:"未知標籤",newTag:"偵測到新標籤",onScale:"在磅秤上",linkSpool:"連結到耗材",linkTagTitle:"將標籤連結到耗材",linkTag:"連結標籤",selectSpool:"選擇要連結此標籤的耗材:",noUntagged:"未找到沒有標籤的耗材",tagDetected:"偵測到標籤",noTag:"無標籤",tagId:"標籤",grossWeight:"毛重",spoolSize:"耗材盤尺寸",close:"關閉",currentSpool:"目前耗材"},modal:{spoolDetected:"偵測到耗材",assignToAms:"分配到 AMS",syncWeight:"同步重量",weightSynced:"已同步!",syncing:"同步中...",newTagDetected:"偵測到新標籤",addToInventory:"新增到庫存",assignToAmsTitle:"分配到 AMS",selectSlot:"選擇槽位",assign:"分配",assigning:"分配中...",assignSuccess:"已分配!",assignError:"分配耗材失敗。請重試。",noPrinterSelected:"選擇印表機...",noAmsDetected:"此印表機未偵測到 AMS",slot:"槽位"},weight:{noReading:"無讀數",stable:"穩定",measuring:"測量中...",tare:"去皮",calibrate:"校準"},spool:{remaining:"剩餘",material:"材料",brand:"品牌",color:"顏色",coreWeight:"軸心重",labelWeight:"標籤重",scaleWeight:"磅秤重",netWeight:"淨重",lastUsed:"上次使用"},ams:{noData:"未偵測到 AMS",connectAms:"連線 AMS 以檢視耗材槽位",noPrinter:"未選擇印表機",selectPrinter:"從頂部欄選擇印表機",printerDisconnected:"印表機已斷開",humidity:"濕度",level:"餘量",active:"活躍",slot:"槽位",empty:"空"},inventory:{search:"搜尋耗材...",empty:"庫存中沒有耗材",noResults:"沒有匹配的耗材",spools:"個耗材",addSpool:"新增耗材"},settings:{tabDevice:"裝置",tabDisplay:"顯示",tabScale:"磅秤",tabUpdates:"更新",nfcReader:"NFC 讀卡器",type:"類型",connection:"連線",notConnected:"不適用",deviceInfo:"裝置資訊",hostname:"主機",uptime:"執行時間",systemConfig:"後端與認證",backendUrl:"Bambuddy 後端 URL",apiToken:"API 權杖",apiTokenPlaceholder:"輸入 API 權杖",saveConfig:"儲存設定",systemQueued:"設定已加入佇列。",nfcDiagnostic:"NFC 診斷",scaleDiagnostic:"磅秤診斷",readTagDiagnostic:"讀取標籤診斷",testNfc:"測試讀卡器",testScale:"測試精度",testReadTag:"讀取標籤",systemFieldsRequired:"後端 URL 為必填項。",brightness:"亮度",saved:"已儲存",noBacklight:"未偵測到 DSI 背光。亮度控制需要 DSI 螢幕。",screenBlank:"螢幕熄滅超時",screenBlankDesc:"不活動後螢幕關閉。觸控喚醒。",displayNote:"亮度作為軟體濾鏡套用。",scaleCalibration:"磅秤校準",currentWeight:"目前重量",tareOffset:"去皮",calFactor:"係數",knownWeight:"已知重量",calStep1:"移除磅秤上所有物品並按設定零點。",calStep2:"將已知重量放在磅秤上。",setZero:"設定零點",calibrateNow:"校準",calibrated:"已校準",tareSet:"去皮命令已傳送。等待裝置回應...",tareFailed:"傳送去皮命令失敗",zeroSet:"零點已設定。將已知重量放在磅秤上。",calibrationDone:"校準完成!",calibrationFailed:"校準失敗",lastCalibrated:"上次校準",stable:"穩定",settling:"穩定中...",firmware:"韌體",scale:"磅秤",noDevice:"未找到 SpoolBuddy 裝置",daemonVersion:"守護程式版本",currentVersion:"目前",versionPending:"等待守護程式...",checking:"檢查中...",checkUpdates:"檢查更新",updateAvailable:"有可用更新",updateInstructions:"透過 SSH 更新:執行 SpoolBuddy 安裝腳本進行升級。",upToDate:"已是最新",includeBeta:"包含測試版本"},writeTag:{tabExisting:"現有耗材",tabNew:"新耗材",tabReplace:"替換標籤",searchPlaceholder:"按材料、顏色、品牌搜尋...",noUntaggedSpools:"沒有無標籤的耗材",noTaggedSpools:"沒有有標籤的耗材",selectSpool:"選擇一個耗材,然後將空白 NTAG 放在讀卡器上",placeTag:"將 NTAG 放在讀卡器上",tagReady:"偵測到標籤 — 準備寫入",writeTag:"寫入標籤",replaceTag:"替換標籤",writing:"寫入標籤中...",waiting:"等待 SpoolBuddy...",writeSuccess:"標籤寫入成功!",writeFailed:"寫入失敗",queueFailed:"佇列寫入命令失敗",tryAgain:"重試",cancel:"取消",replaceWarning:"舊標籤將被取消連結。新標籤將替換它。",deviceOffline:"SpoolBuddy 離線",material:"材料",colorName:"顏色名稱",color:"顏色",brand:"品牌",weight:"重量 (g)",createSpool:"建立耗材",creating:"建立中...",spoolCreated:"耗材已建立!準備寫入。",createFailed:"建立耗材失敗"},quickMenu:{printerPower:"印表機電源",systemControls:"系統",restartDaemon:"重新啟動守護程式",restartBrowser:"重新啟動瀏覽器",reboot:"重新開機",shutdown:"關機",swipeToClose:"向下滑動關閉",confirmTitle:"確認",confirmShutdown:"確定要關閉 SpoolBuddy 嗎?您需要實體存取才能重新開啟。",confirmReboot:"確定要重新開機 SpoolBuddy 嗎?",confirmRestartDaemon:"重新啟動 SpoolBuddy 守護程式?NFC 和磅秤將暫時不可用。",confirmRestartBrowser:"重新啟動kiosk瀏覽器?螢幕將短暫變黑。",confirm:"確認",confirmPlugOn:"開啟 {{name}}?",confirmPlugOff:"關閉 {{name}}?",turnOn:"開啟",turnOff:"關閉"}},bugReport:{title:"報告錯誤",description:"描述",descriptionPlaceholder:"出了什麼問題?請描述問題...",email:"信箱(可選)",emailPlaceholder:"your@email.com",emailPrivacy:"如果提供,您的信箱將包含在GitHub Issue的摺疊部分中,以便維護者後續跟進。",screenshot:"截圖",uploadOrPaste:"上傳、貼上或拖曳圖片",dataCollectedSummary:"報告中包含哪些資料?",dataIncluded:"包含:",dataIncludedList:"應用程式版本、作業系統、架構、Python版本、資料庫統計(僅計數)、印表機型號、噴嘴數量、韌體版本、連線狀態、整合狀態(Spoolman、MQTT、HA)、非敏感設定、網路介面數量、Docker詳情、依賴版本。",dataNeverIncluded:"絕不包含:",dataNeverIncludedList:"印表機名稱、序列號、存取碼、密碼、IP 位址、信箱地址、API金鑰、權杖、Webhook URL、主機名稱或使用者名稱。",submit:"提交",startLogging:"開始偵錯日誌",stepEnableLogging:"偵錯日誌已啟用",stepReproduce:"請現在重現問題",stepStopLogging:"停止並提交報告",stopAndSubmit:"停止並提交",maxDuration:"{{minutes}} 分鐘後自動停止",stoppingLogs:"正在收集日誌並提交...",submitting:"正在提交錯誤報告...",submitSuccess:"錯誤報告提交成功!",submitFailed:"提交錯誤報告失敗",thankYou:"謝謝!",submitted:"您的錯誤報告已提交。",viewIssue:"檢視 Issue",unexpectedError:"發生了意外錯誤"},failureDetection:{title:"AI 故障檢測",description:"透過自託管的 Obico ML API 監控列印,並對偵測到的故障自動採取行動。",mlUrl:"Obico ML API 地址",mlUrlHint:"您自託管的 Obico ml_api 容器的基礎 URL(例如 http://192.168.1.10:3333)。",test:"測試",testSuccess:"ML API 可存取且正常。",testFailed:"無法存取 ML API。",sensitivity:"靈敏度",sensitivityLow:"低(減少誤報)",sensitivityMedium:"中(平衡)",sensitivityHigh:"高(更早檢測,更多誤報)",sensitivityHint:"調整觸發警告和故障的置信度閾值。",action:"偵測到故障時的操作",actionNotify:"僅通知",actionPause:"暫停列印",actionPauseOff:"暫停並切斷電源",pollInterval:"檢查間隔(秒)",pollIntervalHint:"列印過程中每臺印表機的檢查頻率。最小 5 秒,最大 120 秒。",externalUrlMissing:"尚未設定外部 URL。",externalUrlHint:"ML API 透過 URL 擷取攝影機快照。請在一般設定中設定外部 URL,讓 ML API 容器可以連線到 Bambuddy。",perPrinterTitle:"監控的印表機",perPrinterHint:"選擇檢測服務要監視哪些印表機。",monitorAll:"監控所有已連線的印表機",statusTitle:"狀態",serviceRunning:"服務執行中",thresholds:"低 / 高閾值",activePrinters:"活動列印",noActivePrints:"目前沒有正在進行的列印。",historyTitle:"最近檢測",noHistory:"尚無檢測紀錄。"}},Zue={en:{translation:$ue},de:{translation:Vue},fr:{translation:Gue},ja:{translation:Wue},it:{translation:Kue},"pt-BR":{translation:Xue},"zh-CN":{translation:Yue},"zh-TW":{translation:Que}};xo.use(YY).use(Sue).init({resources:Zue,fallbackLng:"en",supportedLngs:["en","de","fr","ja","it","pt-BR","zh-CN","zh-TW"],detection:{order:["localStorage","navigator","htmlTag"],lookupLocalStorage:"bambutrack_language",caches:["localStorage"]},interpolation:{escapeValue:!1},react:{useSuspense:!1}});const iH=[{code:"en",name:"English",nativeName:"English"},{code:"de",name:"German",nativeName:"Deutsch"},{code:"fr",name:"French",nativeName:"Français"},{code:"ja",name:"Japanese",nativeName:"日本語"},{code:"it",name:"Italian",nativeName:"Italiano"},{code:"pt-BR",name:"Portuguese (Brazil)",nativeName:"Português (Brasil)"},{code:"zh-CN",name:"Chinese (Simplified)",nativeName:"简体中文"},{code:"zh-TW",name:"Chinese (Traditional)",nativeName:"繁體中文"}];var sH="popstate";function Jue(t={}){function e(r,i){let{pathname:s,search:o,hash:l}=r.location;return G3("",{pathname:s,search:o,hash:l},i.state&&i.state.usr||null,i.state&&i.state.key||"default")}function n(r,i){return typeof i=="string"?i:tw(i)}return tme(e,n,null,t)}function ja(t,e){if(t===!1||t===null||typeof t>"u")throw new Error(e)}function cc(t,e){if(!t){typeof console<"u"&&console.warn(e);try{throw new Error(e)}catch{}}}function eme(){return Math.random().toString(36).substring(2,10)}function oH(t,e){return{usr:t.state,key:t.key,idx:e}}function G3(t,e,n=null,r){return{pathname:typeof t=="string"?t:t.pathname,search:"",hash:"",...typeof e=="string"?gy(e):e,state:n,key:e&&e.key||r||eme()}}function tw({pathname:t="/",search:e="",hash:n=""}){return e&&e!=="?"&&(t+=e.charAt(0)==="?"?e:"?"+e),n&&n!=="#"&&(t+=n.charAt(0)==="#"?n:"#"+n),t}function gy(t){let e={};if(t){let n=t.indexOf("#");n>=0&&(e.hash=t.substring(n),t=t.substring(0,n));let r=t.indexOf("?");r>=0&&(e.search=t.substring(r),t=t.substring(0,r)),t&&(e.pathname=t)}return e}function tme(t,e,n,r={}){let{window:i=document.defaultView,v5Compat:s=!1}=r,o=i.history,l="POP",c=null,d=u();d==null&&(d=0,o.replaceState({...o.state,idx:d},""));function u(){return(o.state||{idx:null}).idx}function m(){l="POP";let b=u(),g=b==null?null:b-d;d=b,c&&c({action:l,location:v.location,delta:g})}function p(b,g){l="PUSH";let _=G3(v.location,b,g);d=u()+1;let C=oH(_,d),P=v.createHref(_);try{o.pushState(C,"",P)}catch(N){if(N instanceof DOMException&&N.name==="DataCloneError")throw N;i.location.assign(P)}s&&c&&c({action:l,location:v.location,delta:1})}function f(b,g){l="REPLACE";let _=G3(v.location,b,g);d=u();let C=oH(_,d),P=v.createHref(_);o.replaceState(C,"",P),s&&c&&c({action:l,location:v.location,delta:0})}function x(b){return nme(b)}let v={get action(){return l},get location(){return t(i,o)},listen(b){if(c)throw new Error("A history only accepts one active listener");return i.addEventListener(sH,m),c=b,()=>{i.removeEventListener(sH,m),c=null}},createHref(b){return e(i,b)},createURL:x,encodeLocation(b){let g=x(b);return{pathname:g.pathname,search:g.search,hash:g.hash}},push:p,replace:f,go(b){return o.go(b)}};return v}function nme(t,e=!1){let n="http://localhost";typeof window<"u"&&(n=window.location.origin!=="null"?window.location.origin:window.location.href),ja(n,"No window.location.(origin|href) available to create URL");let r=typeof t=="string"?t:tw(t);return r=r.replace(/ $/,"%20"),!e&&r.startsWith("//")&&(r=n+r),new URL(r,n)}function QY(t,e,n="/"){return rme(t,e,n,!1)}function rme(t,e,n,r){let i=typeof e=="string"?gy(e):e,s=fm(i.pathname||"/",n);if(s==null)return null;let o=ZY(t);ame(o);let l=null;for(let c=0;l==null&&c{let u={relativePath:d===void 0?o.path||"":d,caseSensitive:o.caseSensitive===!0,childrenIndex:l,route:o};if(u.relativePath.startsWith("/")){if(!u.relativePath.startsWith(r)&&c)return;ja(u.relativePath.startsWith(r),`Absolute route path "${u.relativePath}" nested under path "${r}" is not valid. An absolute child route path must start with the combined path of all its parent routes.`),u.relativePath=u.relativePath.slice(r.length)}let m=im([r,u.relativePath]),p=n.concat(u);o.children&&o.children.length>0&&(ja(o.index!==!0,`Index routes must not have child routes. Please remove all child routes from route path "${m}".`),ZY(o.children,e,p,m,c)),!(o.path==null&&!o.index)&&e.push({path:m,score:ume(m,o.index),routesMeta:p})};return t.forEach((o,l)=>{if(o.path===""||!o.path?.includes("?"))s(o,l);else for(let c of JY(o.path))s(o,l,!0,c)}),e}function JY(t){let e=t.split("/");if(e.length===0)return[];let[n,...r]=e,i=n.endsWith("?"),s=n.replace(/\?$/,"");if(r.length===0)return i?[s,""]:[s];let o=JY(r.join("/")),l=[];return l.push(...o.map(c=>c===""?s:[s,c].join("/"))),i&&l.push(...o),l.map(c=>t.startsWith("/")&&c===""?"/":c)}function ame(t){t.sort((e,n)=>e.score!==n.score?n.score-e.score:mme(e.routesMeta.map(r=>r.childrenIndex),n.routesMeta.map(r=>r.childrenIndex)))}var ime=/^:[\w-]+$/,sme=3,ome=2,lme=1,cme=10,dme=-2,lH=t=>t==="*";function ume(t,e){let n=t.split("/"),r=n.length;return n.some(lH)&&(r+=dme),e&&(r+=ome),n.filter(i=>!lH(i)).reduce((i,s)=>i+(ime.test(s)?sme:s===""?lme:cme),r)}function mme(t,e){return t.length===e.length&&t.slice(0,-1).every((r,i)=>r===e[i])?t[t.length-1]-e[e.length-1]:0}function hme(t,e,n=!1){let{routesMeta:r}=t,i={},s="/",o=[];for(let l=0;l{if(u==="*"){let x=l[p]||"";o=s.slice(0,s.length-x.length).replace(/(.)\/+$/,"$1")}const f=l[p];return m&&!f?d[u]=void 0:d[u]=(f||"").replace(/%2F/g,"/"),d},{}),pathname:s,pathnameBase:o,pattern:t}}function pme(t,e=!1,n=!0){cc(t==="*"||!t.endsWith("*")||t.endsWith("/*"),`Route path "${t}" will be treated as if it were "${t.replace(/\*$/,"/*")}" because the \`*\` character must always follow a \`/\` in the pattern. To get rid of this warning, please change the route path to "${t.replace(/\*$/,"/*")}".`);let r=[],i="^"+t.replace(/\/*\*?$/,"").replace(/^\/*/,"/").replace(/[\\.*+^${}|()[\]]/g,"\\$&").replace(/\/:([\w-]+)(\?)?/g,(o,l,c)=>(r.push({paramName:l,isOptional:c!=null}),c?"/?([^\\/]+)?":"/([^\\/]+)")).replace(/\/([\w-]+)\?(\/|$)/g,"(/$1)?$2");return t.endsWith("*")?(r.push({paramName:"*"}),i+=t==="*"||t==="/*"?"(.*)$":"(?:\\/(.+)|\\/*)$"):n?i+="\\/*$":t!==""&&t!=="/"&&(i+="(?:(?=\\/|$))"),[new RegExp(i,e?void 0:"i"),r]}function fme(t){try{return t.split("/").map(e=>decodeURIComponent(e).replace(/\//g,"%2F")).join("/")}catch(e){return cc(!1,`The URL path "${t}" could not be decoded because it is a malformed URL segment. This is probably due to a bad percent encoding (${e}).`),t}}function fm(t,e){if(e==="/")return t;if(!t.toLowerCase().startsWith(e.toLowerCase()))return null;let n=e.endsWith("/")?e.length-1:e.length,r=t.charAt(n);return r&&r!=="/"?null:t.slice(n)||"/"}var gme=/^(?:[a-z][a-z0-9+.-]*:|\/\/)/i;function bme(t,e="/"){let{pathname:n,search:r="",hash:i=""}=typeof t=="string"?gy(t):t,s;return n?(n=n.replace(/\/\/+/g,"/"),n.startsWith("/")?s=cH(n.substring(1),"/"):s=cH(n,e)):s=e,{pathname:s,search:vme(r),hash:wme(i)}}function cH(t,e){let n=e.replace(/\/+$/,"").split("/");return t.split("/").forEach(i=>{i===".."?n.length>1&&n.pop():i!=="."&&n.push(i)}),n.length>1?n.join("/"):"/"}function CM(t,e,n,r){return`Cannot include a '${t}' character in a manually specified \`to.${e}\` field [${JSON.stringify(r)}]. Please separate it out to the \`to.${n}\` field. Alternatively you may provide the full path as a string in and the router will parse it for you.`}function xme(t){return t.filter((e,n)=>n===0||e.route.path&&e.route.path.length>0)}function pO(t){let e=xme(t);return e.map((n,r)=>r===e.length-1?n.pathname:n.pathnameBase)}function fO(t,e,n,r=!1){let i;typeof t=="string"?i=gy(t):(i={...t},ja(!i.pathname||!i.pathname.includes("?"),CM("?","pathname","search",i)),ja(!i.pathname||!i.pathname.includes("#"),CM("#","pathname","hash",i)),ja(!i.search||!i.search.includes("#"),CM("#","search","hash",i)));let s=t===""||i.pathname==="",o=s?"/":i.pathname,l;if(o==null)l=n;else{let m=e.length-1;if(!r&&o.startsWith("..")){let p=o.split("/");for(;p[0]==="..";)p.shift(),m-=1;i.pathname=p.join("/")}l=m>=0?e[m]:"/"}let c=bme(i,l),d=o&&o!=="/"&&o.endsWith("/"),u=(s||o===".")&&n.endsWith("/");return!c.pathname.endsWith("/")&&(d||u)&&(c.pathname+="/"),c}var im=t=>t.join("/").replace(/\/\/+/g,"/"),yme=t=>t.replace(/\/+$/,"").replace(/^\/*/,"/"),vme=t=>!t||t==="?"?"":t.startsWith("?")?t:"?"+t,wme=t=>!t||t==="#"?"":t.startsWith("#")?t:"#"+t,Sme=class{constructor(t,e,n,r=!1){this.status=t,this.statusText=e||"",this.internal=r,n instanceof Error?(this.data=n.toString(),this.error=n):this.data=n}};function _me(t){return t!=null&&typeof t.status=="number"&&typeof t.statusText=="string"&&typeof t.internal=="boolean"&&"data"in t}function kme(t){return t.map(e=>e.route.path).filter(Boolean).join("/").replace(/\/\/*/g,"/")||"/"}var eQ=typeof window<"u"&&typeof window.document<"u"&&typeof window.document.createElement<"u";function tQ(t,e){let n=t;if(typeof n!="string"||!gme.test(n))return{absoluteURL:void 0,isExternal:!1,to:n};let r=n,i=!1;if(eQ)try{let s=new URL(window.location.href),o=n.startsWith("//")?new URL(s.protocol+n):new URL(n),l=fm(o.pathname,e);o.origin===s.origin&&l!=null?n=l+o.search+o.hash:i=!0}catch{cc(!1,` contains an invalid URL which will probably break when clicked - please update to a valid URL path.`)}return{absoluteURL:r,isExternal:i,to:n}}Object.getOwnPropertyNames(Object.prototype).sort().join("\0");var nQ=["POST","PUT","PATCH","DELETE"];new Set(nQ);var Nme=["GET",...nQ];new Set(Nme);var by=w.createContext(null);by.displayName="DataRouter";var UP=w.createContext(null);UP.displayName="DataRouterState";var Cme=w.createContext(!1),rQ=w.createContext({isTransitioning:!1});rQ.displayName="ViewTransition";var Pme=w.createContext(new Map);Pme.displayName="Fetchers";var Tme=w.createContext(null);Tme.displayName="Await";var _l=w.createContext(null);_l.displayName="Navigation";var Xw=w.createContext(null);Xw.displayName="Location";var yc=w.createContext({outlet:null,matches:[],isDataRoute:!1});yc.displayName="Route";var gO=w.createContext(null);gO.displayName="RouteError";var aQ="REACT_ROUTER_ERROR",Ame="REDIRECT",jme="ROUTE_ERROR_RESPONSE";function Mme(t){if(t.startsWith(`${aQ}:${Ame}:{`))try{let e=JSON.parse(t.slice(28));if(typeof e=="object"&&e&&typeof e.status=="number"&&typeof e.statusText=="string"&&typeof e.location=="string"&&typeof e.reloadDocument=="boolean"&&typeof e.replace=="boolean")return e}catch{}}function Eme(t){if(t.startsWith(`${aQ}:${jme}:{`))try{let e=JSON.parse(t.slice(40));if(typeof e=="object"&&e&&typeof e.status=="number"&&typeof e.statusText=="string")return new Sme(e.status,e.statusText,e.data)}catch{}}function Dme(t,{relative:e}={}){ja(xy(),"useHref() may be used only in the context of a component.");let{basename:n,navigator:r}=w.useContext(_l),{hash:i,pathname:s,search:o}=Qw(t,{relative:e}),l=s;return n!=="/"&&(l=s==="/"?n:im([n,s])),r.createHref({pathname:l,search:o,hash:i})}function xy(){return w.useContext(Xw)!=null}function td(){return ja(xy(),"useLocation() may be used only in the context of a component."),w.useContext(Xw).location}var iQ="You should call navigate() in a React.useEffect(), not when your component is first rendered.";function sQ(t){w.useContext(_l).static||w.useLayoutEffect(t)}function qo(){let{isDataRoute:t}=w.useContext(yc);return t?Wme():Fme()}function Fme(){ja(xy(),"useNavigate() may be used only in the context of a component.");let t=w.useContext(by),{basename:e,navigator:n}=w.useContext(_l),{matches:r}=w.useContext(yc),{pathname:i}=td(),s=JSON.stringify(pO(r)),o=w.useRef(!1);return sQ(()=>{o.current=!0}),w.useCallback((c,d={})=>{if(cc(o.current,iQ),!o.current)return;if(typeof c=="number"){n.go(c);return}let u=fO(c,JSON.parse(s),i,d.relative==="path");t==null&&e!=="/"&&(u.pathname=u.pathname==="/"?e:im([e,u.pathname])),(d.replace?n.replace:n.push)(u,d.state,d)},[e,n,s,i,t])}var oQ=w.createContext(null);function yy(){return w.useContext(oQ)}function Rme(t){let e=w.useContext(yc).outlet;return w.useMemo(()=>e&&w.createElement(oQ.Provider,{value:t},e),[e,t])}function Yw(){let{matches:t}=w.useContext(yc),e=t[t.length-1];return e?e.params:{}}function Qw(t,{relative:e}={}){let{matches:n}=w.useContext(yc),{pathname:r}=td(),i=JSON.stringify(pO(n));return w.useMemo(()=>fO(t,JSON.parse(i),r,e==="path"),[t,i,r,e])}function Lme(t,e){return lQ(t,e)}function lQ(t,e,n,r,i){ja(xy(),"useRoutes() may be used only in the context of a component.");let{navigator:s}=w.useContext(_l),{matches:o}=w.useContext(yc),l=o[o.length-1],c=l?l.params:{},d=l?l.pathname:"/",u=l?l.pathnameBase:"/",m=l&&l.route;{let _=m&&m.path||"";dQ(d,!m||_.endsWith("*")||_.endsWith("*?"),`You rendered descendant (or called \`useRoutes()\`) at "${d}" (under ) but the parent route path has no trailing "*". This means if you navigate deeper, the parent won't match anymore and therefore the child routes will never render. -Please change the parent to .`)}let p=ed(),f;if(e){let _=typeof e=="string"?by(e):e;Aa(u==="/"||_.pathname?.startsWith(u),`When overriding the location using \`\` or \`useRoutes(routes, location)\`, the location pathname must begin with the portion of the URL pathname that was matched by all parent routes. The current pathname base is "${u}" but pathname "${_.pathname}" was given in the \`location\` prop.`),f=_}else f=p;let x=f.pathname||"/",v=x;if(u!=="/"){let _=u.replace(/^\//,"").split("/");v="/"+x.replace(/^\//,"").split("/").slice(_.length).join("/")}let b=QY(t,{pathname:v});lc(m||b!=null,`No routes matched location "${f.pathname}${f.search}${f.hash}" `),lc(b==null||b[b.length-1].route.element!==void 0||b[b.length-1].route.Component!==void 0||b[b.length-1].route.lazy!==void 0,`Matched leaf route at location "${f.pathname}${f.search}${f.hash}" does not have an element or Component. This means it will render an with a null value by default resulting in an "empty" page.`);let g=Bme(b&&b.map(_=>Object.assign({},_,{params:Object.assign({},c,_.params),pathname:am([u,s.encodeLocation?s.encodeLocation(_.pathname.replace(/\?/g,"%3F").replace(/#/g,"%23")).pathname:_.pathname]),pathnameBase:_.pathnameBase==="/"?u:am([u,s.encodeLocation?s.encodeLocation(_.pathnameBase.replace(/\?/g,"%3F").replace(/#/g,"%23")).pathname:_.pathnameBase])})),o,n,r,i);return e&&g?w.createElement(Vw.Provider,{value:{location:{pathname:"/",search:"",hash:"",state:null,key:"default",...f},navigationType:"POP"}},g):g}function Ome(){let t=Gme(),e=_me(t)?`${t.status} ${t.statusText}`:t instanceof Error?t.message:JSON.stringify(t),n=t instanceof Error?t.stack:null,r="rgba(200,200,200, 0.5)",i={padding:"0.5rem",backgroundColor:r},s={padding:"2px 4px",backgroundColor:r},o=null;return console.error("Error handled by React Router default ErrorBoundary:",t),o=w.createElement(w.Fragment,null,w.createElement("p",null,"💿 Hey developer 👋"),w.createElement("p",null,"You can provide a way better UX than this when your app throws errors by providing your own ",w.createElement("code",{style:s},"ErrorBoundary")," or"," ",w.createElement("code",{style:s},"errorElement")," prop on your route.")),w.createElement(w.Fragment,null,w.createElement("h2",null,"Unexpected Application Error!"),w.createElement("h3",{style:{fontStyle:"italic"}},e),n?w.createElement("pre",{style:i},n):null,o)}var Ime=w.createElement(Ome,null),cQ=class extends w.Component{constructor(t){super(t),this.state={location:t.location,revalidation:t.revalidation,error:t.error}}static getDerivedStateFromError(t){return{error:t}}static getDerivedStateFromProps(t,e){return e.location!==t.location||e.revalidation!=="idle"&&t.revalidation==="idle"?{error:t.error,location:t.location,revalidation:t.revalidation}:{error:t.error!==void 0?t.error:e.error,location:e.location,revalidation:t.revalidation||e.revalidation}}componentDidCatch(t,e){this.props.onError?this.props.onError(t,e):console.error("React Router caught the following error during render",t)}render(){let t=this.state.error;if(this.context&&typeof t=="object"&&t&&"digest"in t&&typeof t.digest=="string"){const n=Eme(t.digest);n&&(t=n)}let e=t!==void 0?w.createElement(xc.Provider,{value:this.props.routeContext},w.createElement(mO.Provider,{value:t,children:this.props.component})):this.props.children;return this.context?w.createElement(zme,{error:t},e):e}};cQ.contextType=Cme;var _M=new WeakMap;function zme({children:t,error:e}){let{basename:n}=w.useContext(Sl);if(typeof e=="object"&&e&&"digest"in e&&typeof e.digest=="string"){let r=Mme(e.digest);if(r){let i=_M.get(e);if(i)throw i;let s=tQ(r.location,n);if(eQ&&!_M.get(e))if(s.isExternal||r.reloadDocument)window.location.href=s.absoluteURL||s.to;else{const o=Promise.resolve().then(()=>window.__reactRouterDataRouter.navigate(s.to,{replace:r.replace}));throw _M.set(e,o),o}return w.createElement("meta",{httpEquiv:"refresh",content:`0;url=${s.absoluteURL||s.to}`})}}return t}function Ume({routeContext:t,match:e,children:n}){let r=w.useContext(xy);return r&&r.static&&r.staticContext&&(e.route.errorElement||e.route.ErrorBoundary)&&(r.staticContext._deepestRenderedBoundaryId=e.route.id),w.createElement(xc.Provider,{value:t},n)}function Bme(t,e=[],n=null,r=null,i=null){if(t==null){if(!n)return null;if(n.errors)t=n.matches;else if(e.length===0&&!n.initialized&&n.matches.length>0)t=n.matches;else return null}let s=t,o=n?.errors;if(o!=null){let u=s.findIndex(m=>m.route.id&&o?.[m.route.id]!==void 0);Aa(u>=0,`Could not find a matching route for errors on route IDs: ${Object.keys(o).join(",")}`),s=s.slice(0,Math.min(s.length,u+1))}let l=!1,c=-1;if(n)for(let u=0;u=0?s=s.slice(0,c+1):s=[s[0]];break}}}let d=n&&r?(u,m)=>{r(u,{location:n.location,params:n.matches?.[0]?.params??{},unstable_pattern:kme(n.matches),errorInfo:m})}:void 0;return s.reduceRight((u,m,p)=>{let f,x=!1,v=null,b=null;n&&(f=o&&m.route.id?o[m.route.id]:void 0,v=m.route.errorElement||Ime,l&&(c<0&&p===0?(dQ("route-fallback",!1,"No `HydrateFallback` element provided to render during initial hydration"),x=!0,b=null):c===p&&(x=!0,b=m.route.hydrateFallbackElement||null)));let g=e.concat(s.slice(0,p+1)),_=()=>{let C;return f?C=v:x?C=b:m.route.Component?C=w.createElement(m.route.Component,null):m.route.element?C=m.route.element:C=u,w.createElement(Ume,{match:m,routeContext:{outlet:u,matches:g,isDataRoute:n!=null},children:C})};return n&&(m.route.ErrorBoundary||m.route.errorElement||p===0)?w.createElement(cQ,{location:n.location,revalidation:n.revalidation,component:v,error:f,children:_(),routeContext:{outlet:null,matches:g,isDataRoute:!0},onError:d}):_()},null)}function hO(t){return`${t} must be used within a data router. See https://reactrouter.com/en/main/routers/picking-a-router.`}function Hme(t){let e=w.useContext(xy);return Aa(e,hO(t)),e}function qme(t){let e=w.useContext(OP);return Aa(e,hO(t)),e}function $me(t){let e=w.useContext(xc);return Aa(e,hO(t)),e}function pO(t){let e=$me(t),n=e.matches[e.matches.length-1];return Aa(n.route.id,`${t} can only be used on routes that contain a unique "id"`),n.route.id}function Vme(){return pO("useRouteId")}function Gme(){let t=w.useContext(mO),e=qme("useRouteError"),n=pO("useRouteError");return t!==void 0?t:e.errors?.[n]}function Wme(){let{router:t}=Hme("useNavigate"),e=pO("useNavigate"),n=w.useRef(!1);return sQ(()=>{n.current=!0}),w.useCallback(async(i,s={})=>{lc(n.current,iQ),n.current&&(typeof i=="number"?await t.navigate(i):await t.navigate(i,{fromRouteId:e,...s}))},[t,e])}var dH={};function dQ(t,e,n){!e&&!dH[t]&&(dH[t]=!0,lc(!1,n))}w.memo(Kme);function Kme({routes:t,future:e,state:n,onError:r}){return lQ(t,void 0,n,r,e)}function Hx({to:t,replace:e,state:n,relative:r}){Aa(yy()," may be used only in the context of a component.");let{static:i}=w.useContext(Sl);lc(!i," must not be used on the initial render in a . This is a no-op, but you should modify your code so the is only ever rendered in response to some user interaction or state change.");let{matches:s}=w.useContext(xc),{pathname:o}=ed(),l=Uo(),c=uO(t,dO(s),o,r==="path"),d=JSON.stringify(c);return w.useEffect(()=>{l(JSON.parse(d),{replace:e,state:n,relative:r})},[l,d,r,e,n]),null}function uQ(t){return Rme(t.context)}function Rr(t){Aa(!1,"A is only ever to be used as the child of element, never rendered directly. Please wrap your in a .")}function Xme({basename:t="/",children:e=null,location:n,navigationType:r="POP",navigator:i,static:s=!1,unstable_useTransitions:o}){Aa(!yy(),"You cannot render a inside another . You should never have more than one in your app.");let l=t.replace(/^\/*/,"/"),c=w.useMemo(()=>({basename:l,navigator:i,static:s,unstable_useTransitions:o,future:{}}),[l,i,s,o]);typeof n=="string"&&(n=by(n));let{pathname:d="/",search:u="",hash:m="",state:p=null,key:f="default"}=n,x=w.useMemo(()=>{let v=pm(d,l);return v==null?null:{location:{pathname:v,search:u,hash:m,state:p,key:f},navigationType:r}},[l,d,u,m,p,f,r]);return lc(x!=null,` is not able to match the URL "${d}${u}${m}" because it does not start with the basename, so the won't render anything.`),x==null?null:w.createElement(Sl.Provider,{value:c},w.createElement(Vw.Provider,{children:e,value:x}))}function Yme({children:t,location:e}){return Lme(q3(t),e)}function q3(t,e=[]){let n=[];return w.Children.forEach(t,(r,i)=>{if(!w.isValidElement(r))return;let s=[...e,i];if(r.type===w.Fragment){n.push.apply(n,q3(r.props.children,s));return}Aa(r.type===Rr,`[${typeof r.type=="string"?r.type:r.type.name}] is not a component. All component children of must be a or `),Aa(!r.props.index||!r.props.children,"An index route cannot have child routes.");let o={id:r.props.id||s.join("-"),caseSensitive:r.props.caseSensitive,element:r.props.element,Component:r.props.Component,index:r.props.index,path:r.props.path,middleware:r.props.middleware,loader:r.props.loader,action:r.props.action,hydrateFallbackElement:r.props.hydrateFallbackElement,HydrateFallback:r.props.HydrateFallback,errorElement:r.props.errorElement,ErrorBoundary:r.props.ErrorBoundary,hasErrorBoundary:r.props.hasErrorBoundary===!0||r.props.ErrorBoundary!=null||r.props.errorElement!=null,shouldRevalidate:r.props.shouldRevalidate,handle:r.props.handle,lazy:r.props.lazy};r.props.children&&(o.children=q3(r.props.children,s)),n.push(o)}),n}var Rk="get",Lk="application/x-www-form-urlencoded";function IP(t){return typeof HTMLElement<"u"&&t instanceof HTMLElement}function Qme(t){return IP(t)&&t.tagName.toLowerCase()==="button"}function Zme(t){return IP(t)&&t.tagName.toLowerCase()==="form"}function Jme(t){return IP(t)&&t.tagName.toLowerCase()==="input"}function ehe(t){return!!(t.metaKey||t.altKey||t.ctrlKey||t.shiftKey)}function the(t,e){return t.button===0&&(!e||e==="_self")&&!ehe(t)}function $3(t=""){return new URLSearchParams(typeof t=="string"||Array.isArray(t)||t instanceof URLSearchParams?t:Object.keys(t).reduce((e,n)=>{let r=t[n];return e.concat(Array.isArray(r)?r.map(i=>[n,i]):[[n,r]])},[]))}function nhe(t,e){let n=$3(t);return e&&e.forEach((r,i)=>{n.has(i)||e.getAll(i).forEach(s=>{n.append(i,s)})}),n}var G_=null;function rhe(){if(G_===null)try{new FormData(document.createElement("form"),0),G_=!1}catch{G_=!0}return G_}var ahe=new Set(["application/x-www-form-urlencoded","multipart/form-data","text/plain"]);function kM(t){return t!=null&&!ahe.has(t)?(lc(!1,`"${t}" is not a valid \`encType\` for \`
\`/\`\` and will default to "${Lk}"`),null):t}function ihe(t,e){let n,r,i,s,o;if(Zme(t)){let l=t.getAttribute("action");r=l?pm(l,e):null,n=t.getAttribute("method")||Rk,i=kM(t.getAttribute("enctype"))||Lk,s=new FormData(t)}else if(Qme(t)||Jme(t)&&(t.type==="submit"||t.type==="image")){let l=t.form;if(l==null)throw new Error('Cannot submit a