diff --git a/README.md b/README.md index c967c88e..4b0bcb21 100644 --- a/README.md +++ b/README.md @@ -216,6 +216,13 @@ is processed by the agent and goes to its model's provider, unless the model runs on your computer. Setup per agent, troubleshooting and every option are in [mcp/README.md](mcp/README.md). +## Desktop shell + +An Electron shell for Windows lives in [desktop/](desktop/README.md): it runs the +local build with no browser, picks the graphics backend (DirectX 11/12, Vulkan, +OpenGL), translates the page at run time (Portuguese today), opens Creality Scan +`project.obp` files, and pairs the agent relay with the window you have open. + ## Automatic reverse engineering An AI agent can drive Reverse engineer from start to finish: give it a scan diff --git a/desktop/.gitignore b/desktop/.gitignore new file mode 100644 index 00000000..3a0bfc2c --- /dev/null +++ b/desktop/.gitignore @@ -0,0 +1,2 @@ +release/ +release-new/ diff --git a/desktop/README.md b/desktop/README.md new file mode 100644 index 00000000..1f290bd3 --- /dev/null +++ b/desktop/README.md @@ -0,0 +1,50 @@ +# ScanRuler Desktop + +An Electron shell around the ScanRuler build in this repository: the app runs +from the local `dist/`, with no browser and no scanruler.com, paired with the +`scanruler-mcp` relay so agents reach the window you are looking at. + +```bash +# in the repository root: npm ci && npm --prefix mcp ci && npm run build +cd desktop +npm install +npm start # runs the app from the checkout +npm run dist # packages release/win-unpacked/ScanRuler.exe (@electron/packager) +npm test # dictionaries, smoke (isolation, threads, WebGL2, WebGPU), Creality watcher +node test/i18n.mjs # the page stays translated as React redraws +node test/e2e-agent.mjs # an MCP client reaches the page through the relay +``` + +Windows x64 only for now. Packaged with `@electron/packager`, not electron-builder, whose bundled 7-Zip is LGPL (see CONTRIBUTING.md). + +## What it adds + +- **Graphics backend**: Help → Hardware picks the backend ANGLE translates WebGL + to — DirectX 11 (default), DirectX 12, Vulkan or OpenGL — and shows the GPU, + WebGPU, thread count, cross-origin isolation and WebNN. A change applies on + restart; the window title names the API in use. WebGPU shows as available only + on DirectX 11: Chromium on Windows hands out no WebGPU adapter under the + Vulkan or OpenGL ANGLE backends (verified on Chromium 156, every + `--use-webgpu-adapter` value) — ScanRuler draws with WebGL2 and does not use it. +- **Language**: the ScanRuler page is translated at run time, node by node, from + `i18n/.json`, and kept translated as React redraws; the source of the + app is not touched. Shipped: Deutsch, Español, Français, Italiano, 日本語, + Português (Brasil), 简体中文. The first language follows the OS; the Language + menu changes it; the shell's own menu and Hardware panel follow. To add one, + copy `i18n/_english-keys.json` to `i18n/.json` as `{key: translation}` + with `_name`, `_menu` and `_hw` as in the others — `node test/dicts.mjs` + checks it. Text drawn on canvas and numbers built at run time are not covered. +- **Creality Scan projects**: `project.obp` opens from the menu, the command + line, drag and drop or the file picker, resolving to the project's + `result/Mesh.ply` (or the newest session's `Fused.ply`). File → Watch Creality + Scan folder… offers each new `Mesh.ply` the scanner software writes. +- **Agents on the open window**: the app starts the relay itself and writes + `agent.json` (port and token) under its user-data folder; `scanruler-mcp.cmd` + next to the exe joins that app, so Claude Code or Codex drive the window the + user has open instead of a background copy: + + ```bash + claude mcp add --scope user scanruler -- "\release\win-unpacked\scanruler-mcp.cmd" + codex mcp add scanruler -- "\release\win-unpacked\scanruler-mcp.cmd" + ``` + diff --git a/desktop/agent-bridge.mjs b/desktop/agent-bridge.mjs new file mode 100644 index 00000000..adffc826 --- /dev/null +++ b/desktop/agent-bridge.mjs @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: AGPL-3.0-only +// Entry of scanruler-mcp.cmd: joins the ScanRuler app that is open — its port and token are +// kept in agent.json beside its settings — then runs the agent server. Without the app +// open (no file), or with SCANRULER_MCP_TOKEN / _PORT set by hand, nothing changes. +import { existsSync, readFileSync } from 'node:fs' +import { dirname, join } from 'node:path' +import { fileURLToPath, pathToFileURL } from 'node:url' + +const here = dirname(fileURLToPath(import.meta.url)) +const file = process.env.SCANRULER_AGENT_FILE ?? join(process.env.APPDATA ?? '', 'scanruler-desktop', 'agent.json') +try { + const a = JSON.parse(readFileSync(file, 'utf8')) + process.env.SCANRULER_MCP_TOKEN ||= a.token + process.env.SCANRULER_MCP_PORT ||= String(a.port) + // The app removes the file when it quits. Left running, this server would take the + // app's port over and the next app could not start; so it leaves with the app. + setInterval(() => { + if (!existsSync(file)) process.exit(0) + }, 2000) +} catch {} +await import(pathToFileURL(join(here, 'mcp/bin/scanruler-mcp.js')).href) diff --git a/desktop/creality.js b/desktop/creality.js new file mode 100644 index 00000000..aa9cd95e --- /dev/null +++ b/desktop/creality.js @@ -0,0 +1,55 @@ +// SPDX-License-Identifier: AGPL-3.0-only +// Watches a folder of Creality Scan projects for a finished //result/Mesh.ply +// (the scan's final mesh) and says when one appears or changes, once its file stops growing. +import { watch, statSync, readFileSync, existsSync } from 'node:fs' +import { dirname, join, relative, sep } from 'node:path' + +const MESH = /[\\/]result[\\/]Mesh\.ply$/i +const SETTLE_MS = 1500 + +export const isMesh = (path) => MESH.test(path) + +/** Calls onMesh(path, project) for each Mesh.ply under `dir` once its size is steady; returns stop(). */ +export function watchMeshes(dir, onMesh, { settleMs = SETTLE_MS } = {}) { + const waiting = new Map() // path -> timer + const check = (path, last) => { + let size + try { + size = statSync(path).size + } catch { + return waiting.delete(path) // gone again before it settled + } + if (size > 0 && size === last) { + waiting.delete(path) + return onMesh(path, relative(dir, path).split(sep)[0]) + } + waiting.set(path, setTimeout(() => check(path, size), settleMs)) + } + const watcher = watch(dir, { recursive: true }, (_event, name) => { + if (!name) return + const path = `${dir}${sep}${name}` + if (!isMesh(path)) return + clearTimeout(waiting.get(path)) + waiting.set(path, setTimeout(() => check(path, -1), settleMs)) + }) + return () => { + watcher.close() + for (const t of waiting.values()) clearTimeout(t) + waiting.clear() + } +} + +/** The scan a project.obp stands for: its final Mesh.ply, else the newest session's Fused.ply. */ +export function meshOfProject(obp) { + const dir = dirname(obp) + const text = readFileSync(obp, 'utf8') + const data = /^data_dir:\s*(\S+)/m.exec(text)?.[1] + const final = data && join(dir, data, 'result', 'Mesh.ply') + if (final && existsSync(final)) return final + const sessions = [...text.matchAll(/^\s*-\s*(\S+)\s*$/gm)] + .map((m) => join(dir, m[1], 'result', 'Fused.ply')) + .filter((f) => existsSync(f)) + .sort((a, b) => statSync(b).mtimeMs - statSync(a).mtimeMs) + if (sessions.length) return sessions[0] + throw new Error('Este projeto do Creality Scan ainda não tem malha (Mesh.ply) nem scan fundido (Fused.ply).') +} diff --git a/desktop/hardware.html b/desktop/hardware.html new file mode 100644 index 00000000..65fea0ec --- /dev/null +++ b/desktop/hardware.html @@ -0,0 +1,55 @@ + + + + +ScanRuler — Hardware + + +

Hardware

+
+
+ + + +
+

+ diff --git a/desktop/i18n.js b/desktop/i18n.js new file mode 100644 index 00000000..75382903 --- /dev/null +++ b/desktop/i18n.js @@ -0,0 +1,137 @@ +// SPDX-License-Identifier: AGPL-3.0-only +// Runtime translation of the ScanRuler page: the English text the app draws is +// swapped for the dictionary's text, node by node, and kept in step as React +// redraws. The ScanRuler source is not touched. Text drawn on a canvas (3D +// labels) and text built from numbers at run time are not covered. +import { readdirSync, readFileSync } from 'node:fs' +import { dirname, join } from 'node:path' +import { fileURLToPath } from 'node:url' + +const here = dirname(fileURLToPath(import.meta.url)) + +// The shell's own text, in English; a dictionary's "_menu" and "_hw" override it. +const MENU = { file: 'File', open: 'Open scan…', quit: 'Quit', view: 'View', help: 'Help', hardware: 'Hardware…', language: 'Language', watch: 'Watch Creality Scan folder…', unwatch: 'Stop watching Creality Scan', fresh: 'New Mesh.ply from project', openIt: 'Open in ScanRuler', ignore: 'Ignore' } +const HW = { title: 'Hardware', gpu: 'GPU', backendChosen: 'ANGLE backend chosen', backendRunning: 'ANGLE backend running', renderer: 'WebGL2 / renderer', webgpu: 'WebGPU', cpu: 'CPU (threads)', threads: 'logical threads', isolation: 'Cross-origin isolation (WASM threads)', npu: 'NPU / WebNN', versions: 'Electron / Chromium', backend: 'Graphics backend:', restart: 'Restart to apply', changed: 'Backend changed: restart to apply.', yes: 'yes', no: 'no', available: 'available', unavailable: 'unavailable', webgpuWhy: 'Chromium on Windows offers WebGPU only on the DirectX 11 backend; ScanRuler does not use it.', default: 'default', experimental: 'experimental' } + +const files = new Map() // lang -> parsed i18n/.json +const read = (lang) => { + if (!files.has(lang)) { + try { + files.set(lang, JSON.parse(readFileSync(join(here, 'i18n', `${lang}.json`), 'utf8'))) + } catch { + files.set(lang, null) + } + } + return files.get(lang) +} + +/** Every language with a file in i18n/, plus English: id -> its own name. */ +export const LANGUAGES = Object.fromEntries([ + ['en', 'English'], + ...readdirSync(join(here, 'i18n')) + .filter((f) => f.endsWith('.json') && !f.startsWith('_')) // _english-keys.json lists the source strings for translators + .map((f) => f.slice(0, -5)) + .map((lang) => [lang, read(lang)?._name ?? lang]), +]) + +/** The language to start in: the OS's, where a file for it exists, else English. */ +export function defaultLanguage(locale) { + const ids = Object.keys(LANGUAGES) + return ids.find((id) => id.toLowerCase() === locale.toLowerCase()) ?? ids.find((id) => id.split('-')[0] === locale.split('-')[0]) ?? 'en' +} + +/** The page's words of a language, or null for English (the page as it is). */ +export function loadDict(lang) { + const d = read(lang) + return d ? Object.fromEntries(Object.entries(d).filter(([k]) => !k.startsWith('_'))) : null +} + +export const menuOf = (lang) => ({ ...MENU, ...read(lang)?._menu }) +export const hwOf = (lang) => ({ ...HW, ...read(lang)?._hw }) + +/** A script that installs the translator in the page, or switches its dictionary. */ +export function injectScript(dict) { + return `(${engine.toString()})(${JSON.stringify(dict)})` +} + +function engine(dict) { + if (window.__srI18n) return window.__srI18n.set(dict) + const ATTRS = ['title', 'aria-label', 'placeholder', 'alt'] + const tracked = new Set() // WeakRef of {node, attr?, orig, mine} + const byTarget = new WeakMap() // node|element -> {text?: rec, attrs: {name: rec}} + let words = null + + const skip = (el) => !el || /^(SCRIPT|STYLE|TEXTAREA|CODE|PRE)$/.test(el.tagName) || el.isContentEditable || el.closest?.('[data-no-i18n]') + const split = (s) => [s.match(/^\s*/)[0], s.trim(), s.match(/\s*$/)[0]] + + function setText(node) { + if (skip(node.parentElement)) return + let rec = byTarget.get(node)?.text + if (rec && node.nodeValue === rec.mine) return // our own write + const [lead, core, tail] = split(node.nodeValue) + if (!core) return + const to = words?.[core] + if (!to) { if (rec) rec.mine = null; return } + if (!rec) { + rec = { ref: new WeakRef(node), kind: 'text' } + byTarget.set(node, { ...(byTarget.get(node) ?? { attrs: {} }), text: rec }) + tracked.add(rec) + } + rec.orig = node.nodeValue + rec.mine = lead + to + tail + node.nodeValue = rec.mine + } + + function setAttr(el, name) { + const cur = el.getAttribute(name) + if (cur == null) return + const entry = byTarget.get(el) ?? { attrs: {} } + byTarget.set(el, entry) + let rec = entry.attrs[name] + if (rec && cur === rec.mine) return + const to = words?.[cur.trim()] + if (!to) return + if (!rec) { rec = { ref: new WeakRef(el), kind: 'attr', name }; entry.attrs[name] = rec; tracked.add(rec) } + rec.orig = cur + rec.mine = to + el.setAttribute(name, to) + } + + function walk(root) { + if (!words) return + if (root.nodeType === Node.TEXT_NODE) return setText(root) + if (root.nodeType !== Node.ELEMENT_NODE) return + const w = document.createTreeWalker(root, NodeFilter.SHOW_TEXT) + for (let n; (n = w.nextNode()); ) setText(n) + const els = root.matches?.('[title],[aria-label],[placeholder],[alt]') ? [root] : [] + for (const el of [...els, ...root.querySelectorAll('[title],[aria-label],[placeholder],[alt]')]) for (const a of ATTRS) setAttr(el, a) + } + + function restore() { + for (const rec of tracked) { + const t = rec.ref.deref() + if (!t) { tracked.delete(rec); continue } + if (rec.kind === 'text' && t.nodeValue === rec.mine) t.nodeValue = rec.orig + if (rec.kind === 'attr' && t.getAttribute(rec.name) === rec.mine) t.setAttribute(rec.name, rec.orig) + rec.mine = null + } + } + + const observer = new MutationObserver((list) => { + for (const m of list) { + if (m.type === 'childList') m.addedNodes.forEach(walk) + else if (m.type === 'characterData') setText(m.target) + else if (m.type === 'attributes') setAttr(m.target, m.attributeName) + } + }) + observer.observe(document.body, { subtree: true, childList: true, characterData: true, attributes: true, attributeFilter: ATTRS }) + + window.__srI18n = { + set(next) { + restore() + words = next + if (words) walk(document.body) + }, + } + window.__srI18n.set(dict) +} diff --git a/desktop/i18n/_english-keys.json b/desktop/i18n/_english-keys.json new file mode 100644 index 00000000..1d85778b --- /dev/null +++ b/desktop/i18n/_english-keys.json @@ -0,0 +1,347 @@ +[ + "CNC Kitchen · 3D scan analysis ·", + "3D Measure", + "Surface Deviation", + "Wall Thickness", + "2D Measure", + "Reverse engineer", + "Mesh Tools", + "No unsaved changes", + "New", + "Project", + "Save", + "Load", + "Settings", + "Model", + "No scan loaded", + "STL, PLY, OBJ, 3MF or STEP · mm", + "Open…", + "Drop it anywhere in the window.", + "Alignment", + "Auto-align", + "Align part", + "Move / rotate by numbers", + "Create element", + "Point", + "Line", + "Plane", + "Sphere", + "Cylinder", + "Cone", + "Circle", + "Torus", + "Section", + "Create dimensions", + "New dimension", + "New tolerance", + "Fitting elements", + "Load a scan, then pick features on it to fit spheres, cylinders and planes and measure between them. STL, PLY, OBJ, 3MF or STEP, in millimetres — everything stays in this browser.", + "Scan", + "The part as measured", + "Drop it here", + "Choose file…", + "Drag & drop works anywhere in the window — the first empty slot takes it.", + "Thanks for using ScanRuler!", + "This tool is provided", + "completely free", + "by CNC Kitchen. Once you have finished measuring, why not have a look at the store that keeps us making things like this?", + "Visit CNCKitchen.STORE", + "Send a tip on PayPal", + "Send a tip on Ko-fi", + "Transparent", + "Mesh", + "Backfaces", + "Elements", + "Labels", + "READY", + "IMPRINT", + "LOCAL ONLY", + "· FILES NEVER LEAVE YOUR BROWSER", + "Units of", + "An STL file carries no units — a 1 in it is whatever the program that wrote it meant. Say what this one is in and it is read in millimetres, like everything measured here.", + "Coordinates in", + "Millimetres", + "Centimetres", + "Metres", + "Inches", + "Don't ask again", + "— read every STL in these units. Settings brings the question back.", + "Cancel", + "Open", + "Your data will no longer stay on this computer", + "Without an agent, everything stays in this browser. With one connected, what the agent reads — measurements, reports, pictures of the part, file names, notes, and any file it opens itself — is processed by the agent and the AI model behind it.", + "Unless that model runs on this computer, all of this goes to the model’s provider", + ", under that provider’s terms.", + "ScanRuler itself sends nothing anywhere: the connection only reaches the agent’s helper on this computer. Don’t connect an agent while you work on parts you may not share with its provider.", + "Asked once in this browser. Settings → Agents switches the connection off at any time.", + "Connect the agent", + "3D Measure\nFit spheres, cylinders and planes, and measure between them", + "Surface Deviation\nBest-fit the scan to a nominal part and map the difference", + "Wall Thickness\nMap the wall thickness of the part itself — no reference needed", + "2D Measure\nMeasure a flatbed scan the way a measuring microscope would", + "Reverse engineer\nSketch on sections, extrude and revolve, and export a solid STEP body built over the scan", + "Mesh Tools\nClean the scan up — islands, holes, scatter — expand, shrink or hollow it, and reduce it to a light mesh for CAD held within a tolerance you set", + "The project's name — what Save Project saves it under and every export is named on. Unnamed, they go by the scan or the image it holds.", + "Project name", + "Untitled project", + "Checkpoints are stored only in this browser. Checkpoints and project files include completed work; finish active drawings before leaving. Save Project downloads a portable copy.", + "Undo — Ctrl+Z", + "Undo", + "Redo — Ctrl+Y / Ctrl+Shift+Z", + "Redo", + "Start a new project: the scan, the reference and the image closed, every measurement and every workspace's work let go, the name taken off — after asking, when there are changes not saved", + "Save the scan, the reference, the image and every measurement as one .scanruler file", + "Open a .scanruler project — or a plain scan or image to start fresh", + "Interface theme, colour mode, mouse controls, line widths and guided hints", + "Source code of the open-source core (AGPL-3.0-only) on GitHub", + "More about The scan", + "More about Alignment", + "More about Elements", + "Cut the scan with a plane, to measure the cut in the 2D Measure workspace", + "More about Dimensions", + "More about Geometric tolerances", + "Fit to view — bring the whole model back on screen, without turning it", + "Fit to view", + "Close", + "Load a scan first", + "How the scan is drawn", + "Draw the scan transparent — what lies inside it, the fitted elements and the pinned readings show instead of hiding behind the surface in front of them", + "Draw the triangle edges on the scan, to see how fine it is and where it is patchy. The mesh comes up as you zoom in: from a distance the triangles are smaller than a pixel, and the edges fade out rather than turning the part black", + "Colour the far side of every triangle — holes in the scan and inverted normals stop looking like solid part", + "Nothing measured yet — fit an element or cut a section and it shows here", + "How the elements are drawn", + "Show the name tags and readouts on the part. The fitted elements, the sections and the dimension lines stay either way", + "Cut the parts open at a plane to look inside — the scan, the reference and everything measured on them. Closed parts are closed where they are cut; an open scan stays a shell. Slide the plane with the arrow, tilt it with the rings", + "Measurement", + "Method", + "Ray along the normal", + "Sphere across the wall", + "Max. thickness", + "Rays", + "Single ray — fast", + "6-ray cone — tighter", + "12-ray cone — tightest", + "Far surface must face back", + "Max. deviation of normals", + "Measure wall thickness", + "Everything above shapes the search, so changing it means measuring again.", + "Wall thickness", + "Load a scan and measure how thick its walls are, everywhere at once. No reference model, no alignment. STL, PLY, OBJ, 3MF or STEP, in millimetres — everything stays in this browser.", + "More about How thickness is measured", + "More about Max. thickness", + "More about Rays", + "More about Far surface must face back", + "More about Max. deviation of normals", + "Image", + "No image loaded", + "PNG or JPEG", + "Measuring a flatbed scan", + "Scan the part face-down on a flatbed scanner and open the image — then fit points, lines and circles to its edges and measure between them, the way a measuring microscope does. PNG or JPEG, at the highest optical resolution you have. Everything stays in this browser.", + "The flatbed scan of the part", + "More about What is measured", + "CAD kernel ready — OCCT 8.0.1 via occt-wasm 5.7.1.", + "Parameters", + "+ Add", + "Create feature", + "Sketch on", + "Choose a plane…", + "Extrude", + "Revolve", + "Extract", + "Import STEP", + "Shell", + "Fillet", + "Chamfer", + "Draft", + "Move face", + "Delete face", + "Hole", + "Thread", + "Cut by plane", + "Mirror", + "Combine", + "Sheet", + "Split", + "Loft", + "Sweep", + "Pattern", + "Move body", + "Offset body", + "Fit surface", + "Patch network", + "Trim", + "Thicken", + "Start with a sketch: choose a coordinate plane above. No scan is needed to model.", + "Bodies", + "XY plane", + "XZ plane", + "YZ plane", + "Curvature", + "Overlaps", + "Tangent edges", + "Drawn", + "Dimmed", + "Off", + "Deviation", + "More about Reverse engineering", + "Show the parameters", + "More about Parameters", + "Add a parameter", + "More about Features", + "Extrude a sketch's regions — pick them in the viewport", + "Revolve a sketch's regions about an axis", + "A measured plane, cylinder, cone or sphere as a body at its extents, extended as in 3D Measure — a boss or a pin without a sketch — or a shadow section as its block, the outline through the part's thickness", + "Bring a STEP file in as bodies of its own — a bought-in part, a mating part, the CAD model to check the scan against. A body for every solid in the file, named as the file names its parts; Move body puts them where they belong", + "Hollow the body to a wall — click the faces to open, or none for a closed hollow; the wall comes from the Thickness map when one was measured", + "Round edges of the body — click them; the radius is read off the scan across the first", + "Chamfer edges of the body — click them", + "Lean faces of the body about a neutral plane — a moulding's draft, put on after the fact: click the flat face that stays, then the faces to lean", + "Offset faces of the body by a distance — a flat face along its normal, a round face in radius; several picked together grow or shrink as a whole, a cylinder's wall and its ends. One face can be snapped onto the scan instead: pick it, take Snap to scan, and click the scan where it should lie", + "Delete faces of the body and heal the gap — a fillet, a chamfer, a hole or a boss taken out, the faces round it run on to close over where it was: click a face of it, and the rest of the round or hole comes with it", + "A hole through the body along a cylinder or circle element, or a round face of the body", + "A modeled screw thread on a round face of the body — a bolt's on a boss, a nut's in a bore: ISO metric, UNC or UNF, a 45° form that prints without supports, or a diameter and pitch of your own, with an offset for clearance when printing", + "Cut the body at a plane and keep one side", + "Mirror the body about a plane, joined with the source", + "Join, cut or intersect two bodies made separately — or trim a sheet by a solid, or sew two sheets into one, before Thicken", + "A sketch's pieces — an open chain will do — extruded as a surface: a cutter to split a body with", + "Split a body by a sheet and keep the fragments that lie on the scan — or trim a sheet by sheets or solids, keeping its pieces on the scan", + "A solid through the profiles of two or more sketches on their own planes", + "A sketch's region swept along a chain of lines and arcs in another sketch — a bent bar, a handle, a rim that follows a curve", + "Copies of the body about an axis, along a direction, or in a grid", + "Move and turn bodies — the ones selected on the part or picked in the body list (Ctrl+click or Shift+click rows for several), or click them on the part: drag the gizmo's arrows and rings, or type the distances and angles. Tick Copy to move a copy and leave the body where it is", + "Grow a body all round by a distance, or shrink it — kept beside it as a body of its own, a keep-out round a part or the pocket that holds it with clearance", + "A smooth freeform surface fitted through a region of the scan — click faces or mark them — for the trim to close into a solid with the others", + "Curves drawn on the scan, and the regions they close filled with patches fitted to the scan together — tangent across every curve not marked a crease, sharing their edges: a sheet, or a solid where the network closes. For a freeform skin that folds back or wants more than one patch", + "Close a set of fitted surfaces, sheets and measured planes, cylinders, cones and spheres into a solid: the faces they cut space into are kept where they lie on the scan and sewn", + "Make a sheet or a fitted surface a solid of a thickness — a freeform thin shell's wall from its one skin, where a shell or an offset of a lofted body will not go", + "The XY plane through the origin — click it in the viewport to sketch on it", + "Hide the XY plane", + "The XZ plane through the origin — click it in the viewport to sketch on it", + "Hide the XZ plane", + "The YZ plane through the origin — click it in the viewport to sketch on it", + "Hide the YZ plane", + "Load a scan first — the map reads its surface", + "Build a body first", + "How the bodies are drawn", + "Draw the bodies transparent — the scan, the elements and the bodies inside them show through. With the scan solid as well, each part shows where it stands proud of the other", + "Draw where two bodies run into each other in hatched red — what they share, over the faces of both that would otherwise fight for the same pixels. Off for bodies that overlap on purpose, a modifier volume for a slicer; saved with the project", + "How the edges are drawn where a round runs into a flat. A round face's seam and the cut a join leaves across one surface are never drawn", + "Tangent edges drawn — where a round runs into a flat — as the sharp ones are, the way CAD draws them", + "Tangent edges dimmed", + "Tangent edges left out, and not picked: a round shows by its outline against the background", + "Nothing measured yet — fit elements or cut sections in the 3D Measure workspace and they show here", + "Build a body first — the map reads the scan against it", + "Tools", + "Reduce mesh", + "Remove islands", + "Delete marked", + "Flip normals", + "Cut", + "Fill holes", + "Smooth", + "Shrink/expand", + "Boolean", + "Trim to scan", + "Shell/hollow", + "Tolerance", + "Triangle limit", + "Reduce", + "Both figures shape the reduction, so changing them means reducing again.", + "Cleaning up and reducing a scan", + "Load a scan and clean it up — clear the stray islands, delete what is not the part, turn it the right way out, cut off the base it stood on, fill its holes, smooth out the scatter, expand or shrink it, hollow it — and reduce it to a light mesh to model over in CAD, held within a tolerance you set, measured against the scan to prove it, and exported where you aligned it. STL, PLY, OBJ or 3MF, in millimetres — everything stays in this browser.", + "Reduced", + "Accuracy", + "More about Mesh tools", + "Mesh tool", + "A light copy of the scan to model over in CAD, held within a tolerance you set — the scan itself stays as it is", + "Find the specks floating off the scan — every piece joined to nothing else and smaller than a size you set — mark them on the part, and delete them once you say so", + "Mark surface on the part by hand — a window, a brush or a lasso — and delete it from the scan: a fixture, a clamp, a patch of the table", + "Turn the surface over so it faces the other way — the whole part, exported inside-out, or a patch you mark that faces in", + "Cut the scan with a plane and take one side off — the turntable it stood on, or half the part — closing it flat where the plane went through", + "Close the holes a scanner left, every one smaller than a size you set, with patches that carry the surface across", + "Take the scanner’s scatter out of the surface, never moving any point further than a limit you set, sharp edges held", + "Move the surface along its normal by a distance — out or in, the whole part or a marked region — not a scale: a bore gets narrower as the part grows", + "Take a solid body out of the scan, add it, or keep what the two share — a bore the scanner could not see to the bottom of, cut out with a cylinder modelled in Reverse engineer", + "Cut a body modelled to mend a broken part to fit the scanned break — the missing piece, to print and glue in", + "Hollow a closed part, leaving a wall of a thickness you set round a sealed cavity — or thicken an open surface into a solid", + "More about Reducing a scan for CAD", + "More about Tolerance", + "More about Triangle limit", + "What to show of the reduction", + "Reduce the scan first — the view keys show the result", + "Edit the scan first — the deviation shows how far the edited copy is from the scan in use", + "How the instrument looks and behaves. Remembered in this browser — nothing here is a property of the part, and none of it is saved with a project.", + "Units", + "Lengths in", + "What is shown and typed. Files, projects and agent answers stay in millimetres.", + "Appearance", + "Interface", + "Light", + "Dark", + "System", + "Light or dark chassis.", + "follows the operating system's setting, and changes with it.", + "Colour mode", + "Studio grey", + "Scanner blue", + "Glossy blue under a hard light — surface detail stands out", + ". What has been measured keeps its colour either way — the element tints and the deviation ramp are the same in both.", + "Colour map", + "Blue through cyan, green and yellow to red, a saturated green in the middle — the inspection-software classic. Uneven in lightness, so cyan and yellow show as bands the part does not have", + ". Every map painted on the part, and the scale beside it, is drawn in it; the block at either end is what a reading past that end of the scale wears.", + "Sharp edges", + "Always sharp", + "Auto — sharp on a CAD-like mesh", + "Always smooth", + "Sharp from", + "Whether an edge of the scan is shaded as a crease, each face with its own normal, or smoothed across so a box looks pillowed — and from what angle between its two faces an edge counts as sharp.", + "draws a CAD mesh's faces flat and a scan's real edges — the rim of a bore, a step — as edges; noise seldom tips a scan's triangles that far, and if it still shows, a larger angle, 50° or 60°, leaves it smooth.", + "Auto", + "draws the creases only on a mesh that reads as a tessellation of CAD, a scan smooth all over. A reference part is always drawn sharp, from", + "Navigation", + "Mouse controls", + "left-drag orbit · right-drag pan · scroll zoom", + "works beside any of these, in Chrome and Edge: push or pull the puck to zoom, slide it to pan, tilt, twist and roll it to turn the part. It connects the first time you touch it, and drives the view the mouse was last over.", + "Files", + "Ask what units an STL is in", + "— the format carries none, and a part read at the wrong scale measures wrong in every number after. Off, every STL is read in the units below without asking.", + "STL import units", + "What an STL is read in when the question is off, and the answer the question offers first — the last one given. A PLY or OBJ is always read in millimetres; a STEP file says its own.", + "Lines", + "Section cuts", + "How heavy a section's cut is drawn on the part in 3D Measure. The curves measured on its sheet follow in proportion.", + "2D fitted curves", + "The lines, circles and splines fitted over a flatbed scan in 2D Measure, and the callouts drawn with them.", + "2D edges", + "The edge chains the curves are fitted to — found in the scan, or cut by a section.", + "Guidance", + "Guided hints", + "— ring the control to press next, until you have been through a workspace twice. Switching it back on starts the guidance over.", + "Agents", + "Local agent connection", + "— let an AI agent on this computer (Claude Code, Claude Desktop, Cursor …) drive this tab through its ScanRuler tools, the scanruler-mcp server. You watch every step here and can undo it.", + "Your data no longer stays on this computer:", + "what the agent reads — measurements, reports, pictures of the part, file names, notes — is processed by the agent and goes to its model’s provider, unless the model runs on this computer. ScanRuler itself sends nothing anywhere; the connection is to 127.0.0.1.", + "Port", + "Token", + "prints the port and the token, and a link that fills them in.", + "Support", + "Support card", + "— the thank-you in the corner of the stage, with the ways to give something back. Its × closes it for this visit; switched off here it stays away, the big one below with it.", + "Big card on saving", + "— the same thank-you in the middle of the screen after a save or an export, at most once in ten minutes. The file does not wait for it. Its \"Don't show this again for a week\" shows here as off until the week is over.", + "Dark blue through teal and green to yellow. Perceptually uniform — an even step in the reading is an even step in colour — and as legible to colour-blind eyes and in greyscale", + "Black through purple and pink to pale cream. Perceptually uniform and colour-blind safe", + "Indigo through magenta and orange to yellow — no black end. Perceptually uniform and colour-blind safe", + "Black through purple, red and orange to pale yellow. Perceptually uniform and colour-blind safe", + "Navy through grey to yellow, made to look the same to red–green colour blindness as to anyone else. Perceptually uniform", + "Black through deep blue and teal to mint. Perceptually uniform and colour-blind safe", + "Black through purple and red to cream. Perceptually uniform and colour-blind safe", + "A rainbow like jet, dark blue through green to dark red, but smooth — without the bands jet shows. Not for red–green colour blindness", + "Across", + "Flip", + "What the plane is laid across: a coordinate plane through the part’s middle, or an element with a direction — a plane’s normal, the axis of a cylinder, cone or line, a circle’s normal. The half toward you is taken away.", + "Where along its direction the plane lies — drag the arrow on the part, or type it here", + "Take away the other half instead", + "set otherwise in its menu" +] \ No newline at end of file diff --git a/desktop/i18n/de.json b/desktop/i18n/de.json new file mode 100644 index 00000000..b82534b4 --- /dev/null +++ b/desktop/i18n/de.json @@ -0,0 +1,385 @@ +{ + "_name": "Deutsch", + "_menu": { + "file": "Datei", + "open": "Scan öffnen…", + "quit": "Beenden", + "view": "Ansicht", + "help": "Hilfe", + "hardware": "Hardware…", + "language": "Sprache", + "watch": "Creality-Scan-Ordner überwachen…", + "unwatch": "Creality-Scan-Überwachung beenden", + "fresh": "Neue Mesh.ply aus Projekt", + "openIt": "In ScanRuler öffnen", + "ignore": "Ignorieren" + }, + "_hw": { + "title": "Hardware", + "gpu": "GPU", + "backendChosen": "Gewähltes ANGLE-Backend", + "backendRunning": "Laufendes ANGLE-Backend", + "renderer": "WebGL2 / Renderer", + "webgpu": "WebGPU", + "cpu": "CPU (Threads)", + "threads": "logische Threads", + "isolation": "Cross-Origin-Isolation (WASM-Threads)", + "npu": "NPU / WebNN", + "versions": "Electron / Chromium", + "backend": "Grafik-Backend:", + "restart": "Zum Anwenden neu starten", + "changed": "Backend geändert: zum Anwenden neu starten.", + "yes": "ja", + "no": "nein", + "available": "verfügbar", + "unavailable": "nicht verfügbar", + "default": "Standard", + "experimental": "experimentell", + "webgpuWhy": "Chromium unter Windows bietet WebGPU nur mit dem DirectX-11-Backend; ScanRuler nutzt es nicht." + }, + "CNC Kitchen · 3D scan analysis ·": "CNC Kitchen · 3D-Scan-Analyse ·", + "3D Measure": "3D-Messung", + "Surface Deviation": "Flächenabweichung", + "Wall Thickness": "Wandstärke", + "2D Measure": "2D-Messung", + "Reverse engineer": "Reverse Engineering", + "Mesh Tools": "Netzwerkzeuge", + "No unsaved changes": "Keine ungesicherten Änderungen", + "New": "Neu", + "Project": "Projekt", + "Save": "Speichern", + "Load": "Laden", + "Settings": "Einstellungen", + "Model": "Modell", + "No scan loaded": "Kein Scan geladen", + "STL, PLY, OBJ, 3MF or STEP · mm": "STL, PLY, OBJ, 3MF oder STEP · mm", + "Open…": "Öffnen…", + "Drop it anywhere in the window.": "Überall im Fenster ablegen.", + "Alignment": "Ausrichtung", + "Auto-align": "Auto-Ausrichten", + "Align part": "Teil ausrichten", + "Move / rotate by numbers": "Verschieben / Drehen per Zahlen", + "Create element": "Element erstellen", + "Point": "Punkt", + "Line": "Linie", + "Plane": "Ebene", + "Sphere": "Kugel", + "Cylinder": "Zylinder", + "Cone": "Kegel", + "Circle": "Kreis", + "Torus": "Torus", + "Section": "Schnitt", + "Create dimensions": "Bemaßungen erstellen", + "New dimension": "Neue Bemaßung", + "New tolerance": "Neue Toleranz", + "Fitting elements": "Ausgleichselemente", + "Load a scan, then pick features on it to fit spheres, cylinders and planes and measure between them. STL, PLY, OBJ, 3MF or STEP, in millimetres — everything stays in this browser.": "Laden Sie einen Scan und wählen Sie darauf Merkmale, um Kugeln, Zylinder und Ebenen einzupassen und dazwischen zu messen. STL, PLY, OBJ, 3MF oder STEP, in Millimetern — alles bleibt in diesem Browser.", + "Scan": "Scan", + "The part as measured": "Das Teil, wie gemessen", + "Drop it here": "Hier ablegen", + "Choose file…": "Datei wählen…", + "Drag & drop works anywhere in the window — the first empty slot takes it.": "Drag & Drop funktioniert überall im Fenster — der erste freie Platz übernimmt die Datei.", + "Thanks for using ScanRuler!": "Danke, dass Sie ScanRuler nutzen!", + "This tool is provided": "Dieses Werkzeug wird", + "completely free": "völlig kostenlos", + "by CNC Kitchen. Once you have finished measuring, why not have a look at the store that keeps us making things like this?": "von CNC Kitchen bereitgestellt. Wenn Sie fertig gemessen haben, werfen Sie doch einen Blick in den Shop, der uns solche Projekte ermöglicht.", + "Visit CNCKitchen.STORE": "CNCKitchen.STORE besuchen", + "Send a tip on PayPal": "Trinkgeld per PayPal senden", + "Send a tip on Ko-fi": "Trinkgeld per Ko-fi senden", + "Transparent": "Transparent", + "Mesh": "Netz", + "Backfaces": "Rückseiten", + "Elements": "Elemente", + "Labels": "Beschriftungen", + "READY": "BEREIT", + "IMPRINT": "IMPRESSUM", + "LOCAL ONLY": "NUR LOKAL", + "· FILES NEVER LEAVE YOUR BROWSER": "· DATEIEN VERLASSEN NIE IHREN BROWSER", + "Units of": "Einheiten von", + "An STL file carries no units — a 1 in it is whatever the program that wrote it meant. Say what this one is in and it is read in millimetres, like everything measured here.": "Eine STL-Datei enthält keine Einheiten — eine 1 darin bedeutet, was das erzeugende Programm meinte. Geben Sie die Einheit dieser Datei an, dann wird sie in Millimetern gelesen, wie alles, was hier gemessen wird.", + "Coordinates in": "Koordinaten in", + "Millimetres": "Millimeter", + "Centimetres": "Zentimeter", + "Metres": "Meter", + "Inches": "Zoll", + "Don't ask again": "Nicht mehr fragen", + "— read every STL in these units. Settings brings the question back.": "— jede STL in diesen Einheiten lesen. Die Einstellungen bringen die Frage zurück.", + "Cancel": "Abbrechen", + "Open": "Öffnen", + "Your data will no longer stay on this computer": "Ihre Daten bleiben nicht mehr auf diesem Computer", + "Without an agent, everything stays in this browser. With one connected, what the agent reads — measurements, reports, pictures of the part, file names, notes, and any file it opens itself — is processed by the agent and the AI model behind it.": "Ohne Agent bleibt alles in diesem Browser. Ist einer verbunden, wird alles, was der Agent liest — Messungen, Berichte, Bilder des Teils, Dateinamen, Notizen und jede Datei, die er selbst öffnet — vom Agenten und dem dahinterliegenden KI-Modell verarbeitet.", + "Unless that model runs on this computer, all of this goes to the model’s provider": "Sofern dieses Modell nicht auf diesem Computer läuft, geht all das an den Anbieter des Modells", + ", under that provider’s terms.": ", gemäß dessen Bedingungen.", + "ScanRuler itself sends nothing anywhere: the connection only reaches the agent’s helper on this computer. Don’t connect an agent while you work on parts you may not share with its provider.": "ScanRuler selbst sendet nirgendwohin etwas: Die Verbindung erreicht nur den Hilfsdienst des Agenten auf diesem Computer. Verbinden Sie keinen Agenten, während Sie an Teilen arbeiten, die Sie nicht mit dessen Anbieter teilen dürfen.", + "Asked once in this browser. Settings → Agents switches the connection off at any time.": "Wird in diesem Browser einmal gefragt. Einstellungen → Agenten schaltet die Verbindung jederzeit ab.", + "Connect the agent": "Agent verbinden", + "3D Measure\nFit spheres, cylinders and planes, and measure between them": "3D-Messung\nKugeln, Zylinder und Ebenen einpassen und dazwischen messen", + "Surface Deviation\nBest-fit the scan to a nominal part and map the difference": "Flächenabweichung\nScan per Best-Fit an ein Soll-Teil anpassen und die Abweichung abbilden", + "Wall Thickness\nMap the wall thickness of the part itself — no reference needed": "Wandstärke\nDie Wandstärke des Teils selbst abbilden — ohne Referenz", + "2D Measure\nMeasure a flatbed scan the way a measuring microscope would": "2D-Messung\nEinen Flachbettscan messen wie mit einem Messmikroskop", + "Reverse engineer\nSketch on sections, extrude and revolve, and export a solid STEP body built over the scan": "Reverse Engineering\nAuf Schnitten skizzieren, extrudieren und rotieren und einen über dem Scan aufgebauten STEP-Volumenkörper exportieren", + "Mesh Tools\nClean the scan up — islands, holes, scatter — expand, shrink or hollow it, and reduce it to a light mesh for CAD held within a tolerance you set": "Netzwerkzeuge\nDen Scan bereinigen — Inseln, Löcher, Rauschen — aufweiten, schrumpfen oder aushöhlen und auf ein leichtes CAD-Netz innerhalb einer von Ihnen gesetzten Toleranz reduzieren", + "The project's name — what Save Project saves it under and every export is named on. Unnamed, they go by the scan or the image it holds.": "Der Projektname — unter ihm speichert „Projekt speichern“, und nach ihm wird jeder Export benannt. Ohne Namen gelten der Scan oder das Bild darin.", + "Project name": "Projektname", + "Untitled project": "Unbenanntes Projekt", + "Checkpoints are stored only in this browser. Checkpoints and project files include completed work; finish active drawings before leaving. Save Project downloads a portable copy.": "Wiederherstellungspunkte werden nur in diesem Browser gespeichert. Wiederherstellungspunkte und Projektdateien enthalten abgeschlossene Arbeit; beenden Sie laufende Zeichnungen, bevor Sie gehen. „Projekt speichern“ lädt eine portable Kopie herunter.", + "Undo — Ctrl+Z": "Rückgängig — Strg+Z", + "Undo": "Rückgängig", + "Redo — Ctrl+Y / Ctrl+Shift+Z": "Wiederholen — Strg+Y / Strg+Umschalt+Z", + "Redo": "Wiederholen", + "Start a new project: the scan, the reference and the image closed, every measurement and every workspace's work let go, the name taken off — after asking, when there are changes not saved": "Neues Projekt beginnen: Scan, Referenz und Bild geschlossen, jede Messung und die Arbeit jedes Arbeitsbereichs verworfen, der Name entfernt — mit Rückfrage, wenn ungesicherte Änderungen vorliegen", + "Save the scan, the reference, the image and every measurement as one .scanruler file": "Scan, Referenz, Bild und alle Messungen als eine .scanruler-Datei speichern", + "Open a .scanruler project — or a plain scan or image to start fresh": "Ein .scanruler-Projekt öffnen — oder einen einfachen Scan oder ein Bild für einen Neustart", + "Interface theme, colour mode, mouse controls, line widths and guided hints": "Oberflächendesign, Farbmodus, Maussteuerung, Linienstärken und geführte Hinweise", + "Source code of the open-source core (AGPL-3.0-only) on GitHub": "Quellcode des Open-Source-Kerns (AGPL-3.0-only) auf GitHub", + "More about The scan": "Mehr zum Scan", + "More about Alignment": "Mehr zur Ausrichtung", + "More about Elements": "Mehr zu den Elementen", + "Cut the scan with a plane, to measure the cut in the 2D Measure workspace": "Den Scan mit einer Ebene schneiden, um den Schnitt im Arbeitsbereich 2D-Messung zu messen", + "More about Dimensions": "Mehr zu den Bemaßungen", + "More about Geometric tolerances": "Mehr zu Geometrietoleranzen", + "Fit to view — bring the whole model back on screen, without turning it": "An Ansicht anpassen — das ganze Modell wieder ins Bild bringen, ohne es zu drehen", + "Fit to view": "An Ansicht anpassen", + "Close": "Schließen", + "Load a scan first": "Laden Sie zuerst einen Scan", + "How the scan is drawn": "Wie der Scan dargestellt wird", + "Draw the scan transparent — what lies inside it, the fitted elements and the pinned readings show instead of hiding behind the surface in front of them": "Den Scan transparent darstellen — was darin liegt, die eingepassten Elemente und die angehefteten Messwerte werden sichtbar, statt hinter der Fläche davor zu verschwinden", + "Draw the triangle edges on the scan, to see how fine it is and where it is patchy. The mesh comes up as you zoom in: from a distance the triangles are smaller than a pixel, and the edges fade out rather than turning the part black": "Die Dreieckskanten auf dem Scan zeichnen, um zu sehen, wie fein er ist und wo er lückenhaft ist. Das Netz erscheint beim Hineinzoomen: aus der Ferne sind die Dreiecke kleiner als ein Pixel, und die Kanten blenden aus, statt das Teil schwarz zu färben", + "Colour the far side of every triangle — holes in the scan and inverted normals stop looking like solid part": "Die Rückseite jedes Dreiecks einfärben — Löcher im Scan und invertierte Normalen sehen nicht mehr wie massives Teil aus", + "Nothing measured yet — fit an element or cut a section and it shows here": "Noch nichts gemessen — passen Sie ein Element ein oder erzeugen Sie einen Schnitt, dann erscheint er hier", + "How the elements are drawn": "Wie die Elemente dargestellt werden", + "Show the name tags and readouts on the part. The fitted elements, the sections and the dimension lines stay either way": "Namensschilder und Messwerte am Teil anzeigen. Die eingepassten Elemente, die Schnitte und die Maßlinien bleiben in jedem Fall sichtbar", + "Cut the parts open at a plane to look inside — the scan, the reference and everything measured on them. Closed parts are closed where they are cut; an open scan stays a shell. Slide the plane with the arrow, tilt it with the rings": "Die Teile an einer Ebene aufschneiden, um hineinzusehen — Scan, Referenz und alles, was daran gemessen wurde. Geschlossene Teile werden an der Schnittstelle geschlossen; ein offener Scan bleibt eine Hülle. Verschieben Sie die Ebene mit dem Pfeil, kippen Sie sie mit den Ringen", + "Measurement": "Messung", + "Method": "Methode", + "Ray along the normal": "Strahl entlang der Normalen", + "Sphere across the wall": "Kugel durch die Wand", + "Max. thickness": "Max. Stärke", + "Rays": "Strahlen", + "Single ray — fast": "Einzelstrahl — schnell", + "6-ray cone — tighter": "6-Strahl-Kegel — genauer", + "12-ray cone — tightest": "12-Strahl-Kegel — am genauesten", + "Far surface must face back": "Gegenfläche muss zurückzeigen", + "Max. deviation of normals": "Max. Abweichung der Normalen", + "Measure wall thickness": "Wandstärke messen", + "Everything above shapes the search, so changing it means measuring again.": "Alles oben bestimmt die Suche, daher erfordert jede Änderung eine neue Messung.", + "Wall thickness": "Wandstärke", + "Load a scan and measure how thick its walls are, everywhere at once. No reference model, no alignment. STL, PLY, OBJ, 3MF or STEP, in millimetres — everything stays in this browser.": "Laden Sie einen Scan und messen Sie, wie dick seine Wände sind, überall auf einmal. Kein Referenzmodell, keine Ausrichtung. STL, PLY, OBJ, 3MF oder STEP, in Millimetern — alles bleibt in diesem Browser.", + "More about How thickness is measured": "Mehr dazu, wie die Stärke gemessen wird", + "More about Max. thickness": "Mehr zur max. Stärke", + "More about Rays": "Mehr zu den Strahlen", + "More about Far surface must face back": "Mehr zu „Gegenfläche muss zurückzeigen“", + "More about Max. deviation of normals": "Mehr zur max. Abweichung der Normalen", + "Image": "Bild", + "No image loaded": "Kein Bild geladen", + "PNG or JPEG": "PNG oder JPEG", + "Measuring a flatbed scan": "Einen Flachbettscan messen", + "Scan the part face-down on a flatbed scanner and open the image — then fit points, lines and circles to its edges and measure between them, the way a measuring microscope does. PNG or JPEG, at the highest optical resolution you have. Everything stays in this browser.": "Scannen Sie das Teil mit der Vorderseite nach unten auf einem Flachbettscanner und öffnen Sie das Bild — passen Sie dann Punkte, Linien und Kreise an dessen Kanten an und messen Sie dazwischen, wie mit einem Messmikroskop. PNG oder JPEG, in der höchsten optischen Auflösung, die Sie haben. Alles bleibt in diesem Browser.", + "The flatbed scan of the part": "Der Flachbettscan des Teils", + "More about What is measured": "Mehr dazu, was gemessen wird", + "CAD kernel ready — OCCT 8.0.1 via occt-wasm 5.7.1.": "CAD-Kernel bereit — OCCT 8.0.1 über occt-wasm 5.7.1.", + "Parameters": "Parameter", + "+ Add": "+ Hinzufügen", + "Create feature": "Feature erstellen", + "Sketch on": "Skizze auf", + "Choose a plane…": "Ebene wählen…", + "Extrude": "Extrudieren", + "Revolve": "Rotieren", + "Extract": "Extrahieren", + "Import STEP": "STEP importieren", + "Shell": "Schale", + "Fillet": "Verrundung", + "Chamfer": "Fase", + "Draft": "Entformungsschräge", + "Move face": "Fläche verschieben", + "Delete face": "Fläche löschen", + "Hole": "Bohrung", + "Thread": "Gewinde", + "Cut by plane": "Mit Ebene schneiden", + "Mirror": "Spiegeln", + "Combine": "Kombinieren", + "Sheet": "Fläche", + "Split": "Teilen", + "Loft": "Loft", + "Sweep": "Sweep", + "Pattern": "Muster", + "Move body": "Körper verschieben", + "Offset body": "Körper versetzen", + "Fit surface": "Fläche einpassen", + "Patch network": "Patch-Netz", + "Trim": "Trimmen", + "Thicken": "Verdicken", + "Start with a sketch: choose a coordinate plane above. No scan is needed to model.": "Beginnen Sie mit einer Skizze: Wählen Sie oben eine Koordinatenebene. Zum Modellieren ist kein Scan nötig.", + "Bodies": "Körper", + "XY plane": "XY-Ebene", + "XZ plane": "XZ-Ebene", + "YZ plane": "YZ-Ebene", + "Curvature": "Krümmung", + "Overlaps": "Überlappungen", + "Tangent edges": "Tangentenkanten", + "Drawn": "Gezeichnet", + "Dimmed": "Abgeblendet", + "Off": "Aus", + "Deviation": "Abweichung", + "More about Reverse engineering": "Mehr zu Reverse Engineering", + "Show the parameters": "Parameter anzeigen", + "More about Parameters": "Mehr zu den Parametern", + "Add a parameter": "Parameter hinzufügen", + "More about Features": "Mehr zu den Features", + "Extrude a sketch's regions — pick them in the viewport": "Bereiche einer Skizze extrudieren — im Ansichtsfenster auswählen", + "Revolve a sketch's regions about an axis": "Bereiche einer Skizze um eine Achse rotieren", + "A measured plane, cylinder, cone or sphere as a body at its extents, extended as in 3D Measure — a boss or a pin without a sketch — or a shadow section as its block, the outline through the part's thickness": "Eine gemessene Ebene, ein Zylinder, Kegel oder eine Kugel als Körper in ihrer Ausdehnung, erweitert wie in der 3D-Messung — ein Ansatz oder Stift ohne Skizze — oder ein Schattenschnitt als sein Block, die Kontur durch die Teiledicke", + "Bring a STEP file in as bodies of its own — a bought-in part, a mating part, the CAD model to check the scan against. A body for every solid in the file, named as the file names its parts; Move body puts them where they belong": "Eine STEP-Datei als eigene Körper einbringen — ein Zukaufteil, ein Gegenstück, das CAD-Modell, gegen das der Scan geprüft wird. Ein Körper je Volumenkörper der Datei, benannt wie in der Datei; „Körper verschieben“ setzt sie an die richtige Stelle", + "Hollow the body to a wall — click the faces to open, or none for a closed hollow; the wall comes from the Thickness map when one was measured": "Den Körper zu einer Wand aushöhlen — klicken Sie die zu öffnenden Flächen an, oder keine für einen geschlossenen Hohlraum; die Wand stammt aus der Stärkenkarte, sofern eine gemessen wurde", + "Round edges of the body — click them; the radius is read off the scan across the first": "Kanten des Körpers verrunden — anklicken; der Radius wird am Scan über der ersten abgelesen", + "Chamfer edges of the body — click them": "Kanten des Körpers anfasen — anklicken", + "Lean faces of the body about a neutral plane — a moulding's draft, put on after the fact: click the flat face that stays, then the faces to lean": "Flächen des Körpers um eine neutrale Ebene neigen — die Entformungsschräge eines Formteils, nachträglich aufgebracht: Klicken Sie die ebene Fläche an, die bleibt, dann die zu neigenden Flächen", + "Offset faces of the body by a distance — a flat face along its normal, a round face in radius; several picked together grow or shrink as a whole, a cylinder's wall and its ends. One face can be snapped onto the scan instead: pick it, take Snap to scan, and click the scan where it should lie": "Flächen des Körpers um einen Abstand versetzen — eine ebene Fläche entlang ihrer Normalen, eine runde Fläche im Radius; mehrere zusammen gewählte wachsen oder schrumpfen als Ganzes, die Wand eines Zylinders samt Enden. Eine Fläche kann stattdessen auf den Scan gezogen werden: wählen, „Auf Scan einrasten“ nehmen und den Scan dort anklicken, wo sie liegen soll", + "Delete faces of the body and heal the gap — a fillet, a chamfer, a hole or a boss taken out, the faces round it run on to close over where it was: click a face of it, and the rest of the round or hole comes with it": "Flächen des Körpers löschen und die Lücke schließen — eine Verrundung, Fase, Bohrung oder ein Ansatz entfällt, die umgebenden Flächen laufen weiter und schließen die Stelle: Klicken Sie eine Fläche davon an, der Rest der Rundung oder Bohrung folgt", + "A hole through the body along a cylinder or circle element, or a round face of the body": "Eine Bohrung durch den Körper entlang eines Zylinder- oder Kreiselements oder einer runden Fläche des Körpers", + "A modeled screw thread on a round face of the body — a bolt's on a boss, a nut's in a bore: ISO metric, UNC or UNF, a 45° form that prints without supports, or a diameter and pitch of your own, with an offset for clearance when printing": "Ein modelliertes Schraubgewinde auf einer runden Fläche des Körpers — das einer Schraube an einem Ansatz, das einer Mutter in einer Bohrung: ISO-metrisch, UNC oder UNF, eine 45°-Form, die ohne Stützen druckt, oder ein eigener Durchmesser und eine eigene Steigung, mit einem Versatz als Spiel beim Drucken", + "Cut the body at a plane and keep one side": "Den Körper an einer Ebene schneiden und eine Seite behalten", + "Mirror the body about a plane, joined with the source": "Den Körper an einer Ebene spiegeln, mit dem Original vereint", + "Join, cut or intersect two bodies made separately — or trim a sheet by a solid, or sew two sheets into one, before Thicken": "Zwei getrennt erstellte Körper vereinen, abziehen oder schneiden — oder eine Fläche mit einem Volumenkörper trimmen oder zwei Flächen zu einer vernähen, vor „Verdicken“", + "A sketch's pieces — an open chain will do — extruded as a surface: a cutter to split a body with": "Teile einer Skizze — eine offene Kette genügt — als Fläche extrudiert: ein Schneidwerkzeug zum Teilen eines Körpers", + "Split a body by a sheet and keep the fragments that lie on the scan — or trim a sheet by sheets or solids, keeping its pieces on the scan": "Einen Körper mit einer Fläche teilen und die Fragmente behalten, die auf dem Scan liegen — oder eine Fläche mit Flächen oder Volumenkörpern trimmen und ihre Stücke auf dem Scan behalten", + "A solid through the profiles of two or more sketches on their own planes": "Ein Volumenkörper durch die Profile von zwei oder mehr Skizzen auf ihren eigenen Ebenen", + "A sketch's region swept along a chain of lines and arcs in another sketch — a bent bar, a handle, a rim that follows a curve": "Der Bereich einer Skizze, entlang einer Kette aus Linien und Bögen in einer anderen Skizze geführt — ein gebogener Stab, ein Griff, ein Rand, der einer Kurve folgt", + "Copies of the body about an axis, along a direction, or in a grid": "Kopien des Körpers um eine Achse, entlang einer Richtung oder in einem Raster", + "Move and turn bodies — the ones selected on the part or picked in the body list (Ctrl+click or Shift+click rows for several), or click them on the part: drag the gizmo's arrows and rings, or type the distances and angles. Tick Copy to move a copy and leave the body where it is": "Körper verschieben und drehen — die am Teil ausgewählten oder in der Körperliste gewählten (Strg+Klick oder Umschalt+Klick auf Zeilen für mehrere), oder am Teil anklicken: Ziehen Sie die Pfeile und Ringe des Gizmos oder geben Sie Abstände und Winkel ein. „Kopieren“ aktivieren, um eine Kopie zu verschieben und den Körper an Ort und Stelle zu lassen", + "Grow a body all round by a distance, or shrink it — kept beside it as a body of its own, a keep-out round a part or the pocket that holds it with clearance": "Einen Körper ringsum um einen Abstand vergrößern oder verkleinern — als eigener Körper daneben behalten, eine Sperrzone um ein Teil oder die Tasche, die es mit Spiel aufnimmt", + "A smooth freeform surface fitted through a region of the scan — click faces or mark them — for the trim to close into a solid with the others": "Eine glatte Freiformfläche, durch einen Bereich des Scans eingepasst — Flächen anklicken oder markieren — damit das Trimmen sie mit den anderen zu einem Volumenkörper schließt", + "Curves drawn on the scan, and the regions they close filled with patches fitted to the scan together — tangent across every curve not marked a crease, sharing their edges: a sheet, or a solid where the network closes. For a freeform skin that folds back or wants more than one patch": "Auf den Scan gezeichnete Kurven; die von ihnen umschlossenen Bereiche werden mit gemeinsam an den Scan eingepassten Patches gefüllt — tangential über jede nicht als Knick markierte Kurve, mit gemeinsamen Kanten: eine Fläche oder ein Volumenkörper, wo das Netz schließt. Für eine Freiformhaut, die sich zurückfaltet oder mehr als einen Patch braucht", + "Close a set of fitted surfaces, sheets and measured planes, cylinders, cones and spheres into a solid: the faces they cut space into are kept where they lie on the scan and sewn": "Eine Menge eingepasster Flächen, Flächenkörper und gemessener Ebenen, Zylinder, Kegel und Kugeln zu einem Volumenkörper schließen: Die Flächen, in die sie den Raum zerlegen, bleiben dort erhalten, wo sie auf dem Scan liegen, und werden vernäht", + "Make a sheet or a fitted surface a solid of a thickness — a freeform thin shell's wall from its one skin, where a shell or an offset of a lofted body will not go": "Eine Fläche oder eingepasste Oberfläche zu einem Volumenkörper mit Stärke machen — die Wand einer dünnen Freiformschale aus ihrer einen Haut, wo eine Schale oder ein Versatz eines Loft-Körpers nicht funktioniert", + "The XY plane through the origin — click it in the viewport to sketch on it": "Die XY-Ebene durch den Ursprung — im Ansichtsfenster anklicken, um darauf zu skizzieren", + "Hide the XY plane": "XY-Ebene ausblenden", + "The XZ plane through the origin — click it in the viewport to sketch on it": "Die XZ-Ebene durch den Ursprung — im Ansichtsfenster anklicken, um darauf zu skizzieren", + "Hide the XZ plane": "XZ-Ebene ausblenden", + "The YZ plane through the origin — click it in the viewport to sketch on it": "Die YZ-Ebene durch den Ursprung — im Ansichtsfenster anklicken, um darauf zu skizzieren", + "Hide the YZ plane": "YZ-Ebene ausblenden", + "Load a scan first — the map reads its surface": "Laden Sie zuerst einen Scan — die Karte liest dessen Oberfläche", + "Build a body first": "Erstellen Sie zuerst einen Körper", + "How the bodies are drawn": "Wie die Körper dargestellt werden", + "Draw the bodies transparent — the scan, the elements and the bodies inside them show through. With the scan solid as well, each part shows where it stands proud of the other": "Die Körper transparent darstellen — Scan, Elemente und darin liegende Körper scheinen durch. Bei ebenfalls massivem Scan zeigt jedes Teil, wo es über das andere hinausragt", + "Draw where two bodies run into each other in hatched red — what they share, over the faces of both that would otherwise fight for the same pixels. Off for bodies that overlap on purpose, a modifier volume for a slicer; saved with the project": "Dort, wo zwei Körper ineinanderlaufen, rot schraffiert zeichnen — das Gemeinsame, über den Flächen beider, die sonst um dieselben Pixel kämpfen würden. Aus für Körper, die sich absichtlich überlappen, etwa ein Modifikatorvolumen für einen Slicer; wird mit dem Projekt gespeichert", + "How the edges are drawn where a round runs into a flat. A round face's seam and the cut a join leaves across one surface are never drawn": "Wie die Kanten dort dargestellt werden, wo eine runde Fläche auf eine ebene trifft. Die Naht einer runden Fläche und der Schnitt, den eine Verbindung auf einer Fläche hinterlässt, werden nie gezeichnet", + "Tangent edges drawn — where a round runs into a flat — as the sharp ones are, the way CAD draws them": "Tangentenkanten gezeichnet — wo eine runde Fläche auf eine ebene trifft — wie die scharfen, so wie CAD sie zeichnet", + "Tangent edges dimmed": "Tangentenkanten abgeblendet", + "Tangent edges left out, and not picked: a round shows by its outline against the background": "Tangentenkanten weggelassen und nicht wählbar: Eine runde Fläche zeigt sich durch ihre Kontur vor dem Hintergrund", + "Nothing measured yet — fit elements or cut sections in the 3D Measure workspace and they show here": "Noch nichts gemessen — passen Sie im Arbeitsbereich 3D-Messung Elemente ein oder erzeugen Sie Schnitte, dann erscheinen sie hier", + "Build a body first — the map reads the scan against it": "Erstellen Sie zuerst einen Körper — die Karte liest den Scan gegen ihn", + "Tools": "Werkzeuge", + "Reduce mesh": "Netz reduzieren", + "Remove islands": "Inseln entfernen", + "Delete marked": "Markiertes löschen", + "Flip normals": "Normalen umkehren", + "Cut": "Schneiden", + "Fill holes": "Löcher füllen", + "Smooth": "Glätten", + "Shrink/expand": "Schrumpfen/Aufweiten", + "Boolean": "Boolesch", + "Trim to scan": "Auf Scan trimmen", + "Shell/hollow": "Schale/aushöhlen", + "Tolerance": "Toleranz", + "Triangle limit": "Dreiecksgrenze", + "Reduce": "Reduzieren", + "Both figures shape the reduction, so changing them means reducing again.": "Beide Werte bestimmen die Reduktion, daher erfordert jede Änderung eine erneute Reduktion.", + "Cleaning up and reducing a scan": "Einen Scan bereinigen und reduzieren", + "Load a scan and clean it up — clear the stray islands, delete what is not the part, turn it the right way out, cut off the base it stood on, fill its holes, smooth out the scatter, expand or shrink it, hollow it — and reduce it to a light mesh to model over in CAD, held within a tolerance you set, measured against the scan to prove it, and exported where you aligned it. STL, PLY, OBJ or 3MF, in millimetres — everything stays in this browser.": "Laden Sie einen Scan und bereinigen Sie ihn — verstreute Inseln entfernen, löschen, was nicht zum Teil gehört, richtig herum drehen, die Auflagefläche abschneiden, Löcher füllen, das Rauschen glätten, aufweiten oder schrumpfen, aushöhlen — und auf ein leichtes Netz zum Modellieren in CAD reduzieren, innerhalb einer von Ihnen gesetzten Toleranz, zum Nachweis gegen den Scan gemessen und dort exportiert, wo Sie ihn ausgerichtet haben. STL, PLY, OBJ oder 3MF, in Millimetern — alles bleibt in diesem Browser.", + "Reduced": "Reduziert", + "Accuracy": "Genauigkeit", + "More about Mesh tools": "Mehr zu den Netzwerkzeugen", + "Mesh tool": "Netzwerkzeug", + "A light copy of the scan to model over in CAD, held within a tolerance you set — the scan itself stays as it is": "Eine leichte Kopie des Scans zum Modellieren in CAD, innerhalb einer von Ihnen gesetzten Toleranz — der Scan selbst bleibt unverändert", + "Find the specks floating off the scan — every piece joined to nothing else and smaller than a size you set — mark them on the part, and delete them once you say so": "Die losen Splitter am Scan finden — jedes mit nichts verbundene Stück, das kleiner als eine von Ihnen gesetzte Größe ist — am Teil markieren und auf Ihr Kommando löschen", + "Mark surface on the part by hand — a window, a brush or a lasso — and delete it from the scan: a fixture, a clamp, a patch of the table": "Fläche am Teil von Hand markieren — ein Fenster, ein Pinsel oder ein Lasso — und aus dem Scan löschen: eine Vorrichtung, eine Klemme, ein Stück des Tisches", + "Turn the surface over so it faces the other way — the whole part, exported inside-out, or a patch you mark that faces in": "Die Fläche umdrehen, sodass sie in die andere Richtung zeigt — das ganze Teil, das von innen nach außen exportiert wurde, oder einen markierten Bereich, der nach innen zeigt", + "Cut the scan with a plane and take one side off — the turntable it stood on, or half the part — closing it flat where the plane went through": "Den Scan mit einer Ebene schneiden und eine Seite entfernen — den Drehteller, auf dem er stand, oder das halbe Teil — und ihn dort eben schließen, wo die Ebene durchging", + "Close the holes a scanner left, every one smaller than a size you set, with patches that carry the surface across": "Die vom Scanner hinterlassenen Löcher schließen, jedes kleiner als eine von Ihnen gesetzte Größe, mit Patches, die die Fläche fortführen", + "Take the scanner’s scatter out of the surface, never moving any point further than a limit you set, sharp edges held": "Das Scanner-Rauschen aus der Fläche entfernen, ohne einen Punkt weiter als eine von Ihnen gesetzte Grenze zu verschieben, scharfe Kanten bleiben erhalten", + "Move the surface along its normal by a distance — out or in, the whole part or a marked region — not a scale: a bore gets narrower as the part grows": "Die Fläche entlang ihrer Normalen um einen Abstand verschieben — nach außen oder innen, das ganze Teil oder ein markierter Bereich — keine Skalierung: Eine Bohrung wird enger, wenn das Teil wächst", + "Take a solid body out of the scan, add it, or keep what the two share — a bore the scanner could not see to the bottom of, cut out with a cylinder modelled in Reverse engineer": "Einen Volumenkörper vom Scan abziehen, hinzufügen oder behalten, was beide gemeinsam haben — eine Bohrung, deren Grund der Scanner nicht sehen konnte, mit einem in Reverse Engineering modellierten Zylinder ausgeschnitten", + "Cut a body modelled to mend a broken part to fit the scanned break — the missing piece, to print and glue in": "Einen Körper, der zur Reparatur eines gebrochenen Teils modelliert wurde, passend zur gescannten Bruchstelle zuschneiden — das fehlende Stück zum Drucken und Einkleben", + "Hollow a closed part, leaving a wall of a thickness you set round a sealed cavity — or thicken an open surface into a solid": "Ein geschlossenes Teil aushöhlen und dabei eine Wand mit von Ihnen gesetzter Stärke um einen abgeschlossenen Hohlraum lassen — oder eine offene Fläche zu einem Volumenkörper verdicken", + "More about Reducing a scan for CAD": "Mehr zum Reduzieren eines Scans für CAD", + "More about Tolerance": "Mehr zur Toleranz", + "More about Triangle limit": "Mehr zur Dreiecksgrenze", + "What to show of the reduction": "Was von der Reduktion angezeigt wird", + "Reduce the scan first — the view keys show the result": "Reduzieren Sie zuerst den Scan — die Ansichtstasten zeigen das Ergebnis", + "Edit the scan first — the deviation shows how far the edited copy is from the scan in use": "Bearbeiten Sie zuerst den Scan — die Abweichung zeigt, wie weit die bearbeitete Kopie vom verwendeten Scan entfernt ist", + "How the instrument looks and behaves. Remembered in this browser — nothing here is a property of the part, and none of it is saved with a project.": "Wie das Instrument aussieht und sich verhält. In diesem Browser gespeichert — nichts davon ist eine Eigenschaft des Teils, und nichts wird mit einem Projekt gespeichert.", + "Units": "Einheiten", + "Lengths in": "Längen in", + "What is shown and typed. Files, projects and agent answers stay in millimetres.": "Was angezeigt und eingegeben wird. Dateien, Projekte und Agentenantworten bleiben in Millimetern.", + "Appearance": "Erscheinungsbild", + "Interface": "Oberfläche", + "Light": "Hell", + "Dark": "Dunkel", + "System": "System", + "Light or dark chassis.": "Helles oder dunkles Gehäuse.", + "follows the operating system's setting, and changes with it.": "folgt der Einstellung des Betriebssystems und ändert sich mit ihr.", + "Colour mode": "Farbmodus", + "Studio grey": "Studiograu", + "Scanner blue": "Scannerblau", + "Glossy blue under a hard light — surface detail stands out": "Glänzendes Blau unter hartem Licht — Oberflächendetails treten hervor", + ". What has been measured keeps its colour either way — the element tints and the deviation ramp are the same in both.": ". Das Gemessene behält in jedem Fall seine Farbe — die Elementfarben und die Abweichungsskala sind in beiden gleich.", + "Colour map": "Farbskala", + "Blue through cyan, green and yellow to red, a saturated green in the middle — the inspection-software classic. Uneven in lightness, so cyan and yellow show as bands the part does not have": "Blau über Cyan, Grün und Gelb zu Rot, mit einem gesättigten Grün in der Mitte — der Klassiker der Prüfsoftware. Ungleichmäßig in der Helligkeit, daher erscheinen Cyan und Gelb als Bänder, die das Teil nicht hat", + ". Every map painted on the part, and the scale beside it, is drawn in it; the block at either end is what a reading past that end of the scale wears.": ". Jede auf das Teil gemalte Karte und die Skala daneben werden damit gezeichnet; der Block an jedem Ende ist die Farbe eines Messwerts jenseits dieses Skalenendes.", + "Sharp edges": "Scharfe Kanten", + "Always sharp": "Immer scharf", + "Auto — sharp on a CAD-like mesh": "Auto — scharf bei CAD-ähnlichem Netz", + "Always smooth": "Immer glatt", + "Sharp from": "Scharf ab", + "Whether an edge of the scan is shaded as a crease, each face with its own normal, or smoothed across so a box looks pillowed — and from what angle between its two faces an edge counts as sharp.": "Ob eine Kante des Scans als Knick schattiert wird, jede Fläche mit eigener Normale, oder glatt übergeht, sodass ein Quader gepolstert wirkt — und ab welchem Winkel zwischen ihren zwei Flächen eine Kante als scharf gilt.", + "draws a CAD mesh's faces flat and a scan's real edges — the rim of a bore, a step — as edges; noise seldom tips a scan's triangles that far, and if it still shows, a larger angle, 50° or 60°, leaves it smooth.": "zeichnet die Flächen eines CAD-Netzes eben und die echten Kanten eines Scans — den Rand einer Bohrung, eine Stufe — als Kanten; Rauschen kippt die Dreiecke eines Scans selten so weit, und wenn es dennoch sichtbar wird, lässt ein größerer Winkel, 50° oder 60°, ihn glatt.", + "Auto": "Auto", + "draws the creases only on a mesh that reads as a tessellation of CAD, a scan smooth all over. A reference part is always drawn sharp, from": "zeichnet die Knicke nur bei einem Netz, das wie eine CAD-Tessellierung wirkt, bei einem durchgehend glatten Scan nicht. Ein Referenzteil wird immer scharf gezeichnet, ab", + "Navigation": "Navigation", + "Mouse controls": "Maussteuerung", + "left-drag orbit · right-drag pan · scroll zoom": "Linksziehen Orbit · Rechtsziehen Pan · Scrollen Zoom", + "works beside any of these, in Chrome and Edge: push or pull the puck to zoom, slide it to pan, tilt, twist and roll it to turn the part. It connects the first time you touch it, and drives the view the mouse was last over.": "funktioniert neben jeder dieser Optionen, in Chrome und Edge: Den Puck drücken oder ziehen zum Zoomen, schieben zum Verschieben, kippen, drehen und rollen, um das Teil zu drehen. Er verbindet sich bei der ersten Berührung und steuert die Ansicht, über der die Maus zuletzt war.", + "Files": "Dateien", + "Ask what units an STL is in": "Nach den Einheiten einer STL fragen", + "— the format carries none, and a part read at the wrong scale measures wrong in every number after. Off, every STL is read in the units below without asking.": "— das Format enthält keine, und ein im falschen Maßstab gelesenes Teil misst in jeder folgenden Zahl falsch. Aus: Jede STL wird ohne Nachfrage in den unten gewählten Einheiten gelesen.", + "STL import units": "STL-Importeinheiten", + "What an STL is read in when the question is off, and the answer the question offers first — the last one given. A PLY or OBJ is always read in millimetres; a STEP file says its own.": "In welcher Einheit eine STL gelesen wird, wenn die Frage aus ist, und die Antwort, die die Frage zuerst anbietet — die zuletzt gegebene. Eine PLY oder OBJ wird immer in Millimetern gelesen; eine STEP-Datei nennt ihre eigene.", + "Lines": "Linien", + "Section cuts": "Schnittlinien", + "How heavy a section's cut is drawn on the part in 3D Measure. The curves measured on its sheet follow in proportion.": "Wie stark die Schnittlinie eines Schnitts am Teil in der 3D-Messung gezeichnet wird. Die auf seinem Blatt gemessenen Kurven folgen proportional.", + "2D fitted curves": "2D-Ausgleichskurven", + "The lines, circles and splines fitted over a flatbed scan in 2D Measure, and the callouts drawn with them.": "Die in der 2D-Messung über einen Flachbettscan eingepassten Linien, Kreise und Splines sowie die damit gezeichneten Beschriftungen.", + "2D edges": "2D-Kanten", + "The edge chains the curves are fitted to — found in the scan, or cut by a section.": "Die Kantenzüge, an die die Kurven eingepasst werden — im Scan gefunden oder durch einen Schnitt erzeugt.", + "Guidance": "Anleitung", + "Guided hints": "Geführte Hinweise", + "— ring the control to press next, until you have been through a workspace twice. Switching it back on starts the guidance over.": "— hebt das nächste zu drückende Bedienelement hervor, bis Sie einen Arbeitsbereich zweimal durchlaufen haben. Beim erneuten Einschalten beginnt die Anleitung von vorn.", + "Agents": "Agenten", + "Local agent connection": "Lokale Agentenverbindung", + "— let an AI agent on this computer (Claude Code, Claude Desktop, Cursor …) drive this tab through its ScanRuler tools, the scanruler-mcp server. You watch every step here and can undo it.": "— lässt einen KI-Agenten auf diesem Computer (Claude Code, Claude Desktop, Cursor …) diesen Tab über seine ScanRuler-Werkzeuge steuern, den scanruler-mcp-Server. Sie sehen hier jeden Schritt und können ihn rückgängig machen.", + "Your data no longer stays on this computer:": "Ihre Daten bleiben nicht mehr auf diesem Computer:", + "what the agent reads — measurements, reports, pictures of the part, file names, notes — is processed by the agent and goes to its model’s provider, unless the model runs on this computer. ScanRuler itself sends nothing anywhere; the connection is to 127.0.0.1.": "was der Agent liest — Messungen, Berichte, Bilder des Teils, Dateinamen, Notizen — wird vom Agenten verarbeitet und geht an den Anbieter seines Modells, sofern das Modell nicht auf diesem Computer läuft. ScanRuler selbst sendet nirgendwohin etwas; die Verbindung geht zu 127.0.0.1.", + "Port": "Port", + "Token": "Token", + "prints the port and the token, and a link that fills them in.": "gibt den Port und das Token aus, dazu einen Link, der sie einträgt.", + "Support": "Unterstützung", + "Support card": "Unterstützungskarte", + "— the thank-you in the corner of the stage, with the ways to give something back. Its × closes it for this visit; switched off here it stays away, the big one below with it.": "— das Dankeschön in der Ecke der Bühne, mit den Möglichkeiten, etwas zurückzugeben. Mit × schließt es sich für diesen Besuch; hier ausgeschaltet bleibt es weg, die große Karte unten ebenso.", + "Big card on saving": "Große Karte beim Speichern", + "— the same thank-you in the middle of the screen after a save or an export, at most once in ten minutes. The file does not wait for it. Its \"Don't show this again for a week\" shows here as off until the week is over.": "— dasselbe Dankeschön in der Bildschirmmitte nach einem Speichern oder Export, höchstens einmal in zehn Minuten. Die Datei wartet nicht darauf. Ihr „Eine Woche lang nicht mehr anzeigen“ erscheint hier als aus, bis die Woche vorbei ist.", + "Dark blue through teal and green to yellow. Perceptually uniform — an even step in the reading is an even step in colour — and as legible to colour-blind eyes and in greyscale": "Dunkelblau über Petrol und Grün zu Gelb. Wahrnehmungsgleichmäßig — ein gleicher Schritt im Messwert ist ein gleicher Schritt in der Farbe — und ebenso lesbar für Farbenblinde und in Graustufen", + "Black through purple and pink to pale cream. Perceptually uniform and colour-blind safe": "Schwarz über Violett und Rosa zu hellem Creme. Wahrnehmungsgleichmäßig und farbenblindensicher", + "Indigo through magenta and orange to yellow — no black end. Perceptually uniform and colour-blind safe": "Indigo über Magenta und Orange zu Gelb — kein schwarzes Ende. Wahrnehmungsgleichmäßig und farbenblindensicher", + "Black through purple, red and orange to pale yellow. Perceptually uniform and colour-blind safe": "Schwarz über Violett, Rot und Orange zu hellem Gelb. Wahrnehmungsgleichmäßig und farbenblindensicher", + "Navy through grey to yellow, made to look the same to red–green colour blindness as to anyone else. Perceptually uniform": "Marineblau über Grau zu Gelb, so gestaltet, dass es bei Rot-Grün-Sehschwäche genauso aussieht wie für alle anderen. Wahrnehmungsgleichmäßig", + "Black through deep blue and teal to mint. Perceptually uniform and colour-blind safe": "Schwarz über tiefes Blau und Petrol zu Mint. Wahrnehmungsgleichmäßig und farbenblindensicher", + "Black through purple and red to cream. Perceptually uniform and colour-blind safe": "Schwarz über Violett und Rot zu Creme. Wahrnehmungsgleichmäßig und farbenblindensicher", + "A rainbow like jet, dark blue through green to dark red, but smooth — without the bands jet shows. Not for red–green colour blindness": "Ein Regenbogen wie Jet, von Dunkelblau über Grün zu Dunkelrot, aber glatt — ohne die Bänder, die Jet zeigt. Nicht für Rot-Grün-Sehschwäche", + "Across": "Quer zu", + "Flip": "Umkehren", + "What the plane is laid across: a coordinate plane through the part’s middle, or an element with a direction — a plane’s normal, the axis of a cylinder, cone or line, a circle’s normal. The half toward you is taken away.": "Wohin die Ebene gelegt wird: eine Koordinatenebene durch die Teilemitte oder ein Element mit Richtung — die Normale einer Ebene, die Achse eines Zylinders, Kegels oder einer Linie, die Normale eines Kreises. Die Ihnen zugewandte Hälfte wird entfernt.", + "Where along its direction the plane lies — drag the arrow on the part, or type it here": "Wo entlang ihrer Richtung die Ebene liegt — Pfeil am Teil ziehen oder hier eingeben", + "Take away the other half instead": "Stattdessen die andere Hälfte entfernen", + "set otherwise in its menu": "in seinem Menü anders eingestellt" +} diff --git a/desktop/i18n/es.json b/desktop/i18n/es.json new file mode 100644 index 00000000..b06bdc11 --- /dev/null +++ b/desktop/i18n/es.json @@ -0,0 +1,385 @@ +{ + "_name": "Español", + "_menu": { + "file": "Archivo", + "open": "Abrir escaneo…", + "quit": "Salir", + "view": "Ver", + "help": "Ayuda", + "hardware": "Hardware…", + "language": "Idioma", + "watch": "Vigilar carpeta de Creality Scan…", + "unwatch": "Dejar de vigilar Creality Scan", + "fresh": "Nuevo Mesh.ply del proyecto", + "openIt": "Abrir en ScanRuler", + "ignore": "Ignorar" + }, + "_hw": { + "title": "Hardware", + "gpu": "GPU", + "backendChosen": "Backend ANGLE elegido", + "backendRunning": "Backend ANGLE en ejecución", + "renderer": "WebGL2 / renderizador", + "webgpu": "WebGPU", + "cpu": "CPU (hilos)", + "threads": "hilos lógicos", + "isolation": "Aislamiento de origen cruzado (hilos WASM)", + "npu": "NPU / WebNN", + "versions": "Electron / Chromium", + "backend": "Backend gráfico:", + "restart": "Reiniciar para aplicar", + "changed": "Backend modificado: reinicie para aplicar.", + "yes": "sí", + "no": "no", + "available": "disponible", + "unavailable": "no disponible", + "default": "predeterminado", + "experimental": "experimental", + "webgpuWhy": "Chromium en Windows solo ofrece WebGPU con el backend DirectX 11; ScanRuler no lo usa." + }, + "CNC Kitchen · 3D scan analysis ·": "CNC Kitchen · análisis de escaneos 3D ·", + "3D Measure": "Medición 3D", + "Surface Deviation": "Desviación de superficie", + "Wall Thickness": "Espesor de pared", + "2D Measure": "Medición 2D", + "Reverse engineer": "Ingeniería inversa", + "Mesh Tools": "Herramientas de malla", + "No unsaved changes": "Sin cambios sin guardar", + "New": "Nuevo", + "Project": "Proyecto", + "Save": "Guardar", + "Load": "Cargar", + "Settings": "Ajustes", + "Model": "Modelo", + "No scan loaded": "Ningún escaneo cargado", + "STL, PLY, OBJ, 3MF or STEP · mm": "STL, PLY, OBJ, 3MF o STEP · mm", + "Open…": "Abrir…", + "Drop it anywhere in the window.": "Suéltelo en cualquier parte de la ventana.", + "Alignment": "Alineación", + "Auto-align": "Alinear auto", + "Align part": "Alinear pieza", + "Move / rotate by numbers": "Mover / girar por valores", + "Create element": "Crear elemento", + "Point": "Punto", + "Line": "Línea", + "Plane": "Plano", + "Sphere": "Esfera", + "Cylinder": "Cilindro", + "Cone": "Cono", + "Circle": "Círculo", + "Torus": "Toro", + "Section": "Sección", + "Create dimensions": "Crear cotas", + "New dimension": "Nueva cota", + "New tolerance": "Nueva tolerancia", + "Fitting elements": "Ajuste de elementos", + "Load a scan, then pick features on it to fit spheres, cylinders and planes and measure between them. STL, PLY, OBJ, 3MF or STEP, in millimetres — everything stays in this browser.": "Cargue un escaneo y seleccione características en él para ajustar esferas, cilindros y planos y medir entre ellos. STL, PLY, OBJ, 3MF o STEP, en milímetros — todo permanece en esta aplicación.", + "Scan": "Escaneo", + "The part as measured": "La pieza tal como se midió", + "Drop it here": "Suéltelo aquí", + "Choose file…": "Elegir archivo…", + "Drag & drop works anywhere in the window — the first empty slot takes it.": "Arrastrar y soltar funciona en cualquier parte de la ventana — el primer espacio vacío lo recibe.", + "Thanks for using ScanRuler!": "¡Gracias por usar ScanRuler!", + "This tool is provided": "Esta herramienta se ofrece", + "completely free": "totalmente gratis", + "by CNC Kitchen. Once you have finished measuring, why not have a look at the store that keeps us making things like this?": "por CNC Kitchen. Cuando termine de medir, ¿por qué no echa un vistazo a la tienda que nos permite seguir creando cosas como esta?", + "Visit CNCKitchen.STORE": "Visite CNCKitchen.STORE", + "Send a tip on PayPal": "Envíe una propina por PayPal", + "Send a tip on Ko-fi": "Envíe una propina por Ko-fi", + "Transparent": "Transparente", + "Mesh": "Malla", + "Backfaces": "Caras posteriores", + "Elements": "Elementos", + "Labels": "Etiquetas", + "READY": "LISTO", + "IMPRINT": "AVISO LEGAL", + "LOCAL ONLY": "SOLO LOCAL", + "· FILES NEVER LEAVE YOUR BROWSER": "· LOS ARCHIVOS NUNCA SALEN DE SU NAVEGADOR", + "Units of": "Unidades de", + "An STL file carries no units — a 1 in it is whatever the program that wrote it meant. Say what this one is in and it is read in millimetres, like everything measured here.": "Un archivo STL no incluye unidades — un 1 en él vale lo que quiso decir el programa que lo escribió. Indique en qué unidad está este y se leerá en milímetros, como todo lo que se mide aquí.", + "Coordinates in": "Coordenadas en", + "Millimetres": "Milímetros", + "Centimetres": "Centímetros", + "Metres": "Metros", + "Inches": "Pulgadas", + "Don't ask again": "No volver a preguntar", + "— read every STL in these units. Settings brings the question back.": "— leer todo STL en estas unidades. Ajustes vuelve a mostrar la pregunta.", + "Cancel": "Cancelar", + "Open": "Abrir", + "Your data will no longer stay on this computer": "Sus datos dejarán de permanecer en este equipo", + "Without an agent, everything stays in this browser. With one connected, what the agent reads — measurements, reports, pictures of the part, file names, notes, and any file it opens itself — is processed by the agent and the AI model behind it.": "Sin un agente, todo permanece en esta aplicación. Con uno conectado, lo que el agente lee — mediciones, informes, imágenes de la pieza, nombres de archivo, notas y cualquier archivo que abra por sí mismo — es procesado por el agente y por el modelo de IA que lo respalda.", + "Unless that model runs on this computer, all of this goes to the model’s provider": "A menos que ese modelo se ejecute en este equipo, todo esto se envía al proveedor del modelo", + ", under that provider’s terms.": ", según los términos de ese proveedor.", + "ScanRuler itself sends nothing anywhere: the connection only reaches the agent’s helper on this computer. Don’t connect an agent while you work on parts you may not share with its provider.": "ScanRuler no envía nada a ninguna parte: la conexión solo llega al asistente del agente en este equipo. No conecte un agente mientras trabaje con piezas que no puede compartir con su proveedor.", + "Asked once in this browser. Settings → Agents switches the connection off at any time.": "Se pregunta una sola vez en esta aplicación. Ajustes → Agentes desactiva la conexión en cualquier momento.", + "Connect the agent": "Conectar el agente", + "3D Measure\nFit spheres, cylinders and planes, and measure between them": "Medición 3D\nAjuste esferas, cilindros y planos y mida entre ellos", + "Surface Deviation\nBest-fit the scan to a nominal part and map the difference": "Desviación de superficie\nAjuste el escaneo a una pieza nominal y mapee la diferencia", + "Wall Thickness\nMap the wall thickness of the part itself — no reference needed": "Espesor de pared\nMapee el espesor de pared de la propia pieza — sin necesidad de referencia", + "2D Measure\nMeasure a flatbed scan the way a measuring microscope would": "Medición 2D\nMida un escaneo de cama plana como lo haría un microscopio de medición", + "Reverse engineer\nSketch on sections, extrude and revolve, and export a solid STEP body built over the scan": "Ingeniería inversa\nDibuje bocetos sobre secciones, extruya y revolucione, y exporte un cuerpo sólido STEP construido sobre el escaneo", + "Mesh Tools\nClean the scan up — islands, holes, scatter — expand, shrink or hollow it, and reduce it to a light mesh for CAD held within a tolerance you set": "Herramientas de malla\nLimpie el escaneo — islas, agujeros, ruido — expándalo, contráigalo o vacíelo, y redúzcalo a una malla ligera para CAD dentro de una tolerancia que usted defina", + "The project's name — what Save Project saves it under and every export is named on. Unnamed, they go by the scan or the image it holds.": "El nombre del proyecto — con el que Guardar proyecto lo guarda y con el que se nombra cada exportación. Sin nombre, se usa el del escaneo o la imagen que contiene.", + "Project name": "Nombre del proyecto", + "Untitled project": "Proyecto sin título", + "Checkpoints are stored only in this browser. Checkpoints and project files include completed work; finish active drawings before leaving. Save Project downloads a portable copy.": "Los puntos de restauración se guardan solo en esta aplicación. Los puntos de restauración y los archivos de proyecto incluyen el trabajo terminado; finalice los dibujos en curso antes de salir. Guardar proyecto descarga una copia portátil.", + "Undo — Ctrl+Z": "Deshacer — Ctrl+Z", + "Undo": "Deshacer", + "Redo — Ctrl+Y / Ctrl+Shift+Z": "Rehacer — Ctrl+Y / Ctrl+Shift+Z", + "Redo": "Rehacer", + "Start a new project: the scan, the reference and the image closed, every measurement and every workspace's work let go, the name taken off — after asking, when there are changes not saved": "Iniciar un proyecto nuevo: escaneo, referencia e imagen cerrados, todas las mediciones y el trabajo de cada espacio de trabajo descartados, nombre eliminado — con confirmación previa, si hay cambios sin guardar", + "Save the scan, the reference, the image and every measurement as one .scanruler file": "Guardar el escaneo, la referencia, la imagen y todas las mediciones en un único archivo .scanruler", + "Open a .scanruler project — or a plain scan or image to start fresh": "Abrir un proyecto .scanruler — o un escaneo o imagen simple para empezar de cero", + "Interface theme, colour mode, mouse controls, line widths and guided hints": "Tema de la interfaz, modo de color, controles del ratón, grosores de línea y sugerencias guiadas", + "Source code of the open-source core (AGPL-3.0-only) on GitHub": "Código fuente del núcleo de código abierto (AGPL-3.0-only) en GitHub", + "More about The scan": "Más sobre el escaneo", + "More about Alignment": "Más sobre la alineación", + "More about Elements": "Más sobre los elementos", + "Cut the scan with a plane, to measure the cut in the 2D Measure workspace": "Cortar el escaneo con un plano, para medir el corte en el espacio de Medición 2D", + "More about Dimensions": "Más sobre las cotas", + "More about Geometric tolerances": "Más sobre las tolerancias geométricas", + "Fit to view — bring the whole model back on screen, without turning it": "Ajustar a la vista — devuelve todo el modelo a la pantalla, sin girarlo", + "Fit to view": "Ajustar a la vista", + "Close": "Cerrar", + "Load a scan first": "Cargue primero un escaneo", + "How the scan is drawn": "Cómo se dibuja el escaneo", + "Draw the scan transparent — what lies inside it, the fitted elements and the pinned readings show instead of hiding behind the surface in front of them": "Dibuja el escaneo transparente — lo que hay dentro, los elementos ajustados y las lecturas fijadas se muestran en lugar de quedar ocultos tras la superficie que tienen delante", + "Draw the triangle edges on the scan, to see how fine it is and where it is patchy. The mesh comes up as you zoom in: from a distance the triangles are smaller than a pixel, and the edges fade out rather than turning the part black": "Dibuja las aristas de los triángulos sobre el escaneo, para ver cuán fino es y dónde es irregular. La malla aparece al acercarse: de lejos los triángulos son menores que un píxel, y las aristas se desvanecen en lugar de ennegrecer la pieza", + "Colour the far side of every triangle — holes in the scan and inverted normals stop looking like solid part": "Colorea el lado opuesto de cada triángulo — los agujeros del escaneo y las normales invertidas dejan de parecer pieza sólida", + "Nothing measured yet — fit an element or cut a section and it shows here": "Nada medido todavía — ajuste un elemento o corte una sección y aparecerá aquí", + "How the elements are drawn": "Cómo se dibujan los elementos", + "Show the name tags and readouts on the part. The fitted elements, the sections and the dimension lines stay either way": "Muestra las etiquetas de nombre y las lecturas sobre la pieza. Los elementos ajustados, las secciones y las líneas de cota permanecen en cualquier caso", + "Cut the parts open at a plane to look inside — the scan, the reference and everything measured on them. Closed parts are closed where they are cut; an open scan stays a shell. Slide the plane with the arrow, tilt it with the rings": "Corta las piezas con un plano para ver el interior — el escaneo, la referencia y todo lo medido en ellos. Las piezas cerradas se cierran donde se cortan; un escaneo abierto sigue siendo una cáscara. Deslice el plano con la flecha, inclínelo con los anillos", + "Measurement": "Medición", + "Method": "Método", + "Ray along the normal": "Rayo a lo largo de la normal", + "Sphere across the wall": "Esfera a través de la pared", + "Max. thickness": "Espesor máx.", + "Rays": "Rayos", + "Single ray — fast": "Rayo único — rápido", + "6-ray cone — tighter": "Cono de 6 rayos — más preciso", + "12-ray cone — tightest": "Cono de 12 rayos — el más preciso", + "Far surface must face back": "La superficie opuesta debe mirar hacia atrás", + "Max. deviation of normals": "Desviación máx. de las normales", + "Measure wall thickness": "Medir espesor de pared", + "Everything above shapes the search, so changing it means measuring again.": "Todo lo anterior define la búsqueda, por lo que cambiarlo exige medir de nuevo.", + "Wall thickness": "Espesor de pared", + "Load a scan and measure how thick its walls are, everywhere at once. No reference model, no alignment. STL, PLY, OBJ, 3MF or STEP, in millimetres — everything stays in this browser.": "Cargue un escaneo y mida el espesor de sus paredes, en todos los puntos a la vez. Sin modelo de referencia, sin alineación. STL, PLY, OBJ, 3MF o STEP, en milímetros — todo permanece en esta aplicación.", + "More about How thickness is measured": "Más sobre cómo se mide el espesor", + "More about Max. thickness": "Más sobre el espesor máx.", + "More about Rays": "Más sobre los rayos", + "More about Far surface must face back": "Más sobre la superficie opuesta que debe mirar hacia atrás", + "More about Max. deviation of normals": "Más sobre la desviación máx. de las normales", + "Image": "Imagen", + "No image loaded": "Ninguna imagen cargada", + "PNG or JPEG": "PNG o JPEG", + "Measuring a flatbed scan": "Medición de un escaneo de cama plana", + "Scan the part face-down on a flatbed scanner and open the image — then fit points, lines and circles to its edges and measure between them, the way a measuring microscope does. PNG or JPEG, at the highest optical resolution you have. Everything stays in this browser.": "Escanee la pieza boca abajo en un escáner de cama plana y abra la imagen — luego ajuste puntos, líneas y círculos a sus bordes y mida entre ellos, como lo hace un microscopio de medición. PNG o JPEG, a la mayor resolución óptica de que disponga. Todo permanece en esta aplicación.", + "The flatbed scan of the part": "El escaneo de cama plana de la pieza", + "More about What is measured": "Más sobre lo que se mide", + "CAD kernel ready — OCCT 8.0.1 via occt-wasm 5.7.1.": "Núcleo CAD listo — OCCT 8.0.1 mediante occt-wasm 5.7.1.", + "Parameters": "Parámetros", + "+ Add": "+ Añadir", + "Create feature": "Crear operación", + "Sketch on": "Boceto en", + "Choose a plane…": "Elegir un plano…", + "Extrude": "Extruir", + "Revolve": "Revolución", + "Extract": "Extraer", + "Import STEP": "Importar STEP", + "Shell": "Vaciado", + "Fillet": "Redondeo", + "Chamfer": "Chaflán", + "Draft": "Ángulo de desmoldeo", + "Move face": "Mover cara", + "Delete face": "Eliminar cara", + "Hole": "Agujero", + "Thread": "Rosca", + "Cut by plane": "Cortar por plano", + "Mirror": "Simetría", + "Combine": "Combinar", + "Sheet": "Lámina", + "Split": "Dividir", + "Loft": "Loft", + "Sweep": "Barrido", + "Pattern": "Patrón", + "Move body": "Mover cuerpo", + "Offset body": "Desfasar cuerpo", + "Fit surface": "Ajustar superficie", + "Patch network": "Red de parches", + "Trim": "Recortar", + "Thicken": "Engrosar", + "Start with a sketch: choose a coordinate plane above. No scan is needed to model.": "Comience con un boceto: elija arriba un plano de coordenadas. No se necesita un escaneo para modelar.", + "Bodies": "Cuerpos", + "XY plane": "Plano XY", + "XZ plane": "Plano XZ", + "YZ plane": "Plano YZ", + "Curvature": "Curvatura", + "Overlaps": "Solapamientos", + "Tangent edges": "Aristas tangentes", + "Drawn": "Dibujadas", + "Dimmed": "Atenuadas", + "Off": "Desactivado", + "Deviation": "Desviación", + "More about Reverse engineering": "Más sobre ingeniería inversa", + "Show the parameters": "Mostrar los parámetros", + "More about Parameters": "Más sobre los parámetros", + "Add a parameter": "Añadir un parámetro", + "More about Features": "Más sobre las operaciones", + "Extrude a sketch's regions — pick them in the viewport": "Extruye las regiones de un boceto — selecciónelas en la vista", + "Revolve a sketch's regions about an axis": "Revoluciona las regiones de un boceto alrededor de un eje", + "A measured plane, cylinder, cone or sphere as a body at its extents, extended as in 3D Measure — a boss or a pin without a sketch — or a shadow section as its block, the outline through the part's thickness": "Un plano, cilindro, cono o esfera medido como cuerpo en sus extensiones, prolongado como en Medición 3D — un saliente o un pasador sin boceto — o una sección de sombra como su bloque, el contorno a través del espesor de la pieza", + "Bring a STEP file in as bodies of its own — a bought-in part, a mating part, the CAD model to check the scan against. A body for every solid in the file, named as the file names its parts; Move body puts them where they belong": "Trae un archivo STEP como cuerpos propios — una pieza comprada, una pieza de acoplamiento, el modelo CAD con el que comprobar el escaneo. Un cuerpo por cada sólido del archivo, con el nombre que el archivo da a sus piezas; Mover cuerpo los coloca donde corresponde", + "Hollow the body to a wall — click the faces to open, or none for a closed hollow; the wall comes from the Thickness map when one was measured": "Vacía el cuerpo hasta una pared — haga clic en las caras que se abrirán, o en ninguna para un hueco cerrado; la pared proviene del mapa de espesor cuando se midió uno", + "Round edges of the body — click them; the radius is read off the scan across the first": "Redondea aristas del cuerpo — haga clic en ellas; el radio se lee del escaneo sobre la primera", + "Chamfer edges of the body — click them": "Achaflana aristas del cuerpo — haga clic en ellas", + "Lean faces of the body about a neutral plane — a moulding's draft, put on after the fact: click the flat face that stays, then the faces to lean": "Inclina caras del cuerpo respecto a un plano neutro — el ángulo de desmoldeo de una pieza moldeada, aplicado a posteriori: haga clic en la cara plana que permanece y luego en las caras a inclinar", + "Offset faces of the body by a distance — a flat face along its normal, a round face in radius; several picked together grow or shrink as a whole, a cylinder's wall and its ends. One face can be snapped onto the scan instead: pick it, take Snap to scan, and click the scan where it should lie": "Desfasa caras del cuerpo una distancia — una cara plana a lo largo de su normal, una cara redonda en radio; varias seleccionadas juntas crecen o se reducen como un todo, la pared de un cilindro y sus extremos. En su lugar, una cara puede ajustarse al escaneo: selecciónela, use Ajustar al escaneo y haga clic en el escaneo donde debe quedar", + "Delete faces of the body and heal the gap — a fillet, a chamfer, a hole or a boss taken out, the faces round it run on to close over where it was: click a face of it, and the rest of the round or hole comes with it": "Elimina caras del cuerpo y repara el hueco — un redondeo, chaflán, agujero o saliente retirado, las caras de alrededor se prolongan para cubrir donde estaba: haga clic en una cara suya y el resto del redondeo o agujero se va con ella", + "A hole through the body along a cylinder or circle element, or a round face of the body": "Un agujero a través del cuerpo a lo largo de un elemento cilindro o círculo, o de una cara redonda del cuerpo", + "A modeled screw thread on a round face of the body — a bolt's on a boss, a nut's in a bore: ISO metric, UNC or UNF, a 45° form that prints without supports, or a diameter and pitch of your own, with an offset for clearance when printing": "Una rosca modelada en una cara redonda del cuerpo — la de un tornillo en un saliente, la de una tuerca en un taladro: métrica ISO, UNC o UNF, un perfil de 45° que se imprime sin soportes, o un diámetro y paso propios, con un desfase de holgura para la impresión", + "Cut the body at a plane and keep one side": "Corta el cuerpo con un plano y conserva uno de los lados", + "Mirror the body about a plane, joined with the source": "Refleja el cuerpo respecto a un plano, unido al original", + "Join, cut or intersect two bodies made separately — or trim a sheet by a solid, or sew two sheets into one, before Thicken": "Une, corta o interseca dos cuerpos hechos por separado — o recorta una lámina con un sólido, o cose dos láminas en una, antes de Engrosar", + "A sketch's pieces — an open chain will do — extruded as a surface: a cutter to split a body with": "Las partes de un boceto — una cadena abierta sirve — extruidas como superficie: un cortador para dividir un cuerpo", + "Split a body by a sheet and keep the fragments that lie on the scan — or trim a sheet by sheets or solids, keeping its pieces on the scan": "Divide un cuerpo con una lámina y conserva los fragmentos que quedan sobre el escaneo — o recorta una lámina con láminas o sólidos, conservando sus partes sobre el escaneo", + "A solid through the profiles of two or more sketches on their own planes": "Un sólido a través de los perfiles de dos o más bocetos en sus propios planos", + "A sketch's region swept along a chain of lines and arcs in another sketch — a bent bar, a handle, a rim that follows a curve": "La región de un boceto barrida a lo largo de una cadena de líneas y arcos de otro boceto — una barra doblada, un asa, un borde que sigue una curva", + "Copies of the body about an axis, along a direction, or in a grid": "Copias del cuerpo alrededor de un eje, a lo largo de una dirección o en una cuadrícula", + "Move and turn bodies — the ones selected on the part or picked in the body list (Ctrl+click or Shift+click rows for several), or click them on the part: drag the gizmo's arrows and rings, or type the distances and angles. Tick Copy to move a copy and leave the body where it is": "Mueve y gira cuerpos — los seleccionados en la pieza o elegidos en la lista de cuerpos (Ctrl+clic o Mayús+clic en las filas para varios), o haga clic en ellos sobre la pieza: arrastre las flechas y anillos del gizmo, o escriba las distancias y los ángulos. Marque Copiar para mover una copia y dejar el cuerpo donde está", + "Grow a body all round by a distance, or shrink it — kept beside it as a body of its own, a keep-out round a part or the pocket that holds it with clearance": "Aumenta un cuerpo por igual en todos los lados una distancia, o lo reduce — conservado a su lado como un cuerpo propio, una zona de exclusión alrededor de una pieza o el alojamiento que la contiene con holgura", + "A smooth freeform surface fitted through a region of the scan — click faces or mark them — for the trim to close into a solid with the others": "Una superficie de forma libre y suave ajustada a una región del escaneo — haga clic en caras o márquelas — para que el recorte la cierre en un sólido con las demás", + "Curves drawn on the scan, and the regions they close filled with patches fitted to the scan together — tangent across every curve not marked a crease, sharing their edges: a sheet, or a solid where the network closes. For a freeform skin that folds back or wants more than one patch": "Curvas dibujadas sobre el escaneo, y las regiones que cierran rellenadas con parches ajustados al escaneo en conjunto — tangentes en cada curva no marcada como pliegue, compartiendo sus aristas: una lámina, o un sólido donde la red cierra. Para una piel de forma libre que se pliega sobre sí misma o requiere más de un parche", + "Close a set of fitted surfaces, sheets and measured planes, cylinders, cones and spheres into a solid: the faces they cut space into are kept where they lie on the scan and sewn": "Cierra un conjunto de superficies ajustadas, láminas y planos, cilindros, conos y esferas medidos en un sólido: las caras en que dividen el espacio se conservan donde quedan sobre el escaneo y se cosen", + "Make a sheet or a fitted surface a solid of a thickness — a freeform thin shell's wall from its one skin, where a shell or an offset of a lofted body will not go": "Convierte una lámina o superficie ajustada en un sólido de un espesor dado — la pared de una cáscara delgada de forma libre a partir de su única piel, donde un vaciado o un desfase de un cuerpo en loft no funciona", + "The XY plane through the origin — click it in the viewport to sketch on it": "El plano XY por el origen — haga clic en él en la vista para dibujar un boceto sobre él", + "Hide the XY plane": "Ocultar el plano XY", + "The XZ plane through the origin — click it in the viewport to sketch on it": "El plano XZ por el origen — haga clic en él en la vista para dibujar un boceto sobre él", + "Hide the XZ plane": "Ocultar el plano XZ", + "The YZ plane through the origin — click it in the viewport to sketch on it": "El plano YZ por el origen — haga clic en él en la vista para dibujar un boceto sobre él", + "Hide the YZ plane": "Ocultar el plano YZ", + "Load a scan first — the map reads its surface": "Cargue primero un escaneo — el mapa lee su superficie", + "Build a body first": "Construya primero un cuerpo", + "How the bodies are drawn": "Cómo se dibujan los cuerpos", + "Draw the bodies transparent — the scan, the elements and the bodies inside them show through. With the scan solid as well, each part shows where it stands proud of the other": "Dibuja los cuerpos transparentes — el escaneo, los elementos y los cuerpos que hay dentro se ven a través. Con el escaneo también sólido, cada pieza muestra dónde sobresale de la otra", + "Draw where two bodies run into each other in hatched red — what they share, over the faces of both that would otherwise fight for the same pixels. Off for bodies that overlap on purpose, a modifier volume for a slicer; saved with the project": "Dibuja en rojo rayado donde dos cuerpos se interpenetran — lo que comparten, sobre las caras de ambos que de otro modo se disputarían los mismos píxeles. Desactívelo para cuerpos que se solapan a propósito, un volumen modificador para un slicer; se guarda con el proyecto", + "How the edges are drawn where a round runs into a flat. A round face's seam and the cut a join leaves across one surface are never drawn": "Cómo se dibujan las aristas donde una cara redonda se encuentra con una plana. La costura de una cara redonda y el corte que deja una unión sobre una superficie nunca se dibujan", + "Tangent edges drawn — where a round runs into a flat — as the sharp ones are, the way CAD draws them": "Aristas tangentes dibujadas — donde una cara redonda se encuentra con una plana — igual que las vivas, como las dibuja el CAD", + "Tangent edges dimmed": "Aristas tangentes atenuadas", + "Tangent edges left out, and not picked: a round shows by its outline against the background": "Aristas tangentes omitidas y no seleccionables: una cara redonda se ve por su contorno contra el fondo", + "Nothing measured yet — fit elements or cut sections in the 3D Measure workspace and they show here": "Nada medido todavía — ajuste elementos o corte secciones en el espacio de Medición 3D y aparecerán aquí", + "Build a body first — the map reads the scan against it": "Construya primero un cuerpo — el mapa lee el escaneo respecto a él", + "Tools": "Herramientas", + "Reduce mesh": "Reducir malla", + "Remove islands": "Eliminar islas", + "Delete marked": "Eliminar marcados", + "Flip normals": "Invertir normales", + "Cut": "Cortar", + "Fill holes": "Rellenar agujeros", + "Smooth": "Suavizar", + "Shrink/expand": "Contraer/expandir", + "Boolean": "Booleana", + "Trim to scan": "Recortar al escaneo", + "Shell/hollow": "Vaciado/hueco", + "Tolerance": "Tolerancia", + "Triangle limit": "Límite de triángulos", + "Reduce": "Reducir", + "Both figures shape the reduction, so changing them means reducing again.": "Ambos valores definen la reducción, por lo que cambiarlos exige reducir de nuevo.", + "Cleaning up and reducing a scan": "Limpieza y reducción de un escaneo", + "Load a scan and clean it up — clear the stray islands, delete what is not the part, turn it the right way out, cut off the base it stood on, fill its holes, smooth out the scatter, expand or shrink it, hollow it — and reduce it to a light mesh to model over in CAD, held within a tolerance you set, measured against the scan to prove it, and exported where you aligned it. STL, PLY, OBJ or 3MF, in millimetres — everything stays in this browser.": "Cargue un escaneo y límpielo — elimine las islas sueltas, borre lo que no es la pieza, déle la vuelta al derecho, corte la base en que se apoyaba, rellene sus agujeros, suavice el ruido, expándalo o contráigalo, vacíelo — y redúzcalo a una malla ligera para modelar sobre ella en CAD, dentro de una tolerancia que usted defina, medida contra el escaneo para comprobarlo, y exportada donde la alineó. STL, PLY, OBJ o 3MF, en milímetros — todo permanece en esta aplicación.", + "Reduced": "Reducida", + "Accuracy": "Precisión", + "More about Mesh tools": "Más sobre las herramientas de malla", + "Mesh tool": "Herramienta de malla", + "A light copy of the scan to model over in CAD, held within a tolerance you set — the scan itself stays as it is": "Una copia ligera del escaneo para modelar sobre ella en CAD, dentro de una tolerancia que usted defina — el escaneo en sí permanece como está", + "Find the specks floating off the scan — every piece joined to nothing else and smaller than a size you set — mark them on the part, and delete them once you say so": "Encuentra las motas sueltas del escaneo — cada fragmento sin unión con nada más y menor que un tamaño que usted defina — las marca sobre la pieza y las elimina cuando usted lo indique", + "Mark surface on the part by hand — a window, a brush or a lasso — and delete it from the scan: a fixture, a clamp, a patch of the table": "Marque superficie sobre la pieza a mano — una ventana, un pincel o un lazo — y elimínela del escaneo: una fijación, una abrazadera, un trozo de la mesa", + "Turn the surface over so it faces the other way — the whole part, exported inside-out, or a patch you mark that faces in": "Da la vuelta a la superficie para que mire hacia el otro lado — la pieza entera, exportada del revés, o una zona marcada que mira hacia dentro", + "Cut the scan with a plane and take one side off — the turntable it stood on, or half the part — closing it flat where the plane went through": "Corta el escaneo con un plano y retira uno de los lados — el plato giratorio sobre el que estaba, o la mitad de la pieza — cerrándolo plano por donde pasó el plano", + "Close the holes a scanner left, every one smaller than a size you set, with patches that carry the surface across": "Cierra los agujeros que dejó el escáner, todos los menores que un tamaño que usted defina, con parches que continúan la superficie", + "Take the scanner’s scatter out of the surface, never moving any point further than a limit you set, sharp edges held": "Elimina el ruido del escáner de la superficie, sin mover ningún punto más allá de un límite que usted defina, conservando las aristas vivas", + "Move the surface along its normal by a distance — out or in, the whole part or a marked region — not a scale: a bore gets narrower as the part grows": "Mueve la superficie a lo largo de su normal una distancia — hacia fuera o hacia dentro, la pieza entera o una región marcada — no es una escala: un taladro se estrecha a medida que la pieza crece", + "Take a solid body out of the scan, add it, or keep what the two share — a bore the scanner could not see to the bottom of, cut out with a cylinder modelled in Reverse engineer": "Resta un cuerpo sólido del escaneo, lo suma, o conserva lo que ambos comparten — un taladro cuyo fondo el escáner no pudo ver, cortado con un cilindro modelado en Ingeniería inversa", + "Cut a body modelled to mend a broken part to fit the scanned break — the missing piece, to print and glue in": "Corta un cuerpo modelado para reparar una pieza rota de modo que encaje con la rotura escaneada — la pieza faltante, para imprimir y pegar", + "Hollow a closed part, leaving a wall of a thickness you set round a sealed cavity — or thicken an open surface into a solid": "Vacía una pieza cerrada, dejando una pared de un espesor que usted defina alrededor de una cavidad sellada — o engrosa una superficie abierta hasta convertirla en sólido", + "More about Reducing a scan for CAD": "Más sobre reducir un escaneo para CAD", + "More about Tolerance": "Más sobre la tolerancia", + "More about Triangle limit": "Más sobre el límite de triángulos", + "What to show of the reduction": "Qué mostrar de la reducción", + "Reduce the scan first — the view keys show the result": "Reduzca primero el escaneo — las teclas de vista muestran el resultado", + "Edit the scan first — the deviation shows how far the edited copy is from the scan in use": "Edite primero el escaneo — la desviación muestra cuánto se aleja la copia editada del escaneo en uso", + "How the instrument looks and behaves. Remembered in this browser — nothing here is a property of the part, and none of it is saved with a project.": "Cómo se ve y se comporta el instrumento. Se recuerda en esta aplicación — nada de esto es propiedad de la pieza, y nada se guarda con el proyecto.", + "Units": "Unidades", + "Lengths in": "Longitudes en", + "What is shown and typed. Files, projects and agent answers stay in millimetres.": "Lo que se muestra y se escribe. Los archivos, proyectos y respuestas de agentes permanecen en milímetros.", + "Appearance": "Apariencia", + "Interface": "Interfaz", + "Light": "Claro", + "Dark": "Oscuro", + "System": "Sistema", + "Light or dark chassis.": "Chasis claro u oscuro.", + "follows the operating system's setting, and changes with it.": "sigue el ajuste del sistema operativo y cambia con él.", + "Colour mode": "Modo de color", + "Studio grey": "Gris estudio", + "Scanner blue": "Azul escáner", + "Glossy blue under a hard light — surface detail stands out": "Azul brillante bajo luz dura — los detalles de la superficie destacan", + ". What has been measured keeps its colour either way — the element tints and the deviation ramp are the same in both.": ". Lo medido conserva su color en cualquier caso — los tonos de los elementos y la rampa de desviación son los mismos en ambos.", + "Colour map": "Mapa de color", + "Blue through cyan, green and yellow to red, a saturated green in the middle — the inspection-software classic. Uneven in lightness, so cyan and yellow show as bands the part does not have": "Del azul al rojo pasando por cian, verde y amarillo, con un verde saturado en el medio — el clásico del software de inspección. Irregular en luminosidad, por lo que el cian y el amarillo aparecen como bandas que la pieza no tiene", + ". Every map painted on the part, and the scale beside it, is drawn in it; the block at either end is what a reading past that end of the scale wears.": ". Todo mapa pintado sobre la pieza, y la escala junto a él, se dibuja con él; el bloque de cada extremo es el color de una lectura más allá de ese extremo de la escala.", + "Sharp edges": "Aristas vivas", + "Always sharp": "Siempre vivas", + "Auto — sharp on a CAD-like mesh": "Auto — vivas en malla tipo CAD", + "Always smooth": "Siempre suaves", + "Sharp from": "Viva desde", + "Whether an edge of the scan is shaded as a crease, each face with its own normal, or smoothed across so a box looks pillowed — and from what angle between its two faces an edge counts as sharp.": "Si una arista del escaneo se sombrea como un pliegue, cada cara con su propia normal, o se suaviza de lado a lado de modo que una caja parezca acolchada — y a partir de qué ángulo entre sus dos caras una arista cuenta como viva.", + "draws a CAD mesh's faces flat and a scan's real edges — the rim of a bore, a step — as edges; noise seldom tips a scan's triangles that far, and if it still shows, a larger angle, 50° or 60°, leaves it smooth.": "dibuja las caras de una malla CAD planas y las aristas reales de un escaneo — el borde de un taladro, un escalón — como aristas; el ruido rara vez inclina los triángulos de un escaneo tanto, y si aun así se nota, un ángulo mayor, 50° o 60°, lo deja suave.", + "Auto": "Auto", + "draws the creases only on a mesh that reads as a tessellation of CAD, a scan smooth all over. A reference part is always drawn sharp, from": "dibuja los pliegues solo en una malla que parece una teselación de CAD, y un escaneo liso por completo. Una pieza de referencia siempre se dibuja con aristas vivas, desde", + "Navigation": "Navegación", + "Mouse controls": "Controles del ratón", + "left-drag orbit · right-drag pan · scroll zoom": "arrastrar izquierdo: órbita · arrastrar derecho: desplazar · rueda: zoom", + "works beside any of these, in Chrome and Edge: push or pull the puck to zoom, slide it to pan, tilt, twist and roll it to turn the part. It connects the first time you touch it, and drives the view the mouse was last over.": "funciona junto con cualquiera de estos, en Chrome y Edge: empuje o tire del puck para hacer zoom, deslícelo para desplazar, inclínelo, gírelo y hágalo rodar para girar la pieza. Se conecta la primera vez que lo toca y controla la vista sobre la que estuvo el ratón por última vez.", + "Files": "Archivos", + "Ask what units an STL is in": "Preguntar en qué unidades está un STL", + "— the format carries none, and a part read at the wrong scale measures wrong in every number after. Off, every STL is read in the units below without asking.": "— el formato no incluye ninguna, y una pieza leída a la escala equivocada mide mal en todos los números posteriores. Desactivado, todo STL se lee en las unidades indicadas abajo sin preguntar.", + "STL import units": "Unidades de importación de STL", + "What an STL is read in when the question is off, and the answer the question offers first — the last one given. A PLY or OBJ is always read in millimetres; a STEP file says its own.": "La unidad en que se lee un STL cuando la pregunta está desactivada, y la respuesta que la pregunta ofrece primero — la última dada. Un PLY u OBJ siempre se lee en milímetros; un archivo STEP indica la suya.", + "Lines": "Líneas", + "Section cuts": "Cortes de sección", + "How heavy a section's cut is drawn on the part in 3D Measure. The curves measured on its sheet follow in proportion.": "Qué grosor tiene la línea de corte de una sección dibujada sobre la pieza en Medición 3D. Las curvas medidas en su hoja la siguen en proporción.", + "2D fitted curves": "Curvas ajustadas 2D", + "The lines, circles and splines fitted over a flatbed scan in 2D Measure, and the callouts drawn with them.": "Las líneas, círculos y splines ajustados sobre un escaneo de cama plana en Medición 2D, y las llamadas dibujadas con ellos.", + "2D edges": "Aristas 2D", + "The edge chains the curves are fitted to — found in the scan, or cut by a section.": "Las cadenas de aristas a las que se ajustan las curvas — halladas en el escaneo o cortadas por una sección.", + "Guidance": "Orientación", + "Guided hints": "Sugerencias guiadas", + "— ring the control to press next, until you have been through a workspace twice. Switching it back on starts the guidance over.": "— resalta el control que debe pulsar a continuación, hasta que haya pasado dos veces por un espacio de trabajo. Volver a activarlo reinicia la orientación.", + "Agents": "Agentes", + "Local agent connection": "Conexión con agente local", + "— let an AI agent on this computer (Claude Code, Claude Desktop, Cursor …) drive this tab through its ScanRuler tools, the scanruler-mcp server. You watch every step here and can undo it.": "— permite que un agente de IA en este equipo (Claude Code, Claude Desktop, Cursor …) controle esta pestaña mediante sus herramientas de ScanRuler, el servidor scanruler-mcp. Usted ve cada paso aquí y puede deshacerlo.", + "Your data no longer stays on this computer:": "Sus datos dejan de permanecer en este equipo:", + "what the agent reads — measurements, reports, pictures of the part, file names, notes — is processed by the agent and goes to its model’s provider, unless the model runs on this computer. ScanRuler itself sends nothing anywhere; the connection is to 127.0.0.1.": "lo que el agente lee — mediciones, informes, imágenes de la pieza, nombres de archivo, notas — es procesado por el agente y se envía al proveedor de su modelo, a menos que el modelo se ejecute en este equipo. ScanRuler no envía nada a ninguna parte; la conexión es con 127.0.0.1.", + "Port": "Puerto", + "Token": "Token", + "prints the port and the token, and a link that fills them in.": "muestra el puerto y el token, y un enlace que los rellena.", + "Support": "Apoyo", + "Support card": "Tarjeta de apoyo", + "— the thank-you in the corner of the stage, with the ways to give something back. Its × closes it for this visit; switched off here it stays away, the big one below with it.": "— el agradecimiento en la esquina de la pantalla, con las formas de retribuir. Su × la cierra durante esta visita; si se desactiva aquí, no aparece, y la grande de abajo tampoco.", + "Big card on saving": "Tarjeta grande al guardar", + "— the same thank-you in the middle of the screen after a save or an export, at most once in ten minutes. The file does not wait for it. Its \"Don't show this again for a week\" shows here as off until the week is over.": "— el mismo agradecimiento en medio de la pantalla tras guardar o exportar, como máximo una vez cada diez minutos. El archivo no la espera. Su \"No volver a mostrar durante una semana\" aparece aquí como desactivado hasta que termine la semana.", + "Dark blue through teal and green to yellow. Perceptually uniform — an even step in the reading is an even step in colour — and as legible to colour-blind eyes and in greyscale": "Azul oscuro pasando por verde azulado y verde hasta amarillo. Perceptualmente uniforme — un paso igual en la lectura es un paso igual en el color — e igualmente legible para personas daltónicas y en escala de grises", + "Black through purple and pink to pale cream. Perceptually uniform and colour-blind safe": "Negro pasando por púrpura y rosa hasta crema pálido. Perceptualmente uniforme y seguro para daltónicos", + "Indigo through magenta and orange to yellow — no black end. Perceptually uniform and colour-blind safe": "Índigo pasando por magenta y naranja hasta amarillo — sin extremo negro. Perceptualmente uniforme y seguro para daltónicos", + "Black through purple, red and orange to pale yellow. Perceptually uniform and colour-blind safe": "Negro pasando por púrpura, rojo y naranja hasta amarillo pálido. Perceptualmente uniforme y seguro para daltónicos", + "Navy through grey to yellow, made to look the same to red–green colour blindness as to anyone else. Perceptually uniform": "Azul marino pasando por gris hasta amarillo, hecho para verse igual para quien tiene daltonismo rojo–verde que para cualquier otra persona. Perceptualmente uniforme", + "Black through deep blue and teal to mint. Perceptually uniform and colour-blind safe": "Negro pasando por azul profundo y verde azulado hasta menta. Perceptualmente uniforme y seguro para daltónicos", + "Black through purple and red to cream. Perceptually uniform and colour-blind safe": "Negro pasando por púrpura y rojo hasta crema. Perceptualmente uniforme y seguro para daltónicos", + "A rainbow like jet, dark blue through green to dark red, but smooth — without the bands jet shows. Not for red–green colour blindness": "Un arcoíris como jet, de azul oscuro a rojo oscuro pasando por verde, pero suave — sin las bandas que muestra jet. No apto para daltonismo rojo–verde", + "Across": "A través de", + "Flip": "Invertir", + "What the plane is laid across: a coordinate plane through the part’s middle, or an element with a direction — a plane’s normal, the axis of a cylinder, cone or line, a circle’s normal. The half toward you is taken away.": "Sobre qué se coloca el plano: un plano de coordenadas por el centro de la pieza, o un elemento con dirección — la normal de un plano, el eje de un cilindro, cono o línea, la normal de un círculo. Se retira la mitad que está hacia usted.", + "Where along its direction the plane lies — drag the arrow on the part, or type it here": "Dónde, a lo largo de su dirección, queda el plano — arrastre la flecha sobre la pieza o escríbalo aquí", + "Take away the other half instead": "Retirar la otra mitad en su lugar", + "set otherwise in its menu": "definido de otro modo en su menú" +} diff --git a/desktop/i18n/fr.json b/desktop/i18n/fr.json new file mode 100644 index 00000000..0b03ed31 --- /dev/null +++ b/desktop/i18n/fr.json @@ -0,0 +1,385 @@ +{ + "_name": "Français", + "_menu": { + "file": "Fichier", + "open": "Ouvrir un scan…", + "quit": "Quitter", + "view": "Affichage", + "help": "Aide", + "hardware": "Matériel…", + "language": "Langue", + "watch": "Surveiller le dossier Creality Scan…", + "unwatch": "Arrêter de surveiller Creality Scan", + "fresh": "Nouveau Mesh.ply du projet", + "openIt": "Ouvrir dans ScanRuler", + "ignore": "Ignorer" + }, + "_hw": { + "title": "Matériel", + "gpu": "GPU", + "backendChosen": "Backend ANGLE choisi", + "backendRunning": "Backend ANGLE actif", + "renderer": "WebGL2 / moteur de rendu", + "webgpu": "WebGPU", + "cpu": "CPU (threads)", + "threads": "threads logiques", + "isolation": "Isolation cross-origin (threads WASM)", + "npu": "NPU / WebNN", + "versions": "Electron / Chromium", + "backend": "Backend graphique :", + "restart": "Redémarrer pour appliquer", + "changed": "Backend modifié : redémarrez pour appliquer.", + "yes": "oui", + "no": "non", + "available": "disponible", + "unavailable": "indisponible", + "default": "par défaut", + "experimental": "expérimental", + "webgpuWhy": "Sous Windows, Chromium n’offre WebGPU qu’avec le backend DirectX 11 ; ScanRuler ne l’utilise pas." + }, + "CNC Kitchen · 3D scan analysis ·": "CNC Kitchen · analyse de scans 3D ·", + "3D Measure": "Mesure 3D", + "Surface Deviation": "Écart de surface", + "Wall Thickness": "Épaisseur de paroi", + "2D Measure": "Mesure 2D", + "Reverse engineer": "Rétro-ingénierie", + "Mesh Tools": "Outils de maillage", + "No unsaved changes": "Aucune modification non enregistrée", + "New": "Nouveau", + "Project": "Projet", + "Save": "Enregistrer", + "Load": "Charger", + "Settings": "Réglages", + "Model": "Modèle", + "No scan loaded": "Aucun scan chargé", + "STL, PLY, OBJ, 3MF or STEP · mm": "STL, PLY, OBJ, 3MF ou STEP · mm", + "Open…": "Ouvrir…", + "Drop it anywhere in the window.": "Déposez-le n’importe où dans la fenêtre.", + "Alignment": "Alignement", + "Auto-align": "Alignement auto", + "Align part": "Aligner la pièce", + "Move / rotate by numbers": "Déplacer / pivoter par valeurs", + "Create element": "Créer un élément", + "Point": "Point", + "Line": "Ligne", + "Plane": "Plan", + "Sphere": "Sphère", + "Cylinder": "Cylindre", + "Cone": "Cône", + "Circle": "Cercle", + "Torus": "Tore", + "Section": "Coupe", + "Create dimensions": "Créer des cotes", + "New dimension": "Nouvelle cote", + "New tolerance": "Nouvelle tolérance", + "Fitting elements": "Ajustement d’éléments", + "Load a scan, then pick features on it to fit spheres, cylinders and planes and measure between them. STL, PLY, OBJ, 3MF or STEP, in millimetres — everything stays in this browser.": "Chargez un scan, puis sélectionnez-y des entités pour ajuster des sphères, des cylindres et des plans et mesurer entre eux. STL, PLY, OBJ, 3MF ou STEP, en millimètres — tout reste dans cette application.", + "Scan": "Scan", + "The part as measured": "La pièce telle que mesurée", + "Drop it here": "Déposez-le ici", + "Choose file…": "Choisir un fichier…", + "Drag & drop works anywhere in the window — the first empty slot takes it.": "Le glisser-déposer fonctionne partout dans la fenêtre — le premier emplacement vide le reçoit.", + "Thanks for using ScanRuler!": "Merci d’utiliser ScanRuler !", + "This tool is provided": "Cet outil est offert", + "completely free": "entièrement gratuitement", + "by CNC Kitchen. Once you have finished measuring, why not have a look at the store that keeps us making things like this?": "par CNC Kitchen. Une fois vos mesures terminées, pourquoi ne pas visiter la boutique qui nous permet de créer des outils comme celui-ci ?", + "Visit CNCKitchen.STORE": "Visiter CNCKitchen.STORE", + "Send a tip on PayPal": "Laisser un pourboire sur PayPal", + "Send a tip on Ko-fi": "Laisser un pourboire sur Ko-fi", + "Transparent": "Transparent", + "Mesh": "Maillage", + "Backfaces": "Faces arrière", + "Elements": "Éléments", + "Labels": "Étiquettes", + "READY": "PRÊT", + "IMPRINT": "MENTIONS LÉGALES", + "LOCAL ONLY": "LOCAL UNIQUEMENT", + "· FILES NEVER LEAVE YOUR BROWSER": "· LES FICHIERS NE QUITTENT JAMAIS VOTRE APPLICATION", + "Units of": "Unités de", + "An STL file carries no units — a 1 in it is whatever the program that wrote it meant. Say what this one is in and it is read in millimetres, like everything measured here.": "Un fichier STL ne contient aucune unité — un 1 y vaut ce qu’a voulu dire le programme qui l’a écrit. Indiquez celle de celui-ci et il sera lu en millimètres, comme tout ce qui est mesuré ici.", + "Coordinates in": "Coordonnées en", + "Millimetres": "Millimètres", + "Centimetres": "Centimètres", + "Metres": "Mètres", + "Inches": "Pouces", + "Don't ask again": "Ne plus demander", + "— read every STL in these units. Settings brings the question back.": "— lire tous les STL dans ces unités. Les Réglages rétablissent la question.", + "Cancel": "Annuler", + "Open": "Ouvrir", + "Your data will no longer stay on this computer": "Vos données ne resteront plus sur cet ordinateur", + "Without an agent, everything stays in this browser. With one connected, what the agent reads — measurements, reports, pictures of the part, file names, notes, and any file it opens itself — is processed by the agent and the AI model behind it.": "Sans agent, tout reste dans cette application. Avec un agent connecté, ce qu’il lit — mesures, rapports, images de la pièce, noms de fichiers, notes et tout fichier qu’il ouvre lui-même — est traité par l’agent et par le modèle d’IA qui le sous-tend.", + "Unless that model runs on this computer, all of this goes to the model’s provider": "À moins que ce modèle ne tourne sur cet ordinateur, tout cela est envoyé au fournisseur du modèle", + ", under that provider’s terms.": ", selon les conditions de ce fournisseur.", + "ScanRuler itself sends nothing anywhere: the connection only reaches the agent’s helper on this computer. Don’t connect an agent while you work on parts you may not share with its provider.": "ScanRuler lui-même n’envoie rien nulle part : la connexion n’atteint que l’assistant de l’agent sur cet ordinateur. Ne connectez pas d’agent lorsque vous travaillez sur des pièces que vous ne pouvez pas partager avec son fournisseur.", + "Asked once in this browser. Settings → Agents switches the connection off at any time.": "Demandé une seule fois dans cette application. Réglages → Agents coupe la connexion à tout moment.", + "Connect the agent": "Connecter l’agent", + "3D Measure\nFit spheres, cylinders and planes, and measure between them": "Mesure 3D\nAjustez sphères, cylindres et plans, et mesurez entre eux", + "Surface Deviation\nBest-fit the scan to a nominal part and map the difference": "Écart de surface\nAjustez au mieux le scan à une pièce nominale et cartographiez l’écart", + "Wall Thickness\nMap the wall thickness of the part itself — no reference needed": "Épaisseur de paroi\nCartographiez l’épaisseur de paroi de la pièce elle-même — sans référence", + "2D Measure\nMeasure a flatbed scan the way a measuring microscope would": "Mesure 2D\nMesurez un scan à plat comme le ferait un microscope de mesure", + "Reverse engineer\nSketch on sections, extrude and revolve, and export a solid STEP body built over the scan": "Rétro-ingénierie\nEsquissez sur des coupes, extrudez, créez des révolutions et exportez un corps STEP solide construit sur le scan", + "Mesh Tools\nClean the scan up — islands, holes, scatter — expand, shrink or hollow it, and reduce it to a light mesh for CAD held within a tolerance you set": "Outils de maillage\nNettoyez le scan — îlots, trous, bruit — dilatez, réduisez ou évidez-le, et simplifiez-le en un maillage léger pour la CAO dans une tolérance que vous définissez", + "The project's name — what Save Project saves it under and every export is named on. Unnamed, they go by the scan or the image it holds.": "Le nom du projet — sous lequel Enregistrer le projet l’enregistre et qui nomme chaque export. Sans nom, ils reprennent celui du scan ou de l’image qu’il contient.", + "Project name": "Nom du projet", + "Untitled project": "Projet sans titre", + "Checkpoints are stored only in this browser. Checkpoints and project files include completed work; finish active drawings before leaving. Save Project downloads a portable copy.": "Les points de contrôle ne sont conservés que dans cette application. Les points de contrôle et les fichiers de projet incluent le travail terminé ; terminez les dessins en cours avant de quitter. Enregistrer le projet télécharge une copie portable.", + "Undo — Ctrl+Z": "Annuler — Ctrl+Z", + "Undo": "Annuler", + "Redo — Ctrl+Y / Ctrl+Shift+Z": "Rétablir — Ctrl+Y / Ctrl+Shift+Z", + "Redo": "Rétablir", + "Start a new project: the scan, the reference and the image closed, every measurement and every workspace's work let go, the name taken off — after asking, when there are changes not saved": "Démarrer un nouveau projet : scan, référence et image fermés, toutes les mesures et le travail de chaque espace de travail abandonnés, nom retiré — avec confirmation, s’il y a des modifications non enregistrées", + "Save the scan, the reference, the image and every measurement as one .scanruler file": "Enregistrer le scan, la référence, l’image et toutes les mesures dans un seul fichier .scanruler", + "Open a .scanruler project — or a plain scan or image to start fresh": "Ouvrir un projet .scanruler — ou un simple scan ou une image pour repartir de zéro", + "Interface theme, colour mode, mouse controls, line widths and guided hints": "Thème de l’interface, mode couleur, commandes de la souris, épaisseurs de trait et conseils guidés", + "Source code of the open-source core (AGPL-3.0-only) on GitHub": "Code source du noyau open source (AGPL-3.0-only) sur GitHub", + "More about The scan": "En savoir plus sur le scan", + "More about Alignment": "En savoir plus sur l’alignement", + "More about Elements": "En savoir plus sur les éléments", + "Cut the scan with a plane, to measure the cut in the 2D Measure workspace": "Couper le scan avec un plan, pour mesurer la coupe dans l’espace Mesure 2D", + "More about Dimensions": "En savoir plus sur les cotes", + "More about Geometric tolerances": "En savoir plus sur les tolérances géométriques", + "Fit to view — bring the whole model back on screen, without turning it": "Ajuster à la vue — ramène tout le modèle à l’écran, sans le faire pivoter", + "Fit to view": "Ajuster à la vue", + "Close": "Fermer", + "Load a scan first": "Chargez d’abord un scan", + "How the scan is drawn": "Affichage du scan", + "Draw the scan transparent — what lies inside it, the fitted elements and the pinned readings show instead of hiding behind the surface in front of them": "Affiche le scan en transparence — ce qui se trouve à l’intérieur, les éléments ajustés et les valeurs épinglées apparaissent au lieu de se cacher derrière la surface qui les précède", + "Draw the triangle edges on the scan, to see how fine it is and where it is patchy. The mesh comes up as you zoom in: from a distance the triangles are smaller than a pixel, and the edges fade out rather than turning the part black": "Trace les arêtes des triangles sur le scan, pour voir sa finesse et où il est irrégulier. Le maillage apparaît au fil du zoom : de loin, les triangles sont plus petits qu’un pixel et les arêtes s’estompent plutôt que de noircir la pièce", + "Colour the far side of every triangle — holes in the scan and inverted normals stop looking like solid part": "Colore l’envers de chaque triangle — les trous du scan et les normales inversées cessent de ressembler à de la matière pleine", + "Nothing measured yet — fit an element or cut a section and it shows here": "Rien n’est encore mesuré — ajustez un élément ou créez une coupe et il apparaîtra ici", + "How the elements are drawn": "Affichage des éléments", + "Show the name tags and readouts on the part. The fitted elements, the sections and the dimension lines stay either way": "Affiche les étiquettes de nom et les valeurs sur la pièce. Les éléments ajustés, les coupes et les lignes de cote restent affichés dans tous les cas", + "Cut the parts open at a plane to look inside — the scan, the reference and everything measured on them. Closed parts are closed where they are cut; an open scan stays a shell. Slide the plane with the arrow, tilt it with the rings": "Coupe les pièces selon un plan pour voir l’intérieur — le scan, la référence et tout ce qui y est mesuré. Les pièces fermées sont refermées à la coupe ; un scan ouvert reste une coque. Faites glisser le plan avec la flèche, inclinez-le avec les anneaux", + "Measurement": "Mesure", + "Method": "Méthode", + "Ray along the normal": "Rayon suivant la normale", + "Sphere across the wall": "Sphère à travers la paroi", + "Max. thickness": "Épaisseur max.", + "Rays": "Rayons", + "Single ray — fast": "Rayon unique — rapide", + "6-ray cone — tighter": "Cône de 6 rayons — plus précis", + "12-ray cone — tightest": "Cône de 12 rayons — le plus précis", + "Far surface must face back": "La surface opposée doit être orientée vers l’arrière", + "Max. deviation of normals": "Écart max. des normales", + "Measure wall thickness": "Mesurer l’épaisseur de paroi", + "Everything above shapes the search, so changing it means measuring again.": "Tout ce qui précède définit la recherche : le modifier impose donc de mesurer à nouveau.", + "Wall thickness": "Épaisseur de paroi", + "Load a scan and measure how thick its walls are, everywhere at once. No reference model, no alignment. STL, PLY, OBJ, 3MF or STEP, in millimetres — everything stays in this browser.": "Chargez un scan et mesurez l’épaisseur de ses parois, partout à la fois. Sans modèle de référence, sans alignement. STL, PLY, OBJ, 3MF ou STEP, en millimètres — tout reste dans cette application.", + "More about How thickness is measured": "En savoir plus sur la mesure de l’épaisseur", + "More about Max. thickness": "En savoir plus sur l’épaisseur max.", + "More about Rays": "En savoir plus sur les rayons", + "More about Far surface must face back": "En savoir plus sur la surface opposée orientée vers l’arrière", + "More about Max. deviation of normals": "En savoir plus sur l’écart max. des normales", + "Image": "Image", + "No image loaded": "Aucune image chargée", + "PNG or JPEG": "PNG ou JPEG", + "Measuring a flatbed scan": "Mesure d’un scan à plat", + "Scan the part face-down on a flatbed scanner and open the image — then fit points, lines and circles to its edges and measure between them, the way a measuring microscope does. PNG or JPEG, at the highest optical resolution you have. Everything stays in this browser.": "Numérisez la pièce face vers le bas sur un scanner à plat et ouvrez l’image — ajustez ensuite des points, des lignes et des cercles à ses contours et mesurez entre eux, comme le fait un microscope de mesure. PNG ou JPEG, à la résolution optique la plus élevée dont vous disposez. Tout reste dans cette application.", + "The flatbed scan of the part": "Le scan à plat de la pièce", + "More about What is measured": "En savoir plus sur ce qui est mesuré", + "CAD kernel ready — OCCT 8.0.1 via occt-wasm 5.7.1.": "Noyau CAO prêt — OCCT 8.0.1 via occt-wasm 5.7.1.", + "Parameters": "Paramètres", + "+ Add": "+ Ajouter", + "Create feature": "Créer une fonction", + "Sketch on": "Esquisse sur", + "Choose a plane…": "Choisir un plan…", + "Extrude": "Extruder", + "Revolve": "Révolution", + "Extract": "Extraire", + "Import STEP": "Importer STEP", + "Shell": "Coque", + "Fillet": "Congé", + "Chamfer": "Chanfrein", + "Draft": "Dépouille", + "Move face": "Déplacer la face", + "Delete face": "Supprimer la face", + "Hole": "Perçage", + "Thread": "Filetage", + "Cut by plane": "Couper par plan", + "Mirror": "Symétrie", + "Combine": "Combiner", + "Sheet": "Surface", + "Split": "Diviser", + "Loft": "Lissage", + "Sweep": "Balayage", + "Pattern": "Répétition", + "Move body": "Déplacer le corps", + "Offset body": "Décaler le corps", + "Fit surface": "Ajuster une surface", + "Patch network": "Réseau de patchs", + "Trim": "Tronquer", + "Thicken": "Épaissir", + "Start with a sketch: choose a coordinate plane above. No scan is needed to model.": "Commencez par une esquisse : choisissez un plan de coordonnées ci-dessus. Aucun scan n’est nécessaire pour modéliser.", + "Bodies": "Corps", + "XY plane": "Plan XY", + "XZ plane": "Plan XZ", + "YZ plane": "Plan YZ", + "Curvature": "Courbure", + "Overlaps": "Chevauchements", + "Tangent edges": "Arêtes tangentes", + "Drawn": "Tracées", + "Dimmed": "Atténuées", + "Off": "Désactivé", + "Deviation": "Écart", + "More about Reverse engineering": "En savoir plus sur la rétro-ingénierie", + "Show the parameters": "Afficher les paramètres", + "More about Parameters": "En savoir plus sur les paramètres", + "Add a parameter": "Ajouter un paramètre", + "More about Features": "En savoir plus sur les fonctions", + "Extrude a sketch's regions — pick them in the viewport": "Extrude les régions d’une esquisse — sélectionnez-les dans la vue", + "Revolve a sketch's regions about an axis": "Fait pivoter les régions d’une esquisse autour d’un axe", + "A measured plane, cylinder, cone or sphere as a body at its extents, extended as in 3D Measure — a boss or a pin without a sketch — or a shadow section as its block, the outline through the part's thickness": "Un plan, cylindre, cône ou sphère mesuré comme corps à ses étendues, prolongé comme dans Mesure 3D — un bossage ou un axe sans esquisse — ou une coupe d’ombre comme son bloc, le contour sur l’épaisseur de la pièce", + "Bring a STEP file in as bodies of its own — a bought-in part, a mating part, the CAD model to check the scan against. A body for every solid in the file, named as the file names its parts; Move body puts them where they belong": "Importe un fichier STEP sous forme de corps distincts — une pièce achetée, une pièce conjuguée, le modèle CAO avec lequel vérifier le scan. Un corps pour chaque solide du fichier, nommé comme le fichier nomme ses pièces ; Déplacer le corps les place où ils doivent être", + "Hollow the body to a wall — click the faces to open, or none for a closed hollow; the wall comes from the Thickness map when one was measured": "Évide le corps à une paroi — cliquez les faces à ouvrir, ou aucune pour un creux fermé ; la paroi provient de la carte d’épaisseur lorsqu’une a été mesurée", + "Round edges of the body — click them; the radius is read off the scan across the first": "Arrondit les arêtes du corps — cliquez-les ; le rayon est lu sur le scan à travers la première", + "Chamfer edges of the body — click them": "Chanfreine les arêtes du corps — cliquez-les", + "Lean faces of the body about a neutral plane — a moulding's draft, put on after the fact: click the flat face that stays, then the faces to lean": "Incline les faces du corps autour d’un plan neutre — la dépouille d’un moulage, ajoutée après coup : cliquez la face plane qui reste, puis les faces à incliner", + "Offset faces of the body by a distance — a flat face along its normal, a round face in radius; several picked together grow or shrink as a whole, a cylinder's wall and its ends. One face can be snapped onto the scan instead: pick it, take Snap to scan, and click the scan where it should lie": "Décale les faces du corps d’une distance — une face plane suivant sa normale, une face ronde en rayon ; plusieurs faces sélectionnées ensemble grandissent ou rétrécissent d’un bloc, la paroi d’un cylindre et ses extrémités. Une face peut aussi être plaquée sur le scan : sélectionnez-la, choisissez Plaquer sur le scan et cliquez le scan à l’endroit où elle doit se trouver", + "Delete faces of the body and heal the gap — a fillet, a chamfer, a hole or a boss taken out, the faces round it run on to close over where it was: click a face of it, and the rest of the round or hole comes with it": "Supprime des faces du corps et comble le vide — un congé, un chanfrein, un perçage ou un bossage retiré, les faces voisines se prolongent pour refermer son emplacement : cliquez une de ses faces, et le reste du congé ou du perçage suit", + "A hole through the body along a cylinder or circle element, or a round face of the body": "Un perçage à travers le corps le long d’un élément cylindre ou cercle, ou d’une face ronde du corps", + "A modeled screw thread on a round face of the body — a bolt's on a boss, a nut's in a bore: ISO metric, UNC or UNF, a 45° form that prints without supports, or a diameter and pitch of your own, with an offset for clearance when printing": "Un filetage modélisé sur une face ronde du corps — celui d’une vis sur un bossage, celui d’un écrou dans un alésage : métrique ISO, UNC ou UNF, un profil à 45° imprimable sans supports, ou un diamètre et un pas de votre choix, avec un décalage de jeu pour l’impression", + "Cut the body at a plane and keep one side": "Coupe le corps selon un plan et conserve un côté", + "Mirror the body about a plane, joined with the source": "Applique une symétrie du corps par rapport à un plan, unie à l’original", + "Join, cut or intersect two bodies made separately — or trim a sheet by a solid, or sew two sheets into one, before Thicken": "Réunit, soustrait ou intersecte deux corps créés séparément — ou tronque une surface par un solide, ou coud deux surfaces en une, avant Épaissir", + "A sketch's pieces — an open chain will do — extruded as a surface: a cutter to split a body with": "Les éléments d’une esquisse — une chaîne ouverte suffit — extrudés en surface : un outil de coupe pour diviser un corps", + "Split a body by a sheet and keep the fragments that lie on the scan — or trim a sheet by sheets or solids, keeping its pieces on the scan": "Divise un corps par une surface et conserve les fragments situés sur le scan — ou tronque une surface par des surfaces ou des solides, en conservant ses parties sur le scan", + "A solid through the profiles of two or more sketches on their own planes": "Un solide passant par les profils de deux esquisses ou plus sur leurs propres plans", + "A sketch's region swept along a chain of lines and arcs in another sketch — a bent bar, a handle, a rim that follows a curve": "La région d’une esquisse balayée le long d’une chaîne de lignes et d’arcs d’une autre esquisse — une barre pliée, une poignée, un rebord qui suit une courbe", + "Copies of the body about an axis, along a direction, or in a grid": "Copies du corps autour d’un axe, suivant une direction ou en grille", + "Move and turn bodies — the ones selected on the part or picked in the body list (Ctrl+click or Shift+click rows for several), or click them on the part: drag the gizmo's arrows and rings, or type the distances and angles. Tick Copy to move a copy and leave the body where it is": "Déplace et fait pivoter des corps — ceux sélectionnés sur la pièce ou choisis dans la liste des corps (Ctrl+clic ou Shift+clic sur les lignes pour plusieurs), ou cliquez-les sur la pièce : faites glisser les flèches et anneaux du manipulateur, ou saisissez les distances et angles. Cochez Copier pour déplacer une copie et laisser le corps où il est", + "Grow a body all round by a distance, or shrink it — kept beside it as a body of its own, a keep-out round a part or the pocket that holds it with clearance": "Agrandit un corps uniformément d’une distance, ou le réduit — conservé à côté comme corps distinct, une zone d’exclusion autour d’une pièce ou la poche qui la loge avec jeu", + "A smooth freeform surface fitted through a region of the scan — click faces or mark them — for the trim to close into a solid with the others": "Une surface libre lisse ajustée sur une région du scan — cliquez des faces ou marquez-les — pour que la troncature la referme en solide avec les autres", + "Curves drawn on the scan, and the regions they close filled with patches fitted to the scan together — tangent across every curve not marked a crease, sharing their edges: a sheet, or a solid where the network closes. For a freeform skin that folds back or wants more than one patch": "Des courbes tracées sur le scan, et les régions qu’elles ferment remplies de patchs ajustés ensemble au scan — tangents à travers chaque courbe non marquée comme arête vive, partageant leurs bords : une surface, ou un solide là où le réseau se ferme. Pour une peau libre qui se replie ou qui demande plus d’un patch", + "Close a set of fitted surfaces, sheets and measured planes, cylinders, cones and spheres into a solid: the faces they cut space into are kept where they lie on the scan and sewn": "Referme en solide un ensemble de surfaces ajustées, de surfaces et de plans, cylindres, cônes et sphères mesurés : les faces en lesquelles ils découpent l’espace sont conservées là où elles se trouvent sur le scan, puis cousues", + "Make a sheet or a fitted surface a solid of a thickness — a freeform thin shell's wall from its one skin, where a shell or an offset of a lofted body will not go": "Transforme une surface ou une surface ajustée en solide d’une certaine épaisseur — la paroi d’une coque mince libre à partir de sa seule peau, là où une coque ou un décalage d’un corps lissé ne fonctionne pas", + "The XY plane through the origin — click it in the viewport to sketch on it": "Le plan XY passant par l’origine — cliquez-le dans la vue pour esquisser dessus", + "Hide the XY plane": "Masquer le plan XY", + "The XZ plane through the origin — click it in the viewport to sketch on it": "Le plan XZ passant par l’origine — cliquez-le dans la vue pour esquisser dessus", + "Hide the XZ plane": "Masquer le plan XZ", + "The YZ plane through the origin — click it in the viewport to sketch on it": "Le plan YZ passant par l’origine — cliquez-le dans la vue pour esquisser dessus", + "Hide the YZ plane": "Masquer le plan YZ", + "Load a scan first — the map reads its surface": "Chargez d’abord un scan — la carte lit sa surface", + "Build a body first": "Construisez d’abord un corps", + "How the bodies are drawn": "Affichage des corps", + "Draw the bodies transparent — the scan, the elements and the bodies inside them show through. With the scan solid as well, each part shows where it stands proud of the other": "Affiche les corps en transparence — le scan, les éléments et les corps qu’ils contiennent apparaissent au travers. Avec le scan également plein, chaque pièce montre où elle dépasse l’autre", + "Draw where two bodies run into each other in hatched red — what they share, over the faces of both that would otherwise fight for the same pixels. Off for bodies that overlap on purpose, a modifier volume for a slicer; saved with the project": "Trace en rouge hachuré les zones où deux corps s’interpénètrent — ce qu’ils partagent, sur les faces des deux qui se disputeraient sinon les mêmes pixels. Désactivé pour les corps qui se chevauchent volontairement, un volume modificateur pour un trancheur ; enregistré avec le projet", + "How the edges are drawn where a round runs into a flat. A round face's seam and the cut a join leaves across one surface are never drawn": "Affichage des arêtes là où une face ronde rejoint une face plane. La couture d’une face ronde et la coupure qu’une jonction laisse sur une surface ne sont jamais tracées", + "Tangent edges drawn — where a round runs into a flat — as the sharp ones are, the way CAD draws them": "Arêtes tangentes tracées — là où une face ronde rejoint une face plane — comme les arêtes vives, à la manière de la CAO", + "Tangent edges dimmed": "Arêtes tangentes atténuées", + "Tangent edges left out, and not picked: a round shows by its outline against the background": "Arêtes tangentes omises et non sélectionnables : une face ronde se distingue par son contour sur le fond", + "Nothing measured yet — fit elements or cut sections in the 3D Measure workspace and they show here": "Rien n’est encore mesuré — ajustez des éléments ou créez des coupes dans l’espace Mesure 3D et ils apparaîtront ici", + "Build a body first — the map reads the scan against it": "Construisez d’abord un corps — la carte lit le scan par rapport à lui", + "Tools": "Outils", + "Reduce mesh": "Réduire le maillage", + "Remove islands": "Supprimer les îlots", + "Delete marked": "Supprimer les marqués", + "Flip normals": "Inverser les normales", + "Cut": "Couper", + "Fill holes": "Boucher les trous", + "Smooth": "Lisser", + "Shrink/expand": "Réduire/dilater", + "Boolean": "Booléen", + "Trim to scan": "Tronquer au scan", + "Shell/hollow": "Coque/évider", + "Tolerance": "Tolérance", + "Triangle limit": "Limite de triangles", + "Reduce": "Réduire", + "Both figures shape the reduction, so changing them means reducing again.": "Les deux valeurs définissent la réduction : les modifier impose donc de réduire à nouveau.", + "Cleaning up and reducing a scan": "Nettoyage et réduction d’un scan", + "Load a scan and clean it up — clear the stray islands, delete what is not the part, turn it the right way out, cut off the base it stood on, fill its holes, smooth out the scatter, expand or shrink it, hollow it — and reduce it to a light mesh to model over in CAD, held within a tolerance you set, measured against the scan to prove it, and exported where you aligned it. STL, PLY, OBJ or 3MF, in millimetres — everything stays in this browser.": "Chargez un scan et nettoyez-le — supprimez les îlots isolés, effacez ce qui n’est pas la pièce, remettez-le à l’endroit, coupez la base sur laquelle il reposait, bouchez ses trous, lissez le bruit, dilatez ou réduisez-le, évidez-le — puis simplifiez-le en un maillage léger sur lequel modéliser en CAO, dans une tolérance que vous définissez, mesurée par rapport au scan pour le prouver, et exporté là où vous l’avez aligné. STL, PLY, OBJ ou 3MF, en millimètres — tout reste dans cette application.", + "Reduced": "Réduit", + "Accuracy": "Précision", + "More about Mesh tools": "En savoir plus sur les outils de maillage", + "Mesh tool": "Outil de maillage", + "A light copy of the scan to model over in CAD, held within a tolerance you set — the scan itself stays as it is": "Une copie légère du scan sur laquelle modéliser en CAO, dans une tolérance que vous définissez — le scan lui-même reste inchangé", + "Find the specks floating off the scan — every piece joined to nothing else and smaller than a size you set — mark them on the part, and delete them once you say so": "Repère les poussières flottant hors du scan — chaque morceau relié à rien d’autre et plus petit qu’une taille que vous définissez — les marque sur la pièce et les supprime quand vous le demandez", + "Mark surface on the part by hand — a window, a brush or a lasso — and delete it from the scan: a fixture, a clamp, a patch of the table": "Marquez à la main une surface sur la pièce — fenêtre, pinceau ou lasso — et supprimez-la du scan : un montage, une bride, un morceau de table", + "Turn the surface over so it faces the other way — the whole part, exported inside-out, or a patch you mark that faces in": "Retourne la surface pour qu’elle regarde de l’autre côté — la pièce entière, exportée à l’envers, ou une zone marquée orientée vers l’intérieur", + "Cut the scan with a plane and take one side off — the turntable it stood on, or half the part — closing it flat where the plane went through": "Coupe le scan avec un plan et retire un côté — le plateau tournant sur lequel il reposait, ou la moitié de la pièce — en le refermant à plat là où le plan est passé", + "Close the holes a scanner left, every one smaller than a size you set, with patches that carry the surface across": "Bouche les trous laissés par un scanner, tous ceux plus petits qu’une taille que vous définissez, avec des patchs qui prolongent la surface", + "Take the scanner’s scatter out of the surface, never moving any point further than a limit you set, sharp edges held": "Retire le bruit du scanner de la surface, sans déplacer aucun point au-delà d’une limite que vous définissez, arêtes vives conservées", + "Move the surface along its normal by a distance — out or in, the whole part or a marked region — not a scale: a bore gets narrower as the part grows": "Déplace la surface le long de sa normale d’une distance — vers l’extérieur ou l’intérieur, la pièce entière ou une région marquée — ce n’est pas une mise à l’échelle : un alésage se rétrécit quand la pièce grossit", + "Take a solid body out of the scan, add it, or keep what the two share — a bore the scanner could not see to the bottom of, cut out with a cylinder modelled in Reverse engineer": "Soustrait un corps solide du scan, l’ajoute, ou conserve ce que les deux ont en commun — un alésage dont le scanner n’a pas vu le fond, découpé avec un cylindre modélisé dans Rétro-ingénierie", + "Cut a body modelled to mend a broken part to fit the scanned break — the missing piece, to print and glue in": "Découpe un corps modélisé pour réparer une pièce cassée afin qu’il épouse la cassure scannée — la pièce manquante, à imprimer et à coller", + "Hollow a closed part, leaving a wall of a thickness you set round a sealed cavity — or thicken an open surface into a solid": "Évide une pièce fermée en laissant une paroi d’une épaisseur que vous définissez autour d’une cavité scellée — ou épaissit une surface ouverte en solide", + "More about Reducing a scan for CAD": "En savoir plus sur la réduction d’un scan pour la CAO", + "More about Tolerance": "En savoir plus sur la tolérance", + "More about Triangle limit": "En savoir plus sur la limite de triangles", + "What to show of the reduction": "Ce qu’il faut afficher de la réduction", + "Reduce the scan first — the view keys show the result": "Réduisez d’abord le scan — les touches de vue affichent le résultat", + "Edit the scan first — the deviation shows how far the edited copy is from the scan in use": "Modifiez d’abord le scan — l’écart montre de combien la copie modifiée s’éloigne du scan utilisé", + "How the instrument looks and behaves. Remembered in this browser — nothing here is a property of the part, and none of it is saved with a project.": "L’apparence et le comportement de l’instrument. Mémorisés dans cette application — rien ici n’est une propriété de la pièce, et rien n’est enregistré avec un projet.", + "Units": "Unités", + "Lengths in": "Longueurs en", + "What is shown and typed. Files, projects and agent answers stay in millimetres.": "Ce qui est affiché et saisi. Les fichiers, projets et réponses d’agents restent en millimètres.", + "Appearance": "Apparence", + "Interface": "Interface", + "Light": "Clair", + "Dark": "Sombre", + "System": "Système", + "Light or dark chassis.": "Châssis clair ou sombre.", + "follows the operating system's setting, and changes with it.": "suit le réglage du système d’exploitation et change avec lui.", + "Colour mode": "Mode couleur", + "Studio grey": "Gris studio", + "Scanner blue": "Bleu scanner", + "Glossy blue under a hard light — surface detail stands out": "Bleu brillant sous une lumière dure — les détails de surface ressortent", + ". What has been measured keeps its colour either way — the element tints and the deviation ramp are the same in both.": ". Ce qui a été mesuré garde sa couleur dans tous les cas — les teintes des éléments et la rampe d’écart sont les mêmes dans les deux.", + "Colour map": "Carte de couleurs", + "Blue through cyan, green and yellow to red, a saturated green in the middle — the inspection-software classic. Uneven in lightness, so cyan and yellow show as bands the part does not have": "Du bleu au rouge en passant par le cyan, le vert et le jaune, avec un vert saturé au milieu — le classique des logiciels d’inspection. Inégal en luminosité, si bien que le cyan et le jaune apparaissent comme des bandes que la pièce n’a pas", + ". Every map painted on the part, and the scale beside it, is drawn in it; the block at either end is what a reading past that end of the scale wears.": ". Toute carte peinte sur la pièce, et l’échelle à côté, est dessinée avec elle ; le bloc à chaque extrémité est la couleur d’une valeur au-delà de cette extrémité de l’échelle.", + "Sharp edges": "Arêtes vives", + "Always sharp": "Toujours vives", + "Auto — sharp on a CAD-like mesh": "Auto — vives sur un maillage de type CAO", + "Always smooth": "Toujours lisses", + "Sharp from": "Vive à partir de", + "Whether an edge of the scan is shaded as a crease, each face with its own normal, or smoothed across so a box looks pillowed — and from what angle between its two faces an edge counts as sharp.": "Si une arête du scan est ombrée comme un pli, chaque face avec sa propre normale, ou lissée de part en part, de sorte qu’une boîte paraisse bombée — et à partir de quel angle entre ses deux faces une arête compte comme vive.", + "draws a CAD mesh's faces flat and a scan's real edges — the rim of a bore, a step — as edges; noise seldom tips a scan's triangles that far, and if it still shows, a larger angle, 50° or 60°, leaves it smooth.": "trace à plat les faces d’un maillage CAO et les vraies arêtes d’un scan — le bord d’un alésage, un gradin — comme des arêtes ; le bruit incline rarement autant les triangles d’un scan, et s’il apparaît quand même, un angle plus grand, 50° ou 60°, le laisse lisse.", + "Auto": "Auto", + "draws the creases only on a mesh that reads as a tessellation of CAD, a scan smooth all over. A reference part is always drawn sharp, from": "trace les plis uniquement sur un maillage qui se lit comme une tessellation CAO, et un scan lisse partout. Une pièce de référence est toujours tracée avec arêtes vives, à partir de", + "Navigation": "Navigation", + "Mouse controls": "Commandes de la souris", + "left-drag orbit · right-drag pan · scroll zoom": "glisser gauche : orbite · glisser droit : panoramique · molette : zoom", + "works beside any of these, in Chrome and Edge: push or pull the puck to zoom, slide it to pan, tilt, twist and roll it to turn the part. It connects the first time you touch it, and drives the view the mouse was last over.": "fonctionne en complément de n’importe lequel de ceux-ci, dans Chrome et Edge : poussez ou tirez le bouton pour zoomer, faites-le glisser pour déplacer, inclinez-le, tournez-le et faites-le rouler pour faire pivoter la pièce. Il se connecte dès le premier contact et pilote la vue sur laquelle la souris s’est trouvée en dernier.", + "Files": "Fichiers", + "Ask what units an STL is in": "Demander en quelle unité est un STL", + "— the format carries none, and a part read at the wrong scale measures wrong in every number after. Off, every STL is read in the units below without asking.": "— le format n’en contient aucune, et une pièce lue à la mauvaise échelle est fausse dans tous les chiffres qui suivent. Désactivé, tout STL est lu dans les unités ci-dessous sans demander.", + "STL import units": "Unités d’import STL", + "What an STL is read in when the question is off, and the answer the question offers first — the last one given. A PLY or OBJ is always read in millimetres; a STEP file says its own.": "L’unité dans laquelle un STL est lu lorsque la question est désactivée, et la réponse que la question propose en premier — la dernière donnée. Un PLY ou un OBJ est toujours lu en millimètres ; un fichier STEP indique la sienne.", + "Lines": "Traits", + "Section cuts": "Traits de coupe", + "How heavy a section's cut is drawn on the part in 3D Measure. The curves measured on its sheet follow in proportion.": "Épaisseur du trait de coupe d’une section sur la pièce dans Mesure 3D. Les courbes mesurées sur sa feuille suivent proportionnellement.", + "2D fitted curves": "Courbes ajustées 2D", + "The lines, circles and splines fitted over a flatbed scan in 2D Measure, and the callouts drawn with them.": "Les lignes, cercles et splines ajustés sur un scan à plat dans Mesure 2D, et les annotations tracées avec eux.", + "2D edges": "Arêtes 2D", + "The edge chains the curves are fitted to — found in the scan, or cut by a section.": "Les chaînes d’arêtes auxquelles les courbes sont ajustées — trouvées dans le scan ou coupées par une section.", + "Guidance": "Guidage", + "Guided hints": "Conseils guidés", + "— ring the control to press next, until you have been through a workspace twice. Switching it back on starts the guidance over.": "— entoure la commande sur laquelle appuyer ensuite, jusqu’à ce que vous ayez parcouru deux fois un espace de travail. Le réactiver relance le guidage.", + "Agents": "Agents", + "Local agent connection": "Connexion à un agent local", + "— let an AI agent on this computer (Claude Code, Claude Desktop, Cursor …) drive this tab through its ScanRuler tools, the scanruler-mcp server. You watch every step here and can undo it.": "— laisse un agent IA sur cet ordinateur (Claude Code, Claude Desktop, Cursor …) piloter cette fenêtre via ses outils ScanRuler, le serveur scanruler-mcp. Vous voyez chaque étape ici et pouvez l’annuler.", + "Your data no longer stays on this computer:": "Vos données ne restent plus sur cet ordinateur :", + "what the agent reads — measurements, reports, pictures of the part, file names, notes — is processed by the agent and goes to its model’s provider, unless the model runs on this computer. ScanRuler itself sends nothing anywhere; the connection is to 127.0.0.1.": "ce que l’agent lit — mesures, rapports, images de la pièce, noms de fichiers, notes — est traité par l’agent et envoyé au fournisseur de son modèle, sauf si le modèle tourne sur cet ordinateur. ScanRuler lui-même n’envoie rien nulle part ; la connexion se fait vers 127.0.0.1.", + "Port": "Port", + "Token": "Jeton", + "prints the port and the token, and a link that fills them in.": "affiche le port et le jeton, ainsi qu’un lien qui les renseigne.", + "Support": "Soutien", + "Support card": "Carte de soutien", + "— the thank-you in the corner of the stage, with the ways to give something back. Its × closes it for this visit; switched off here it stays away, the big one below with it.": "— le remerciement dans le coin de la scène, avec les façons de rendre la pareille. Son × le ferme pour cette visite ; désactivée ici, elle reste absente, la grande ci-dessous aussi.", + "Big card on saving": "Grande carte à l’enregistrement", + "— the same thank-you in the middle of the screen after a save or an export, at most once in ten minutes. The file does not wait for it. Its \"Don't show this again for a week\" shows here as off until the week is over.": "— le même remerciement au milieu de l’écran après un enregistrement ou un export, au plus une fois toutes les dix minutes. Le fichier ne l’attend pas. Son « Ne plus afficher pendant une semaine » apparaît ici comme désactivé jusqu’à la fin de la semaine.", + "Dark blue through teal and green to yellow. Perceptually uniform — an even step in the reading is an even step in colour — and as legible to colour-blind eyes and in greyscale": "Bleu foncé passant par le bleu-vert et le vert jusqu’au jaune. Perceptuellement uniforme — un pas égal dans la valeur est un pas égal en couleur — et tout aussi lisible pour les daltoniens et en niveaux de gris", + "Black through purple and pink to pale cream. Perceptually uniform and colour-blind safe": "Noir passant par le violet et le rose jusqu’au crème pâle. Perceptuellement uniforme et adapté aux daltoniens", + "Indigo through magenta and orange to yellow — no black end. Perceptually uniform and colour-blind safe": "Indigo passant par le magenta et l’orange jusqu’au jaune — sans extrémité noire. Perceptuellement uniforme et adapté aux daltoniens", + "Black through purple, red and orange to pale yellow. Perceptually uniform and colour-blind safe": "Noir passant par le violet, le rouge et l’orange jusqu’au jaune pâle. Perceptuellement uniforme et adapté aux daltoniens", + "Navy through grey to yellow, made to look the same to red–green colour blindness as to anyone else. Perceptually uniform": "Bleu marine passant par le gris jusqu’au jaune, conçu pour paraître identique au daltonisme rouge–vert comme à tout autre. Perceptuellement uniforme", + "Black through deep blue and teal to mint. Perceptually uniform and colour-blind safe": "Noir passant par le bleu profond et le bleu-vert jusqu’à la menthe. Perceptuellement uniforme et adapté aux daltoniens", + "Black through purple and red to cream. Perceptually uniform and colour-blind safe": "Noir passant par le violet et le rouge jusqu’au crème. Perceptuellement uniforme et adapté aux daltoniens", + "A rainbow like jet, dark blue through green to dark red, but smooth — without the bands jet shows. Not for red–green colour blindness": "Un arc-en-ciel comme jet, du bleu foncé au rouge foncé en passant par le vert, mais lisse — sans les bandes que montre jet. Déconseillé pour le daltonisme rouge–vert", + "Across": "À travers", + "Flip": "Inverser", + "What the plane is laid across: a coordinate plane through the part’s middle, or an element with a direction — a plane’s normal, the axis of a cylinder, cone or line, a circle’s normal. The half toward you is taken away.": "Ce sur quoi le plan est posé : un plan de coordonnées passant par le milieu de la pièce, ou un élément doté d’une direction — la normale d’un plan, l’axe d’un cylindre, d’un cône ou d’une droite, la normale d’un cercle. La moitié tournée vers vous est retirée.", + "Where along its direction the plane lies — drag the arrow on the part, or type it here": "Où, le long de sa direction, se trouve le plan — faites glisser la flèche sur la pièce ou saisissez-le ici", + "Take away the other half instead": "Retirer plutôt l’autre moitié", + "set otherwise in its menu": "défini autrement dans son menu" +} diff --git a/desktop/i18n/it.json b/desktop/i18n/it.json new file mode 100644 index 00000000..c119fb2a --- /dev/null +++ b/desktop/i18n/it.json @@ -0,0 +1,385 @@ +{ + "_name": "Italiano", + "_menu": { + "file": "File", + "open": "Apri scansione…", + "quit": "Esci", + "view": "Visualizza", + "help": "Aiuto", + "hardware": "Hardware…", + "language": "Lingua", + "watch": "Monitora cartella Creality Scan…", + "unwatch": "Interrompi monitoraggio di Creality Scan", + "fresh": "Nuovo Mesh.ply dal progetto", + "openIt": "Apri in ScanRuler", + "ignore": "Ignora" + }, + "_hw": { + "title": "Hardware", + "gpu": "GPU", + "backendChosen": "Backend ANGLE scelto", + "backendRunning": "Backend ANGLE in esecuzione", + "renderer": "WebGL2 / renderer", + "webgpu": "WebGPU", + "cpu": "CPU (thread)", + "threads": "thread logici", + "isolation": "Isolamento cross-origin (thread WASM)", + "npu": "NPU / WebNN", + "versions": "Electron / Chromium", + "backend": "Backend grafico:", + "restart": "Riavvia per applicare", + "changed": "Backend modificato: riavviare per applicare.", + "yes": "sì", + "no": "no", + "available": "disponibile", + "unavailable": "non disponibile", + "default": "predefinito", + "experimental": "sperimentale", + "webgpuWhy": "Chromium su Windows offre WebGPU solo con il backend DirectX 11; ScanRuler non lo usa." + }, + "CNC Kitchen · 3D scan analysis ·": "CNC Kitchen · analisi di scansioni 3D ·", + "3D Measure": "Misura 3D", + "Surface Deviation": "Scostamento superficiale", + "Wall Thickness": "Spessore parete", + "2D Measure": "Misura 2D", + "Reverse engineer": "Reverse engineering", + "Mesh Tools": "Strumenti mesh", + "No unsaved changes": "Nessuna modifica non salvata", + "New": "Nuovo", + "Project": "Progetto", + "Save": "Salva", + "Load": "Carica", + "Settings": "Impostazioni", + "Model": "Modello", + "No scan loaded": "Nessuna scansione caricata", + "STL, PLY, OBJ, 3MF or STEP · mm": "STL, PLY, OBJ, 3MF o STEP · mm", + "Open…": "Apri…", + "Drop it anywhere in the window.": "Trascinalo in qualsiasi punto della finestra.", + "Alignment": "Allineamento", + "Auto-align": "Allinea auto", + "Align part": "Allinea pezzo", + "Move / rotate by numbers": "Sposta / ruota per valori", + "Create element": "Crea elemento", + "Point": "Punto", + "Line": "Linea", + "Plane": "Piano", + "Sphere": "Sfera", + "Cylinder": "Cilindro", + "Cone": "Cono", + "Circle": "Cerchio", + "Torus": "Toro", + "Section": "Sezione", + "Create dimensions": "Crea quote", + "New dimension": "Nuova quota", + "New tolerance": "Nuova tolleranza", + "Fitting elements": "Adattamento elementi", + "Load a scan, then pick features on it to fit spheres, cylinders and planes and measure between them. STL, PLY, OBJ, 3MF or STEP, in millimetres — everything stays in this browser.": "Carichi una scansione, poi selezioni le caratteristiche su di essa per adattare sfere, cilindri e piani e misurare tra di essi. STL, PLY, OBJ, 3MF o STEP, in millimetri — tutto resta in questa applicazione.", + "Scan": "Scansione", + "The part as measured": "Il pezzo come misurato", + "Drop it here": "Trascinalo qui", + "Choose file…": "Scegli file…", + "Drag & drop works anywhere in the window — the first empty slot takes it.": "Il trascinamento funziona in qualsiasi punto della finestra — lo riceve il primo spazio libero.", + "Thanks for using ScanRuler!": "Grazie per aver usato ScanRuler!", + "This tool is provided": "Questo strumento è offerto", + "completely free": "completamente gratis", + "by CNC Kitchen. Once you have finished measuring, why not have a look at the store that keeps us making things like this?": "da CNC Kitchen. Terminata la misura, perché non dare un'occhiata al negozio che ci permette di creare cose come questa?", + "Visit CNCKitchen.STORE": "Visita CNCKitchen.STORE", + "Send a tip on PayPal": "Invia una mancia su PayPal", + "Send a tip on Ko-fi": "Invia una mancia su Ko-fi", + "Transparent": "Trasparente", + "Mesh": "Mesh", + "Backfaces": "Facce posteriori", + "Elements": "Elementi", + "Labels": "Etichette", + "READY": "PRONTO", + "IMPRINT": "NOTE LEGALI", + "LOCAL ONLY": "SOLO LOCALE", + "· FILES NEVER LEAVE YOUR BROWSER": "· I FILE NON LASCIANO MAI LA VOSTRA APPLICAZIONE", + "Units of": "Unità di", + "An STL file carries no units — a 1 in it is whatever the program that wrote it meant. Say what this one is in and it is read in millimetres, like everything measured here.": "Un file STL non contiene unità — un 1 al suo interno vale ciò che intendeva il programma che l'ha scritto. Indichi l'unità di questo e verrà letto in millimetri, come tutto ciò che si misura qui.", + "Coordinates in": "Coordinate in", + "Millimetres": "Millimetri", + "Centimetres": "Centimetri", + "Metres": "Metri", + "Inches": "Pollici", + "Don't ask again": "Non chiedere più", + "— read every STL in these units. Settings brings the question back.": "— leggi ogni STL in queste unità. Le Impostazioni ripristinano la domanda.", + "Cancel": "Annulla", + "Open": "Apri", + "Your data will no longer stay on this computer": "I suoi dati non resteranno più su questo computer", + "Without an agent, everything stays in this browser. With one connected, what the agent reads — measurements, reports, pictures of the part, file names, notes, and any file it opens itself — is processed by the agent and the AI model behind it.": "Senza un agente, tutto resta in questa applicazione. Con un agente connesso, ciò che l'agente legge — misure, report, immagini del pezzo, nomi dei file, note e qualsiasi file che apre da sé — viene elaborato dall'agente e dal modello di IA che vi sta dietro.", + "Unless that model runs on this computer, all of this goes to the model’s provider": "A meno che quel modello non giri su questo computer, tutto ciò viene inviato al fornitore del modello", + ", under that provider’s terms.": ", secondo i termini di tale fornitore.", + "ScanRuler itself sends nothing anywhere: the connection only reaches the agent’s helper on this computer. Don’t connect an agent while you work on parts you may not share with its provider.": "ScanRuler stesso non invia nulla da nessuna parte: la connessione raggiunge solo l'helper dell'agente su questo computer. Non connetta un agente mentre lavora su pezzi che non può condividere con il suo fornitore.", + "Asked once in this browser. Settings → Agents switches the connection off at any time.": "Chiesto una sola volta in questa applicazione. Impostazioni → Agenti disattiva la connessione in qualsiasi momento.", + "Connect the agent": "Connetti l'agente", + "3D Measure\nFit spheres, cylinders and planes, and measure between them": "Misura 3D\nAdatti sfere, cilindri e piani e misuri tra di essi", + "Surface Deviation\nBest-fit the scan to a nominal part and map the difference": "Scostamento superficiale\nAdatti la scansione a un pezzo nominale e mappi la differenza", + "Wall Thickness\nMap the wall thickness of the part itself — no reference needed": "Spessore parete\nMappi lo spessore parete del pezzo stesso — nessun riferimento necessario", + "2D Measure\nMeasure a flatbed scan the way a measuring microscope would": "Misura 2D\nMisuri una scansione piana come farebbe un microscopio di misura", + "Reverse engineer\nSketch on sections, extrude and revolve, and export a solid STEP body built over the scan": "Reverse engineering\nDisegni schizzi su sezioni, estruda e riveli, ed esporti un corpo solido STEP costruito sulla scansione", + "Mesh Tools\nClean the scan up — islands, holes, scatter — expand, shrink or hollow it, and reduce it to a light mesh for CAD held within a tolerance you set": "Strumenti mesh\nPulisca la scansione — isole, fori, rumore — la espanda, riduca o svuoti, e la semplifichi in una mesh leggera per CAD entro una tolleranza da lei impostata", + "The project's name — what Save Project saves it under and every export is named on. Unnamed, they go by the scan or the image it holds.": "Il nome del progetto — con cui Salva progetto lo registra e con cui è denominata ogni esportazione. Senza nome, si usa quello della scansione o dell'immagine contenuta.", + "Project name": "Nome progetto", + "Untitled project": "Progetto senza titolo", + "Checkpoints are stored only in this browser. Checkpoints and project files include completed work; finish active drawings before leaving. Save Project downloads a portable copy.": "I punti di ripristino sono conservati solo in questa applicazione. Punti di ripristino e file di progetto includono il lavoro completato; termini i disegni in corso prima di uscire. Salva progetto scarica una copia portatile.", + "Undo — Ctrl+Z": "Annulla — Ctrl+Z", + "Undo": "Annulla", + "Redo — Ctrl+Y / Ctrl+Shift+Z": "Ripeti — Ctrl+Y / Ctrl+Shift+Z", + "Redo": "Ripeti", + "Start a new project: the scan, the reference and the image closed, every measurement and every workspace's work let go, the name taken off — after asking, when there are changes not saved": "Avvia un nuovo progetto: scansione, riferimento e immagine chiusi, ogni misura e il lavoro di ogni area di lavoro scartati, nome rimosso — dopo conferma, se ci sono modifiche non salvate", + "Save the scan, the reference, the image and every measurement as one .scanruler file": "Salva la scansione, il riferimento, l'immagine e ogni misura in un unico file .scanruler", + "Open a .scanruler project — or a plain scan or image to start fresh": "Apre un progetto .scanruler — o una semplice scansione o immagine per ricominciare", + "Interface theme, colour mode, mouse controls, line widths and guided hints": "Tema dell'interfaccia, modalità colore, controlli del mouse, spessori di linea e suggerimenti guidati", + "Source code of the open-source core (AGPL-3.0-only) on GitHub": "Codice sorgente del nucleo open source (AGPL-3.0-only) su GitHub", + "More about The scan": "Altro sulla scansione", + "More about Alignment": "Altro sull'allineamento", + "More about Elements": "Altro sugli elementi", + "Cut the scan with a plane, to measure the cut in the 2D Measure workspace": "Taglia la scansione con un piano, per misurare il taglio nell'area Misura 2D", + "More about Dimensions": "Altro sulle quote", + "More about Geometric tolerances": "Altro sulle tolleranze geometriche", + "Fit to view — bring the whole model back on screen, without turning it": "Adatta alla vista — riporta l'intero modello sullo schermo, senza ruotarlo", + "Fit to view": "Adatta alla vista", + "Close": "Chiudi", + "Load a scan first": "Carichi prima una scansione", + "How the scan is drawn": "Come viene disegnata la scansione", + "Draw the scan transparent — what lies inside it, the fitted elements and the pinned readings show instead of hiding behind the surface in front of them": "Disegna la scansione trasparente — ciò che si trova al suo interno, gli elementi adattati e le letture fissate si vedono invece di nascondersi dietro la superficie che hanno davanti", + "Draw the triangle edges on the scan, to see how fine it is and where it is patchy. The mesh comes up as you zoom in: from a distance the triangles are smaller than a pixel, and the edges fade out rather than turning the part black": "Disegna gli spigoli dei triangoli sulla scansione, per vedere quanto è fine e dove è irregolare. La mesh compare man mano che si ingrandisce: da lontano i triangoli sono più piccoli di un pixel e gli spigoli sfumano invece di annerire il pezzo", + "Colour the far side of every triangle — holes in the scan and inverted normals stop looking like solid part": "Colora il lato opposto di ogni triangolo — i fori nella scansione e le normali invertite non sembrano più parte solida", + "Nothing measured yet — fit an element or cut a section and it shows here": "Ancora nulla di misurato — adatti un elemento o tagli una sezione e comparirà qui", + "How the elements are drawn": "Come vengono disegnati gli elementi", + "Show the name tags and readouts on the part. The fitted elements, the sections and the dimension lines stay either way": "Mostra le etichette dei nomi e le letture sul pezzo. Gli elementi adattati, le sezioni e le linee di quota restano comunque", + "Cut the parts open at a plane to look inside — the scan, the reference and everything measured on them. Closed parts are closed where they are cut; an open scan stays a shell. Slide the plane with the arrow, tilt it with the rings": "Taglia i pezzi su un piano per guardarne l'interno — la scansione, il riferimento e tutto ciò che è misurato su di essi. I pezzi chiusi vengono chiusi dove sono tagliati; una scansione aperta resta un guscio. Faccia scorrere il piano con la freccia, lo inclini con gli anelli", + "Measurement": "Misura", + "Method": "Metodo", + "Ray along the normal": "Raggio lungo la normale", + "Sphere across the wall": "Sfera attraverso la parete", + "Max. thickness": "Spessore max.", + "Rays": "Raggi", + "Single ray — fast": "Raggio singolo — veloce", + "6-ray cone — tighter": "Cono a 6 raggi — più preciso", + "12-ray cone — tightest": "Cono a 12 raggi — il più preciso", + "Far surface must face back": "La superficie opposta deve essere rivolta all'indietro", + "Max. deviation of normals": "Deviazione max. delle normali", + "Measure wall thickness": "Misura spessore parete", + "Everything above shapes the search, so changing it means measuring again.": "Tutto quanto sopra definisce la ricerca, quindi modificarlo richiede di misurare di nuovo.", + "Wall thickness": "Spessore parete", + "Load a scan and measure how thick its walls are, everywhere at once. No reference model, no alignment. STL, PLY, OBJ, 3MF or STEP, in millimetres — everything stays in this browser.": "Carichi una scansione e misuri lo spessore delle sue pareti, in ogni punto contemporaneamente. Nessun modello di riferimento, nessun allineamento. STL, PLY, OBJ, 3MF o STEP, in millimetri — tutto resta in questa applicazione.", + "More about How thickness is measured": "Altro su come si misura lo spessore", + "More about Max. thickness": "Altro sullo spessore max.", + "More about Rays": "Altro sui raggi", + "More about Far surface must face back": "Altro sulla superficie opposta rivolta all'indietro", + "More about Max. deviation of normals": "Altro sulla deviazione max. delle normali", + "Image": "Immagine", + "No image loaded": "Nessuna immagine caricata", + "PNG or JPEG": "PNG o JPEG", + "Measuring a flatbed scan": "Misura di una scansione piana", + "Scan the part face-down on a flatbed scanner and open the image — then fit points, lines and circles to its edges and measure between them, the way a measuring microscope does. PNG or JPEG, at the highest optical resolution you have. Everything stays in this browser.": "Scansioni il pezzo a faccia in giù su uno scanner piano e apra l'immagine — poi adatti punti, linee e cerchi ai suoi bordi e misuri tra di essi, come fa un microscopio di misura. PNG o JPEG, alla massima risoluzione ottica disponibile. Tutto resta in questa applicazione.", + "The flatbed scan of the part": "La scansione piana del pezzo", + "More about What is measured": "Altro su ciò che viene misurato", + "CAD kernel ready — OCCT 8.0.1 via occt-wasm 5.7.1.": "Kernel CAD pronto — OCCT 8.0.1 tramite occt-wasm 5.7.1.", + "Parameters": "Parametri", + "+ Add": "+ Aggiungi", + "Create feature": "Crea feature", + "Sketch on": "Schizzo su", + "Choose a plane…": "Scegli un piano…", + "Extrude": "Estrudi", + "Revolve": "Rivoluzione", + "Extract": "Estrai", + "Import STEP": "Importa STEP", + "Shell": "Guscio", + "Fillet": "Raccordo", + "Chamfer": "Smusso", + "Draft": "Sformo", + "Move face": "Sposta faccia", + "Delete face": "Elimina faccia", + "Hole": "Foro", + "Thread": "Filettatura", + "Cut by plane": "Taglia con piano", + "Mirror": "Specchia", + "Combine": "Combina", + "Sheet": "Superficie", + "Split": "Dividi", + "Loft": "Loft", + "Sweep": "Sweep", + "Pattern": "Matrice", + "Move body": "Sposta corpo", + "Offset body": "Offset corpo", + "Fit surface": "Adatta superficie", + "Patch network": "Rete di patch", + "Trim": "Taglia", + "Thicken": "Ispessisci", + "Start with a sketch: choose a coordinate plane above. No scan is needed to model.": "Inizi con uno schizzo: scelga un piano di coordinate qui sopra. Per modellare non serve alcuna scansione.", + "Bodies": "Corpi", + "XY plane": "Piano XY", + "XZ plane": "Piano XZ", + "YZ plane": "Piano YZ", + "Curvature": "Curvatura", + "Overlaps": "Sovrapposizioni", + "Tangent edges": "Spigoli tangenti", + "Drawn": "Disegnati", + "Dimmed": "Attenuati", + "Off": "Disattivato", + "Deviation": "Scostamento", + "More about Reverse engineering": "Altro sul reverse engineering", + "Show the parameters": "Mostra i parametri", + "More about Parameters": "Altro sui parametri", + "Add a parameter": "Aggiungi un parametro", + "More about Features": "Altro sulle feature", + "Extrude a sketch's regions — pick them in the viewport": "Estrude le regioni di uno schizzo — le selezioni nella vista", + "Revolve a sketch's regions about an axis": "Ruota le regioni di uno schizzo attorno a un asse", + "A measured plane, cylinder, cone or sphere as a body at its extents, extended as in 3D Measure — a boss or a pin without a sketch — or a shadow section as its block, the outline through the part's thickness": "Un piano, cilindro, cono o sfera misurato come corpo alle sue estensioni, esteso come in Misura 3D — un risalto o uno spinotto senza schizzo — oppure una sezione d'ombra come suo blocco, il contorno lungo lo spessore del pezzo", + "Bring a STEP file in as bodies of its own — a bought-in part, a mating part, the CAD model to check the scan against. A body for every solid in the file, named as the file names its parts; Move body puts them where they belong": "Importa un file STEP come corpi propri — un componente commerciale, un pezzo di accoppiamento, il modello CAD con cui verificare la scansione. Un corpo per ogni solido del file, denominato come il file denomina le sue parti; Sposta corpo li porta dove devono stare", + "Hollow the body to a wall — click the faces to open, or none for a closed hollow; the wall comes from the Thickness map when one was measured": "Svuota il corpo fino a una parete — clicchi sulle facce da aprire, o su nessuna per un vuoto chiuso; la parete deriva dalla mappa dello spessore, se ne è stata misurata una", + "Round edges of the body — click them; the radius is read off the scan across the first": "Raccorda gli spigoli del corpo — vi clicchi sopra; il raggio è letto dalla scansione in corrispondenza del primo", + "Chamfer edges of the body — click them": "Smussa gli spigoli del corpo — vi clicchi sopra", + "Lean faces of the body about a neutral plane — a moulding's draft, put on after the fact: click the flat face that stays, then the faces to lean": "Inclina le facce del corpo attorno a un piano neutro — lo sformo di uno stampaggio, applicato a posteriori: clicchi sulla faccia piana che resta, poi sulle facce da inclinare", + "Offset faces of the body by a distance — a flat face along its normal, a round face in radius; several picked together grow or shrink as a whole, a cylinder's wall and its ends. One face can be snapped onto the scan instead: pick it, take Snap to scan, and click the scan where it should lie": "Sposta le facce del corpo di una distanza — una faccia piana lungo la normale, una faccia tonda nel raggio; più facce selezionate insieme crescono o si riducono nel complesso, la parete di un cilindro e le sue estremità. In alternativa una faccia può essere agganciata alla scansione: la selezioni, scelga Aggancia alla scansione e clicchi sulla scansione dove deve trovarsi", + "Delete faces of the body and heal the gap — a fillet, a chamfer, a hole or a boss taken out, the faces round it run on to close over where it was: click a face of it, and the rest of the round or hole comes with it": "Elimina le facce del corpo e richiude la lacuna — un raccordo, uno smusso, un foro o un risalto rimosso, le facce circostanti si prolungano per chiudere dove si trovava: clicchi su una sua faccia e il resto del raccordo o del foro viene con essa", + "A hole through the body along a cylinder or circle element, or a round face of the body": "Un foro attraverso il corpo lungo un elemento cilindro o cerchio, oppure una faccia tonda del corpo", + "A modeled screw thread on a round face of the body — a bolt's on a boss, a nut's in a bore: ISO metric, UNC or UNF, a 45° form that prints without supports, or a diameter and pitch of your own, with an offset for clearance when printing": "Una filettatura modellata su una faccia tonda del corpo — quella di una vite su un risalto, quella di un dado in un foro: metrica ISO, UNC o UNF, un profilo a 45° che si stampa senza supporti, oppure un diametro e un passo propri, con uno scostamento per il gioco in stampa", + "Cut the body at a plane and keep one side": "Taglia il corpo su un piano e mantiene un lato", + "Mirror the body about a plane, joined with the source": "Specchia il corpo rispetto a un piano, unito all'originale", + "Join, cut or intersect two bodies made separately — or trim a sheet by a solid, or sew two sheets into one, before Thicken": "Unisce, sottrae o interseca due corpi realizzati separatamente — oppure taglia una superficie con un solido, o cuce due superfici in una, prima di Ispessisci", + "A sketch's pieces — an open chain will do — extruded as a surface: a cutter to split a body with": "Le parti di uno schizzo — basta una catena aperta — estruse come superficie: un utensile di taglio per dividere un corpo", + "Split a body by a sheet and keep the fragments that lie on the scan — or trim a sheet by sheets or solids, keeping its pieces on the scan": "Divide un corpo con una superficie e mantiene i frammenti che giacciono sulla scansione — oppure taglia una superficie con superfici o solidi, mantenendone le parti sulla scansione", + "A solid through the profiles of two or more sketches on their own planes": "Un solido attraverso i profili di due o più schizzi sui rispettivi piani", + "A sketch's region swept along a chain of lines and arcs in another sketch — a bent bar, a handle, a rim that follows a curve": "La regione di uno schizzo fatta scorrere lungo una catena di linee e archi di un altro schizzo — una barra piegata, un'impugnatura, un bordo che segue una curva", + "Copies of the body about an axis, along a direction, or in a grid": "Copie del corpo attorno a un asse, lungo una direzione o in una griglia", + "Move and turn bodies — the ones selected on the part or picked in the body list (Ctrl+click or Shift+click rows for several), or click them on the part: drag the gizmo's arrows and rings, or type the distances and angles. Tick Copy to move a copy and leave the body where it is": "Sposta e ruota i corpi — quelli selezionati sul pezzo o scelti nell'elenco dei corpi (Ctrl+clic o Maiusc+clic sulle righe per più corpi), oppure li clicchi sul pezzo: trascini le frecce e gli anelli del gizmo, o digiti distanze e angoli. Spunti Copia per spostare una copia e lasciare il corpo dov'è", + "Grow a body all round by a distance, or shrink it — kept beside it as a body of its own, a keep-out round a part or the pocket that holds it with clearance": "Ingrandisce un corpo uniformemente di una distanza, o lo riduce — mantenuto accanto come corpo a sé, una zona di esclusione attorno a un pezzo o la tasca che lo alloggia con gioco", + "A smooth freeform surface fitted through a region of the scan — click faces or mark them — for the trim to close into a solid with the others": "Una superficie libera e regolare adattata a una regione della scansione — clicchi sulle facce o le marchi — perché il taglio la chiuda in un solido con le altre", + "Curves drawn on the scan, and the regions they close filled with patches fitted to the scan together — tangent across every curve not marked a crease, sharing their edges: a sheet, or a solid where the network closes. For a freeform skin that folds back or wants more than one patch": "Curve disegnate sulla scansione, e le regioni che delimitano riempite con patch adattate insieme alla scansione — tangenti lungo ogni curva non marcata come spigolo vivo, con spigoli condivisi: una superficie, o un solido dove la rete si chiude. Per una pelle libera che si ripiega o richiede più di una patch", + "Close a set of fitted surfaces, sheets and measured planes, cylinders, cones and spheres into a solid: the faces they cut space into are kept where they lie on the scan and sewn": "Chiude in un solido un insieme di superfici adattate, superfici e piani, cilindri, coni e sfere misurati: le facce in cui dividono lo spazio vengono mantenute dove giacciono sulla scansione e cucite", + "Make a sheet or a fitted surface a solid of a thickness — a freeform thin shell's wall from its one skin, where a shell or an offset of a lofted body will not go": "Trasforma una superficie o una superficie adattata in un solido di un certo spessore — la parete di un guscio sottile libero a partire dalla sua unica pelle, dove un guscio o un offset di un corpo loft non è applicabile", + "The XY plane through the origin — click it in the viewport to sketch on it": "Il piano XY passante per l'origine — vi clicchi nella vista per disegnarvi uno schizzo", + "Hide the XY plane": "Nascondi il piano XY", + "The XZ plane through the origin — click it in the viewport to sketch on it": "Il piano XZ passante per l'origine — vi clicchi nella vista per disegnarvi uno schizzo", + "Hide the XZ plane": "Nascondi il piano XZ", + "The YZ plane through the origin — click it in the viewport to sketch on it": "Il piano YZ passante per l'origine — vi clicchi nella vista per disegnarvi uno schizzo", + "Hide the YZ plane": "Nascondi il piano YZ", + "Load a scan first — the map reads its surface": "Carichi prima una scansione — la mappa ne legge la superficie", + "Build a body first": "Costruisca prima un corpo", + "How the bodies are drawn": "Come vengono disegnati i corpi", + "Draw the bodies transparent — the scan, the elements and the bodies inside them show through. With the scan solid as well, each part shows where it stands proud of the other": "Disegna i corpi trasparenti — la scansione, gli elementi e i corpi al loro interno si vedono attraverso. Con la scansione solida, ogni pezzo mostra dove sporge rispetto all'altro", + "Draw where two bodies run into each other in hatched red — what they share, over the faces of both that would otherwise fight for the same pixels. Off for bodies that overlap on purpose, a modifier volume for a slicer; saved with the project": "Disegna in rosso tratteggiato dove due corpi si compenetrano — ciò che condividono, sulle facce di entrambi che altrimenti si contenderebbero gli stessi pixel. Disattivare per corpi che si sovrappongono di proposito, un volume modificatore per uno slicer; salvato con il progetto", + "How the edges are drawn where a round runs into a flat. A round face's seam and the cut a join leaves across one surface are never drawn": "Come vengono disegnati gli spigoli dove una faccia tonda incontra una piana. La giunzione di una faccia tonda e il taglio che un'unione lascia su una superficie non vengono mai disegnati", + "Tangent edges drawn — where a round runs into a flat — as the sharp ones are, the way CAD draws them": "Spigoli tangenti disegnati — dove una faccia tonda incontra una piana — come quelli vivi, nel modo in cui li disegna il CAD", + "Tangent edges dimmed": "Spigoli tangenti attenuati", + "Tangent edges left out, and not picked: a round shows by its outline against the background": "Spigoli tangenti omessi e non selezionabili: una faccia tonda si mostra con il suo contorno sullo sfondo", + "Nothing measured yet — fit elements or cut sections in the 3D Measure workspace and they show here": "Ancora nulla di misurato — adatti elementi o tagli sezioni nell'area Misura 3D e compariranno qui", + "Build a body first — the map reads the scan against it": "Costruisca prima un corpo — la mappa legge la scansione rispetto ad esso", + "Tools": "Strumenti", + "Reduce mesh": "Riduci mesh", + "Remove islands": "Rimuovi isole", + "Delete marked": "Elimina marcati", + "Flip normals": "Inverti normali", + "Cut": "Taglia", + "Fill holes": "Riempi fori", + "Smooth": "Leviga", + "Shrink/expand": "Riduci/espandi", + "Boolean": "Booleana", + "Trim to scan": "Taglia sulla scansione", + "Shell/hollow": "Guscio/svuota", + "Tolerance": "Tolleranza", + "Triangle limit": "Limite triangoli", + "Reduce": "Riduci", + "Both figures shape the reduction, so changing them means reducing again.": "Entrambi i valori definiscono la riduzione, quindi modificarli richiede di ridurre di nuovo.", + "Cleaning up and reducing a scan": "Pulizia e riduzione di una scansione", + "Load a scan and clean it up — clear the stray islands, delete what is not the part, turn it the right way out, cut off the base it stood on, fill its holes, smooth out the scatter, expand or shrink it, hollow it — and reduce it to a light mesh to model over in CAD, held within a tolerance you set, measured against the scan to prove it, and exported where you aligned it. STL, PLY, OBJ or 3MF, in millimetres — everything stays in this browser.": "Carichi una scansione e la pulisca — elimini le isole isolate, cancelli ciò che non fa parte del pezzo, la rivolti dal lato giusto, tagli la base su cui poggiava, riempia i fori, levighi il rumore, la espanda o riduca, la svuoti — e la semplifichi in una mesh leggera su cui modellare in CAD, entro una tolleranza da lei impostata, misurata rispetto alla scansione a riprova, ed esportata dove l'ha allineata. STL, PLY, OBJ o 3MF, in millimetri — tutto resta in questa applicazione.", + "Reduced": "Ridotta", + "Accuracy": "Precisione", + "More about Mesh tools": "Altro sugli strumenti mesh", + "Mesh tool": "Strumento mesh", + "A light copy of the scan to model over in CAD, held within a tolerance you set — the scan itself stays as it is": "Una copia leggera della scansione su cui modellare in CAD, entro una tolleranza da lei impostata — la scansione stessa resta com'è", + "Find the specks floating off the scan — every piece joined to nothing else and smaller than a size you set — mark them on the part, and delete them once you say so": "Trova i frammenti isolati della scansione — ogni pezzo non collegato ad altro e più piccolo di una dimensione da lei impostata — li marca sul pezzo e li elimina quando lo conferma", + "Mark surface on the part by hand — a window, a brush or a lasso — and delete it from the scan: a fixture, a clamp, a patch of the table": "Marchi a mano la superficie sul pezzo — con una finestra, un pennello o un lazo — e la elimini dalla scansione: un'attrezzatura, un morsetto, un tratto del tavolo", + "Turn the surface over so it faces the other way — the whole part, exported inside-out, or a patch you mark that faces in": "Rivolta la superficie in modo che guardi dall'altra parte — l'intero pezzo, esportato al rovescio, o un tratto marcato rivolto verso l'interno", + "Cut the scan with a plane and take one side off — the turntable it stood on, or half the part — closing it flat where the plane went through": "Taglia la scansione con un piano e rimuove un lato — il piatto girevole su cui poggiava, o metà del pezzo — chiudendola piana dove è passato il piano", + "Close the holes a scanner left, every one smaller than a size you set, with patches that carry the surface across": "Chiude i fori lasciati dallo scanner, tutti quelli più piccoli di una dimensione da lei impostata, con patch che continuano la superficie", + "Take the scanner’s scatter out of the surface, never moving any point further than a limit you set, sharp edges held": "Elimina il rumore dello scanner dalla superficie, senza spostare alcun punto oltre un limite da lei impostato, mantenendo gli spigoli vivi", + "Move the surface along its normal by a distance — out or in, the whole part or a marked region — not a scale: a bore gets narrower as the part grows": "Sposta la superficie lungo la normale di una distanza — verso l'esterno o l'interno, l'intero pezzo o una regione marcata — non è una scala: un foro si restringe man mano che il pezzo cresce", + "Take a solid body out of the scan, add it, or keep what the two share — a bore the scanner could not see to the bottom of, cut out with a cylinder modelled in Reverse engineer": "Sottrae un corpo solido dalla scansione, lo aggiunge, o mantiene ciò che i due condividono — un foro di cui lo scanner non ha visto il fondo, ricavato con un cilindro modellato in Reverse engineering", + "Cut a body modelled to mend a broken part to fit the scanned break — the missing piece, to print and glue in": "Taglia un corpo modellato per riparare un pezzo rotto in modo che combaci con la rottura scansionata — il pezzo mancante, da stampare e incollare", + "Hollow a closed part, leaving a wall of a thickness you set round a sealed cavity — or thicken an open surface into a solid": "Svuota un pezzo chiuso, lasciando una parete di spessore da lei impostato attorno a una cavità sigillata — oppure ispessisce una superficie aperta fino a farne un solido", + "More about Reducing a scan for CAD": "Altro sulla riduzione di una scansione per CAD", + "More about Tolerance": "Altro sulla tolleranza", + "More about Triangle limit": "Altro sul limite triangoli", + "What to show of the reduction": "Cosa mostrare della riduzione", + "Reduce the scan first — the view keys show the result": "Riduca prima la scansione — i tasti vista mostrano il risultato", + "Edit the scan first — the deviation shows how far the edited copy is from the scan in use": "Modifichi prima la scansione — lo scostamento mostra quanto la copia modificata si discosta dalla scansione in uso", + "How the instrument looks and behaves. Remembered in this browser — nothing here is a property of the part, and none of it is saved with a project.": "Come appare e si comporta lo strumento. Memorizzato in questa applicazione — nulla qui è una proprietà del pezzo e nulla viene salvato con il progetto.", + "Units": "Unità", + "Lengths in": "Lunghezze in", + "What is shown and typed. Files, projects and agent answers stay in millimetres.": "Ciò che viene mostrato e digitato. File, progetti e risposte degli agenti restano in millimetri.", + "Appearance": "Aspetto", + "Interface": "Interfaccia", + "Light": "Chiaro", + "Dark": "Scuro", + "System": "Sistema", + "Light or dark chassis.": "Telaio chiaro o scuro.", + "follows the operating system's setting, and changes with it.": "segue l'impostazione del sistema operativo e cambia con essa.", + "Colour mode": "Modalità colore", + "Studio grey": "Grigio studio", + "Scanner blue": "Blu scanner", + "Glossy blue under a hard light — surface detail stands out": "Blu lucido sotto una luce dura — i dettagli della superficie risaltano", + ". What has been measured keeps its colour either way — the element tints and the deviation ramp are the same in both.": ". Ciò che è stato misurato mantiene comunque il proprio colore — le tinte degli elementi e la scala degli scostamenti sono identiche in entrambe.", + "Colour map": "Mappa colori", + "Blue through cyan, green and yellow to red, a saturated green in the middle — the inspection-software classic. Uneven in lightness, so cyan and yellow show as bands the part does not have": "Dal blu al rosso passando per ciano, verde e giallo, con un verde saturo al centro — il classico dei software di ispezione. Irregolare in luminosità, quindi ciano e giallo appaiono come bande che il pezzo non ha", + ". Every map painted on the part, and the scale beside it, is drawn in it; the block at either end is what a reading past that end of the scale wears.": ". Ogni mappa dipinta sul pezzo, e la scala accanto, è disegnata con essa; il blocco a ciascuna estremità è il colore di una lettura oltre quell'estremità della scala.", + "Sharp edges": "Spigoli vivi", + "Always sharp": "Sempre vivi", + "Auto — sharp on a CAD-like mesh": "Auto — vivi su mesh di tipo CAD", + "Always smooth": "Sempre lisci", + "Sharp from": "Vivo da", + "Whether an edge of the scan is shaded as a crease, each face with its own normal, or smoothed across so a box looks pillowed — and from what angle between its two faces an edge counts as sharp.": "Se uno spigolo della scansione è ombreggiato come una piega, con ogni faccia con la propria normale, oppure levigato da parte a parte così che una scatola sembri imbottita — e da quale angolo tra le due facce uno spigolo conta come vivo.", + "draws a CAD mesh's faces flat and a scan's real edges — the rim of a bore, a step — as edges; noise seldom tips a scan's triangles that far, and if it still shows, a larger angle, 50° or 60°, leaves it smooth.": "disegna le facce di una mesh CAD piane e gli spigoli reali di una scansione — il bordo di un foro, un gradino — come spigoli; il rumore raramente inclina tanto i triangoli di una scansione e, se ancora si nota, un angolo maggiore, 50° o 60°, la lascia liscia.", + "Auto": "Auto", + "draws the creases only on a mesh that reads as a tessellation of CAD, a scan smooth all over. A reference part is always drawn sharp, from": "disegna le pieghe solo su una mesh che appare come una tassellazione CAD, e una scansione liscia ovunque. Un pezzo di riferimento è sempre disegnato con spigoli vivi, da", + "Navigation": "Navigazione", + "Mouse controls": "Controlli del mouse", + "left-drag orbit · right-drag pan · scroll zoom": "trascina sinistro: orbita · trascina destro: panoramica · rotellina: zoom", + "works beside any of these, in Chrome and Edge: push or pull the puck to zoom, slide it to pan, tilt, twist and roll it to turn the part. It connects the first time you touch it, and drives the view the mouse was last over.": "funziona insieme a qualsiasi di questi, in Chrome ed Edge: spinga o tiri il puck per ingrandire, lo faccia scorrere per la panoramica, lo inclini, giri e ruoti per far girare il pezzo. Si connette la prima volta che lo tocca e comanda la vista su cui si trovava per ultimo il mouse.", + "Files": "File", + "Ask what units an STL is in": "Chiedi in che unità è un STL", + "— the format carries none, and a part read at the wrong scale measures wrong in every number after. Off, every STL is read in the units below without asking.": "— il formato non ne contiene, e un pezzo letto nella scala sbagliata misura in modo errato in ogni numero successivo. Disattivato, ogni STL viene letto nelle unità qui sotto senza chiedere.", + "STL import units": "Unità di importazione STL", + "What an STL is read in when the question is off, and the answer the question offers first — the last one given. A PLY or OBJ is always read in millimetres; a STEP file says its own.": "L'unità in cui viene letto un STL quando la domanda è disattivata, e la risposta che la domanda propone per prima — l'ultima data. Un PLY o OBJ è sempre letto in millimetri; un file STEP indica la propria.", + "Lines": "Linee", + "Section cuts": "Tagli di sezione", + "How heavy a section's cut is drawn on the part in 3D Measure. The curves measured on its sheet follow in proportion.": "Quanto è spessa la linea di taglio di una sezione disegnata sul pezzo in Misura 3D. Le curve misurate sul suo foglio seguono in proporzione.", + "2D fitted curves": "Curve adattate 2D", + "The lines, circles and splines fitted over a flatbed scan in 2D Measure, and the callouts drawn with them.": "Le linee, i cerchi e le spline adattati su una scansione piana in Misura 2D, e le indicazioni disegnate con essi.", + "2D edges": "Spigoli 2D", + "The edge chains the curves are fitted to — found in the scan, or cut by a section.": "Le catene di spigoli a cui vengono adattate le curve — trovate nella scansione o tagliate da una sezione.", + "Guidance": "Guida", + "Guided hints": "Suggerimenti guidati", + "— ring the control to press next, until you have been through a workspace twice. Switching it back on starts the guidance over.": "— evidenzia il controllo da premere successivamente, finché non ha attraversato un'area di lavoro due volte. Riattivarlo fa ripartire la guida.", + "Agents": "Agenti", + "Local agent connection": "Connessione ad agente locale", + "— let an AI agent on this computer (Claude Code, Claude Desktop, Cursor …) drive this tab through its ScanRuler tools, the scanruler-mcp server. You watch every step here and can undo it.": "— consente a un agente di IA su questo computer (Claude Code, Claude Desktop, Cursor …) di controllare questa scheda tramite i suoi strumenti ScanRuler, il server scanruler-mcp. Lei segue ogni passaggio qui e può annullarlo.", + "Your data no longer stays on this computer:": "I suoi dati non restano più su questo computer:", + "what the agent reads — measurements, reports, pictures of the part, file names, notes — is processed by the agent and goes to its model’s provider, unless the model runs on this computer. ScanRuler itself sends nothing anywhere; the connection is to 127.0.0.1.": "ciò che l'agente legge — misure, report, immagini del pezzo, nomi dei file, note — viene elaborato dall'agente e inviato al fornitore del suo modello, a meno che il modello non giri su questo computer. ScanRuler stesso non invia nulla da nessuna parte; la connessione è verso 127.0.0.1.", + "Port": "Porta", + "Token": "Token", + "prints the port and the token, and a link that fills them in.": "stampa la porta e il token, e un link che li compila.", + "Support": "Sostegno", + "Support card": "Scheda di sostegno", + "— the thank-you in the corner of the stage, with the ways to give something back. Its × closes it for this visit; switched off here it stays away, the big one below with it.": "— il ringraziamento nell'angolo della scena, con i modi per ricambiare. La sua × lo chiude per questa visita; disattivato qui non compare più, e con esso quello grande qui sotto.", + "Big card on saving": "Scheda grande al salvataggio", + "— the same thank-you in the middle of the screen after a save or an export, at most once in ten minutes. The file does not wait for it. Its \"Don't show this again for a week\" shows here as off until the week is over.": "— lo stesso ringraziamento al centro dello schermo dopo un salvataggio o un'esportazione, al massimo una volta ogni dieci minuti. Il file non lo aspetta. Il suo \"Non mostrare più per una settimana\" appare qui come disattivato fino al termine della settimana.", + "Dark blue through teal and green to yellow. Perceptually uniform — an even step in the reading is an even step in colour — and as legible to colour-blind eyes and in greyscale": "Dal blu scuro al giallo passando per verde acqua e verde. Percettivamente uniforme — un passo uniforme nella lettura è un passo uniforme nel colore — e altrettanto leggibile per i daltonici e in scala di grigi", + "Black through purple and pink to pale cream. Perceptually uniform and colour-blind safe": "Dal nero al crema chiaro passando per viola e rosa. Percettivamente uniforme e sicuro per i daltonici", + "Indigo through magenta and orange to yellow — no black end. Perceptually uniform and colour-blind safe": "Dall'indaco al giallo passando per magenta e arancione — senza estremità nera. Percettivamente uniforme e sicuro per i daltonici", + "Black through purple, red and orange to pale yellow. Perceptually uniform and colour-blind safe": "Dal nero al giallo chiaro passando per viola, rosso e arancione. Percettivamente uniforme e sicuro per i daltonici", + "Navy through grey to yellow, made to look the same to red–green colour blindness as to anyone else. Perceptually uniform": "Dal blu marino al giallo passando per il grigio, concepito per apparire uguale a chi ha daltonismo rosso–verde come a chiunque altro. Percettivamente uniforme", + "Black through deep blue and teal to mint. Perceptually uniform and colour-blind safe": "Dal nero al verde menta passando per blu profondo e verde acqua. Percettivamente uniforme e sicuro per i daltonici", + "Black through purple and red to cream. Perceptually uniform and colour-blind safe": "Dal nero al crema passando per viola e rosso. Percettivamente uniforme e sicuro per i daltonici", + "A rainbow like jet, dark blue through green to dark red, but smooth — without the bands jet shows. Not for red–green colour blindness": "Un arcobaleno come jet, dal blu scuro al rosso scuro passando per il verde, ma uniforme — senza le bande che jet mostra. Non adatto al daltonismo rosso–verde", + "Across": "Attraverso", + "Flip": "Inverti", + "What the plane is laid across: a coordinate plane through the part’s middle, or an element with a direction — a plane’s normal, the axis of a cylinder, cone or line, a circle’s normal. The half toward you is taken away.": "Su che cosa viene posizionato il piano: un piano di coordinate passante per il centro del pezzo, oppure un elemento con una direzione — la normale di un piano, l'asse di un cilindro, cono o linea, la normale di un cerchio. La metà rivolta verso di lei viene rimossa.", + "Where along its direction the plane lies — drag the arrow on the part, or type it here": "In che punto lungo la sua direzione si trova il piano — trascini la freccia sul pezzo o lo digiti qui", + "Take away the other half instead": "Rimuovi invece l'altra metà", + "set otherwise in its menu": "impostato diversamente nel suo menu" +} diff --git a/desktop/i18n/ja.json b/desktop/i18n/ja.json new file mode 100644 index 00000000..f52db4b2 --- /dev/null +++ b/desktop/i18n/ja.json @@ -0,0 +1,385 @@ +{ + "_name": "日本語", + "_menu": { + "file": "ファイル", + "open": "スキャンを開く…", + "quit": "終了", + "view": "表示", + "help": "ヘルプ", + "hardware": "ハードウェア…", + "language": "言語", + "watch": "Creality Scan フォルダーを監視…", + "unwatch": "Creality Scan の監視を停止", + "fresh": "プロジェクトからの新しい Mesh.ply", + "openIt": "ScanRuler で開く", + "ignore": "無視" + }, + "_hw": { + "title": "ハードウェア", + "gpu": "GPU", + "backendChosen": "選択された ANGLE バックエンド", + "backendRunning": "実行中の ANGLE バックエンド", + "renderer": "WebGL2 / レンダラー", + "webgpu": "WebGPU", + "cpu": "CPU(スレッド)", + "threads": "論理スレッド", + "isolation": "クロスオリジン分離(WASM スレッド)", + "npu": "NPU / WebNN", + "versions": "Electron / Chromium", + "backend": "グラフィックスバックエンド:", + "restart": "再起動して適用", + "changed": "バックエンドが変更されました:再起動して適用してください。", + "yes": "はい", + "no": "いいえ", + "available": "利用可能", + "unavailable": "利用不可", + "default": "既定", + "experimental": "試験的", + "webgpuWhy": "Windows の Chromium は DirectX 11 バックエンドでのみ WebGPU を提供します。ScanRuler は使用しません。" + }, + "CNC Kitchen · 3D scan analysis ·": "CNC Kitchen · 3D スキャン解析 ·", + "3D Measure": "3D 測定", + "Surface Deviation": "表面偏差", + "Wall Thickness": "肉厚", + "2D Measure": "2D 測定", + "Reverse engineer": "リバースエンジニアリング", + "Mesh Tools": "メッシュツール", + "No unsaved changes": "未保存の変更はありません", + "New": "新規", + "Project": "プロジェクト", + "Save": "保存", + "Load": "読み込み", + "Settings": "設定", + "Model": "モデル", + "No scan loaded": "スキャンが読み込まれていません", + "STL, PLY, OBJ, 3MF or STEP · mm": "STL、PLY、OBJ、3MF、または STEP · mm", + "Open…": "開く…", + "Drop it anywhere in the window.": "ウィンドウ内のどこにでもドロップできます。", + "Alignment": "位置合わせ", + "Auto-align": "自動位置合わせ", + "Align part": "パーツを位置合わせ", + "Move / rotate by numbers": "数値で移動 / 回転", + "Create element": "要素を作成", + "Point": "点", + "Line": "線", + "Plane": "平面", + "Sphere": "球", + "Cylinder": "円柱", + "Cone": "円錐", + "Circle": "円", + "Torus": "トーラス", + "Section": "断面", + "Create dimensions": "寸法を作成", + "New dimension": "新規寸法", + "New tolerance": "新規公差", + "Fitting elements": "要素のフィッティング", + "Load a scan, then pick features on it to fit spheres, cylinders and planes and measure between them. STL, PLY, OBJ, 3MF or STEP, in millimetres — everything stays in this browser.": "スキャンを読み込み、その上の特徴を選んで、球・円柱・平面をフィッティングし、それらの間を測定します。STL、PLY、OBJ、3MF、または STEP、単位はミリメートルです — すべての処理はこのブラウザー内で完結します。", + "Scan": "スキャン", + "The part as measured": "測定されたパーツ", + "Drop it here": "ここにドロップ", + "Choose file…": "ファイルを選択…", + "Drag & drop works anywhere in the window — the first empty slot takes it.": "ドラッグ&ドロップはウィンドウ内のどこでも使えます — 最初の空きスロットに読み込まれます。", + "Thanks for using ScanRuler!": "ScanRuler をご利用いただきありがとうございます!", + "This tool is provided": "このツールは", + "completely free": "完全無料", + "by CNC Kitchen. Once you have finished measuring, why not have a look at the store that keeps us making things like this?": "で、CNC Kitchen が提供しています。測定が終わったら、このようなツールづくりを支えているストアをのぞいてみませんか?", + "Visit CNCKitchen.STORE": "CNCKitchen.STORE を見る", + "Send a tip on PayPal": "PayPal でチップを送る", + "Send a tip on Ko-fi": "Ko-fi でチップを送る", + "Transparent": "透過", + "Mesh": "メッシュ", + "Backfaces": "裏面", + "Elements": "要素", + "Labels": "ラベル", + "READY": "準備完了", + "IMPRINT": "運営者情報", + "LOCAL ONLY": "ローカルのみ", + "· FILES NEVER LEAVE YOUR BROWSER": "· ファイルはブラウザーの外に出ません", + "Units of": "単位:", + "An STL file carries no units — a 1 in it is whatever the program that wrote it meant. Say what this one is in and it is read in millimetres, like everything measured here.": "STL ファイルには単位の情報がありません — 中の「1」は、書き出したプログラムがそう決めた値にすぎません。この単位を指定すると、ここで測定するすべてのものと同様にミリメートルとして読み込まれます。", + "Coordinates in": "座標の単位", + "Millimetres": "ミリメートル", + "Centimetres": "センチメートル", + "Metres": "メートル", + "Inches": "インチ", + "Don't ask again": "今後は確認しない", + "— read every STL in these units. Settings brings the question back.": "— すべての STL をこの単位で読み込みます。設定から確認を再び表示できます。", + "Cancel": "キャンセル", + "Open": "開く", + "Your data will no longer stay on this computer": "データがこのコンピューターにとどまらなくなります", + "Without an agent, everything stays in this browser. With one connected, what the agent reads — measurements, reports, pictures of the part, file names, notes, and any file it opens itself — is processed by the agent and the AI model behind it.": "エージェントを使わない場合、すべてはこのブラウザー内にとどまります。エージェントを接続すると、エージェントが読み取る内容(測定値、レポート、パーツの画像、ファイル名、メモ、エージェント自身が開くファイルなど)は、エージェントとその背後の AI モデルによって処理されます。", + "Unless that model runs on this computer, all of this goes to the model’s provider": "そのモデルがこのコンピューター上で動作していない限り、これらすべてがモデルの提供元に送信されます", + ", under that provider’s terms.": "、提供元の規約に従って扱われます。", + "ScanRuler itself sends nothing anywhere: the connection only reaches the agent’s helper on this computer. Don’t connect an agent while you work on parts you may not share with its provider.": "ScanRuler 自体は何も外部に送信しません。接続が届くのは、このコンピューター上のエージェントのヘルパーだけです。提供元と共有してはならないパーツを扱う間は、エージェントを接続しないでください。", + "Asked once in this browser. Settings → Agents switches the connection off at any time.": "このブラウザーで一度だけ確認します。設定 → エージェントで、いつでも接続をオフにできます。", + "Connect the agent": "エージェントを接続", + "3D Measure\nFit spheres, cylinders and planes, and measure between them": "3D 測定\n球・円柱・平面をフィッティングし、それらの間を測定します", + "Surface Deviation\nBest-fit the scan to a nominal part and map the difference": "表面偏差\nスキャンを基準パーツにベストフィットさせ、差を図示します", + "Wall Thickness\nMap the wall thickness of the part itself — no reference needed": "肉厚\nパーツ自体の肉厚を図示します — 基準は不要です", + "2D Measure\nMeasure a flatbed scan the way a measuring microscope would": "2D 測定\n測定顕微鏡のように、フラットベッドスキャンを測定します", + "Reverse engineer\nSketch on sections, extrude and revolve, and export a solid STEP body built over the scan": "リバースエンジニアリング\n断面上でスケッチし、押し出しと回転を行い、スキャンに重ねて作成したソリッドの STEP ボディを書き出します", + "Mesh Tools\nClean the scan up — islands, holes, scatter — expand, shrink or hollow it, and reduce it to a light mesh for CAD held within a tolerance you set": "メッシュツール\nスキャンをクリーンアップ(アイランド、穴、ノイズ)し、拡大・縮小・中空化して、設定した公差内に収めたまま、CAD 用の軽量メッシュに間引きます", + "The project's name — what Save Project saves it under and every export is named on. Unnamed, they go by the scan or the image it holds.": "プロジェクト名 — プロジェクトを保存するときの名前で、すべての書き出しにもこの名前が付きます。未設定の場合は、読み込まれているスキャンまたは画像の名前になります。", + "Project name": "プロジェクト名", + "Untitled project": "無題のプロジェクト", + "Checkpoints are stored only in this browser. Checkpoints and project files include completed work; finish active drawings before leaving. Save Project downloads a portable copy.": "チェックポイントはこのブラウザーにのみ保存されます。チェックポイントとプロジェクトファイルには完了した作業が含まれます。作図中のものは、離れる前に完了させてください。プロジェクトを保存すると、持ち運べるコピーがダウンロードされます。", + "Undo — Ctrl+Z": "元に戻す — Ctrl+Z", + "Undo": "元に戻す", + "Redo — Ctrl+Y / Ctrl+Shift+Z": "やり直す — Ctrl+Y / Ctrl+Shift+Z", + "Redo": "やり直す", + "Start a new project: the scan, the reference and the image closed, every measurement and every workspace's work let go, the name taken off — after asking, when there are changes not saved": "新しいプロジェクトを開始します:スキャン、基準、画像を閉じ、すべての測定とすべてのワークスペースの作業を破棄し、名前を外します — 未保存の変更がある場合は、先に確認します", + "Save the scan, the reference, the image and every measurement as one .scanruler file": "スキャン、基準、画像、すべての測定を 1 つの .scanruler ファイルとして保存します", + "Open a .scanruler project — or a plain scan or image to start fresh": ".scanruler プロジェクトを開きます — または、通常のスキャンや画像を開いて新規に始めます", + "Interface theme, colour mode, mouse controls, line widths and guided hints": "インターフェースのテーマ、カラーモード、マウス操作、線の太さ、ガイドヒント", + "Source code of the open-source core (AGPL-3.0-only) on GitHub": "オープンソースコア(AGPL-3.0-only)のソースコード(GitHub)", + "More about The scan": "スキャンについての詳細", + "More about Alignment": "位置合わせについての詳細", + "More about Elements": "要素についての詳細", + "Cut the scan with a plane, to measure the cut in the 2D Measure workspace": "平面でスキャンを切断し、2D 測定ワークスペースでその断面を測定します", + "More about Dimensions": "寸法についての詳細", + "More about Geometric tolerances": "幾何公差についての詳細", + "Fit to view — bring the whole model back on screen, without turning it": "ビューに合わせる — モデルを回転させずに全体を画面内に戻します", + "Fit to view": "ビューに合わせる", + "Close": "閉じる", + "Load a scan first": "先にスキャンを読み込んでください", + "How the scan is drawn": "スキャンの表示方法", + "Draw the scan transparent — what lies inside it, the fitted elements and the pinned readings show instead of hiding behind the surface in front of them": "スキャンを透過表示にします — 内部にあるもの、フィッティングした要素、固定した読み値が、手前の表面に隠れずに表示されます", + "Draw the triangle edges on the scan, to see how fine it is and where it is patchy. The mesh comes up as you zoom in: from a distance the triangles are smaller than a pixel, and the edges fade out rather than turning the part black": "スキャン上に三角形のエッジを描画し、どれだけ細かいか、どこが粗いかを確認します。メッシュはズームするにつれて現れます。遠くでは三角形が 1 ピクセルより小さく、エッジはパーツを黒くせずに薄れていきます", + "Colour the far side of every triangle — holes in the scan and inverted normals stop looking like solid part": "すべての三角形の裏側に色を付けます — スキャンの穴や反転した法線が、ソリッドなパーツのように見えなくなります", + "Nothing measured yet — fit an element or cut a section and it shows here": "まだ何も測定されていません — 要素をフィッティングするか断面を切ると、ここに表示されます", + "How the elements are drawn": "要素の表示方法", + "Show the name tags and readouts on the part. The fitted elements, the sections and the dimension lines stay either way": "パーツ上の名前タグと読み出し値を表示します。フィッティングした要素、断面、寸法線は、どちらの場合も表示されたままです", + "Cut the parts open at a plane to look inside — the scan, the reference and everything measured on them. Closed parts are closed where they are cut; an open scan stays a shell. Slide the plane with the arrow, tilt it with the rings": "平面でパーツを切り開いて内部を見ます — スキャン、基準、およびそれらに対して測定したすべてが対象です。閉じたパーツは切断面で閉じられ、開いたスキャンはシェルのままです。矢印で平面をスライドし、リングで傾けます", + "Measurement": "測定", + "Method": "方法", + "Ray along the normal": "法線方向のレイ", + "Sphere across the wall": "壁を横切る球", + "Max. thickness": "最大厚さ", + "Rays": "レイ", + "Single ray — fast": "単一レイ — 高速", + "6-ray cone — tighter": "6 レイ円錐 — より精密", + "12-ray cone — tightest": "12 レイ円錐 — 最も精密", + "Far surface must face back": "反対側の表面が向かい合っていること", + "Max. deviation of normals": "法線の最大偏差", + "Measure wall thickness": "肉厚を測定", + "Everything above shapes the search, so changing it means measuring again.": "上記の設定はすべて探索に影響するため、変更した場合は再測定が必要です。", + "Wall thickness": "肉厚", + "Load a scan and measure how thick its walls are, everywhere at once. No reference model, no alignment. STL, PLY, OBJ, 3MF or STEP, in millimetres — everything stays in this browser.": "スキャンを読み込んで、壁の厚さをあらゆる場所で一度に測定します。基準モデルも位置合わせも不要です。STL、PLY、OBJ、3MF、または STEP、単位はミリメートルです — すべての処理はこのブラウザー内で完結します。", + "More about How thickness is measured": "厚さの測定方法についての詳細", + "More about Max. thickness": "最大厚さについての詳細", + "More about Rays": "レイについての詳細", + "More about Far surface must face back": "反対側の表面が向かい合っていることについての詳細", + "More about Max. deviation of normals": "法線の最大偏差についての詳細", + "Image": "画像", + "No image loaded": "画像が読み込まれていません", + "PNG or JPEG": "PNG または JPEG", + "Measuring a flatbed scan": "フラットベッドスキャンの測定", + "Scan the part face-down on a flatbed scanner and open the image — then fit points, lines and circles to its edges and measure between them, the way a measuring microscope does. PNG or JPEG, at the highest optical resolution you have. Everything stays in this browser.": "パーツを下向きにしてフラットベッドスキャナーでスキャンし、画像を開きます。続いて、そのエッジに点・線・円をフィッティングし、測定顕微鏡のようにそれらの間を測定します。PNG または JPEG、お持ちの最高の光学解像度で。すべての処理はこのブラウザー内で完結します。", + "The flatbed scan of the part": "パーツのフラットベッドスキャン", + "More about What is measured": "測定対象についての詳細", + "CAD kernel ready — OCCT 8.0.1 via occt-wasm 5.7.1.": "CAD カーネルの準備ができました — occt-wasm 5.7.1 経由の OCCT 8.0.1。", + "Parameters": "パラメータ", + "+ Add": "+ 追加", + "Create feature": "フィーチャーを作成", + "Sketch on": "スケッチ面", + "Choose a plane…": "平面を選択…", + "Extrude": "押し出し", + "Revolve": "回転", + "Extract": "抽出", + "Import STEP": "STEP をインポート", + "Shell": "シェル", + "Fillet": "フィレット", + "Chamfer": "面取り", + "Draft": "抜き勾配", + "Move face": "面を移動", + "Delete face": "面を削除", + "Hole": "穴", + "Thread": "ねじ", + "Cut by plane": "平面で切断", + "Mirror": "ミラー", + "Combine": "結合", + "Sheet": "シート", + "Split": "分割", + "Loft": "ロフト", + "Sweep": "スイープ", + "Pattern": "パターン", + "Move body": "ボディを移動", + "Offset body": "ボディをオフセット", + "Fit surface": "サーフェスフィット", + "Patch network": "パッチネットワーク", + "Trim": "トリム", + "Thicken": "厚み付け", + "Start with a sketch: choose a coordinate plane above. No scan is needed to model.": "スケッチから始めます:上の座標平面を選んでください。モデリングにスキャンは必要ありません。", + "Bodies": "ボディ", + "XY plane": "XY 平面", + "XZ plane": "XZ 平面", + "YZ plane": "YZ 平面", + "Curvature": "曲率", + "Overlaps": "重なり", + "Tangent edges": "接線エッジ", + "Drawn": "描画", + "Dimmed": "減光", + "Off": "オフ", + "Deviation": "偏差", + "More about Reverse engineering": "リバースエンジニアリングについての詳細", + "Show the parameters": "パラメータを表示", + "More about Parameters": "パラメータについての詳細", + "Add a parameter": "パラメータを追加", + "More about Features": "フィーチャーについての詳細", + "Extrude a sketch's regions — pick them in the viewport": "スケッチの領域を押し出します — ビューポートで領域を選択してください", + "Revolve a sketch's regions about an axis": "スケッチの領域を軸まわりに回転させます", + "A measured plane, cylinder, cone or sphere as a body at its extents, extended as in 3D Measure — a boss or a pin without a sketch — or a shadow section as its block, the outline through the part's thickness": "測定した平面・円柱・円錐・球を、3D 測定と同様に延長して、その範囲のボディにします — スケッチなしのボスやピン — または、シャドウ断面を、パーツの厚みを通る輪郭としてそのブロックにします", + "Bring a STEP file in as bodies of its own — a bought-in part, a mating part, the CAD model to check the scan against. A body for every solid in the file, named as the file names its parts; Move body puts them where they belong": "STEP ファイルを個別のボディとして取り込みます — 購入部品、相手部品、スキャンと照合する CAD モデルなど。ファイル内のソリッドごとにボディが作られ、ファイルの部品名がそのまま付きます。ボディを移動で正しい位置に配置します", + "Hollow the body to a wall — click the faces to open, or none for a closed hollow; the wall comes from the Thickness map when one was measured": "ボディを一定の壁厚で中空にします — 開口する面をクリックして選ぶか、何も選ばずに閉じた中空にします。肉厚マップを測定済みの場合は、壁厚はそこから取られます", + "Round edges of the body — click them; the radius is read off the scan across the first": "ボディのエッジを丸めます — エッジをクリックして選びます。半径は最初のエッジ付近でスキャンから読み取られます", + "Chamfer edges of the body — click them": "ボディのエッジを面取りします — エッジをクリックして選びます", + "Lean faces of the body about a neutral plane — a moulding's draft, put on after the fact: click the flat face that stays, then the faces to lean": "中立面を基準にボディの面を傾けます — 成形品の抜き勾配を後から付けます。残す平らな面をクリックし、続いて傾ける面をクリックします", + "Offset faces of the body by a distance — a flat face along its normal, a round face in radius; several picked together grow or shrink as a whole, a cylinder's wall and its ends. One face can be snapped onto the scan instead: pick it, take Snap to scan, and click the scan where it should lie": "距離を指定してボディの面をオフセットします — 平面は法線方向に、丸い面は半径方向に。複数をまとめて選ぶと全体として拡大・縮小し、円柱の壁とその端面も一緒に動きます。1 つの面はスキャンにスナップさせることもできます:その面を選び、「スキャンにスナップ」を選んで、置きたい位置のスキャンをクリックします", + "Delete faces of the body and heal the gap — a fillet, a chamfer, a hole or a boss taken out, the faces round it run on to close over where it was: click a face of it, and the rest of the round or hole comes with it": "ボディの面を削除して隙間を修復します — フィレット、面取り、穴、ボスを取り除くと、周囲の面が延長されて元の場所を覆います。その一部の面をクリックすると、残りの丸みや穴も一緒に選ばれます", + "A hole through the body along a cylinder or circle element, or a round face of the body": "円柱または円の要素、あるいはボディの丸い面に沿って、ボディを貫通する穴を開けます", + "A modeled screw thread on a round face of the body — a bolt's on a boss, a nut's in a bore: ISO metric, UNC or UNF, a 45° form that prints without supports, or a diameter and pitch of your own, with an offset for clearance when printing": "ボディの丸い面にモデル化したねじ山を作成します — ボルトならボス上に、ナットなら穴の内側に。ISO メートル、UNC、UNF、サポートなしで印刷できる 45° 形状、または独自の直径とピッチを選べ、印刷時のクリアランス用オフセットも指定できます", + "Cut the body at a plane and keep one side": "平面でボディを切断し、片側を残します", + "Mirror the body about a plane, joined with the source": "平面を基準にボディをミラーし、元のボディと結合します", + "Join, cut or intersect two bodies made separately — or trim a sheet by a solid, or sew two sheets into one, before Thicken": "別々に作成した 2 つのボディを結合・切削・交差します — または、ソリッドでシートをトリムするか、厚み付けの前に 2 枚のシートを 1 つに縫い合わせます", + "A sketch's pieces — an open chain will do — extruded as a surface: a cutter to split a body with": "スケッチの要素(開いたチェーンでも可)を、サーフェスとして押し出します:ボディを分割するためのカッターです", + "Split a body by a sheet and keep the fragments that lie on the scan — or trim a sheet by sheets or solids, keeping its pieces on the scan": "シートでボディを分割し、スキャン上にあるフラグメントを残します — または、シートやソリッドでシートをトリムし、スキャン上にあるピースを残します", + "A solid through the profiles of two or more sketches on their own planes": "それぞれの平面上にある 2 つ以上のスケッチのプロファイルを通るソリッド", + "A sketch's region swept along a chain of lines and arcs in another sketch — a bent bar, a handle, a rim that follows a curve": "別のスケッチの線と円弧のチェーンに沿って、スケッチの領域をスイープします — 曲がったバー、ハンドル、曲線に沿うリム", + "Copies of the body about an axis, along a direction, or in a grid": "軸まわり、方向に沿って、またはグリッド状にボディをコピーします", + "Move and turn bodies — the ones selected on the part or picked in the body list (Ctrl+click or Shift+click rows for several), or click them on the part: drag the gizmo's arrows and rings, or type the distances and angles. Tick Copy to move a copy and leave the body where it is": "ボディを移動・回転します — パーツ上で選択したもの、またはボディ一覧で選んだもの(複数行は Ctrl+クリックか Shift+クリック)、あるいはパーツ上で直接クリックしたもの:ギズモの矢印とリングをドラッグするか、距離と角度を入力します。コピーにチェックを入れると、元のボディをそのままにコピーを移動します", + "Grow a body all round by a distance, or shrink it — kept beside it as a body of its own, a keep-out round a part or the pocket that holds it with clearance": "ボディを距離分だけ全方向に拡大または縮小します — 元のボディの隣に独立したボディとして残ります:パーツまわりの立入禁止領域や、クリアランス付きでそれを収めるポケットに使えます", + "A smooth freeform surface fitted through a region of the scan — click faces or mark them — for the trim to close into a solid with the others": "スキャンの領域を通るなめらかな自由曲面をフィッティングします — 面をクリックするかマークして指定します — トリムが他の面とともにソリッドへ閉じるためのものです", + "Curves drawn on the scan, and the regions they close filled with patches fitted to the scan together — tangent across every curve not marked a crease, sharing their edges: a sheet, or a solid where the network closes. For a freeform skin that folds back or wants more than one patch": "スキャン上に描いた曲線と、それらが囲む領域を、スキャンにまとめてフィッティングしたパッチで埋めます — クリース指定のない曲線をまたいで接線連続で、エッジを共有します:シート、またはネットワークが閉じる場合はソリッドになります。折り返す自由曲面のスキンや、複数のパッチが必要な場合向けです", + "Close a set of fitted surfaces, sheets and measured planes, cylinders, cones and spheres into a solid: the faces they cut space into are kept where they lie on the scan and sewn": "フィッティングしたサーフェス、シート、測定した平面・円柱・円錐・球を閉じてソリッドにします:それらが空間を分割してできる面のうち、スキャン上にあるものを残して縫い合わせます", + "Make a sheet or a fitted surface a solid of a thickness — a freeform thin shell's wall from its one skin, where a shell or an offset of a lofted body will not go": "シートやフィッティングしたサーフェスに厚みを付けてソリッドにします — シェルやロフトボディのオフセットが使えない場合に、1 枚のスキンから自由曲面の薄いシェルの壁を作ります", + "The XY plane through the origin — click it in the viewport to sketch on it": "原点を通る XY 平面 — ビューポートでクリックすると、その上にスケッチできます", + "Hide the XY plane": "XY 平面を非表示", + "The XZ plane through the origin — click it in the viewport to sketch on it": "原点を通る XZ 平面 — ビューポートでクリックすると、その上にスケッチできます", + "Hide the XZ plane": "XZ 平面を非表示", + "The YZ plane through the origin — click it in the viewport to sketch on it": "原点を通る YZ 平面 — ビューポートでクリックすると、その上にスケッチできます", + "Hide the YZ plane": "YZ 平面を非表示", + "Load a scan first — the map reads its surface": "先にスキャンを読み込んでください — マップはその表面を読み取ります", + "Build a body first": "先にボディを作成してください", + "How the bodies are drawn": "ボディの表示方法", + "Draw the bodies transparent — the scan, the elements and the bodies inside them show through. With the scan solid as well, each part shows where it stands proud of the other": "ボディを透過表示にします — スキャン、要素、内側のボディが透けて見えます。スキャンもソリッドにすると、一方が他方からはみ出している部分がそれぞれに表示されます", + "Draw where two bodies run into each other in hatched red — what they share, over the faces of both that would otherwise fight for the same pixels. Off for bodies that overlap on purpose, a modifier volume for a slicer; saved with the project": "2 つのボディが交差する場所を赤い斜線で描画します — 共有している部分を、同じピクセルを奪い合う両方の面の上に表示します。意図的に重ねるボディ(スライサー用のモディファイアボリュームなど)ではオフにします。プロジェクトと一緒に保存されます", + "How the edges are drawn where a round runs into a flat. A round face's seam and the cut a join leaves across one surface are never drawn": "丸みが平面に接する部分のエッジの描画方法。丸い面のシームや、結合によって 1 つの面に残る切れ目は描画されません", + "Tangent edges drawn — where a round runs into a flat — as the sharp ones are, the way CAD draws them": "接線エッジを描画します — 丸みが平面に接する部分を — シャープなエッジと同様に、CAD の描き方で", + "Tangent edges dimmed": "接線エッジを減光", + "Tangent edges left out, and not picked: a round shows by its outline against the background": "接線エッジを省略し、選択もできません:丸みは背景に対する輪郭で見えます", + "Nothing measured yet — fit elements or cut sections in the 3D Measure workspace and they show here": "まだ何も測定されていません — 3D 測定ワークスペースで要素をフィッティングするか断面を切ると、ここに表示されます", + "Build a body first — the map reads the scan against it": "先にボディを作成してください — マップはスキャンをそれに対して読み取ります", + "Tools": "ツール", + "Reduce mesh": "メッシュを間引く", + "Remove islands": "アイランドを削除", + "Delete marked": "マークしたものを削除", + "Flip normals": "法線を反転", + "Cut": "切り取り", + "Fill holes": "穴を埋める", + "Smooth": "スムージング", + "Shrink/expand": "縮小/拡大", + "Boolean": "ブーリアン", + "Trim to scan": "スキャンにトリム", + "Shell/hollow": "シェル/中空化", + "Tolerance": "公差", + "Triangle limit": "三角形数の上限", + "Reduce": "間引く", + "Both figures shape the reduction, so changing them means reducing again.": "両方の値が間引きに影響するため、変更した場合は再度間引く必要があります。", + "Cleaning up and reducing a scan": "スキャンのクリーンアップと間引き", + "Load a scan and clean it up — clear the stray islands, delete what is not the part, turn it the right way out, cut off the base it stood on, fill its holes, smooth out the scatter, expand or shrink it, hollow it — and reduce it to a light mesh to model over in CAD, held within a tolerance you set, measured against the scan to prove it, and exported where you aligned it. STL, PLY, OBJ or 3MF, in millimetres — everything stays in this browser.": "スキャンを読み込んでクリーンアップします — 浮遊するアイランドを除去し、パーツ以外を削除し、裏表を正しく向け直し、載っていたベースを切り落とし、穴を埋め、ノイズをなめらかにし、拡大または縮小し、中空にします — そして CAD でモデリングするための軽量メッシュに間引きます。設定した公差内に収め、スキャンとの比較で証明し、位置合わせした位置で書き出します。STL、PLY、OBJ、または 3MF、単位はミリメートルです — すべての処理はこのブラウザー内で完結します。", + "Reduced": "間引き結果", + "Accuracy": "精度", + "More about Mesh tools": "メッシュツールについての詳細", + "Mesh tool": "メッシュツール", + "A light copy of the scan to model over in CAD, held within a tolerance you set — the scan itself stays as it is": "CAD でモデリングするための、スキャンの軽量コピー。設定した公差内に収まります — スキャン自体はそのままです", + "Find the specks floating off the scan — every piece joined to nothing else and smaller than a size you set — mark them on the part, and delete them once you say so": "スキャンから浮いている微小片を探します — ほかの何にも接続されておらず、設定したサイズより小さいすべてのピース — パーツ上にマークし、指示があってから削除します", + "Mark surface on the part by hand — a window, a brush or a lasso — and delete it from the scan: a fixture, a clamp, a patch of the table": "パーツ上の面を手作業でマークし — ウィンドウ、ブラシ、投げ縄 — スキャンから削除します:治具、クランプ、テーブルの一部など", + "Turn the surface over so it faces the other way — the whole part, exported inside-out, or a patch you mark that faces in": "面を裏返して反対向きにします — パーツ全体(裏表逆に書き出されたもの)、またはマークした内向きのパッチ", + "Cut the scan with a plane and take one side off — the turntable it stood on, or half the part — closing it flat where the plane went through": "平面でスキャンを切断し、片側を取り除きます — 載っていたターンテーブル、またはパーツの半分 — 平面が通った場所は平らに閉じられます", + "Close the holes a scanner left, every one smaller than a size you set, with patches that carry the surface across": "スキャナーが残した穴のうち、設定したサイズより小さいものをすべて、表面をつなぐパッチで塞ぎます", + "Take the scanner’s scatter out of the surface, never moving any point further than a limit you set, sharp edges held": "スキャナーのノイズを表面から取り除きます。どの点も設定した上限以上は動かさず、シャープなエッジは保持されます", + "Move the surface along its normal by a distance — out or in, the whole part or a marked region — not a scale: a bore gets narrower as the part grows": "表面を法線方向に一定距離動かします — 外向きまたは内向き、パーツ全体またはマークした領域 — スケールではありません:パーツが大きくなると穴は狭くなります", + "Take a solid body out of the scan, add it, or keep what the two share — a bore the scanner could not see to the bottom of, cut out with a cylinder modelled in Reverse engineer": "ソリッドボディをスキャンから取り除くか、追加するか、両者の共有部分だけを残します — スキャナーが底まで見えなかった穴を、リバースエンジニアリングでモデル化した円柱で切り抜く場合など", + "Cut a body modelled to mend a broken part to fit the scanned break — the missing piece, to print and glue in": "壊れたパーツを補修するためにモデル化したボディを、スキャンした破断面に合わせて切り出します — 欠けた部分を印刷して接着するためのものです", + "Hollow a closed part, leaving a wall of a thickness you set round a sealed cavity — or thicken an open surface into a solid": "閉じたパーツを中空にし、密閉された空洞のまわりに設定した厚さの壁を残します — または、開いたサーフェスをソリッドに厚み付けします", + "More about Reducing a scan for CAD": "CAD 用のスキャンの間引きについての詳細", + "More about Tolerance": "公差についての詳細", + "More about Triangle limit": "三角形数の上限についての詳細", + "What to show of the reduction": "間引き結果の表示内容", + "Reduce the scan first — the view keys show the result": "先にスキャンを間引いてください — ビューキーで結果が表示されます", + "Edit the scan first — the deviation shows how far the edited copy is from the scan in use": "先にスキャンを編集してください — 偏差は、編集したコピーが使用中のスキャンからどれだけ離れているかを示します", + "How the instrument looks and behaves. Remembered in this browser — nothing here is a property of the part, and none of it is saved with a project.": "計測器の見た目と動作。このブラウザーに記憶されます — ここにあるものはパーツの特性ではなく、プロジェクトと一緒に保存されることもありません。", + "Units": "単位", + "Lengths in": "長さの単位", + "What is shown and typed. Files, projects and agent answers stay in millimetres.": "表示と入力に使われる単位です。ファイル、プロジェクト、エージェントの回答は引き続きミリメートルです。", + "Appearance": "外観", + "Interface": "インターフェース", + "Light": "ライト", + "Dark": "ダーク", + "System": "システム", + "Light or dark chassis.": "ライトまたはダークの筐体。", + "follows the operating system's setting, and changes with it.": "はオペレーティングシステムの設定に従い、その変更にも追従します。", + "Colour mode": "カラーモード", + "Studio grey": "スタジオグレー", + "Scanner blue": "スキャナーブルー", + "Glossy blue under a hard light — surface detail stands out": "強い光に照らされた光沢のあるブルー — 表面のディテールが際立ちます", + ". What has been measured keeps its colour either way — the element tints and the deviation ramp are the same in both.": "。測定済みのものは、どちらでも色が変わりません — 要素の色合いと偏差のカラーランプは両方で同じです。", + "Colour map": "カラーマップ", + "Blue through cyan, green and yellow to red, a saturated green in the middle — the inspection-software classic. Uneven in lightness, so cyan and yellow show as bands the part does not have": "青からシアン、緑、黄を経て赤へ、中央は彩度の高い緑 — 検査ソフトの定番です。明度が不均一なため、シアンや黄がパーツにはない帯として見えます", + ". Every map painted on the part, and the scale beside it, is drawn in it; the block at either end is what a reading past that end of the scale wears.": "。パーツに描かれるすべてのマップとその横のスケールはこれで描画されます。両端のブロックは、スケールの端を超えた読み値の色です。", + "Sharp edges": "シャープエッジ", + "Always sharp": "常にシャープ", + "Auto — sharp on a CAD-like mesh": "自動 — CAD 風メッシュではシャープ", + "Always smooth": "常にスムーズ", + "Sharp from": "シャープにする角度", + "Whether an edge of the scan is shaded as a crease, each face with its own normal, or smoothed across so a box looks pillowed — and from what angle between its two faces an edge counts as sharp.": "スキャンのエッジを、各面が独自の法線を持つクリースとしてシェーディングするか、面をまたいで滑らかにして箱が丸みを帯びて見えるようにするか — また、2 つの面のなす角がいくつ以上でエッジをシャープとみなすか。", + "draws a CAD mesh's faces flat and a scan's real edges — the rim of a bore, a step — as edges; noise seldom tips a scan's triangles that far, and if it still shows, a larger angle, 50° or 60°, leaves it smooth.": "は、CAD メッシュの面をフラットに、スキャンの実際のエッジ(穴の縁、段差)をエッジとして描画します。ノイズでスキャンの三角形がそこまで傾くことはほとんどなく、それでも見える場合は、50° や 60° など大きめの角度にすると滑らかになります。", + "Auto": "自動", + "draws the creases only on a mesh that reads as a tessellation of CAD, a scan smooth all over. A reference part is always drawn sharp, from": "は、CAD のテッセレーションに見えるメッシュにのみクリースを描画し、全体が滑らかなスキャンには描画しません。基準パーツは常にシャープに描画されます。しきい値:", + "Navigation": "ナビゲーション", + "Mouse controls": "マウス操作", + "left-drag orbit · right-drag pan · scroll zoom": "左ドラッグ 回転 · 右ドラッグ パン · スクロール ズーム", + "works beside any of these, in Chrome and Edge: push or pull the puck to zoom, slide it to pan, tilt, twist and roll it to turn the part. It connects the first time you touch it, and drives the view the mouse was last over.": "はこれらのどれとも併用でき、Chrome と Edge で動作します。パックを押す・引くでズーム、スライドでパン、傾ける・ひねる・ロールさせるとパーツが回ります。初めて触れたときに接続され、マウスが最後に乗っていたビューを操作します。", + "Files": "ファイル", + "Ask what units an STL is in": "STL の単位を確認する", + "— the format carries none, and a part read at the wrong scale measures wrong in every number after. Off, every STL is read in the units below without asking.": "— この形式には単位の情報がなく、誤ったスケールで読み込んだパーツは、以降のあらゆる数値が誤ったものになります。オフにすると、確認なしで下の単位ですべての STL を読み込みます。", + "STL import units": "STL 読み込み時の単位", + "What an STL is read in when the question is off, and the answer the question offers first — the last one given. A PLY or OBJ is always read in millimetres; a STEP file says its own.": "確認をオフにしたときに STL を読み込む単位で、確認で最初に提示される答えでもあります — 最後に選んだものです。PLY と OBJ は常にミリメートルで読み込まれ、STEP ファイルは自身の単位を持っています。", + "Lines": "線", + "Section cuts": "断面カット", + "How heavy a section's cut is drawn on the part in 3D Measure. The curves measured on its sheet follow in proportion.": "3D 測定でパーツ上に描かれる断面の切断線の太さ。そのシート上で測定した曲線も比例して変わります。", + "2D fitted curves": "2D フィッティング曲線", + "The lines, circles and splines fitted over a flatbed scan in 2D Measure, and the callouts drawn with them.": "2D 測定でフラットベッドスキャンにフィッティングした線、円、スプラインと、それに付くコールアウト。", + "2D edges": "2D エッジ", + "The edge chains the curves are fitted to — found in the scan, or cut by a section.": "曲線のフィッティング元になるエッジチェーン — スキャンから検出されたもの、または断面で切り出されたもの。", + "Guidance": "ガイダンス", + "Guided hints": "ガイドヒント", + "— ring the control to press next, until you have been through a workspace twice. Switching it back on starts the guidance over.": "— 次に押すコントロールをリングで示します。各ワークスペースを 2 回使うまで続きます。再びオンにすると、ガイダンスが最初からやり直されます。", + "Agents": "エージェント", + "Local agent connection": "ローカルエージェント接続", + "— let an AI agent on this computer (Claude Code, Claude Desktop, Cursor …) drive this tab through its ScanRuler tools, the scanruler-mcp server. You watch every step here and can undo it.": "— このコンピューター上の AI エージェント(Claude Code、Claude Desktop、Cursor …)が、ScanRuler のツールである scanruler-mcp サーバー経由でこのタブを操作できるようにします。すべての操作がここに表示され、元に戻せます。", + "Your data no longer stays on this computer:": "データがこのコンピューターにとどまらなくなります:", + "what the agent reads — measurements, reports, pictures of the part, file names, notes — is processed by the agent and goes to its model’s provider, unless the model runs on this computer. ScanRuler itself sends nothing anywhere; the connection is to 127.0.0.1.": "エージェントが読み取る内容(測定値、レポート、パーツの画像、ファイル名、メモ)は、エージェントによって処理され、そのモデルがこのコンピューター上で動作していない限り、モデルの提供元に送信されます。ScanRuler 自体は何も外部に送信しません。接続先は 127.0.0.1 です。", + "Port": "ポート", + "Token": "トークン", + "prints the port and the token, and a link that fills them in.": "はポートとトークン、およびそれらを自動入力するリンクを出力します。", + "Support": "サポート", + "Support card": "サポートカード", + "— the thank-you in the corner of the stage, with the ways to give something back. Its × closes it for this visit; switched off here it stays away, the big one below with it.": "— ステージの隅にあるお礼のカードで、お返しの方法が載っています。その × で今回の訪問中は閉じられます。ここでオフにすると表示されなくなり、下の大きなカードも同様です。", + "Big card on saving": "保存時の大きなカード", + "— the same thank-you in the middle of the screen after a save or an export, at most once in ten minutes. The file does not wait for it. Its \"Don't show this again for a week\" shows here as off until the week is over.": "— 保存または書き出しの後、画面中央に同じお礼を表示します。10 分に 1 回までです。ファイルはこれを待ちません。「今後 1 週間表示しない」を選ぶと、1 週間が過ぎるまで、ここではオフと表示されます。", + "Dark blue through teal and green to yellow. Perceptually uniform — an even step in the reading is an even step in colour — and as legible to colour-blind eyes and in greyscale": "濃い青からティールと緑を経て黄へ。知覚的に均一です — 値の等間隔の変化が色の等間隔の変化になり — 色覚特性のある方にもグレースケールでも見やすい", + "Black through purple and pink to pale cream. Perceptually uniform and colour-blind safe": "黒から紫、ピンクを経て淡いクリーム色へ。知覚的に均一で、色覚特性に配慮されています", + "Indigo through magenta and orange to yellow — no black end. Perceptually uniform and colour-blind safe": "インディゴからマゼンタ、オレンジを経て黄へ — 黒の端はありません。知覚的に均一で、色覚特性に配慮されています", + "Black through purple, red and orange to pale yellow. Perceptually uniform and colour-blind safe": "黒から紫、赤、オレンジを経て淡い黄へ。知覚的に均一で、色覚特性に配慮されています", + "Navy through grey to yellow, made to look the same to red–green colour blindness as to anyone else. Perceptually uniform": "ネイビーからグレーを経て黄へ、赤緑色覚の方にもそれ以外の方にも同じように見えるように作られています。知覚的に均一です", + "Black through deep blue and teal to mint. Perceptually uniform and colour-blind safe": "黒から濃い青、ティールを経てミントへ。知覚的に均一で、色覚特性に配慮されています", + "Black through purple and red to cream. Perceptually uniform and colour-blind safe": "黒から紫、赤を経てクリームへ。知覚的に均一で、色覚特性に配慮されています", + "A rainbow like jet, dark blue through green to dark red, but smooth — without the bands jet shows. Not for red–green colour blindness": "jet のような虹色で、濃い青から緑を経て濃い赤へ、ただしなめらかで jet に見られる帯がありません。赤緑色覚には不向きです", + "Across": "切断方向", + "Flip": "反転", + "What the plane is laid across: a coordinate plane through the part’s middle, or an element with a direction — a plane’s normal, the axis of a cylinder, cone or line, a circle’s normal. The half toward you is taken away.": "平面を置く基準:パーツの中心を通る座標平面、または方向を持つ要素(平面の法線、円柱・円錐・線の軸、円の法線)。手前側の半分が取り除かれます。", + "Where along its direction the plane lies — drag the arrow on the part, or type it here": "平面が方向に沿ってどこにあるか — パーツ上の矢印をドラッグするか、ここに入力します", + "Take away the other half instead": "もう一方の半分を取り除く", + "set otherwise in its menu": "メニューで別に設定されています" +} diff --git a/desktop/i18n/pt-BR.json b/desktop/i18n/pt-BR.json new file mode 100644 index 00000000..df23e43d --- /dev/null +++ b/desktop/i18n/pt-BR.json @@ -0,0 +1,385 @@ +{ + "_name": "Português (Brasil)", + "_menu": { + "file": "Arquivo", + "open": "Abrir scan…", + "quit": "Sair", + "view": "Exibir", + "help": "Ajuda", + "hardware": "Hardware…", + "language": "Idioma", + "watch": "Observar pasta do Creality Scan…", + "unwatch": "Parar de observar o Creality Scan", + "fresh": "Novo Mesh.ply do projeto", + "openIt": "Abrir no ScanRuler", + "ignore": "Ignorar" + }, + "_hw": { + "title": "Hardware", + "gpu": "GPU", + "backendChosen": "Backend ANGLE escolhido", + "backendRunning": "Backend ANGLE em execução", + "renderer": "WebGL2 / renderizador", + "webgpu": "WebGPU", + "cpu": "CPU (threads)", + "threads": "threads lógicas", + "isolation": "Isolamento de origem cruzada (threads WASM)", + "npu": "NPU / WebNN", + "versions": "Electron / Chromium", + "backend": "Backend gráfico:", + "restart": "Reiniciar para aplicar", + "changed": "Backend alterado: reinicie para aplicar.", + "yes": "sim", + "no": "não", + "available": "disponível", + "unavailable": "indisponível", + "default": "padrão", + "experimental": "experimental", + "webgpuWhy": "O Chromium no Windows só oferece WebGPU com o backend DirectX 11; o ScanRuler não o usa." + }, + "CNC Kitchen · 3D scan analysis ·": "CNC Kitchen · análise de scans 3D ·", + "3D Measure": "Medição 3D", + "Surface Deviation": "Desvio de superfície", + "Wall Thickness": "Espessura de parede", + "2D Measure": "Medição 2D", + "Reverse engineer": "Engenharia reversa", + "Mesh Tools": "Ferramentas de malha", + "No unsaved changes": "Sem alterações não salvas", + "New": "Novo", + "Project": "Projeto", + "Save": "Salvar", + "Load": "Carregar", + "Settings": "Ajustes", + "Model": "Modelo", + "No scan loaded": "Nenhum scan carregado", + "STL, PLY, OBJ, 3MF or STEP · mm": "STL, PLY, OBJ, 3MF ou STEP · mm", + "Open…": "Abrir…", + "Drop it anywhere in the window.": "Solte em qualquer lugar da janela.", + "Alignment": "Alinhamento", + "Auto-align": "Alinhar auto", + "Align part": "Alinhar peça", + "Move / rotate by numbers": "Mover / girar por valores", + "Create element": "Criar elemento", + "Point": "Ponto", + "Line": "Linha", + "Plane": "Plano", + "Sphere": "Esfera", + "Cylinder": "Cilindro", + "Cone": "Cone", + "Circle": "Círculo", + "Torus": "Toro", + "Section": "Seção", + "Create dimensions": "Criar cotas", + "New dimension": "Nova cota", + "New tolerance": "Nova tolerância", + "Fitting elements": "Ajuste de elementos", + "Load a scan, then pick features on it to fit spheres, cylinders and planes and measure between them. STL, PLY, OBJ, 3MF or STEP, in millimetres — everything stays in this browser.": "Carregue um scan e selecione feições nele para ajustar esferas, cilindros e planos e medir entre eles. STL, PLY, OBJ, 3MF ou STEP, em milímetros — tudo fica neste aplicativo.", + "Scan": "Scan", + "The part as measured": "A peça como medida", + "Drop it here": "Solte aqui", + "Choose file…": "Escolher arquivo…", + "Drag & drop works anywhere in the window — the first empty slot takes it.": "Arrastar e soltar funciona em qualquer lugar da janela — o primeiro espaço vazio recebe o arquivo.", + "Thanks for using ScanRuler!": "Obrigado por usar o ScanRuler!", + "This tool is provided": "Esta ferramenta é oferecida", + "completely free": "totalmente grátis", + "by CNC Kitchen. Once you have finished measuring, why not have a look at the store that keeps us making things like this?": "pela CNC Kitchen. Quando terminar de medir, que tal conhecer a loja que nos mantém criando coisas como esta?", + "Visit CNCKitchen.STORE": "Visite a CNCKitchen.STORE", + "Send a tip on PayPal": "Envie uma gorjeta pelo PayPal", + "Send a tip on Ko-fi": "Envie uma gorjeta pelo Ko-fi", + "Transparent": "Transparente", + "Mesh": "Malha", + "Backfaces": "Faces traseiras", + "Elements": "Elementos", + "Labels": "Rótulos", + "READY": "PRONTO", + "IMPRINT": "AVISO LEGAL", + "LOCAL ONLY": "SOMENTE LOCAL", + "· FILES NEVER LEAVE YOUR BROWSER": "· OS ARQUIVOS NUNCA SAEM DO SEU APLICATIVO", + "Units of": "Unidades de", + "An STL file carries no units — a 1 in it is whatever the program that wrote it meant. Say what this one is in and it is read in millimetres, like everything measured here.": "Um arquivo STL não traz unidades — um 1 nele vale o que o programa que o gravou quis dizer. Informe a unidade deste e ele será lido em milímetros, como tudo o que é medido aqui.", + "Coordinates in": "Coordenadas em", + "Millimetres": "Milímetros", + "Centimetres": "Centímetros", + "Metres": "Metros", + "Inches": "Polegadas", + "Don't ask again": "Não perguntar de novo", + "— read every STL in these units. Settings brings the question back.": "— ler todo STL nestas unidades. Os Ajustes trazem a pergunta de volta.", + "Cancel": "Cancelar", + "Open": "Abrir", + "Your data will no longer stay on this computer": "Seus dados deixarão de ficar neste computador", + "Without an agent, everything stays in this browser. With one connected, what the agent reads — measurements, reports, pictures of the part, file names, notes, and any file it opens itself — is processed by the agent and the AI model behind it.": "Sem um agente, tudo fica neste aplicativo. Com um conectado, o que o agente lê — medições, relatórios, imagens da peça, nomes de arquivos, anotações e qualquer arquivo que ele mesmo abrir — é processado pelo agente e pelo modelo de IA por trás dele.", + "Unless that model runs on this computer, all of this goes to the model’s provider": "A menos que esse modelo rode neste computador, tudo isso vai para o provedor do modelo", + ", under that provider’s terms.": ", sob os termos desse provedor.", + "ScanRuler itself sends nothing anywhere: the connection only reaches the agent’s helper on this computer. Don’t connect an agent while you work on parts you may not share with its provider.": "O próprio ScanRuler não envia nada a lugar nenhum: a conexão só alcança o auxiliar do agente neste computador. Não conecte um agente enquanto trabalhar com peças que você não pode compartilhar com o provedor dele.", + "Asked once in this browser. Settings → Agents switches the connection off at any time.": "Perguntado uma vez neste aplicativo. Ajustes → Agentes desliga a conexão a qualquer momento.", + "Connect the agent": "Conectar o agente", + "3D Measure\nFit spheres, cylinders and planes, and measure between them": "Medição 3D\nAjuste esferas, cilindros e planos e meça entre eles", + "Surface Deviation\nBest-fit the scan to a nominal part and map the difference": "Desvio de superfície\nAjuste o scan a uma peça nominal e mapeie a diferença", + "Wall Thickness\nMap the wall thickness of the part itself — no reference needed": "Espessura de parede\nMapeie a espessura de parede da própria peça — sem precisar de referência", + "2D Measure\nMeasure a flatbed scan the way a measuring microscope would": "Medição 2D\nMeça um scan de mesa como faria um microscópio de medição", + "Reverse engineer\nSketch on sections, extrude and revolve, and export a solid STEP body built over the scan": "Engenharia reversa\nFaça esboços sobre seções, extrude e revolucione, e exporte um corpo sólido STEP construído sobre o scan", + "Mesh Tools\nClean the scan up — islands, holes, scatter — expand, shrink or hollow it, and reduce it to a light mesh for CAD held within a tolerance you set": "Ferramentas de malha\nLimpe o scan — ilhas, furos, ruído — expanda, contraia ou esvazie, e simplifique-o em uma malha leve para CAD dentro de uma tolerância definida por você", + "The project's name — what Save Project saves it under and every export is named on. Unnamed, they go by the scan or the image it holds.": "O nome do projeto — com o qual Salvar projeto o grava e que dá nome a cada exportação. Sem nome, usam o do scan ou da imagem que ele contém.", + "Project name": "Nome do projeto", + "Untitled project": "Projeto sem título", + "Checkpoints are stored only in this browser. Checkpoints and project files include completed work; finish active drawings before leaving. Save Project downloads a portable copy.": "Os pontos de restauração ficam guardados apenas neste aplicativo. Pontos de restauração e arquivos de projeto incluem o trabalho concluído; termine os desenhos em andamento antes de sair. Salvar projeto baixa uma cópia portátil.", + "Undo — Ctrl+Z": "Desfazer — Ctrl+Z", + "Undo": "Desfazer", + "Redo — Ctrl+Y / Ctrl+Shift+Z": "Refazer — Ctrl+Y / Ctrl+Shift+Z", + "Redo": "Refazer", + "Start a new project: the scan, the reference and the image closed, every measurement and every workspace's work let go, the name taken off — after asking, when there are changes not saved": "Iniciar um novo projeto: scan, referência e imagem fechados, todas as medições e o trabalho de cada área de trabalho descartados, nome removido — com confirmação, quando houver alterações não salvas", + "Save the scan, the reference, the image and every measurement as one .scanruler file": "Salvar o scan, a referência, a imagem e todas as medições em um único arquivo .scanruler", + "Open a .scanruler project — or a plain scan or image to start fresh": "Abrir um projeto .scanruler — ou um scan ou imagem simples para começar do zero", + "Interface theme, colour mode, mouse controls, line widths and guided hints": "Tema da interface, modo de cor, controles do mouse, espessuras de linha e dicas guiadas", + "Source code of the open-source core (AGPL-3.0-only) on GitHub": "Código-fonte do núcleo de código aberto (AGPL-3.0-only) no GitHub", + "More about The scan": "Mais sobre o scan", + "More about Alignment": "Mais sobre o alinhamento", + "More about Elements": "Mais sobre os elementos", + "Cut the scan with a plane, to measure the cut in the 2D Measure workspace": "Cortar o scan com um plano, para medir o corte na área de Medição 2D", + "More about Dimensions": "Mais sobre as cotas", + "More about Geometric tolerances": "Mais sobre tolerâncias geométricas", + "Fit to view — bring the whole model back on screen, without turning it": "Ajustar à vista — traz o modelo inteiro de volta à tela, sem girá-lo", + "Fit to view": "Ajustar à vista", + "Close": "Fechar", + "Load a scan first": "Carregue um scan primeiro", + "How the scan is drawn": "Como o scan é desenhado", + "Draw the scan transparent — what lies inside it, the fitted elements and the pinned readings show instead of hiding behind the surface in front of them": "Desenha o scan transparente — o que está dentro dele, os elementos ajustados e as leituras fixadas aparecem em vez de ficarem atrás da superfície à frente", + "Draw the triangle edges on the scan, to see how fine it is and where it is patchy. The mesh comes up as you zoom in: from a distance the triangles are smaller than a pixel, and the edges fade out rather than turning the part black": "Desenha as arestas dos triângulos no scan, para ver quão fino ele é e onde é irregular. A malha aparece conforme você aproxima: de longe os triângulos são menores que um pixel, e as arestas somem em vez de escurecer a peça", + "Colour the far side of every triangle — holes in the scan and inverted normals stop looking like solid part": "Colore o lado oposto de cada triângulo — furos no scan e normais invertidas deixam de parecer peça sólida", + "Nothing measured yet — fit an element or cut a section and it shows here": "Nada medido ainda — ajuste um elemento ou corte uma seção e aparecerá aqui", + "How the elements are drawn": "Como os elementos são desenhados", + "Show the name tags and readouts on the part. The fitted elements, the sections and the dimension lines stay either way": "Mostra os rótulos de nome e as leituras sobre a peça. Os elementos ajustados, as seções e as linhas de cota permanecem de qualquer forma", + "Cut the parts open at a plane to look inside — the scan, the reference and everything measured on them. Closed parts are closed where they are cut; an open scan stays a shell. Slide the plane with the arrow, tilt it with the rings": "Corta as peças em um plano para ver o interior — o scan, a referência e tudo o que foi medido neles. Peças fechadas são fechadas onde são cortadas; um scan aberto continua uma casca. Deslize o plano com a seta, incline-o com os anéis", + "Measurement": "Medição", + "Method": "Método", + "Ray along the normal": "Raio ao longo da normal", + "Sphere across the wall": "Esfera através da parede", + "Max. thickness": "Espessura máx.", + "Rays": "Raios", + "Single ray — fast": "Raio único — rápido", + "6-ray cone — tighter": "Cone de 6 raios — mais preciso", + "12-ray cone — tightest": "Cone de 12 raios — o mais preciso", + "Far surface must face back": "Superfície oposta deve estar voltada para trás", + "Max. deviation of normals": "Desvio máx. das normais", + "Measure wall thickness": "Medir espessura de parede", + "Everything above shapes the search, so changing it means measuring again.": "Tudo acima define a busca, então alterar qualquer item exige medir de novo.", + "Wall thickness": "Espessura de parede", + "Load a scan and measure how thick its walls are, everywhere at once. No reference model, no alignment. STL, PLY, OBJ, 3MF or STEP, in millimetres — everything stays in this browser.": "Carregue um scan e meça a espessura de suas paredes, em todos os pontos de uma vez. Sem modelo de referência, sem alinhamento. STL, PLY, OBJ, 3MF ou STEP, em milímetros — tudo fica neste aplicativo.", + "More about How thickness is measured": "Mais sobre como a espessura é medida", + "More about Max. thickness": "Mais sobre a espessura máx.", + "More about Rays": "Mais sobre os raios", + "More about Far surface must face back": "Mais sobre a superfície oposta voltada para trás", + "More about Max. deviation of normals": "Mais sobre o desvio máx. das normais", + "Image": "Imagem", + "No image loaded": "Nenhuma imagem carregada", + "PNG or JPEG": "PNG ou JPEG", + "Measuring a flatbed scan": "Medindo um scan de mesa", + "Scan the part face-down on a flatbed scanner and open the image — then fit points, lines and circles to its edges and measure between them, the way a measuring microscope does. PNG or JPEG, at the highest optical resolution you have. Everything stays in this browser.": "Escaneie a peça virada para baixo em um scanner de mesa e abra a imagem — depois ajuste pontos, linhas e círculos às bordas dela e meça entre eles, como faz um microscópio de medição. PNG ou JPEG, na maior resolução óptica que você tiver. Tudo fica neste aplicativo.", + "The flatbed scan of the part": "O scan de mesa da peça", + "More about What is measured": "Mais sobre o que é medido", + "CAD kernel ready — OCCT 8.0.1 via occt-wasm 5.7.1.": "Kernel CAD pronto — OCCT 8.0.1 via occt-wasm 5.7.1.", + "Parameters": "Parâmetros", + "+ Add": "+ Adicionar", + "Create feature": "Criar recurso", + "Sketch on": "Esboço em", + "Choose a plane…": "Escolher um plano…", + "Extrude": "Extrudar", + "Revolve": "Revolucionar", + "Extract": "Extrair", + "Import STEP": "Importar STEP", + "Shell": "Casca", + "Fillet": "Arredondamento", + "Chamfer": "Chanfro", + "Draft": "Ângulo de saída", + "Move face": "Mover face", + "Delete face": "Excluir face", + "Hole": "Furo", + "Thread": "Rosca", + "Cut by plane": "Cortar por plano", + "Mirror": "Espelhar", + "Combine": "Combinar", + "Sheet": "Folha", + "Split": "Dividir", + "Loft": "Loft", + "Sweep": "Varredura", + "Pattern": "Padrão", + "Move body": "Mover corpo", + "Offset body": "Deslocar corpo", + "Fit surface": "Ajustar superfície", + "Patch network": "Rede de remendos", + "Trim": "Aparar", + "Thicken": "Espessar", + "Start with a sketch: choose a coordinate plane above. No scan is needed to model.": "Comece com um esboço: escolha um plano de coordenadas acima. Não é preciso scan para modelar.", + "Bodies": "Corpos", + "XY plane": "Plano XY", + "XZ plane": "Plano XZ", + "YZ plane": "Plano YZ", + "Curvature": "Curvatura", + "Overlaps": "Sobreposições", + "Tangent edges": "Arestas tangentes", + "Drawn": "Desenhadas", + "Dimmed": "Atenuadas", + "Off": "Desligado", + "Deviation": "Desvio", + "More about Reverse engineering": "Mais sobre engenharia reversa", + "Show the parameters": "Mostrar os parâmetros", + "More about Parameters": "Mais sobre os parâmetros", + "Add a parameter": "Adicionar um parâmetro", + "More about Features": "Mais sobre os recursos", + "Extrude a sketch's regions — pick them in the viewport": "Extruda as regiões de um esboço — selecione-as na vista", + "Revolve a sketch's regions about an axis": "Revoluciona as regiões de um esboço em torno de um eixo", + "A measured plane, cylinder, cone or sphere as a body at its extents, extended as in 3D Measure — a boss or a pin without a sketch — or a shadow section as its block, the outline through the part's thickness": "Um plano, cilindro, cone ou esfera medido como corpo em suas extensões, estendido como na Medição 3D — um ressalto ou pino sem esboço — ou uma seção de sombra como seu bloco, o contorno através da espessura da peça", + "Bring a STEP file in as bodies of its own — a bought-in part, a mating part, the CAD model to check the scan against. A body for every solid in the file, named as the file names its parts; Move body puts them where they belong": "Traz um arquivo STEP como corpos próprios — uma peça comprada, uma peça de encaixe, o modelo CAD para conferir o scan. Um corpo para cada sólido do arquivo, nomeado como o arquivo nomeia suas peças; Mover corpo os coloca onde devem ficar", + "Hollow the body to a wall — click the faces to open, or none for a closed hollow; the wall comes from the Thickness map when one was measured": "Esvazia o corpo até uma parede — clique nas faces a abrir, ou em nenhuma para um oco fechado; a parede vem do mapa de espessura, quando um foi medido", + "Round edges of the body — click them; the radius is read off the scan across the first": "Arredonda arestas do corpo — clique nelas; o raio é lido do scan na primeira", + "Chamfer edges of the body — click them": "Chanfra arestas do corpo — clique nelas", + "Lean faces of the body about a neutral plane — a moulding's draft, put on after the fact: click the flat face that stays, then the faces to lean": "Inclina faces do corpo em torno de um plano neutro — o ângulo de saída de uma moldagem, aplicado depois: clique na face plana que fica, depois nas faces a inclinar", + "Offset faces of the body by a distance — a flat face along its normal, a round face in radius; several picked together grow or shrink as a whole, a cylinder's wall and its ends. One face can be snapped onto the scan instead: pick it, take Snap to scan, and click the scan where it should lie": "Desloca faces do corpo por uma distância — uma face plana ao longo da normal, uma face redonda no raio; várias selecionadas juntas crescem ou encolhem como um todo, a parede de um cilindro e suas extremidades. Uma face pode, em vez disso, ser encaixada no scan: selecione-a, use Encaixar no scan e clique no scan onde ela deve ficar", + "Delete faces of the body and heal the gap — a fillet, a chamfer, a hole or a boss taken out, the faces round it run on to close over where it was: click a face of it, and the rest of the round or hole comes with it": "Exclui faces do corpo e fecha a lacuna — um arredondamento, chanfro, furo ou ressalto removido, as faces ao redor se prolongam para cobrir onde estava: clique numa face dele e o restante do arredondamento ou furo vai junto", + "A hole through the body along a cylinder or circle element, or a round face of the body": "Um furo através do corpo ao longo de um elemento cilindro ou círculo, ou de uma face redonda do corpo", + "A modeled screw thread on a round face of the body — a bolt's on a boss, a nut's in a bore: ISO metric, UNC or UNF, a 45° form that prints without supports, or a diameter and pitch of your own, with an offset for clearance when printing": "Uma rosca modelada em uma face redonda do corpo — a de um parafuso em um ressalto, a de uma porca em um furo: métrica ISO, UNC ou UNF, um perfil de 45° que imprime sem suportes, ou um diâmetro e passo seus, com uma folga de compensação para impressão", + "Cut the body at a plane and keep one side": "Corta o corpo em um plano e mantém um dos lados", + "Mirror the body about a plane, joined with the source": "Espelha o corpo em relação a um plano, unido ao original", + "Join, cut or intersect two bodies made separately — or trim a sheet by a solid, or sew two sheets into one, before Thicken": "Une, corta ou intersecta dois corpos feitos separadamente — ou apara uma folha por um sólido, ou costura duas folhas em uma, antes de Espessar", + "A sketch's pieces — an open chain will do — extruded as a surface: a cutter to split a body with": "As partes de um esboço — uma cadeia aberta serve — extrudadas como superfície: um cortador para dividir um corpo", + "Split a body by a sheet and keep the fragments that lie on the scan — or trim a sheet by sheets or solids, keeping its pieces on the scan": "Divide um corpo por uma folha e mantém os fragmentos que ficam sobre o scan — ou apara uma folha por folhas ou sólidos, mantendo suas partes sobre o scan", + "A solid through the profiles of two or more sketches on their own planes": "Um sólido através dos perfis de dois ou mais esboços em seus próprios planos", + "A sketch's region swept along a chain of lines and arcs in another sketch — a bent bar, a handle, a rim that follows a curve": "A região de um esboço varrida ao longo de uma cadeia de linhas e arcos em outro esboço — uma barra dobrada, uma alça, uma borda que segue uma curva", + "Copies of the body about an axis, along a direction, or in a grid": "Cópias do corpo em torno de um eixo, ao longo de uma direção ou em grade", + "Move and turn bodies — the ones selected on the part or picked in the body list (Ctrl+click or Shift+click rows for several), or click them on the part: drag the gizmo's arrows and rings, or type the distances and angles. Tick Copy to move a copy and leave the body where it is": "Move e gira corpos — os selecionados na peça ou escolhidos na lista de corpos (Ctrl+clique ou Shift+clique nas linhas para vários), ou clique neles na peça: arraste as setas e anéis do manipulador, ou digite as distâncias e ângulos. Marque Copiar para mover uma cópia e deixar o corpo onde está", + "Grow a body all round by a distance, or shrink it — kept beside it as a body of its own, a keep-out round a part or the pocket that holds it with clearance": "Aumenta um corpo por igual em todos os lados por uma distância, ou o contrai — mantido ao lado como um corpo próprio, uma zona de exclusão ao redor de uma peça ou o bolsão que a abriga com folga", + "A smooth freeform surface fitted through a region of the scan — click faces or mark them — for the trim to close into a solid with the others": "Uma superfície livre e suave ajustada a uma região do scan — clique em faces ou marque-as — para o aparo fechar em um sólido com as demais", + "Curves drawn on the scan, and the regions they close filled with patches fitted to the scan together — tangent across every curve not marked a crease, sharing their edges: a sheet, or a solid where the network closes. For a freeform skin that folds back or wants more than one patch": "Curvas desenhadas sobre o scan, e as regiões que elas fecham preenchidas com remendos ajustados ao scan em conjunto — tangentes em cada curva não marcada como vinco, compartilhando suas arestas: uma folha, ou um sólido onde a rede fecha. Para uma pele livre que se dobra sobre si ou precisa de mais de um remendo", + "Close a set of fitted surfaces, sheets and measured planes, cylinders, cones and spheres into a solid: the faces they cut space into are kept where they lie on the scan and sewn": "Fecha um conjunto de superfícies ajustadas, folhas e planos, cilindros, cones e esferas medidos em um sólido: as faces em que dividem o espaço são mantidas onde ficam sobre o scan e costuradas", + "Make a sheet or a fitted surface a solid of a thickness — a freeform thin shell's wall from its one skin, where a shell or an offset of a lofted body will not go": "Transforma uma folha ou superfície ajustada em um sólido de determinada espessura — a parede de uma casca fina livre a partir de sua única pele, onde uma casca ou um deslocamento de um corpo em loft não funciona", + "The XY plane through the origin — click it in the viewport to sketch on it": "O plano XY pela origem — clique nele na vista para esboçar sobre ele", + "Hide the XY plane": "Ocultar o plano XY", + "The XZ plane through the origin — click it in the viewport to sketch on it": "O plano XZ pela origem — clique nele na vista para esboçar sobre ele", + "Hide the XZ plane": "Ocultar o plano XZ", + "The YZ plane through the origin — click it in the viewport to sketch on it": "O plano YZ pela origem — clique nele na vista para esboçar sobre ele", + "Hide the YZ plane": "Ocultar o plano YZ", + "Load a scan first — the map reads its surface": "Carregue um scan primeiro — o mapa lê a superfície dele", + "Build a body first": "Construa um corpo primeiro", + "How the bodies are drawn": "Como os corpos são desenhados", + "Draw the bodies transparent — the scan, the elements and the bodies inside them show through. With the scan solid as well, each part shows where it stands proud of the other": "Desenha os corpos transparentes — o scan, os elementos e os corpos dentro deles aparecem através. Com o scan sólido também, cada peça mostra onde se projeta além da outra", + "Draw where two bodies run into each other in hatched red — what they share, over the faces of both that would otherwise fight for the same pixels. Off for bodies that overlap on purpose, a modifier volume for a slicer; saved with the project": "Desenha em vermelho hachurado onde dois corpos se interpenetram — o que compartilham, sobre as faces de ambos que de outra forma disputariam os mesmos pixels. Desligue para corpos que se sobrepõem de propósito, um volume modificador para um fatiador; salvo com o projeto", + "How the edges are drawn where a round runs into a flat. A round face's seam and the cut a join leaves across one surface are never drawn": "Como as arestas são desenhadas onde uma face redonda encontra uma plana. A emenda de uma face redonda e o corte que uma junção deixa numa superfície nunca são desenhados", + "Tangent edges drawn — where a round runs into a flat — as the sharp ones are, the way CAD draws them": "Arestas tangentes desenhadas — onde uma face redonda encontra uma plana — como as vivas, do jeito que o CAD as desenha", + "Tangent edges dimmed": "Arestas tangentes atenuadas", + "Tangent edges left out, and not picked: a round shows by its outline against the background": "Arestas tangentes omitidas e não selecionáveis: uma face redonda aparece pelo contorno contra o fundo", + "Nothing measured yet — fit elements or cut sections in the 3D Measure workspace and they show here": "Nada medido ainda — ajuste elementos ou corte seções na área de Medição 3D e aparecerão aqui", + "Build a body first — the map reads the scan against it": "Construa um corpo primeiro — o mapa lê o scan em relação a ele", + "Tools": "Ferramentas", + "Reduce mesh": "Reduzir malha", + "Remove islands": "Remover ilhas", + "Delete marked": "Excluir marcados", + "Flip normals": "Inverter normais", + "Cut": "Cortar", + "Fill holes": "Preencher furos", + "Smooth": "Suavizar", + "Shrink/expand": "Contrair/expandir", + "Boolean": "Booleana", + "Trim to scan": "Aparar ao scan", + "Shell/hollow": "Casca/esvaziar", + "Tolerance": "Tolerância", + "Triangle limit": "Limite de triângulos", + "Reduce": "Reduzir", + "Both figures shape the reduction, so changing them means reducing again.": "Os dois valores definem a redução, então alterá-los exige reduzir de novo.", + "Cleaning up and reducing a scan": "Limpando e reduzindo um scan", + "Load a scan and clean it up — clear the stray islands, delete what is not the part, turn it the right way out, cut off the base it stood on, fill its holes, smooth out the scatter, expand or shrink it, hollow it — and reduce it to a light mesh to model over in CAD, held within a tolerance you set, measured against the scan to prove it, and exported where you aligned it. STL, PLY, OBJ or 3MF, in millimetres — everything stays in this browser.": "Carregue um scan e limpe-o — remova as ilhas soltas, apague o que não é a peça, vire-o para o lado certo, corte a base em que estava apoiado, preencha os furos, suavize o ruído, expanda ou contraia, esvazie — e simplifique-o em uma malha leve para modelar sobre ela no CAD, dentro de uma tolerância definida por você, medida contra o scan para comprovar, e exportada onde você a alinhou. STL, PLY, OBJ ou 3MF, em milímetros — tudo fica neste aplicativo.", + "Reduced": "Reduzida", + "Accuracy": "Precisão", + "More about Mesh tools": "Mais sobre as ferramentas de malha", + "Mesh tool": "Ferramenta de malha", + "A light copy of the scan to model over in CAD, held within a tolerance you set — the scan itself stays as it is": "Uma cópia leve do scan para modelar sobre ela no CAD, dentro de uma tolerância definida por você — o próprio scan permanece como está", + "Find the specks floating off the scan — every piece joined to nothing else and smaller than a size you set — mark them on the part, and delete them once you say so": "Encontra os fragmentos soltos do scan — cada pedaço sem ligação com o resto e menor que um tamanho definido por você — marca-os na peça e os exclui quando você mandar", + "Mark surface on the part by hand — a window, a brush or a lasso — and delete it from the scan: a fixture, a clamp, a patch of the table": "Marque superfície na peça à mão — uma janela, um pincel ou um laço — e exclua-a do scan: um gabarito, uma presilha, um pedaço da mesa", + "Turn the surface over so it faces the other way — the whole part, exported inside-out, or a patch you mark that faces in": "Vira a superfície para que fique voltada para o outro lado — a peça inteira, exportada do avesso, ou um trecho marcado que está voltado para dentro", + "Cut the scan with a plane and take one side off — the turntable it stood on, or half the part — closing it flat where the plane went through": "Corta o scan com um plano e remove um dos lados — o prato giratório em que estava, ou metade da peça — fechando-o plano onde o plano passou", + "Close the holes a scanner left, every one smaller than a size you set, with patches that carry the surface across": "Fecha os furos deixados pelo scanner, todos menores que um tamanho definido por você, com remendos que continuam a superfície", + "Take the scanner’s scatter out of the surface, never moving any point further than a limit you set, sharp edges held": "Remove o ruído do scanner da superfície, sem mover nenhum ponto além de um limite definido por você, mantendo as arestas vivas", + "Move the surface along its normal by a distance — out or in, the whole part or a marked region — not a scale: a bore gets narrower as the part grows": "Move a superfície ao longo da normal por uma distância — para fora ou para dentro, a peça inteira ou uma região marcada — não é escala: um furo fica mais estreito conforme a peça cresce", + "Take a solid body out of the scan, add it, or keep what the two share — a bore the scanner could not see to the bottom of, cut out with a cylinder modelled in Reverse engineer": "Subtrai um corpo sólido do scan, soma-o, ou mantém o que os dois compartilham — um furo cujo fundo o scanner não alcançou, cortado com um cilindro modelado em Engenharia reversa", + "Cut a body modelled to mend a broken part to fit the scanned break — the missing piece, to print and glue in": "Corta um corpo modelado para consertar uma peça quebrada de modo a encaixar na quebra escaneada — a peça faltante, para imprimir e colar", + "Hollow a closed part, leaving a wall of a thickness you set round a sealed cavity — or thicken an open surface into a solid": "Esvazia uma peça fechada, deixando uma parede de espessura definida por você em torno de uma cavidade selada — ou espessa uma superfície aberta até virar um sólido", + "More about Reducing a scan for CAD": "Mais sobre reduzir um scan para CAD", + "More about Tolerance": "Mais sobre a tolerância", + "More about Triangle limit": "Mais sobre o limite de triângulos", + "What to show of the reduction": "O que mostrar da redução", + "Reduce the scan first — the view keys show the result": "Reduza o scan primeiro — as teclas de vista mostram o resultado", + "Edit the scan first — the deviation shows how far the edited copy is from the scan in use": "Edite o scan primeiro — o desvio mostra o quanto a cópia editada se afasta do scan em uso", + "How the instrument looks and behaves. Remembered in this browser — nothing here is a property of the part, and none of it is saved with a project.": "Como o instrumento se apresenta e se comporta. Lembrado neste aplicativo — nada aqui é propriedade da peça, e nada é salvo com o projeto.", + "Units": "Unidades", + "Lengths in": "Comprimentos em", + "What is shown and typed. Files, projects and agent answers stay in millimetres.": "O que é exibido e digitado. Arquivos, projetos e respostas de agentes permanecem em milímetros.", + "Appearance": "Aparência", + "Interface": "Interface", + "Light": "Claro", + "Dark": "Escuro", + "System": "Sistema", + "Light or dark chassis.": "Chassi claro ou escuro.", + "follows the operating system's setting, and changes with it.": "segue a configuração do sistema operacional e muda junto com ela.", + "Colour mode": "Modo de cor", + "Studio grey": "Cinza estúdio", + "Scanner blue": "Azul scanner", + "Glossy blue under a hard light — surface detail stands out": "Azul brilhante sob luz dura — os detalhes da superfície se destacam", + ". What has been measured keeps its colour either way — the element tints and the deviation ramp are the same in both.": ". O que foi medido mantém a cor de qualquer forma — as cores dos elementos e a rampa de desvio são as mesmas nos dois.", + "Colour map": "Mapa de cores", + "Blue through cyan, green and yellow to red, a saturated green in the middle — the inspection-software classic. Uneven in lightness, so cyan and yellow show as bands the part does not have": "Do azul ao vermelho passando por ciano, verde e amarelo, com um verde saturado no meio — o clássico dos softwares de inspeção. Irregular em luminosidade, então ciano e amarelo aparecem como faixas que a peça não tem", + ". Every map painted on the part, and the scale beside it, is drawn in it; the block at either end is what a reading past that end of the scale wears.": ". Todo mapa pintado na peça, e a escala ao lado, é desenhado com ele; o bloco em cada extremidade é a cor de uma leitura além dessa extremidade da escala.", + "Sharp edges": "Arestas vivas", + "Always sharp": "Sempre vivas", + "Auto — sharp on a CAD-like mesh": "Auto — vivas em malha tipo CAD", + "Always smooth": "Sempre suaves", + "Sharp from": "Viva a partir de", + "Whether an edge of the scan is shaded as a crease, each face with its own normal, or smoothed across so a box looks pillowed — and from what angle between its two faces an edge counts as sharp.": "Se uma aresta do scan é sombreada como um vinco, cada face com sua própria normal, ou suavizada de lado a lado, de modo que uma caixa pareça almofadada — e a partir de que ângulo entre as duas faces uma aresta conta como viva.", + "draws a CAD mesh's faces flat and a scan's real edges — the rim of a bore, a step — as edges; noise seldom tips a scan's triangles that far, and if it still shows, a larger angle, 50° or 60°, leaves it smooth.": "desenha as faces de uma malha CAD planas e as arestas reais de um scan — a borda de um furo, um degrau — como arestas; o ruído raramente inclina os triângulos de um scan tanto assim, e se ainda aparecer, um ângulo maior, 50° ou 60°, a deixa suave.", + "Auto": "Auto", + "draws the creases only on a mesh that reads as a tessellation of CAD, a scan smooth all over. A reference part is always drawn sharp, from": "desenha os vincos apenas em uma malha que parece uma tesselação de CAD, e um scan liso por inteiro. Uma peça de referência é sempre desenhada com arestas vivas, a partir de", + "Navigation": "Navegação", + "Mouse controls": "Controles do mouse", + "left-drag orbit · right-drag pan · scroll zoom": "arrastar esquerdo: órbita · arrastar direito: deslocar · rolagem: zoom", + "works beside any of these, in Chrome and Edge: push or pull the puck to zoom, slide it to pan, tilt, twist and roll it to turn the part. It connects the first time you touch it, and drives the view the mouse was last over.": "funciona junto com qualquer um destes, no Chrome e no Edge: empurre ou puxe o botão para dar zoom, deslize para deslocar, incline, gire e role para girar a peça. Conecta na primeira vez que você o toca e controla a vista sobre a qual o mouse esteve por último.", + "Files": "Arquivos", + "Ask what units an STL is in": "Perguntar em que unidades está um STL", + "— the format carries none, and a part read at the wrong scale measures wrong in every number after. Off, every STL is read in the units below without asking.": "— o formato não traz nenhuma, e uma peça lida na escala errada mede errado em todos os números seguintes. Desligado, todo STL é lido nas unidades abaixo sem perguntar.", + "STL import units": "Unidades de importação de STL", + "What an STL is read in when the question is off, and the answer the question offers first — the last one given. A PLY or OBJ is always read in millimetres; a STEP file says its own.": "A unidade em que um STL é lido quando a pergunta está desligada, e a resposta que a pergunta oferece primeiro — a última dada. Um PLY ou OBJ é sempre lido em milímetros; um arquivo STEP informa a sua própria.", + "Lines": "Linhas", + "Section cuts": "Cortes de seção", + "How heavy a section's cut is drawn on the part in 3D Measure. The curves measured on its sheet follow in proportion.": "Quão grossa é desenhada a linha de corte de uma seção na peça, na Medição 3D. As curvas medidas em sua folha acompanham em proporção.", + "2D fitted curves": "Curvas ajustadas 2D", + "The lines, circles and splines fitted over a flatbed scan in 2D Measure, and the callouts drawn with them.": "As linhas, círculos e splines ajustados sobre um scan de mesa na Medição 2D, e as chamadas desenhadas com eles.", + "2D edges": "Arestas 2D", + "The edge chains the curves are fitted to — found in the scan, or cut by a section.": "As cadeias de arestas às quais as curvas são ajustadas — encontradas no scan ou cortadas por uma seção.", + "Guidance": "Orientação", + "Guided hints": "Dicas guiadas", + "— ring the control to press next, until you have been through a workspace twice. Switching it back on starts the guidance over.": "— destaca o controle a pressionar em seguida, até você ter passado duas vezes por uma área de trabalho. Reativar reinicia a orientação.", + "Agents": "Agentes", + "Local agent connection": "Conexão com agente local", + "— let an AI agent on this computer (Claude Code, Claude Desktop, Cursor …) drive this tab through its ScanRuler tools, the scanruler-mcp server. You watch every step here and can undo it.": "— permite que um agente de IA neste computador (Claude Code, Claude Desktop, Cursor …) controle esta janela por meio de suas ferramentas do ScanRuler, o servidor scanruler-mcp. Você acompanha cada passo aqui e pode desfazê-lo.", + "Your data no longer stays on this computer:": "Seus dados deixam de ficar neste computador:", + "what the agent reads — measurements, reports, pictures of the part, file names, notes — is processed by the agent and goes to its model’s provider, unless the model runs on this computer. ScanRuler itself sends nothing anywhere; the connection is to 127.0.0.1.": "o que o agente lê — medições, relatórios, imagens da peça, nomes de arquivos, anotações — é processado pelo agente e vai para o provedor do modelo, a menos que o modelo rode neste computador. O próprio ScanRuler não envia nada a lugar nenhum; a conexão é com 127.0.0.1.", + "Port": "Porta", + "Token": "Token", + "prints the port and the token, and a link that fills them in.": "exibe a porta e o token, e um link que os preenche.", + "Support": "Apoio", + "Support card": "Cartão de apoio", + "— the thank-you in the corner of the stage, with the ways to give something back. Its × closes it for this visit; switched off here it stays away, the big one below with it.": "— o agradecimento no canto da tela, com as formas de retribuir. O × o fecha nesta visita; desligado aqui, ele não aparece, e o grande abaixo também não.", + "Big card on saving": "Cartão grande ao salvar", + "— the same thank-you in the middle of the screen after a save or an export, at most once in ten minutes. The file does not wait for it. Its \"Don't show this again for a week\" shows here as off until the week is over.": "— o mesmo agradecimento no meio da tela após salvar ou exportar, no máximo uma vez a cada dez minutos. O arquivo não espera por ele. O \"Não mostrar de novo por uma semana\" dele aparece aqui como desligado até a semana acabar.", + "Dark blue through teal and green to yellow. Perceptually uniform — an even step in the reading is an even step in colour — and as legible to colour-blind eyes and in greyscale": "Azul escuro passando por azul-petróleo e verde até amarelo. Perceptualmente uniforme — um passo igual na leitura é um passo igual na cor — e igualmente legível para daltônicos e em escala de cinza", + "Black through purple and pink to pale cream. Perceptually uniform and colour-blind safe": "Preto passando por roxo e rosa até creme claro. Perceptualmente uniforme e seguro para daltônicos", + "Indigo through magenta and orange to yellow — no black end. Perceptually uniform and colour-blind safe": "Índigo passando por magenta e laranja até amarelo — sem extremidade preta. Perceptualmente uniforme e seguro para daltônicos", + "Black through purple, red and orange to pale yellow. Perceptually uniform and colour-blind safe": "Preto passando por roxo, vermelho e laranja até amarelo claro. Perceptualmente uniforme e seguro para daltônicos", + "Navy through grey to yellow, made to look the same to red–green colour blindness as to anyone else. Perceptually uniform": "Azul-marinho passando por cinza até amarelo, feito para parecer igual para quem tem daltonismo vermelho–verde e para qualquer outra pessoa. Perceptualmente uniforme", + "Black through deep blue and teal to mint. Perceptually uniform and colour-blind safe": "Preto passando por azul profundo e azul-petróleo até verde-menta. Perceptualmente uniforme e seguro para daltônicos", + "Black through purple and red to cream. Perceptually uniform and colour-blind safe": "Preto passando por roxo e vermelho até creme. Perceptualmente uniforme e seguro para daltônicos", + "A rainbow like jet, dark blue through green to dark red, but smooth — without the bands jet shows. Not for red–green colour blindness": "Um arco-íris como o jet, do azul escuro ao vermelho escuro passando por verde, mas suave — sem as faixas que o jet mostra. Não indicado para daltonismo vermelho–verde", + "Across": "Através de", + "Flip": "Inverter", + "What the plane is laid across: a coordinate plane through the part’s middle, or an element with a direction — a plane’s normal, the axis of a cylinder, cone or line, a circle’s normal. The half toward you is taken away.": "Sobre o que o plano é posicionado: um plano de coordenadas pelo meio da peça, ou um elemento com direção — a normal de um plano, o eixo de um cilindro, cone ou linha, a normal de um círculo. A metade voltada para você é removida.", + "Where along its direction the plane lies — drag the arrow on the part, or type it here": "Onde, ao longo da direção, o plano fica — arraste a seta na peça ou digite aqui", + "Take away the other half instead": "Remover a outra metade", + "set otherwise in its menu": "definido de outra forma no menu dele" +} diff --git a/desktop/i18n/zh-CN.json b/desktop/i18n/zh-CN.json new file mode 100644 index 00000000..55fc9e6d --- /dev/null +++ b/desktop/i18n/zh-CN.json @@ -0,0 +1,385 @@ +{ + "_name": "简体中文", + "_menu": { + "file": "文件", + "open": "打开扫描…", + "quit": "退出", + "view": "视图", + "help": "帮助", + "hardware": "硬件…", + "language": "语言", + "watch": "监视 Creality Scan 文件夹…", + "unwatch": "停止监视 Creality Scan", + "fresh": "项目中的新 Mesh.ply", + "openIt": "在 ScanRuler 中打开", + "ignore": "忽略" + }, + "_hw": { + "title": "硬件", + "gpu": "GPU", + "backendChosen": "已选择的 ANGLE 后端", + "backendRunning": "正在运行的 ANGLE 后端", + "renderer": "WebGL2 / 渲染器", + "webgpu": "WebGPU", + "cpu": "CPU(线程)", + "threads": "逻辑线程", + "isolation": "跨源隔离(WASM 线程)", + "npu": "NPU / WebNN", + "versions": "Electron / Chromium", + "backend": "图形后端:", + "restart": "重启以应用", + "changed": "后端已更改:请重启以应用。", + "yes": "是", + "no": "否", + "available": "可用", + "unavailable": "不可用", + "default": "默认", + "experimental": "实验性", + "webgpuWhy": "Windows 上的 Chromium 仅在 DirectX 11 后端提供 WebGPU;ScanRuler 不使用它。" + }, + "CNC Kitchen · 3D scan analysis ·": "CNC Kitchen · 3D 扫描分析 ·", + "3D Measure": "3D 测量", + "Surface Deviation": "表面偏差", + "Wall Thickness": "壁厚", + "2D Measure": "2D 测量", + "Reverse engineer": "逆向工程", + "Mesh Tools": "网格工具", + "No unsaved changes": "没有未保存的更改", + "New": "新建", + "Project": "项目", + "Save": "保存", + "Load": "加载", + "Settings": "设置", + "Model": "模型", + "No scan loaded": "未加载扫描", + "STL, PLY, OBJ, 3MF or STEP · mm": "STL、PLY、OBJ、3MF 或 STEP · mm", + "Open…": "打开…", + "Drop it anywhere in the window.": "可拖放到窗口中的任意位置。", + "Alignment": "对齐", + "Auto-align": "自动对齐", + "Align part": "对齐零件", + "Move / rotate by numbers": "按数值移动 / 旋转", + "Create element": "创建元素", + "Point": "点", + "Line": "直线", + "Plane": "平面", + "Sphere": "球", + "Cylinder": "圆柱", + "Cone": "圆锥", + "Circle": "圆", + "Torus": "圆环", + "Section": "剖面", + "Create dimensions": "创建尺寸", + "New dimension": "新建尺寸", + "New tolerance": "新建公差", + "Fitting elements": "拟合元素", + "Load a scan, then pick features on it to fit spheres, cylinders and planes and measure between them. STL, PLY, OBJ, 3MF or STEP, in millimetres — everything stays in this browser.": "加载扫描后,在其上选取特征以拟合球、圆柱和平面,并测量它们之间的尺寸。支持 STL、PLY、OBJ、3MF 或 STEP,单位为毫米 — 所有数据都保留在此应用中。", + "Scan": "扫描", + "The part as measured": "被测零件的实际形状", + "Drop it here": "拖放到此处", + "Choose file…": "选择文件…", + "Drag & drop works anywhere in the window — the first empty slot takes it.": "可在窗口任意位置拖放 — 由第一个空位接收文件。", + "Thanks for using ScanRuler!": "感谢使用 ScanRuler!", + "This tool is provided": "本工具由", + "completely free": "完全免费", + "by CNC Kitchen. Once you have finished measuring, why not have a look at the store that keeps us making things like this?": "提供,来自 CNC Kitchen。测量完成后,不妨看看支持我们持续制作此类工具的商店?", + "Visit CNCKitchen.STORE": "访问 CNCKitchen.STORE", + "Send a tip on PayPal": "通过 PayPal 打赏", + "Send a tip on Ko-fi": "通过 Ko-fi 打赏", + "Transparent": "透明", + "Mesh": "网格", + "Backfaces": "背面", + "Elements": "元素", + "Labels": "标签", + "READY": "就绪", + "IMPRINT": "法律声明", + "LOCAL ONLY": "仅限本地", + "· FILES NEVER LEAVE YOUR BROWSER": "· 文件绝不会离开您的应用", + "Units of": "单位:", + "An STL file carries no units — a 1 in it is whatever the program that wrote it meant. Say what this one is in and it is read in millimetres, like everything measured here.": "STL 文件不含单位 — 其中的 1 表示什么,取决于生成该文件的程序。请指明此文件所用的单位,它将被换算为毫米读取,与这里测量的其他内容一致。", + "Coordinates in": "坐标单位:", + "Millimetres": "毫米", + "Centimetres": "厘米", + "Metres": "米", + "Inches": "英寸", + "Don't ask again": "不再询问", + "— read every STL in these units. Settings brings the question back.": "— 以这些单位读取所有 STL。可在设置中重新开启此提问。", + "Cancel": "取消", + "Open": "打开", + "Your data will no longer stay on this computer": "您的数据将不再仅保留在这台电脑上", + "Without an agent, everything stays in this browser. With one connected, what the agent reads — measurements, reports, pictures of the part, file names, notes, and any file it opens itself — is processed by the agent and the AI model behind it.": "未连接智能体时,所有内容都保留在此应用中。连接智能体后,智能体读取的内容 — 测量结果、报告、零件图片、文件名、备注,以及它自行打开的任何文件 — 都会由该智能体及其背后的 AI 模型处理。", + "Unless that model runs on this computer, all of this goes to the model’s provider": "除非该模型运行在这台电脑上,否则这些内容都会发送给该模型的提供方", + ", under that provider’s terms.": ",并适用该提供方的条款。", + "ScanRuler itself sends nothing anywhere: the connection only reaches the agent’s helper on this computer. Don’t connect an agent while you work on parts you may not share with its provider.": "ScanRuler 本身不会向任何地方发送数据:该连接只会到达这台电脑上的智能体辅助程序。处理不可与其提供方共享的零件时,请勿连接智能体。", + "Asked once in this browser. Settings → Agents switches the connection off at any time.": "在此应用中仅询问一次。可随时在 设置 → 智能体 中关闭连接。", + "Connect the agent": "连接智能体", + "3D Measure\nFit spheres, cylinders and planes, and measure between them": "3D 测量\n拟合球、圆柱和平面,并测量它们之间的尺寸", + "Surface Deviation\nBest-fit the scan to a nominal part and map the difference": "表面偏差\n将扫描最佳拟合到名义零件,并绘制差异图", + "Wall Thickness\nMap the wall thickness of the part itself — no reference needed": "壁厚\n绘制零件自身的壁厚图 — 无需参考", + "2D Measure\nMeasure a flatbed scan the way a measuring microscope would": "2D 测量\n像测量显微镜一样测量平板扫描图", + "Reverse engineer\nSketch on sections, extrude and revolve, and export a solid STEP body built over the scan": "逆向工程\n在剖面上绘制草图,拉伸与旋转,并导出基于扫描构建的实体 STEP 模型", + "Mesh Tools\nClean the scan up — islands, holes, scatter — expand, shrink or hollow it, and reduce it to a light mesh for CAD held within a tolerance you set": "网格工具\n清理扫描 — 孤岛、孔洞、噪点 — 并可外扩、内缩或抽壳,再在您设定的公差内简化为适用于 CAD 的轻量网格", + "The project's name — what Save Project saves it under and every export is named on. Unnamed, they go by the scan or the image it holds.": "项目名称 — 保存项目时使用该名称,所有导出文件也以此命名。未命名时,则沿用其中扫描或图像的名称。", + "Project name": "项目名称", + "Untitled project": "未命名项目", + "Checkpoints are stored only in this browser. Checkpoints and project files include completed work; finish active drawings before leaving. Save Project downloads a portable copy.": "检查点仅保存在此应用中。检查点和项目文件包含已完成的工作;离开前请先完成正在进行的绘制。保存项目会下载一份可携带的副本。", + "Undo — Ctrl+Z": "撤销 — Ctrl+Z", + "Undo": "撤销", + "Redo — Ctrl+Y / Ctrl+Shift+Z": "重做 — Ctrl+Y / Ctrl+Shift+Z", + "Redo": "重做", + "Start a new project: the scan, the reference and the image closed, every measurement and every workspace's work let go, the name taken off — after asking, when there are changes not saved": "新建项目:关闭扫描、参考和图像,丢弃所有测量及各工作区的成果,并清除名称 — 若有未保存的更改,会先询问", + "Save the scan, the reference, the image and every measurement as one .scanruler file": "将扫描、参考、图像和所有测量保存为一个 .scanruler 文件", + "Open a .scanruler project — or a plain scan or image to start fresh": "打开 .scanruler 项目 — 或打开普通扫描或图像以重新开始", + "Interface theme, colour mode, mouse controls, line widths and guided hints": "界面主题、配色模式、鼠标操作、线宽和引导提示", + "Source code of the open-source core (AGPL-3.0-only) on GitHub": "开源核心(AGPL-3.0-only)在 GitHub 上的源代码", + "More about The scan": "进一步了解扫描", + "More about Alignment": "进一步了解对齐", + "More about Elements": "进一步了解元素", + "Cut the scan with a plane, to measure the cut in the 2D Measure workspace": "用平面剖切扫描,以便在 2D 测量工作区中测量剖面", + "More about Dimensions": "进一步了解尺寸", + "More about Geometric tolerances": "进一步了解几何公差", + "Fit to view — bring the whole model back on screen, without turning it": "适合视图 — 将整个模型带回屏幕内,且不旋转", + "Fit to view": "适合视图", + "Close": "关闭", + "Load a scan first": "请先加载扫描", + "How the scan is drawn": "扫描的显示方式", + "Draw the scan transparent — what lies inside it, the fitted elements and the pinned readings show instead of hiding behind the surface in front of them": "将扫描显示为透明 — 其内部的内容、拟合元素和固定的读数会显示出来,而不会被前方的表面遮挡", + "Draw the triangle edges on the scan, to see how fine it is and where it is patchy. The mesh comes up as you zoom in: from a distance the triangles are smaller than a pixel, and the edges fade out rather than turning the part black": "在扫描上绘制三角形边线,以查看其精细程度及不均匀之处。放大时网格逐渐显现:远距离下三角形小于一个像素,边线会淡出,而不会把零件染黑", + "Colour the far side of every triangle — holes in the scan and inverted normals stop looking like solid part": "为每个三角形的背面着色 — 扫描中的孔洞和反转的法线将不再看起来像实体部分", + "Nothing measured yet — fit an element or cut a section and it shows here": "尚无测量结果 — 拟合元素或剖切剖面后将显示在此", + "How the elements are drawn": "元素的显示方式", + "Show the name tags and readouts on the part. The fitted elements, the sections and the dimension lines stay either way": "在零件上显示名称标签和读数。无论是否显示,拟合元素、剖面和尺寸线都会保留", + "Cut the parts open at a plane to look inside — the scan, the reference and everything measured on them. Closed parts are closed where they are cut; an open scan stays a shell. Slide the plane with the arrow, tilt it with the rings": "沿平面剖开零件以查看内部 — 包括扫描、参考以及在它们上测得的一切。封闭的零件在剖切处会被封口;开放的扫描仍保持为壳体。用箭头滑动平面,用圆环倾斜平面", + "Measurement": "测量", + "Method": "方法", + "Ray along the normal": "沿法线的射线", + "Sphere across the wall": "跨壁的球", + "Max. thickness": "最大厚度", + "Rays": "射线", + "Single ray — fast": "单射线 — 快速", + "6-ray cone — tighter": "6 射线锥 — 更精确", + "12-ray cone — tightest": "12 射线锥 — 最精确", + "Far surface must face back": "对面表面必须朝向背面", + "Max. deviation of normals": "法线最大偏差", + "Measure wall thickness": "测量壁厚", + "Everything above shapes the search, so changing it means measuring again.": "以上所有设置都会影响搜索,因此更改后需要重新测量。", + "Wall thickness": "壁厚", + "Load a scan and measure how thick its walls are, everywhere at once. No reference model, no alignment. STL, PLY, OBJ, 3MF or STEP, in millimetres — everything stays in this browser.": "加载扫描,一次性测出各处的壁厚。无需参考模型,无需对齐。支持 STL、PLY、OBJ、3MF 或 STEP,单位为毫米 — 所有数据都保留在此应用中。", + "More about How thickness is measured": "进一步了解厚度的测量方式", + "More about Max. thickness": "进一步了解最大厚度", + "More about Rays": "进一步了解射线", + "More about Far surface must face back": "进一步了解对面表面必须朝向背面", + "More about Max. deviation of normals": "进一步了解法线最大偏差", + "Image": "图像", + "No image loaded": "未加载图像", + "PNG or JPEG": "PNG 或 JPEG", + "Measuring a flatbed scan": "测量平板扫描图", + "Scan the part face-down on a flatbed scanner and open the image — then fit points, lines and circles to its edges and measure between them, the way a measuring microscope does. PNG or JPEG, at the highest optical resolution you have. Everything stays in this browser.": "将零件正面朝下放在平板扫描仪上扫描并打开图像 — 然后在其边缘上拟合点、直线和圆,并测量它们之间的尺寸,就像测量显微镜那样。支持 PNG 或 JPEG,请使用您所能达到的最高光学分辨率。所有数据都保留在此应用中。", + "The flatbed scan of the part": "零件的平板扫描图", + "More about What is measured": "进一步了解测量内容", + "CAD kernel ready — OCCT 8.0.1 via occt-wasm 5.7.1.": "CAD 内核已就绪 — OCCT 8.0.1,通过 occt-wasm 5.7.1。", + "Parameters": "参数", + "+ Add": "+ 添加", + "Create feature": "创建特征", + "Sketch on": "草图基准:", + "Choose a plane…": "选择平面…", + "Extrude": "拉伸", + "Revolve": "旋转", + "Extract": "提取", + "Import STEP": "导入 STEP", + "Shell": "抽壳", + "Fillet": "圆角", + "Chamfer": "倒角", + "Draft": "拔模", + "Move face": "移动面", + "Delete face": "删除面", + "Hole": "孔", + "Thread": "螺纹", + "Cut by plane": "平面切割", + "Mirror": "镜像", + "Combine": "组合", + "Sheet": "曲面片", + "Split": "分割", + "Loft": "放样", + "Sweep": "扫掠", + "Pattern": "阵列", + "Move body": "移动实体", + "Offset body": "偏移实体", + "Fit surface": "拟合曲面", + "Patch network": "补片网络", + "Trim": "修剪", + "Thicken": "加厚", + "Start with a sketch: choose a coordinate plane above. No scan is needed to model.": "从草图开始:请在上方选择一个坐标平面。建模无需扫描。", + "Bodies": "实体", + "XY plane": "XY 平面", + "XZ plane": "XZ 平面", + "YZ plane": "YZ 平面", + "Curvature": "曲率", + "Overlaps": "重叠", + "Tangent edges": "相切边", + "Drawn": "显示", + "Dimmed": "淡化", + "Off": "关闭", + "Deviation": "偏差", + "More about Reverse engineering": "进一步了解逆向工程", + "Show the parameters": "显示参数", + "More about Parameters": "进一步了解参数", + "Add a parameter": "添加参数", + "More about Features": "进一步了解特征", + "Extrude a sketch's regions — pick them in the viewport": "拉伸草图的区域 — 在视口中选取", + "Revolve a sketch's regions about an axis": "将草图的区域绕轴旋转", + "A measured plane, cylinder, cone or sphere as a body at its extents, extended as in 3D Measure — a boss or a pin without a sketch — or a shadow section as its block, the outline through the part's thickness": "将测得的平面、圆柱、圆锥或球按其范围生成实体,范围的延伸方式与 3D 测量相同 — 无需草图即可得到凸台或销钉 — 或将投影剖面生成块体,即贯穿零件厚度的轮廓", + "Bring a STEP file in as bodies of its own — a bought-in part, a mating part, the CAD model to check the scan against. A body for every solid in the file, named as the file names its parts; Move body puts them where they belong": "将 STEP 文件导入为独立实体 — 外购件、配合件,或用于与扫描比对的 CAD 模型。文件中的每个实体对应一个实体,名称沿用文件中的零件名;用“移动实体”将它们放到正确位置", + "Hollow the body to a wall — click the faces to open, or none for a closed hollow; the wall comes from the Thickness map when one was measured": "将实体抽壳为壁 — 点击要开口的面,不点则为封闭空腔;若已测量过厚度图,壁厚取自厚度图", + "Round edges of the body — click them; the radius is read off the scan across the first": "为实体的边倒圆 — 点击这些边;半径取自扫描在第一条边处的读数", + "Chamfer edges of the body — click them": "为实体的边倒角 — 点击这些边", + "Lean faces of the body about a neutral plane — a moulding's draft, put on after the fact: click the flat face that stays, then the faces to lean": "将实体的面绕中性面倾斜 — 即事后添加的模具拔模:先点击保持不动的平面,再点击要倾斜的面", + "Offset faces of the body by a distance — a flat face along its normal, a round face in radius; several picked together grow or shrink as a whole, a cylinder's wall and its ends. One face can be snapped onto the scan instead: pick it, take Snap to scan, and click the scan where it should lie": "将实体的面偏移一段距离 — 平面沿其法线偏移,圆面则按半径偏移;同时选取的多个面会整体增大或缩小,例如圆柱的壁及其两端。也可改为将单个面吸附到扫描上:选取该面,选择“吸附到扫描”,然后在扫描上点击它应处的位置", + "Delete faces of the body and heal the gap — a fillet, a chamfer, a hole or a boss taken out, the faces round it run on to close over where it was: click a face of it, and the rest of the round or hole comes with it": "删除实体的面并修复缺口 — 去除圆角、倒角、孔或凸台后,周围的面会延伸并封住原位置:点击其中一个面,圆角或孔的其余部分会一并删除", + "A hole through the body along a cylinder or circle element, or a round face of the body": "沿圆柱或圆元素,或实体的圆面,在实体上打通孔", + "A modeled screw thread on a round face of the body — a bolt's on a boss, a nut's in a bore: ISO metric, UNC or UNF, a 45° form that prints without supports, or a diameter and pitch of your own, with an offset for clearance when printing": "在实体的圆面上建模的螺纹 — 螺栓的在凸台上,螺母的在孔内:ISO 公制、UNC 或 UNF,无需支撑即可打印的 45° 牙型,或自定义直径和螺距,并带有用于打印间隙的偏移量", + "Cut the body at a plane and keep one side": "用平面切割实体并保留一侧", + "Mirror the body about a plane, joined with the source": "将实体沿平面镜像,并与原实体合并", + "Join, cut or intersect two bodies made separately — or trim a sheet by a solid, or sew two sheets into one, before Thicken": "对分别创建的两个实体进行合并、切除或求交 — 或用实体修剪曲面片,或将两个曲面片缝合为一个,再进行加厚", + "A sketch's pieces — an open chain will do — extruded as a surface: a cutter to split a body with": "将草图的各段(开放链也可)拉伸为曲面:用作分割实体的切割工具", + "Split a body by a sheet and keep the fragments that lie on the scan — or trim a sheet by sheets or solids, keeping its pieces on the scan": "用曲面片分割实体,并保留位于扫描上的碎片 — 或用曲面片或实体修剪曲面片,并保留位于扫描上的部分", + "A solid through the profiles of two or more sketches on their own planes": "穿过各自所在平面上两个或更多草图轮廓的实体", + "A sketch's region swept along a chain of lines and arcs in another sketch — a bent bar, a handle, a rim that follows a curve": "将一个草图的区域沿另一草图中由直线和圆弧组成的链扫掠 — 如弯杆、把手或沿曲线延伸的边缘", + "Copies of the body about an axis, along a direction, or in a grid": "将实体绕轴、沿方向或按网格进行阵列复制", + "Move and turn bodies — the ones selected on the part or picked in the body list (Ctrl+click or Shift+click rows for several), or click them on the part: drag the gizmo's arrows and rings, or type the distances and angles. Tick Copy to move a copy and leave the body where it is": "移动和旋转实体 — 可选取零件上已选中的实体,或在实体列表中选取(按住 Ctrl 或 Shift 点击多行),也可直接在零件上点击:拖动控制器的箭头和圆环,或输入距离和角度。勾选“复制”可移动副本,原实体保持不动", + "Grow a body all round by a distance, or shrink it — kept beside it as a body of its own, a keep-out round a part or the pocket that holds it with clearance": "将实体向四周均匀扩大一段距离,或缩小 — 作为独立实体保留在其旁边,可用作零件周围的禁入区,或带间隙容纳该零件的凹槽", + "A smooth freeform surface fitted through a region of the scan — click faces or mark them — for the trim to close into a solid with the others": "穿过扫描某一区域拟合的光滑自由曲面(点击面或标记面)—— 供修剪后与其他曲面闭合为实体", + "Curves drawn on the scan, and the regions they close filled with patches fitted to the scan together — tangent across every curve not marked a crease, sharing their edges: a sheet, or a solid where the network closes. For a freeform skin that folds back or wants more than one patch": "在扫描上绘制曲线,并以共同拟合到扫描的补片填充它们围成的区域 — 除标记为折痕的曲线外,补片在每条曲线处相切并共用边:得到曲面片,或在网络闭合处得到实体。适用于会折回或需要多个补片的自由曲面蒙皮", + "Close a set of fitted surfaces, sheets and measured planes, cylinders, cones and spheres into a solid: the faces they cut space into are kept where they lie on the scan and sewn": "将一组拟合曲面、曲面片以及测得的平面、圆柱、圆锥和球闭合为实体:它们将空间分割出的面中,位于扫描上的部分会被保留并缝合", + "Make a sheet or a fitted surface a solid of a thickness — a freeform thin shell's wall from its one skin, where a shell or an offset of a lofted body will not go": "将曲面片或拟合曲面变为具有一定厚度的实体 — 由单层蒙皮生成自由曲面薄壳的壁,适用于抽壳或放样实体偏移无法处理的情形", + "The XY plane through the origin — click it in the viewport to sketch on it": "过原点的 XY 平面 — 在视口中点击它即可在其上绘制草图", + "Hide the XY plane": "隐藏 XY 平面", + "The XZ plane through the origin — click it in the viewport to sketch on it": "过原点的 XZ 平面 — 在视口中点击它即可在其上绘制草图", + "Hide the XZ plane": "隐藏 XZ 平面", + "The YZ plane through the origin — click it in the viewport to sketch on it": "过原点的 YZ 平面 — 在视口中点击它即可在其上绘制草图", + "Hide the YZ plane": "隐藏 YZ 平面", + "Load a scan first — the map reads its surface": "请先加载扫描 — 图将读取其表面", + "Build a body first": "请先创建实体", + "How the bodies are drawn": "实体的显示方式", + "Draw the bodies transparent — the scan, the elements and the bodies inside them show through. With the scan solid as well, each part shows where it stands proud of the other": "将实体显示为透明 — 其中的扫描、元素和实体都会透出。若扫描也为实体显示,则每个零件会显示其凸出于另一个零件的位置", + "Draw where two bodies run into each other in hatched red — what they share, over the faces of both that would otherwise fight for the same pixels. Off for bodies that overlap on purpose, a modifier volume for a slicer; saved with the project": "以红色剖面线标出两个实体相互穿插的位置 — 即它们共有的部分,以免两者的面争抢同一像素。对于有意重叠的实体(如切片软件的修改器体积)请关闭;随项目一同保存", + "How the edges are drawn where a round runs into a flat. A round face's seam and the cut a join leaves across one surface are never drawn": "圆面与平面相接处边线的显示方式。圆面的接缝,以及合并在同一表面上留下的切口,始终不会绘制", + "Tangent edges drawn — where a round runs into a flat — as the sharp ones are, the way CAD draws them": "相切边照常绘制 — 即圆面与平面相接处 — 与尖锐边相同,与 CAD 的绘制方式一致", + "Tangent edges dimmed": "相切边淡化显示", + "Tangent edges left out, and not picked: a round shows by its outline against the background": "相切边被省略,也无法选取:圆面以其轮廓与背景区分显示", + "Nothing measured yet — fit elements or cut sections in the 3D Measure workspace and they show here": "尚无测量结果 — 在 3D 测量工作区中拟合元素或剖切剖面后将显示在此", + "Build a body first — the map reads the scan against it": "请先创建实体 — 图将以它为基准读取扫描", + "Tools": "工具", + "Reduce mesh": "简化网格", + "Remove islands": "移除孤岛", + "Delete marked": "删除已标记", + "Flip normals": "翻转法线", + "Cut": "切割", + "Fill holes": "填补孔洞", + "Smooth": "平滑", + "Shrink/expand": "内缩/外扩", + "Boolean": "布尔运算", + "Trim to scan": "按扫描修剪", + "Shell/hollow": "抽壳/掏空", + "Tolerance": "公差", + "Triangle limit": "三角形上限", + "Reduce": "简化", + "Both figures shape the reduction, so changing them means reducing again.": "这两个数值共同决定简化结果,因此更改后需要重新简化。", + "Cleaning up and reducing a scan": "清理并简化扫描", + "Load a scan and clean it up — clear the stray islands, delete what is not the part, turn it the right way out, cut off the base it stood on, fill its holes, smooth out the scatter, expand or shrink it, hollow it — and reduce it to a light mesh to model over in CAD, held within a tolerance you set, measured against the scan to prove it, and exported where you aligned it. STL, PLY, OBJ or 3MF, in millimetres — everything stays in this browser.": "加载扫描并进行清理 — 清除零散孤岛,删除非零件部分,修正朝向,切掉其放置的底座,填补孔洞,抹平噪点,外扩或内缩,抽壳 — 再将其简化为轻量网格,以便在 CAD 中基于它建模;简化结果保持在您设定的公差内,并与扫描对比以验证,且按您对齐的位置导出。支持 STL、PLY、OBJ 或 3MF,单位为毫米 — 所有数据都保留在此应用中。", + "Reduced": "已简化", + "Accuracy": "精度", + "More about Mesh tools": "进一步了解网格工具", + "Mesh tool": "网格工具", + "A light copy of the scan to model over in CAD, held within a tolerance you set — the scan itself stays as it is": "扫描的轻量副本,用于在 CAD 中基于它建模,保持在您设定的公差内 — 扫描本身保持不变", + "Find the specks floating off the scan — every piece joined to nothing else and smaller than a size you set — mark them on the part, and delete them once you say so": "找出游离于扫描之外的碎屑 — 即不与其他部分相连且小于您设定尺寸的每一块 — 在零件上标记,待您确认后再删除", + "Mark surface on the part by hand — a window, a brush or a lasso — and delete it from the scan: a fixture, a clamp, a patch of the table": "手动在零件上标记表面(框选、画笔或套索),并将其从扫描中删除:如夹具、压板或一块桌面", + "Turn the surface over so it faces the other way — the whole part, exported inside-out, or a patch you mark that faces in": "将表面翻转到另一侧 — 整个零件(导出时内外颠倒的),或您标记的朝内的一块区域", + "Cut the scan with a plane and take one side off — the turntable it stood on, or half the part — closing it flat where the plane went through": "用平面切割扫描并去掉一侧 — 如其所放置的转台,或零件的一半 — 并在平面穿过处封成平面", + "Close the holes a scanner left, every one smaller than a size you set, with patches that carry the surface across": "用延续表面的补片,封闭扫描仪遗留的孔洞,每个孔洞均小于您设定的尺寸", + "Take the scanner’s scatter out of the surface, never moving any point further than a limit you set, sharp edges held": "去除表面上的扫描噪点,任何点的移动都不超过您设定的限值,并保留尖锐边缘", + "Move the surface along its normal by a distance — out or in, the whole part or a marked region — not a scale: a bore gets narrower as the part grows": "将表面沿法线移动一段距离 — 向外或向内,作用于整个零件或已标记的区域 — 这不是缩放:零件变大时,孔会变窄", + "Take a solid body out of the scan, add it, or keep what the two share — a bore the scanner could not see to the bottom of, cut out with a cylinder modelled in Reverse engineer": "从扫描中减去实体、加上实体,或保留两者的公共部分 — 例如扫描仪看不到底的孔,可用在“逆向工程”中建模的圆柱将其切出", + "Cut a body modelled to mend a broken part to fit the scanned break — the missing piece, to print and glue in": "切割为修补破损零件而建模的实体,使其贴合扫描到的断口 — 即缺失的部分,可打印后粘上", + "Hollow a closed part, leaving a wall of a thickness you set round a sealed cavity — or thicken an open surface into a solid": "将封闭零件掏空,在密闭空腔周围留下您设定厚度的壁 — 或将开放表面加厚为实体", + "More about Reducing a scan for CAD": "进一步了解为 CAD 简化扫描", + "More about Tolerance": "进一步了解公差", + "More about Triangle limit": "进一步了解三角形上限", + "What to show of the reduction": "显示简化的哪些内容", + "Reduce the scan first — the view keys show the result": "请先简化扫描 — 视图按键会显示结果", + "Edit the scan first — the deviation shows how far the edited copy is from the scan in use": "请先编辑扫描 — 偏差显示编辑后的副本与当前使用的扫描相差多少", + "How the instrument looks and behaves. Remembered in this browser — nothing here is a property of the part, and none of it is saved with a project.": "仪器的外观与行为。设置保存在此应用中 — 这里的任何内容都不是零件的属性,也不会随项目保存。", + "Units": "单位", + "Lengths in": "长度单位:", + "What is shown and typed. Files, projects and agent answers stay in millimetres.": "用于显示和输入。文件、项目和智能体的回答仍以毫米为单位。", + "Appearance": "外观", + "Interface": "界面", + "Light": "浅色", + "Dark": "深色", + "System": "系统", + "Light or dark chassis.": "浅色或深色机身。", + "follows the operating system's setting, and changes with it.": "跟随操作系统的设置,并随之变化。", + "Colour mode": "配色模式", + "Studio grey": "影棚灰", + "Scanner blue": "扫描仪蓝", + "Glossy blue under a hard light — surface detail stands out": "强光下的光泽蓝 — 表面细节更突出", + ". What has been measured keeps its colour either way — the element tints and the deviation ramp are the same in both.": "。无论选哪种,已测量内容的颜色都保持不变 — 元素的色调和偏差色带在两种模式下相同。", + "Colour map": "色图", + "Blue through cyan, green and yellow to red, a saturated green in the middle — the inspection-software classic. Uneven in lightness, so cyan and yellow show as bands the part does not have": "由蓝经青、绿、黄到红,中间为饱和的绿色 — 检测软件的经典配色。亮度不均匀,因此青色和黄色会显示为零件上并不存在的色带", + ". Every map painted on the part, and the scale beside it, is drawn in it; the block at either end is what a reading past that end of the scale wears.": "。零件上绘制的所有色图及其旁边的色标都使用它;两端的色块表示超出色标该端的读数所用的颜色。", + "Sharp edges": "尖锐边", + "Always sharp": "始终尖锐", + "Auto — sharp on a CAD-like mesh": "自动 — CAD 类网格上显示尖锐边", + "Always smooth": "始终平滑", + "Sharp from": "尖锐角度起点:", + "Whether an edge of the scan is shaded as a crease, each face with its own normal, or smoothed across so a box looks pillowed — and from what angle between its two faces an edge counts as sharp.": "扫描的边是按折痕着色(每个面使用各自的法线),还是整体平滑过渡(以致方盒看起来像枕头)— 以及两个面之间夹角达到多少时,该边才算尖锐。", + "draws a CAD mesh's faces flat and a scan's real edges — the rim of a bore, a step — as edges; noise seldom tips a scan's triangles that far, and if it still shows, a larger angle, 50° or 60°, leaves it smooth.": "会将 CAD 网格的面绘制为平面,并将扫描的真实边缘(孔口边缘、台阶)绘制为边线;噪点很少会让扫描的三角形倾斜到这种程度,若仍然出现,请改用更大的角度,如 50° 或 60°,即可保持平滑。", + "Auto": "自动", + "draws the creases only on a mesh that reads as a tessellation of CAD, a scan smooth all over. A reference part is always drawn sharp, from": "仅在看起来是 CAD 曲面细分的网格上绘制折痕,而整体平滑的扫描则不绘制。参考零件始终以尖锐边绘制,角度起点为", + "Navigation": "导航", + "Mouse controls": "鼠标操作", + "left-drag orbit · right-drag pan · scroll zoom": "左键拖动:环绕 · 右键拖动:平移 · 滚轮:缩放", + "works beside any of these, in Chrome and Edge: push or pull the puck to zoom, slide it to pan, tilt, twist and roll it to turn the part. It connects the first time you touch it, and drives the view the mouse was last over.": "可与上述任一方式配合使用,支持 Chrome 和 Edge:推拉控制帽进行缩放,滑动进行平移,倾斜、扭转和滚动以旋转零件。首次触碰时自动连接,并控制鼠标最后停留的视图。", + "Files": "文件", + "Ask what units an STL is in": "询问 STL 使用的单位", + "— the format carries none, and a part read at the wrong scale measures wrong in every number after. Off, every STL is read in the units below without asking.": "— 该格式不含单位,零件若按错误比例读取,之后的每个数值都会测错。关闭后,所有 STL 都按下方的单位读取,不再询问。", + "STL import units": "STL 导入单位", + "What an STL is read in when the question is off, and the answer the question offers first — the last one given. A PLY or OBJ is always read in millimetres; a STEP file says its own.": "关闭提问时读取 STL 所用的单位,也是提问时首先提供的答案 — 即上次给出的答案。PLY 或 OBJ 始终按毫米读取;STEP 文件则自带单位。", + "Lines": "线条", + "Section cuts": "剖切线", + "How heavy a section's cut is drawn on the part in 3D Measure. The curves measured on its sheet follow in proportion.": "3D 测量中零件上剖面切线的粗细。在其图纸上测得的曲线按比例随之变化。", + "2D fitted curves": "2D 拟合曲线", + "The lines, circles and splines fitted over a flatbed scan in 2D Measure, and the callouts drawn with them.": "在 2D 测量中拟合到平板扫描图上的直线、圆和样条曲线,以及随之绘制的标注。", + "2D edges": "2D 边缘", + "The edge chains the curves are fitted to — found in the scan, or cut by a section.": "曲线所拟合的边缘链 — 取自扫描,或由剖面切出。", + "Guidance": "引导", + "Guided hints": "引导提示", + "— ring the control to press next, until you have been through a workspace twice. Switching it back on starts the guidance over.": "— 圈出下一个要按的控件,直到您在某个工作区中完整走过两遍。重新开启后,引导将从头开始。", + "Agents": "智能体", + "Local agent connection": "本地智能体连接", + "— let an AI agent on this computer (Claude Code, Claude Desktop, Cursor …) drive this tab through its ScanRuler tools, the scanruler-mcp server. You watch every step here and can undo it.": "— 允许这台电脑上的 AI 智能体(Claude Code、Claude Desktop、Cursor …)通过其 ScanRuler 工具(即 scanruler-mcp 服务器)操控此标签页。每一步您都能在此看到,并可撤销。", + "Your data no longer stays on this computer:": "您的数据不再仅保留在这台电脑上:", + "what the agent reads — measurements, reports, pictures of the part, file names, notes — is processed by the agent and goes to its model’s provider, unless the model runs on this computer. ScanRuler itself sends nothing anywhere; the connection is to 127.0.0.1.": "智能体读取的内容 — 测量结果、报告、零件图片、文件名、备注 — 会由智能体处理,并发送给其模型的提供方,除非该模型运行在这台电脑上。ScanRuler 本身不会向任何地方发送数据;该连接指向 127.0.0.1。", + "Port": "端口", + "Token": "令牌", + "prints the port and the token, and a link that fills them in.": "会输出端口和令牌,以及一个可自动填入它们的链接。", + "Support": "支持", + "Support card": "支持卡片", + "— the thank-you in the corner of the stage, with the ways to give something back. Its × closes it for this visit; switched off here it stays away, the big one below with it.": "— 位于舞台角落的感谢卡片,附有回馈支持的方式。点击其 × 可在本次访问中关闭;在此关闭后它将不再出现,下方的大卡片也一并不再出现。", + "Big card on saving": "保存时显示大卡片", + "— the same thank-you in the middle of the screen after a save or an export, at most once in ten minutes. The file does not wait for it. Its \"Don't show this again for a week\" shows here as off until the week is over.": "— 保存或导出后在屏幕中央显示的同一感谢卡片,十分钟内最多显示一次。文件不会等待它。其“一周内不再显示”选项在一周结束前,在此处会显示为关闭。", + "Dark blue through teal and green to yellow. Perceptually uniform — an even step in the reading is an even step in colour — and as legible to colour-blind eyes and in greyscale": "由深蓝经青绿、绿到黄。感知均匀 — 读数的等量变化对应颜色的等量变化 — 色盲用户和灰度显示下同样易于辨读", + "Black through purple and pink to pale cream. Perceptually uniform and colour-blind safe": "由黑经紫、粉到淡奶油色。感知均匀,对色盲友好", + "Indigo through magenta and orange to yellow — no black end. Perceptually uniform and colour-blind safe": "由靛蓝经洋红、橙到黄 — 无黑色端。感知均匀,对色盲友好", + "Black through purple, red and orange to pale yellow. Perceptually uniform and colour-blind safe": "由黑经紫、红、橙到淡黄。感知均匀,对色盲友好", + "Navy through grey to yellow, made to look the same to red–green colour blindness as to anyone else. Perceptually uniform": "由藏青经灰到黄,红绿色盲者看到的效果与其他人一致。感知均匀", + "Black through deep blue and teal to mint. Perceptually uniform and colour-blind safe": "由黑经深蓝、青绿到薄荷绿。感知均匀,对色盲友好", + "Black through purple and red to cream. Perceptually uniform and colour-blind safe": "由黑经紫、红到奶油色。感知均匀,对色盲友好", + "A rainbow like jet, dark blue through green to dark red, but smooth — without the bands jet shows. Not for red–green colour blindness": "类似 jet 的彩虹色,由深蓝经绿到深红,但更平滑 — 没有 jet 那样的色带。不适合红绿色盲", + "Across": "横跨:", + "Flip": "翻转", + "What the plane is laid across: a coordinate plane through the part’s middle, or an element with a direction — a plane’s normal, the axis of a cylinder, cone or line, a circle’s normal. The half toward you is taken away.": "平面所横跨的对象:穿过零件中部的坐标平面,或带方向的元素 — 平面的法线、圆柱、圆锥或直线的轴线、圆的法线。朝向您的一半会被移除。", + "Where along its direction the plane lies — drag the arrow on the part, or type it here": "平面沿其方向所处的位置 — 在零件上拖动箭头,或在此输入", + "Take away the other half instead": "改为移除另一半", + "set otherwise in its menu": "在其菜单中另行设置" +} diff --git a/desktop/main.js b/desktop/main.js new file mode 100644 index 00000000..7181b41a --- /dev/null +++ b/desktop/main.js @@ -0,0 +1,325 @@ +// SPDX-License-Identifier: AGPL-3.0-only +// ScanRuler Desktop: serves the built ScanRuler and the agent link from this +// process and shows the app in its own window, paired with the link — no +// browser, no hosted site. The ScanRuler code is not touched. +import { app, BrowserWindow, Menu, dialog, ipcMain, session } from 'electron' +import { existsSync, readFileSync, unlinkSync, writeFileSync } from 'node:fs' +import { basename, dirname, join, resolve } from 'node:path' +import { fileURLToPath, pathToFileURL } from 'node:url' +import { cpus } from 'node:os' +import { BACKENDS, gpuSwitches, loadSettings, saveSettings } from './settings.js' +import { LANGUAGES, hwOf, injectScript, loadDict, menuOf } from './i18n.js' +import { meshOfProject, watchMeshes } from './creality.js' + +const here = dirname(fileURLToPath(import.meta.url)) +const DEFAULT_PORT = 7317 +const OPENABLE = /\.(scanruler|stl|ply|obj|3mf|step|stp|obp)$/i +const CHUNK = 12 * 1024 * 1024 // bytes of file per script, ~16 MB of base64 + +// Packaged: resources/app-dist and resources/mcp; from a checkout: ../src. +const distDir = app.isPackaged ? join(process.resourcesPath, 'app-dist') : resolve(here, '../dist') +const mcpDir = app.isPackaged ? join(process.resourcesPath, 'mcp') : resolve(here, '../mcp') + +// Graphics switches have to be set before Electron is ready. +const settings = loadSettings(app.getPath('userData')) +const backendRunning = settings.backend +for (const [name, value] of gpuSwitches(settings.backend)) app.commandLine.appendSwitch(name, value) + +// Nothing here talks to the network on its own. +process.env.SCANRULER_MCP_NO_UPDATE = '1' + +// A second copy hands its file to the first and leaves before starting anything. +const first = app.requestSingleInstanceLock() +if (!first) app.exit(0) + +let main = null +let link = null +let http = null +let pairingUrl = null +let angleActive = null +const pendingFiles = [] +let stopWatching = null +let asking = false + +const fileArg = (argv) => argv.slice(1).find((a) => OPENABLE.test(a) && existsSync(a)) + +/** Hand a file to the page as if it were dropped on it, which is the app's own way in. */ +async function openFile(path) { + if (/\.obp$/i.test(path)) path = meshOfProject(path) + if (!main || main.webContents.isLoading()) { + pendingFiles.push(path) + return + } + // Sent in pieces: a scan can be hundreds of MB, too much for one script string. + const bytes = readFileSync(path) + const wc = main.webContents + await wc.executeJavaScript('window.__srOpen = []') + for (let at = 0; at < bytes.length; at += CHUNK) { + await wc.executeJavaScript(`window.__srOpen.push("${bytes.subarray(at, at + CHUNK).toString('base64')}")`) + } + await wc.executeJavaScript(`(() => { + const parts = window.__srOpen.map((b64) => Uint8Array.from(atob(b64), (c) => c.charCodeAt(0))) + window.__srOpen = null + const dt = new DataTransfer() + dt.items.add(new File(parts, ${JSON.stringify(basename(path))})) + window.dispatchEvent(new DragEvent('drop', { dataTransfer: dt, bubbles: true, cancelable: true })) + })()`) +} + +/** Watch a Creality Scan projects folder; a finished Mesh.ply is offered to the page, never opened unasked. */ +function watchCreality(dir) { + stopWatching?.() + stopWatching = null + settings.crealityDir = dir + saveSettings(app.getPath('userData'), settings) + if (dir) { + try { + stopWatching = watchMeshes(dir, async (path, project) => { + if (asking) return + asking = true + try { + const m = menuOf(settings.language) + const r = await dialog.showMessageBox(main ?? undefined, { type: 'question', message: `${m.fresh} “${project}”`, detail: path, buttons: [m.openIt, m.ignore], defaultId: 0, cancelId: 1 }) + if (r.response === 0) await openFile(path) + } catch (e) { + dialog.showErrorBox('ScanRuler', String(e)) + } finally { + asking = false + } + }) + } catch (e) { + dialog.showErrorBox('ScanRuler', String(e)) + settings.crealityDir = '' + saveSettings(app.getPath('userData'), settings) + } + } + buildMenu() +} + +// The page rejects a project.obp; a drop or a file pick of one goes to the main process instead, +// which opens the project's mesh (the page cannot see the folder around the file). +const PROJECT_HOOK = `(() => { + if (window.__srProjectHook) return + window.__srProjectHook = true + const isObp = (f) => f && /\\.obp$/i.test(f.name) + const take = (e, file) => { + e.preventDefault() + e.stopImmediatePropagation() + window.srProject.open(file) + } + window.addEventListener('drop', (e) => { const f = e.dataTransfer?.files?.[0]; if (isObp(f)) take(e, f) }, true) + document.addEventListener('change', (e) => { + const f = e.target?.type === 'file' && e.target.files?.[0] + if (isObp(f)) { take(e, f); e.target.value = '' } + }, true) +})()` + +async function startServer() { + const { httpServer } = await import(pathToFileURL(join(mcpDir, 'src/serve.js')).href) + const { SharedLink } = await import(pathToFileURL(join(mcpDir, 'src/relay.js')).href) + const { originAllowed } = await import(pathToFileURL(join(mcpDir, 'src/link.js')).href) + const { loadConfig } = await import(pathToFileURL(join(mcpDir, 'src/config.js')).href) + const version = JSON.parse(readFileSync(join(mcpDir, 'package.json'), 'utf8')).version + const port = Number(process.env.SCANRULER_MCP_PORT ?? DEFAULT_PORT) + const token = process.env.SCANRULER_MCP_TOKEN || loadConfig().token + http = httpServer({ serve: distDir, allowOrigin: (o) => originAllowed(o, []), version }) + link = new SharedLink({ http, port, token, version, log: (l) => console.log(`[mcp] ${l}`) }) + const role = new Promise((ok) => link.on('role', ok)) + link.start() + if ((await Promise.race([role, new Promise((r) => setTimeout(() => r('timeout'), 4000))])) !== 'holder') { + throw new Error( + `Port ${port} is held by another scanruler-mcp or program. Close it, or set SCANRULER_MCP_PORT.`, + ) + } + pairingUrl = `http://127.0.0.1:${port}/#agent=${port}:${token}` + // The agent entry (scanruler-mcp.cmd) reads this to join the app that is open. + writeFileSync(agentFile(), JSON.stringify({ port, token }), { mode: 0o600 }) +} + +const agentFile = () => join(app.getPath('userData'), 'agent.json') + +// Cross-origin isolation (SharedArrayBuffer, multithreaded WASM) for the local app only. +function isolate() { + session.defaultSession.webRequest.onHeadersReceived({ urls: ['http://127.0.0.1/*'] }, (d, cb) => { + cb({ + responseHeaders: { + ...d.responseHeaders, + 'Cross-Origin-Opener-Policy': ['same-origin'], + 'Cross-Origin-Embedder-Policy': ['require-corp'], + }, + }) + }) +} + +/** Put the page in the chosen language (English leaves it as it is). */ +function translate() { + main?.webContents.executeJavaScript(injectScript(loadDict(settings.language))).catch((e) => console.error('i18n', e)) +} + +function setLanguage(language) { + settings.language = language + saveSettings(app.getPath('userData'), settings) + translate() + buildMenu() +} + +function buildMenu() { + const m = menuOf(settings.language) + Menu.setApplicationMenu( + Menu.buildFromTemplate([ + { + label: m.file, + submenu: [ + { + label: m.open, + accelerator: 'CmdOrCtrl+O', + click: async () => { + const r = await dialog.showOpenDialog(main, { filters: [{ name: 'Scan', extensions: ['stl', 'ply', 'obj', '3mf', 'step', 'stp', 'scanruler', 'obp'] }] }) + if (!r.canceled) openFile(r.filePaths[0]).catch((e) => dialog.showErrorBox('ScanRuler', String(e))) + }, + }, + { type: 'separator' }, + settings.crealityDir + ? { label: `${m.unwatch} (${basename(settings.crealityDir)})`, click: () => watchCreality('') } + : { + label: m.watch, + click: async () => { + const r = await dialog.showOpenDialog(main, { properties: ['openDirectory'] }) + if (!r.canceled) watchCreality(r.filePaths[0]) + }, + }, + { type: 'separator' }, + { role: 'quit', label: m.quit }, + ], + }, + { label: m.view, submenu: [{ role: 'reload' }, { role: 'togglefullscreen' }, { role: 'toggleDevTools' }] }, + { + label: m.language, + submenu: Object.entries(LANGUAGES).map(([id, label]) => ({ label, type: 'radio', checked: settings.language === id, click: () => setLanguage(id) })), + }, + { label: m.help, submenu: [{ label: m.hardware, click: showHardware }] }, + ]), + ) +} + +function createWindow() { + main = new BrowserWindow({ + width: 1500, + height: 950, + backgroundColor: '#15171a', + // The 3D API in use is always in sight; Help → Hardware has the detail. + title: `ScanRuler — ${BACKENDS[backendRunning].name}`, + webPreferences: { preload: join(here, 'preload-main.cjs'), contextIsolation: true, sandbox: true, nodeIntegration: false }, + }) + main.on('page-title-updated', (e) => e.preventDefault()) + main.webContents.setWindowOpenHandler(() => ({ action: 'deny' })) + main.webContents.on('will-navigate', (e, url) => { + if (!url.startsWith('http://127.0.0.1:')) e.preventDefault() + }) + main.webContents.on('did-finish-load', async () => { + translate() + main.webContents.executeJavaScript(PROJECT_HOOK).catch((e) => console.error('project hook', e)) + // The page's drop handler exists only once React has drawn; a file handed over earlier is lost. + for (let i = 0; i < 100 && main; i++) { + if (await main.webContents.executeJavaScript(`!!document.querySelector('#root *')`)) break + await new Promise((r) => setTimeout(r, 100)) + } + for (const f of pendingFiles.splice(0)) openFile(f).catch((e) => dialog.showErrorBox('ScanRuler', String(e.message ?? e))) + }) + main.on('closed', () => (main = null)) + main.loadURL(pairingUrl) +} + +async function pageProbe() { + if (!main) return {} + return main.webContents.executeJavaScript(`(async () => { + // webgpu / renderer: a description, true for present without one, false for absent. + const out = { threads: navigator.hardwareConcurrency, isolated: crossOriginIsolated, webnn: !!navigator.ml, webgpu: false, renderer: false } + try { + const a = navigator.gpu && (await navigator.gpu.requestAdapter({ powerPreference: 'high-performance' })) + if (a) { const i = a.info ?? {}; out.webgpu = [i.vendor, i.architecture, i.description].filter(Boolean).join(' ') || true } + } catch {} + try { + const gl = new OffscreenCanvas(1, 1).getContext('webgl2') + const x = gl && gl.getExtension('WEBGL_debug_renderer_info') + out.renderer = gl ? (x ? gl.getParameter(x.UNMASKED_RENDERER_WEBGL) : true) : false + } catch {} + return out + })()`) +} + +function showHardware() { + const w = new BrowserWindow({ + width: 760, + height: 520, + title: `ScanRuler — ${hwOf(settings.language).title}`, + parent: main ?? undefined, + webPreferences: { preload: join(here, 'preload.cjs'), contextIsolation: true, sandbox: true }, + }) + w.removeMenu() + w.loadFile(join(here, 'hardware.html')) +} + +ipcMain.handle('project:open', (e, path) => { + if (e.sender !== main?.webContents || !/\.obp$/i.test(String(path))) return + openFile(path).catch((err) => dialog.showErrorBox('ScanRuler', String(err.message ?? err))) +}) +ipcMain.handle('hw:info', async () => { + const hw = hwOf(settings.language) + const gpu = await app.getGPUInfo('complete').catch(() => ({})) + const gpus = (gpu.gpuDevice ?? []).map((d) => `${d.deviceString ?? '?'} (vendor 0x${(d.vendorId ?? 0).toString(16)})`) + angleActive = gpu.auxAttributes?.glImplementationParts ?? gpu.auxAttributes?.glRenderer ?? angleActive + return { + gpus, + backend: settings.backend, + backendRunning, + backends: Object.fromEntries(Object.entries(BACKENDS).map(([k, v]) => [k, `${v.name} (${hw[v.tag]})`])), + strings: hw, + angleActive, + cpu: cpus()[0]?.model.trim() ?? '?', + page: await pageProbe(), + versions: `Electron ${process.versions.electron} / Chromium ${process.versions.chrome}`, + } +}) +ipcMain.handle('hw:set-backend', (_e, backend) => { + if (backend in BACKENDS) saveSettings(app.getPath('userData'), { ...settings, backend: (settings.backend = backend) }) +}) +ipcMain.handle('hw:restart', () => { + app.relaunch() + app.quit() +}) + +app.on('second-instance', (_e, argv) => { + if (main) { + if (main.isMinimized()) main.restore() + main.focus() + } + const f = fileArg(argv) + if (f) openFile(f).catch((e) => console.error(e)) +}) + +app.whenReady().then(async () => { + if (!first) return + try { + await startServer() + } catch (e) { + dialog.showErrorBox('ScanRuler', String(e.message ?? e)) + app.quit() + return + } + isolate() + buildMenu() + createWindow() + if (settings.crealityDir && existsSync(settings.crealityDir)) watchCreality(settings.crealityDir) + const f = fileArg(process.argv) + if (f) pendingFiles.push(f) +}) + +app.on('window-all-closed', () => app.quit()) +app.on('before-quit', () => { + link?.close() + http?.close() + try { + unlinkSync(agentFile()) + } catch {} +}) diff --git a/desktop/package-lock.json b/desktop/package-lock.json new file mode 100644 index 00000000..82b5ea46 --- /dev/null +++ b/desktop/package-lock.json @@ -0,0 +1,1052 @@ +{ + "name": "scanruler-desktop", + "version": "0.1.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "scanruler-desktop", + "version": "0.1.0", + "license": "AGPL-3.0-only", + "devDependencies": { + "@electron/packager": "^20.3.0", + "electron": "^45.2.1", + "puppeteer-core": "^25.16.0" + } + }, + "node_modules/@electron-internal/extract-zip": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@electron-internal/extract-zip/-/extract-zip-1.0.8.tgz", + "integrity": "sha512-8iajeLFbzNwM0m3AsQZSZgYREWglAImyGvvFSWU34FgMW8sHlzdwj9QKvv9dgN7jMP591EFUF/sLOC3Jl9YIjQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=22.12.0" + } + }, + "node_modules/@electron/asar": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@electron/asar/-/asar-4.3.1.tgz", + "integrity": "sha512-6pI64z/tMSBkUTZLQFYhs2YiB7fS9sKA6gy7/2nH1Ju7GGwT32UPsj4Mxsws5NMmtR6C0zYdXVxEU/rGqJRCJA==", + "dev": true, + "license": "MIT", + "dependencies": { + "glob": "^13.0.2", + "minimatch": "^10.0.1" + }, + "bin": { + "asar": "bin/asar.mjs" + }, + "engines": { + "node": ">=22.12.0" + } + }, + "node_modules/@electron/get": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@electron/get/-/get-5.1.0.tgz", + "integrity": "sha512-3kSBtG8ObcTVfXanm5vVJ6UnBLEVmVsRk1M+vGqCuMBV+XLCbJYuWQful+yIy0GQDsSlK0kHEriEHn7SPk4EnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.1.1", + "env-paths": "^3.0.0", + "graceful-fs": "^4.2.11", + "progress": "^2.0.3", + "semver": "^7.6.3", + "sumchecker": "^3.0.1" + }, + "engines": { + "node": ">=22.12.0" + }, + "optionalDependencies": { + "undici": "^7.24.4" + } + }, + "node_modules/@electron/notarize": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@electron/notarize/-/notarize-3.1.2.tgz", + "integrity": "sha512-S/EH6W/GTe3XiD4oYHW+6xuUWZWjc9F5gayMrNdmRLv7qG9KRYmYE26+RDgC1hYUZVY3qLpa1VftxuL+sL5VQg==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "promise-retry": "^2.0.1" + }, + "engines": { + "node": ">= 22.12.0" + } + }, + "node_modules/@electron/osx-sign": { + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/@electron/osx-sign/-/osx-sign-2.7.2.tgz", + "integrity": "sha512-BD5ZyKDzzC1PLzWQCl1nw+X2/ErRCJGEIPwtjfOnCH+8KRLfBe/Vr9khUuEJC5bbrVACl8uO2Q2O/UqOzrcFgw==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "debug": "^4.3.4", + "isbinaryfile": "^4.0.8", + "plist": "^3.0.5", + "semver": "^7.7.1" + }, + "bin": { + "electron-osx-flat": "bin/electron-osx-flat.mjs", + "electron-osx-sign": "bin/electron-osx-sign.mjs" + }, + "engines": { + "node": ">=22.12.0" + } + }, + "node_modules/@electron/packager": { + "version": "20.3.0", + "resolved": "https://registry.npmjs.org/@electron/packager/-/packager-20.3.0.tgz", + "integrity": "sha512-3MvgJgy6YJ5ti0oGGBKrWKdYwpTaoRrhranMDgHSQ6i5t56yZV9IRDyoXTuqBp97LiKqnZeAMe2wTcF/9+fP5g==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "@electron-internal/extract-zip": "^1.0.1", + "@electron/asar": "^4.0.1", + "@electron/get": "^5.0.0", + "@electron/notarize": "^3.1.0", + "@electron/osx-sign": "^2.2.0", + "@electron/universal": "^3.0.1", + "@electron/windows-sign": "^2.0.2", + "@malept/cross-spawn-promise": "^2.0.0", + "debug": "^4.4.1", + "filenamify": "^6.0.0", + "galactus": "^2.0.2", + "graceful-fs": "^4.2.11", + "junk": "^4.0.1", + "plist": "^3.1.0", + "resedit": "^2.0.3", + "semver": "^7.7.2", + "yargs-parser": "^22.0.0" + }, + "bin": { + "electron-packager": "bin/electron-packager.mjs" + }, + "engines": { + "node": ">= 22.12.0" + }, + "funding": { + "url": "https://github.com/electron/packager?sponsor=1" + } + }, + "node_modules/@electron/universal": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@electron/universal/-/universal-3.0.6.tgz", + "integrity": "sha512-MonS1kfkZdSEkLZI0pdR/TCx8ecxwRSFm7sORfwIkDI9UaIbHnk4Mgeqq+Ob9qDQRV8LZ9+hHCmimpA9BRcNxw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@electron/asar": "^4.0.0", + "debug": "^4.3.1", + "plist": "^3.1.0" + }, + "engines": { + "node": ">=22.12.0" + } + }, + "node_modules/@electron/windows-sign": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@electron/windows-sign/-/windows-sign-2.1.0.tgz", + "integrity": "sha512-hyRE175pWNxU3R0k7B7Zwm4KpSgK/Ql+7yqUPRfnYc9K06uVPJDLvXH6oXpl/RAKbLu0Y114xuH6BdjDhF/pKA==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "debug": "^4.3.4", + "graceful-fs": "^4.2.11", + "postject": "^1.0.0-alpha.6" + }, + "bin": { + "electron-windows-sign": "bin/electron-windows-sign.mjs" + }, + "engines": { + "node": ">=22.12.0" + } + }, + "node_modules/@malept/cross-spawn-promise": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@malept/cross-spawn-promise/-/cross-spawn-promise-2.0.0.tgz", + "integrity": "sha512-1DpKU0Z5ThltBwjNySMC14g0CkbyhCaz9FkhxqNsZI6uAPJXFS8cMXlBKo26FJ8ZuW6S9GCMcR9IO5k2X5/9Fg==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/malept" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/subscription/pkg/npm-.malept-cross-spawn-promise?utm_medium=referral&utm_source=npm_fund" + } + ], + "license": "Apache-2.0", + "dependencies": { + "cross-spawn": "^7.0.1" + }, + "engines": { + "node": ">= 12.13.0" + } + }, + "node_modules/@puppeteer/browsers": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-3.2.6.tgz", + "integrity": "sha512-MJP29gVoHznmqmwhH1tlZqzfeQ3aylTpSSa6WKZXtkwYUH02i9imBmWGTyZrbnAou5paX78SE4jMAUqnEPf8ug==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "modern-tar": "^0.8.6", + "yargs": "^18.0.0" + }, + "bin": { + "browsers": "lib/main-cli.js" + }, + "engines": { + "node": ">=22.12.0" + }, + "peerDependencies": { + "proxy-agent": ">=8.0.1", + "yauzl": "^2.10.0 || ^3.4.0" + }, + "peerDependenciesMeta": { + "proxy-agent": { + "optional": true + }, + "yauzl": { + "optional": true + } + } + }, + "node_modules/@puppeteer/browsers/node_modules/ansi-regex": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.5.0.tgz", + "integrity": "sha512-zhvbbbdt1kbw7EJ9OQoxmQcuaNR9P5kTj7e/YdmOSU10aHWbycofJxA6jKfoEy3vlLdctOF9ZHaxLtmSm998wQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/@puppeteer/browsers/node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@puppeteer/browsers/node_modules/cliui": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-9.0.1.tgz", + "integrity": "sha512-k7ndgKhwoQveBL+/1tqGJYNz097I7WOvwbmmU2AR5+magtbjPWQTS1C5vzGkBC8Ym8UWRzfKUzUUqFLypY4Q+w==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^7.2.0", + "strip-ansi": "^7.1.0", + "wrap-ansi": "^9.0.0" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@puppeteer/browsers/node_modules/cliui/node_modules/string-width": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@puppeteer/browsers/node_modules/emoji-regex": { + "version": "10.6.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz", + "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==", + "dev": true, + "license": "MIT" + }, + "node_modules/@puppeteer/browsers/node_modules/string-width": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-8.3.0.tgz", + "integrity": "sha512-ZbmZM0JCihQN91dWnxoipT2KOEyHqEyfRXUyjuRhW8b/xnqPDoq4gWEVApTVa9db2wN8mmoikgFBbjh71+cGeQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "get-east-asian-width": "^1.5.0", + "strip-ansi": "^7.1.2" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@puppeteer/browsers/node_modules/strip-ansi": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", + "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.2.2" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/@puppeteer/browsers/node_modules/wrap-ansi": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.2.tgz", + "integrity": "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.2.1", + "string-width": "^7.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/@puppeteer/browsers/node_modules/wrap-ansi/node_modules/string-width": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@puppeteer/browsers/node_modules/yargs": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-18.2.0.tgz", + "integrity": "sha512-9OpKOLeaoNFecEp7P6iYbzze/5CqWoH7N3SMs/6y1XF4nMvFMspEGZzJ6uFi9MmQwXhyzqYoSEKO3tvA3Z/o2w==", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^9.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "string-width": "^8.2.1", + "y18n": "^5.0.5", + "yargs-parser": "^22.0.0" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=23" + } + }, + "node_modules/@types/node": { + "version": "24.19.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.19.2.tgz", + "integrity": "sha512-93d49wCWJkCyN87MxLpNN6KXPCwFw7bVUg79mGUV1OS40JGWwVCr8n61DXCSqz7CV6/VfPkb7xec7uP2M+QHWQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": ">=7.24.0 <7.24.7" + } + }, + "node_modules/@xmldom/xmldom": { + "version": "0.9.12", + "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.9.12.tgz", + "integrity": "sha512-5AXjrcMClTryPe9LgZrygpB1lj7s0S9E0+W+AHaVKAVyHanafK86iPSvG5xHVSp/jC+VH1UXu0TAEmY279xH7A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.6" + } + }, + "node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/brace-expansion": { + "version": "5.0.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.12.tgz", + "integrity": "sha512-YovQ3rzhaLMIrDjNDMkNS01tea93qhEhG5xy8f6+R0l+dw3Ki+5sCoIoI942iuLZTHWogWktgwVDhU09iNEimQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/chromium-bidi": { + "version": "157.0.8090-0", + "resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-157.0.8090-0.tgz", + "integrity": "sha512-KJw7IQgqXwxpJgJlEIPg5mr2ZoqA+jkTRtc5EABI7sUJ3bu3cpI7NuEBoj4Gn1AsF78CaT9So5WQWspK3/wYag==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "mitt": "^3.0.1", + "zod": "^4.6.5" + }, + "engines": { + "node": ">=20.19.0 <22.0.0 || >=22.12.0" + }, + "peerDependencies": { + "devtools-protocol": "*" + } + }, + "node_modules/commander": { + "version": "9.5.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz", + "integrity": "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || >=14" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/devtools-protocol": { + "version": "0.0.1704951", + "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1704951.tgz", + "integrity": "sha512-GQhQOs9u/Mvm1zMtJIH872JS4AskuZdUSsPNPk4ChahtWAUqgz6atEtIsMt+nO7MqF1Ic4HxII/k8Cu36JivNg==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/electron": { + "version": "45.2.1", + "resolved": "https://registry.npmjs.org/electron/-/electron-45.2.1.tgz", + "integrity": "sha512-t433yuo8Kh8WG/mTStnPRT/NtpWw8A+caNLJgeU0tS/FlfOkeNEntFsac4ViLgDxZOmDe7BeSkOANVqrqNHhDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@electron-internal/extract-zip": "^1.0.1", + "@electron/get": "^5.0.0", + "@types/node": "^24.9.0" + }, + "bin": { + "electron": "cli.js", + "install-electron": "install.js" + }, + "engines": { + "node": ">= 22.12.0" + } + }, + "node_modules/env-paths": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-3.0.0.tgz", + "integrity": "sha512-dtJUTepzMW3Lm/NPxRf3wP4642UWhjL2sQxc+ym2YMj1m/H2zDNQOlezafzkHwn6sMstjHTwG6iQQsctDW/b1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/err-code": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/err-code/-/err-code-2.0.3.tgz", + "integrity": "sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==", + "dev": true, + "license": "MIT" + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/filename-reserved-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/filename-reserved-regex/-/filename-reserved-regex-3.0.0.tgz", + "integrity": "sha512-hn4cQfU6GOT/7cFHXBqeBg2TbrMBgdD0kcjLhvSQYYwm3s4B6cjvBfb7nBALJLAXqmU5xajSa7X2NnUud/VCdw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/filenamify": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/filenamify/-/filenamify-6.0.0.tgz", + "integrity": "sha512-vqIlNogKeyD3yzrm0yhRMQg8hOVwYcYRfjEoODd49iCprMn4HL85gK3HcykQE53EPIpX3HcAbGA5ELQv216dAQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "filename-reserved-regex": "^3.0.0" + }, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flora-colossus": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/flora-colossus/-/flora-colossus-3.0.2.tgz", + "integrity": "sha512-Jk78K/Tzt6saxQPGChlJw69xuFGpWyTSAS8EdU0h/FyXwD2K46yNOXmo6nRHcZ9ooekyBAzMkwmiGNt7wOC5zg==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.4.1" + }, + "engines": { + "node": ">=22.12.0" + } + }, + "node_modules/galactus": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/galactus/-/galactus-2.0.2.tgz", + "integrity": "sha512-HmKyTFGomdAchz4umx8MwBnrnfFmdpwiTyGA4ZOF7rya2Lmgbc9qate4yweInL+0gUBVImhaz12SBGpW3SY4Yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.4.1", + "flora-colossus": "^3.0.2" + }, + "engines": { + "node": ">=22.12.0" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-east-asian-width": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.7.0.tgz", + "integrity": "sha512-XjH1AECxf0giL2V1aU8vKyRR2ppRUb5c0EvT7zuJTokQ74bNo52zOtghqdWIqrhUD79fo3x0WfKZdOqxF6LG1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/glob": { + "version": "13.0.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-13.0.6.tgz", + "integrity": "sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "minimatch": "^10.2.2", + "minipass": "^7.1.3", + "path-scurry": "^2.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/isbinaryfile": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-4.0.10.tgz", + "integrity": "sha512-iHrqe5shvBUcFbmZq9zOQHBoeOhZJu6RQGrDpBgenUm/Am+F3JM2MgQj+rK3Z601fzrL5gLZWtAPH2OBaSVcyw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/gjtorikian/" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/junk": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/junk/-/junk-4.0.1.tgz", + "integrity": "sha512-Qush0uP+G8ZScpGMZvHUiRfI0YBWuB3gVBYlI0v0vvOJt5FLicco+IkP0a50LqTTQhmts/m6tP5SWE+USyIvcQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lru-cache": { + "version": "11.5.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.5.3.tgz", + "integrity": "sha512-U4N8FgzmWxc8k1VH8Kr6lQg18U7Fjvby6wXHVRX/ZZ7IwWbRMgrRbP0Wrb5q5NVinryp4SQampHKdvtecItxUg==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/minimatch": { + "version": "10.2.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.6.tgz", + "integrity": "sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.8" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/minipass": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz", + "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/mitt": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.1.tgz", + "integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==", + "dev": true, + "license": "MIT" + }, + "node_modules/modern-tar": { + "version": "0.8.6", + "resolved": "https://registry.npmjs.org/modern-tar/-/modern-tar-0.8.6.tgz", + "integrity": "sha512-LYSks0tNVqbtez/9OB0YFP6qLRSqkdAxJWOQpVaR67lNTbj2zNLyYpowkRMia5lXLCZKzxwoxkxH2MbbBR2aEg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-scurry": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.2.tgz", + "integrity": "sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^11.0.0", + "minipass": "^7.1.2" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/pe-library": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/pe-library/-/pe-library-1.0.1.tgz", + "integrity": "sha512-nh39Mo1eGWmZS7y+mK/dQIqg7S1lp38DpRxkyoHf0ZcUs/HDc+yyTjuOtTvSMZHmfSLuSQaX945u05Y2Q6UWZg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14", + "npm": ">=7" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/jet2jet" + } + }, + "node_modules/plist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/plist/-/plist-3.1.1.tgz", + "integrity": "sha512-ZIfcLJC+7E7FBFnDxm9MPmt7D+DidyQ26lewieO75AdhA2ayMtsJSES0iWzqJQbcVRSrTufQoy0DR94xHue0oA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@xmldom/xmldom": "^0.9.10", + "base64-js": "^1.5.1", + "xmlbuilder": "^15.1.1" + }, + "engines": { + "node": ">=10.4.0" + } + }, + "node_modules/postject": { + "version": "1.0.0-alpha.6", + "resolved": "https://registry.npmjs.org/postject/-/postject-1.0.0-alpha.6.tgz", + "integrity": "sha512-b9Eb8h2eVqNE8edvKdwqkrY6O7kAwmI8kcnBv1NScolYJbo59XUF0noFq+lxbC1yN20bmC0WBEbDC5H/7ASb0A==", + "dev": true, + "license": "MIT", + "dependencies": { + "commander": "^9.4.0" + }, + "bin": { + "postject": "dist/cli.js" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/progress": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/promise-retry": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/promise-retry/-/promise-retry-2.0.1.tgz", + "integrity": "sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "err-code": "^2.0.2", + "retry": "^0.12.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/puppeteer-core": { + "version": "25.16.0", + "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-25.16.0.tgz", + "integrity": "sha512-VA5Pf54N8shfopNkZZXCdiiwfnnY8ZJYAMqFQRhlH5mGUhfwf53Ar770GPtdpTDLUqvQeYpIBtC9/G2Jij2IJw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@puppeteer/browsers": "3.2.6", + "chromium-bidi": "157.0.8090-0", + "devtools-protocol": "0.0.1704951", + "typed-query-selector": "^2.12.3", + "webdriver-bidi-protocol": "1.0.1", + "ws": "^8.22.0" + }, + "engines": { + "node": ">=22.12.0" + } + }, + "node_modules/resedit": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/resedit/-/resedit-2.0.3.tgz", + "integrity": "sha512-oTeemxwoMuxxTYxXUwjkrOPfngTQehlv0/HoYFNkB4uzsP1Un1A9nI8JQKGOFkxpqkC7qkMs0lUsGrvUlbLNUA==", + "dev": true, + "license": "MIT", + "dependencies": { + "pe-library": "^1.0.1" + }, + "engines": { + "node": ">=14", + "npm": ">=7" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/jet2jet" + } + }, + "node_modules/retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", + "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/semver": { + "version": "7.8.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", + "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/sumchecker": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/sumchecker/-/sumchecker-3.0.1.tgz", + "integrity": "sha512-MvjXzkz/BOfyVDkG0oFOtBxHX2u3gKbMHIF/dXblZsgD3BWOFLmHovIpZY7BykJdAjcqRCBi1WYBNdEC9yI7vg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "debug": "^4.1.0" + }, + "engines": { + "node": ">= 8.0" + } + }, + "node_modules/typed-query-selector": { + "version": "2.12.3", + "resolved": "https://registry.npmjs.org/typed-query-selector/-/typed-query-selector-2.12.3.tgz", + "integrity": "sha512-/dtuMZyutHLlrxWhhC1uib00ELUr2dZ8C9B8YzZYsJmeE3V56OxRblrSM2UaF142pcMtak7ehZ6+poWGeehNIg==", + "dev": true, + "license": "MIT" + }, + "node_modules/undici": { + "version": "7.30.1", + "resolved": "https://registry.npmjs.org/undici/-/undici-7.30.1.tgz", + "integrity": "sha512-03KrMbjIFDWQR5RNPrSw3bd1r12JpyFQl9/yxjUbJtqkjmllqC5lFjZh6ujeuJCKr8EOQvRW872Oy/az+nLYxg==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=20.18.1" + } + }, + "node_modules/undici-types": { + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.24.6.tgz", + "integrity": "sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg==", + "dev": true, + "license": "MIT" + }, + "node_modules/webdriver-bidi-protocol": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/webdriver-bidi-protocol/-/webdriver-bidi-protocol-1.0.1.tgz", + "integrity": "sha512-WXWs/aXF2a6Al1Z2/V/7Gp2oPZOAAmHjJOTmv4KCXRJ122IMj3tSSMc6a4q3K4QOmhQbse/Ahr+oTVJOU2dUiw==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/ws": { + "version": "8.22.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.22.1.tgz", + "integrity": "sha512-oyL6OPYme/CmiFvNuGzeoxCJ4nOrZE2O1FwM9MWM1AsGri6nLXB2pg+lNA8aLeXMSav0J6eIVS1j6vUPzUtK9g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/xmlbuilder": { + "version": "15.1.1", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-15.1.1.tgz", + "integrity": "sha512-yMqGBqtXyeN1e3TGYvgNgDVZ3j84W4cwkOXQswghol6APgZWaff9lnbvN7MHYJOiXsvGPXtjTYJEiC9J2wv9Eg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.0" + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs-parser": { + "version": "22.0.0", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-22.0.0.tgz", + "integrity": "sha512-rwu/ClNdSMpkSrUb+d6BRsSkLUq1fmfsY6TOpYzTwvwkg1/NRG85KBy3kq++A8LKQwX6lsu+aWad+2khvuXrqw==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=23" + } + }, + "node_modules/zod": { + "version": "4.6.7", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.6.7.tgz", + "integrity": "sha512-g8W2D2kU6a8untc053fX1uVBKzM7yqAxW/3zwk83+6RyF2+SKOuV+c0wBDhUwzV1PaGapmZH+rmcUzPf+3CGwg==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + } + } +} diff --git a/desktop/package.json b/desktop/package.json new file mode 100644 index 00000000..b8e04097 --- /dev/null +++ b/desktop/package.json @@ -0,0 +1,18 @@ +{ + "name": "scanruler-desktop", + "version": "0.1.0", + "description": "ScanRuler desktop shell (Electron)", + "main": "main.js", + "type": "module", + "license": "AGPL-3.0-only", + "scripts": { + "start": "electron .", + "test": "node test/dicts.mjs && node test/smoke.mjs && node test/creality.mjs", + "dist": "node scripts/package.mjs" + }, + "devDependencies": { + "@electron/packager": "^20.3.0", + "electron": "^45.2.1", + "puppeteer-core": "^25.16.0" + } +} diff --git a/desktop/preload-main.cjs b/desktop/preload-main.cjs new file mode 100644 index 00000000..cd0c5640 --- /dev/null +++ b/desktop/preload-main.cjs @@ -0,0 +1,8 @@ +// SPDX-License-Identifier: AGPL-3.0-only +// Preload of the main window: one door, for a Creality project.obp the user drops or picks. +// The page gets no file paths and no Node; the main process only opens the .obp it is handed. +const { contextBridge, ipcRenderer, webUtils } = require('electron') + +contextBridge.exposeInMainWorld('srProject', { + open: (file) => ipcRenderer.invoke('project:open', webUtils.getPathForFile(file)), +}) diff --git a/desktop/preload.cjs b/desktop/preload.cjs new file mode 100644 index 00000000..8997db4c --- /dev/null +++ b/desktop/preload.cjs @@ -0,0 +1,10 @@ +// SPDX-License-Identifier: AGPL-3.0-only +// Preload of the Hardware panel only: the main window gets none, so the +// ScanRuler page has no path to Node. +const { contextBridge, ipcRenderer } = require('electron') + +contextBridge.exposeInMainWorld('hw', { + info: () => ipcRenderer.invoke('hw:info'), + setBackend: (backend) => ipcRenderer.invoke('hw:set-backend', backend), + restart: () => ipcRenderer.invoke('hw:restart'), +}) diff --git a/desktop/scanruler-mcp.cmd b/desktop/scanruler-mcp.cmd new file mode 100644 index 00000000..921cd981 --- /dev/null +++ b/desktop/scanruler-mcp.cmd @@ -0,0 +1,5 @@ +@echo off +rem Agent entry point: claude mcp add scanruler -- "\scanruler-mcp.cmd" +set ELECTRON_RUN_AS_NODE=1 +set SCANRULER_MCP_NO_UPDATE=1 +"%~dp0ScanRuler.exe" "%~dp0resources\agent-bridge.mjs" %* diff --git a/desktop/scripts/package.mjs b/desktop/scripts/package.mjs new file mode 100644 index 00000000..6beb8454 --- /dev/null +++ b/desktop/scripts/package.mjs @@ -0,0 +1,58 @@ +// SPDX-License-Identifier: AGPL-3.0-only +// Package the shell for Windows x64 into release/win-unpacked: Electron with the +// shell's files in an asar, and in resources/ the built web app (app-dist), the +// agent server (mcp, with its node_modules) and agent-bridge.mjs; the +// scanruler-mcp.cmd agents are pointed at sits beside the exe. +// @electron/packager, not electron-builder: the latter bundles 7-Zip, LGPL, +// which the repository's licence policy does not allow. +// +// npm run dist + +import { cpSync, mkdirSync, readFileSync, renameSync, rmSync, writeFileSync } from 'node:fs' +import { join } from 'node:path' +import { fileURLToPath } from 'node:url' +import { packager } from '@electron/packager' + +const here = fileURLToPath(new URL('..', import.meta.url)) +const repo = join(here, '..') +const out = join(here, 'release') +const stage = join(out, 'stage') +const resources = join(out, 'resources') +const pkg = JSON.parse(readFileSync(join(here, 'package.json'), 'utf8')) +const electronVersion = JSON.parse(readFileSync(join(here, 'node_modules', 'electron', 'package.json'), 'utf8')).version + +rmSync(out, { recursive: true, force: true }) +mkdirSync(stage, { recursive: true }) +mkdirSync(resources, { recursive: true }) + +// The shell itself: what package.json names, plus a package.json of its own. +for (const f of ['main.js', 'preload.cjs', 'preload-main.cjs', 'settings.js', 'creality.js', 'i18n.js', 'i18n', 'hardware.html']) cpSync(join(here, f), join(stage, f), { recursive: true }) +writeFileSync(join(stage, 'package.json'), JSON.stringify({ name: pkg.name, productName: 'ScanRuler', version: pkg.version, main: 'main.js', type: 'module' }, null, 2)) + +// Beside it: the web app, the agent server (tests left out) and the bridge. +cpSync(join(repo, 'dist'), join(resources, 'app-dist'), { recursive: true }) +cpSync(join(repo, 'mcp'), join(resources, 'mcp'), { recursive: true, filter: (src) => !/[\\/]mcp[\\/]tests([\\/]|$)/.test(src) }) +cpSync(join(here, 'agent-bridge.mjs'), join(resources, 'agent-bridge.mjs')) + +const [built] = await packager({ + dir: stage, + out, + name: 'ScanRuler', + platform: 'win32', + arch: 'x64', + electronVersion, + appVersion: pkg.version, + icon: join(repo, 'public', 'favicon.ico'), + asar: true, + prune: false, + overwrite: true, + quiet: true, + extraResource: [join(resources, 'app-dist'), join(resources, 'mcp'), join(resources, 'agent-bridge.mjs')], +}) +if (!built) throw new Error('the packager made no app') +const target = join(out, 'win-unpacked') +renameSync(built, target) +cpSync(join(here, 'scanruler-mcp.cmd'), join(target, 'scanruler-mcp.cmd')) +rmSync(stage, { recursive: true, force: true }) +rmSync(resources, { recursive: true, force: true }) +console.log(`Packaged ${target}`) diff --git a/desktop/settings.js b/desktop/settings.js new file mode 100644 index 00000000..a25b1706 --- /dev/null +++ b/desktop/settings.js @@ -0,0 +1,52 @@ +// SPDX-License-Identifier: AGPL-3.0-only +// Hardware choices kept in hardware.json under userData, read before Electron +// is ready because the graphics switches must be set before then. +import { readFileSync, writeFileSync, mkdirSync } from 'node:fs' +import { dirname, join } from 'node:path' + +import { LANGUAGES, defaultLanguage } from './i18n.js' + +/** The graphics backend ANGLE translates WebGL to: the API's name, and whether it is the default or experimental. */ +export const BACKENDS = { + d3d11: { name: 'DirectX 11', tag: 'default', angle: 'd3d11' }, + d3d12: { name: 'DirectX 12', tag: 'experimental', angle: 'd3d11on12' }, + vulkan: { name: 'Vulkan', tag: 'experimental', angle: 'vulkan' }, + gl: { name: 'OpenGL', tag: 'experimental', angle: 'gl' }, +} + +// The OS language picks the first language; the menu changes it. +const DEFAULTS = { backend: 'd3d11', language: defaultLanguage(Intl.DateTimeFormat().resolvedOptions().locale), crealityDir: '' } + +export function loadSettings(dir) { + try { + const saved = JSON.parse(readFileSync(join(dir, 'hardware.json'), 'utf8')) + return { + ...DEFAULTS, + ...(saved.backend in BACKENDS ? { backend: saved.backend } : {}), + ...(saved.language in LANGUAGES ? { language: saved.language } : {}), + ...(typeof saved.crealityDir === 'string' ? { crealityDir: saved.crealityDir } : {}), + } + } catch { + return { ...DEFAULTS } + } +} + +export function saveSettings(dir, settings) { + const file = join(dir, 'hardware.json') + mkdirSync(dirname(file), { recursive: true }) + writeFileSync(file, JSON.stringify(settings, null, 2)) +} + +/** The Chromium switches for a backend: [name, value?] pairs. */ +export function gpuSwitches(backend) { + const features = ['WebGPU'] + if (backend === 'vulkan') features.push('Vulkan') + return [ + ['use-angle', BACKENDS[backend].angle], + ['enable-features', features.join(',')], + ['enable-unsafe-webgpu'], + ['ignore-gpu-blocklist'], + ['enable-gpu-rasterization'], + ['enable-zero-copy'], + ] +} diff --git a/desktop/test/creality.mjs b/desktop/test/creality.mjs new file mode 100644 index 00000000..c1fb1abd --- /dev/null +++ b/desktop/test/creality.mjs @@ -0,0 +1,51 @@ +// SPDX-License-Identifier: AGPL-3.0-only +// The watcher reports a Mesh.ply once, after it stops growing, and ignores the rest of a project. +import { mkdtempSync, mkdirSync, writeFileSync, appendFileSync, rmSync } from 'node:fs' +import { tmpdir } from 'node:os' +import { join } from 'node:path' +import assert from 'node:assert/strict' +import { isMesh, meshOfProject, watchMeshes } from '../creality.js' +import { utimesSync } from 'node:fs' + +assert(isMesh('C:\\Scan\\P\\data\\result\\Mesh.ply')) +assert(isMesh('/scan/p/data/result/mesh.ply')) +assert(!isMesh('C:\\Scan\\P\\data\\result\\Fused.ply')) +assert(!isMesh('C:\\Scan\\P\\Mesh.ply')) + +// project.obp: the final mesh, else the newest session's fused scan, else a clear error +const proj = mkdtempSync(join(tmpdir(), 'obp-')) +const put = (...p) => { mkdirSync(join(proj, ...p.slice(0, -1)), { recursive: true }); writeFileSync(join(proj, ...p), 'x') } +writeFileSync(join(proj, 'project.obp'), 'name: project\ndata_dir: dData\ncombinations:\n []\nsessions:\n - sOld\n - sNew\nplatform: windows\n') +assert.throws(() => meshOfProject(join(proj, 'project.obp')), /ainda não tem malha/) +put('sOld', 'result', 'Fused.ply'); put('sNew', 'result', 'Fused.ply') +utimesSync(join(proj, 'sOld', 'result', 'Fused.ply'), new Date(2020, 0, 1), new Date(2020, 0, 1)) +assert.equal(meshOfProject(join(proj, 'project.obp')), join(proj, 'sNew', 'result', 'Fused.ply')) +put('dData', 'result', 'Mesh.ply') +assert.equal(meshOfProject(join(proj, 'project.obp')), join(proj, 'dData', 'result', 'Mesh.ply')) +rmSync(proj, { recursive: true, force: true }) + +const dir = mkdtempSync(join(tmpdir(), 'creality-')) +const sleep = (ms) => new Promise((r) => setTimeout(r, ms)) +const got = [] +const stop = watchMeshes(dir, (path, project) => got.push({ path, project }), { settleMs: 150 }) +try { + const result = join(dir, 'Peca_1', '20260101', 'result') + mkdirSync(result, { recursive: true }) + writeFileSync(join(result, 'Fused.ply'), 'x') // not the final mesh + writeFileSync(join(result, 'Mesh.ply'), 'a') + await sleep(100) + appendFileSync(join(result, 'Mesh.ply'), 'b') // still being written + await sleep(100) + appendFileSync(join(result, 'Mesh.ply'), 'c') + await sleep(800) + assert.equal(got.length, 1, `one report after it settled, got ${got.length}`) + assert.equal(got[0].project, 'Peca_1') + assert(got[0].path.endsWith('Mesh.ply')) + appendFileSync(join(result, 'Mesh.ply'), 'd') // a re-mesh later reports again + await sleep(800) + assert.equal(got.length, 2) + console.log('creality watcher ok') +} finally { + stop() + rmSync(dir, { recursive: true, force: true }) +} diff --git a/desktop/test/dicts.mjs b/desktop/test/dicts.mjs new file mode 100644 index 00000000..a5f52f82 --- /dev/null +++ b/desktop/test/dicts.mjs @@ -0,0 +1,34 @@ +// SPDX-License-Identifier: AGPL-3.0-only +// Every dictionary in i18n/ holds the same English keys, each with a non-empty +// value, and names itself; the menu and panel strings it carries are known ones. +import assert from 'node:assert/strict' +import { readdirSync, readFileSync } from 'node:fs' +import { LANGUAGES, defaultLanguage, hwOf, loadDict, menuOf } from '../i18n.js' + +const dir = new URL('../i18n/', import.meta.url) +const langs = readdirSync(dir).filter((f) => f.endsWith('.json') && !f.startsWith('_')).map((f) => f.slice(0, -5)) +// The source list translators work from is the pt-BR key set. +assert.deepEqual(JSON.parse(readFileSync(new URL('_english-keys.json', dir), 'utf8')), Object.keys(loadDict('pt-BR')), '_english-keys.json up to date') +assert(langs.includes('pt-BR'), 'pt-BR present') +const keys = Object.keys(loadDict('pt-BR')) +const menuKeys = Object.keys(menuOf('en')) +const hwKeys = Object.keys(hwOf('en')) +for (const lang of langs) { + const raw = JSON.parse(readFileSync(new URL(`${lang}.json`, dir), 'utf8')) + const dict = loadDict(lang) + assert.equal(typeof raw._name, 'string', `${lang}: _name`) + assert.equal(LANGUAGES[lang], raw._name, `${lang}: listed by its own name`) + assert.deepEqual(Object.keys(dict).sort(), [...keys].sort(), `${lang}: same keys as pt-BR`) + for (const [k, v] of Object.entries(dict)) assert(typeof v === 'string' && v.trim(), `${lang}: empty value for "${k}"`) + for (const k of Object.keys(raw._menu ?? {})) assert(menuKeys.includes(k), `${lang}: unknown _menu key ${k}`) + for (const k of Object.keys(raw._hw ?? {})) assert(hwKeys.includes(k), `${lang}: unknown _hw key ${k}`) + // A fragment keeps its shape: what ends in ":" or " ·" in English does so translated. + // Chinese and Japanese write a full-width colon. + const ends = { ':': /[::]$/, ' ·': /·$/, '…': /…$/ } + for (const [k, v] of Object.entries(dict)) for (const [end, re] of Object.entries(ends)) if (k.endsWith(end)) assert(re.test(v.trimEnd()), `${lang}: "${k}" lost its "${end}"`) +} +assert.equal(loadDict('en'), null) +assert.equal(defaultLanguage('pt-BR'), 'pt-BR') +assert.equal(defaultLanguage('pt-PT'), 'pt-BR') +assert.equal(defaultLanguage('xx'), 'en') +console.log(`dicts ok: ${langs.join(', ')}`) diff --git a/desktop/test/e2e-agent.mjs b/desktop/test/e2e-agent.mjs new file mode 100644 index 00000000..4dc1676e --- /dev/null +++ b/desktop/test/e2e-agent.mjs @@ -0,0 +1,41 @@ +// SPDX-License-Identifier: AGPL-3.0-only +// End to end: the app pairs with the agent link; an MCP client reaches the page through the relay. +import { spawn } from 'node:child_process' +import { createRequire } from 'node:module' +import { mkdtempSync } from 'node:fs' +import { tmpdir } from 'node:os' +import { join } from 'node:path' +import puppeteer from 'puppeteer-core' +import assert from 'node:assert/strict' + +const electron = createRequire(import.meta.url)('electron') +// Its own user data: the single-instance lock is per profile, so an installed app stays untouched. +const userData = `--user-data-dir=${mkdtempSync(join(tmpdir(), 'scanruler-test-'))}` +const app = spawn(electron, ['.', '--remote-debugging-port=9334', userData], { cwd: new URL('..', import.meta.url), env: { ...process.env, SCANRULER_MCP_PORT: '7398' }, stdio: 'ignore' }) +const sleep = (ms) => new Promise((r) => setTimeout(r, ms)) +let mcp +try { + let browser + for (let i = 0; i < 60 && !browser; i++) { browser = await puppeteer.connect({ browserURL: 'http://127.0.0.1:9334' }).catch(() => null); if (!browser) await sleep(500) } + let page + for (let i = 0; i < 60 && !page; i++) { page = (await browser.pages()).find((p) => p.url().startsWith('http://127.0.0.1:7398/')); if (!page) await sleep(500) } + await page.waitForSelector('#root *', { timeout: 30000 }) + await sleep(1500) + const buttons = await page.$$eval('button', (b) => b.map((x) => x.textContent.trim()).filter(Boolean)) + console.log('buttons:', buttons.slice(0, 12)) + const accept = await page.evaluateHandle(() => [...document.querySelectorAll('button')].find((b) => /accept|connect|conectar|aceit|understand/i.test(b.textContent))) + if (accept.asElement()) await accept.asElement().click() + await sleep(2000) + mcp = spawn(process.execPath, ['../mcp/bin/scanruler-mcp.js', '--port', '7398', '--no-update'], { stdio: ['pipe', 'pipe', 'inherit'] }) + const send = (o) => mcp.stdin.write(JSON.stringify(o) + '\n') + let buf = '' + const replies = new Map() + mcp.stdout.on('data', (d) => { buf += d; let i; while ((i = buf.indexOf('\n')) >= 0) { const m = JSON.parse(buf.slice(0, i)); buf = buf.slice(i + 1); replies.set(m.id, m) } }) + const call = async (id, method, params) => { send({ jsonrpc: '2.0', id, method, params }); for (let i = 0; i < 60 && !replies.has(id); i++) await sleep(250); return replies.get(id) } + await call(1, 'initialize', { protocolVersion: '2024-11-05', capabilities: {}, clientInfo: { name: 'smoke', version: '0' } }) + send({ jsonrpc: '2.0', method: 'notifications/initialized' }) + const r = await call(2, 'tools/call', { name: 'scanruler_status', arguments: {} }) + const text = JSON.stringify(r) + console.log(text.slice(0, 600)) + assert(/connected|page/i.test(text)) +} finally { mcp?.kill(); app.kill() } diff --git a/desktop/test/i18n.mjs b/desktop/test/i18n.mjs new file mode 100644 index 00000000..445b9bf9 --- /dev/null +++ b/desktop/test/i18n.mjs @@ -0,0 +1,46 @@ +// SPDX-License-Identifier: AGPL-3.0-only +// The page is translated, stays translated as React redraws, and goes back to English. +import { spawn } from 'node:child_process' +import { createRequire } from 'node:module' +import { mkdtempSync, readFileSync } from 'node:fs' +import { tmpdir } from 'node:os' +import { join } from 'node:path' +import puppeteer from 'puppeteer-core' +import assert from 'node:assert/strict' + +const electron = createRequire(import.meta.url)('electron') +// Its own user data: the single-instance lock is per profile, so an installed app stays untouched. +const userData = `--user-data-dir=${mkdtempSync(join(tmpdir(), 'scanruler-test-'))}` +const dict = JSON.parse(readFileSync(new URL('../i18n/pt-BR.json', import.meta.url), 'utf8')) +const child = spawn(electron, ['.', '--remote-debugging-port=9337', userData], { + cwd: new URL('..', import.meta.url), + env: { ...process.env, SCANRULER_MCP_PORT: '7395' }, + stdio: 'ignore', +}) +const sleep = (ms) => new Promise((r) => setTimeout(r, ms)) +const text = (page) => page.evaluate(() => document.body.textContent) +try { + let browser + for (let i = 0; i < 60 && !browser; i++) { browser = await puppeteer.connect({ browserURL: 'http://127.0.0.1:9337' }).catch(() => null); if (!browser) await sleep(500) } + let page + for (let i = 0; i < 60 && !page; i++) { page = (await browser.pages()).find((p) => p.url().startsWith('http://127.0.0.1:7395/')); if (!page) await sleep(500) } + await page.waitForSelector('#root *', { timeout: 30000 }); await sleep(2500) + let t = await text(page) + assert(t.includes(dict['Wall Thickness']), 'tab translated') + assert(!/\bWall Thickness\b/.test(t), 'no English tab left') + // a click makes React draw new text; it must arrive translated + await page.evaluate(() => [...document.querySelectorAll('button')].find((b) => /Espessura|Wall/i.test(b.textContent))?.click()) + await sleep(800) + t = await text(page) + assert(t.includes(dict['Measure wall thickness']), 'panel drawn after a click is translated') + // back to English restores everything + await page.evaluate(() => window.__srI18n.set(null)) + await sleep(300) + t = await text(page) + assert(t.includes('Measure wall thickness') && !t.includes(dict['Measure wall thickness']), 'English restored') + // Short or unchanged values ("Auto") also occur inside English words, so only count real translations. + const left = Object.entries(dict).filter(([k, v]) => v !== k && v.length > 12 && t.includes(v)) + assert.equal(left.length, 0, `Portuguese left after restore: ${left.map(([, v]) => v).join(' | ')}`) + console.log('i18n ok') + await browser.disconnect() +} finally { child.kill() } diff --git a/desktop/test/smoke.mjs b/desktop/test/smoke.mjs new file mode 100644 index 00000000..2cb5c80c --- /dev/null +++ b/desktop/test/smoke.mjs @@ -0,0 +1,54 @@ +// SPDX-License-Identifier: AGPL-3.0-only +// Starts the desktop app on a spare debugging port and checks what the page +// sees: cross-origin isolation, thread count, WebGL2 and WebGPU. +import { spawn } from 'node:child_process' +import { createRequire } from 'node:module' +import { mkdtempSync } from 'node:fs' +import { tmpdir } from 'node:os' +import { join } from 'node:path' +import puppeteer from 'puppeteer-core' +import assert from 'node:assert/strict' + +const electron = createRequire(import.meta.url)('electron') +// Its own user data: the single-instance lock is per profile, so an installed app stays untouched. +const userData = `--user-data-dir=${mkdtempSync(join(tmpdir(), 'scanruler-test-'))}` +const port = 9333 +const exe = process.env.APP_EXE +const child = spawn(exe ?? electron, [...(exe ? [] : ['.']), `--remote-debugging-port=${port}`, userData], { + cwd: new URL('..', import.meta.url), + env: { ...process.env, SCANRULER_MCP_PORT: '7399' }, + stdio: 'inherit', +}) +const stop = () => child.kill() +process.on('exit', stop) + +try { + let browser + for (let i = 0; i < 60 && !browser; i++) { + browser = await puppeteer.connect({ browserURL: `http://127.0.0.1:${port}` }).catch(() => null) + if (!browser) await new Promise((r) => setTimeout(r, 500)) + } + assert(browser, 'app did not expose the debugging port') + let page + for (let i = 0; i < 60 && !page; i++) { + page = (await browser.pages()).find((p) => p.url().startsWith('http://127.0.0.1:7399/')) + if (!page) await new Promise((r) => setTimeout(r, 500)) + } + assert(page, 'main window never loaded the local app') + await page.waitForSelector('#root *', { timeout: 30000 }) + const info = await page.evaluate(async () => ({ + isolated: crossOriginIsolated, + threads: navigator.hardwareConcurrency, + webgl2: !!new OffscreenCanvas(1, 1).getContext('webgl2'), + webgpu: !!(navigator.gpu && (await navigator.gpu.requestAdapter())), + })) + console.log(info) + assert.equal(info.isolated, true, 'crossOriginIsolated') + assert(info.threads >= 4, 'threads') + assert(info.webgl2, 'webgl2') + assert(info.webgpu, 'webgpu') + console.log('smoke ok') + await browser.disconnect() +} finally { + stop() +}