From 0fcbddda8ebdbf6ec381eeed60e245e03e1739f5 Mon Sep 17 00:00:00 2001 From: Jan Klass Date: Mon, 20 Feb 2023 10:44:25 +0100 Subject: [PATCH 01/74] docs: Fix outdated link in README.md (#6263) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c5f7f5cd4..31ee567de 100644 --- a/README.md +++ b/README.md @@ -70,7 +70,7 @@ The Excalidraw editor (npm package) supports: ## Excalidraw.com -The app hosted at [excalidraw.com](https://excalidraw.com) is a minimal showcase of what you can build with Excalidraw. Its [source code](https://github.com/excalidraw/excalidraw/tree/maielo/new-readme/src/excalidraw-app) is part of this repository as well, and the app features: +The app hosted at [excalidraw.com](https://excalidraw.com) is a minimal showcase of what you can build with Excalidraw. Its [source code](https://github.com/excalidraw/excalidraw/tree/master/src/excalidraw-app) is part of this repository as well, and the app features: - 📡 PWA support (works offline). - 🤼 Real-time collaboration. From 88ff32e9b389b15577f0a6fa73df92a6eda7f914 Mon Sep 17 00:00:00 2001 From: Aakansha Doshi Date: Tue, 21 Feb 2023 12:36:43 +0530 Subject: [PATCH 02/74] fix: improve text wrapping in ellipse and alignment (#6172) * fix: improve text wrapping in ellipse * compute height when font properties updated * fix alignment * fix alignment when resizing * fix * ad padding * always compute height when redrawing bounding box and refactor * lint * fix specs * fix * redraw text bounding box when pasted or refreshed * fix * Add specs * fix * restore on font load * add comments --- src/components/App.tsx | 10 ++ src/element/newElement.ts | 10 ++ src/element/textElement.test.ts | 53 ++++++++- src/element/textElement.ts | 194 +++++++++++++++++++------------ src/element/textWysiwyg.test.tsx | 52 ++++----- src/element/textWysiwyg.tsx | 14 ++- 6 files changed, 225 insertions(+), 108 deletions(-) diff --git a/src/components/App.tsx b/src/components/App.tsx index a48510bf9..effa604bf 100644 --- a/src/components/App.tsx +++ b/src/components/App.tsx @@ -108,6 +108,7 @@ import { textWysiwyg, transformElements, updateTextElement, + redrawTextBoundingBox, } from "../element"; import { bindOrUnbindLinearElement, @@ -264,6 +265,7 @@ import { getBoundTextElement, getContainerCenter, getContainerDims, + getContainerElement, getTextBindableContainerAtPosition, isValidTextContainer, } from "../element/textElement"; @@ -1637,6 +1639,14 @@ class App extends React.Component { } this.scene.replaceAllElements(nextElements); + + nextElements.forEach((nextElement) => { + if (isTextElement(nextElement) && isBoundToContainer(nextElement)) { + const container = getContainerElement(nextElement); + redrawTextBoundingBox(nextElement, container); + } + }); + this.history.resumeRecording(); this.setState( diff --git a/src/element/newElement.ts b/src/element/newElement.ts index 8e7b8ee8a..c7f33a462 100644 --- a/src/element/newElement.ts +++ b/src/element/newElement.ts @@ -290,6 +290,11 @@ export const getMaxContainerWidth = (container: ExcalidrawElement) => { return BOUND_TEXT_PADDING * 8 * 2; } return containerWidth; + } else if (container.type === "ellipse") { + // The width of the largest rectangle inscribed inside an ellipse is + // Math.round((ellipse.width / 2) * Math.sqrt(2)) which is derived from + // equation of an ellipse -https://github.com/excalidraw/excalidraw/pull/6172 + return Math.round((width / 2) * Math.sqrt(2)) - BOUND_TEXT_PADDING * 2; } return width - BOUND_TEXT_PADDING * 2; }; @@ -306,6 +311,11 @@ export const getMaxContainerHeight = (container: ExcalidrawElement) => { return BOUND_TEXT_PADDING * 8 * 2; } return height; + } else if (container.type === "ellipse") { + // The height of the largest rectangle inscribed inside an ellipse is + // Math.round((ellipse.height / 2) * Math.sqrt(2)) which is derived from + // equation of an ellipse - https://github.com/excalidraw/excalidraw/pull/6172 + return Math.round((height / 2) * Math.sqrt(2)) - BOUND_TEXT_PADDING * 2; } return height - BOUND_TEXT_PADDING * 2; }; diff --git a/src/element/textElement.test.ts b/src/element/textElement.test.ts index e1b9ff6f0..91974aca2 100644 --- a/src/element/textElement.test.ts +++ b/src/element/textElement.test.ts @@ -1,5 +1,11 @@ import { BOUND_TEXT_PADDING } from "../constants"; -import { measureText, wrapText } from "./textElement"; +import { API } from "../tests/helpers/api"; +import { + computeContainerHeightForBoundText, + getContainerCoords, + measureText, + wrapText, +} from "./textElement"; import { FontString } from "./types"; describe("Test wrapText", () => { @@ -193,4 +199,49 @@ describe("Test measureText", () => { `); }); + + describe("Test getContainerCoords", () => { + const params = { width: 200, height: 100, x: 10, y: 20 }; + it("should compute coords correctly when ellipse", () => { + const ellipse = API.createElement({ + type: "ellipse", + ...params, + }); + expect(getContainerCoords(ellipse)).toEqual({ + x: 44.2893218813452455, + y: 39.64466094067262, + }); + }); + it("should compute coords correctly when rectangle", () => { + const rectangle = API.createElement({ + type: "rectangle", + ...params, + }); + expect(getContainerCoords(rectangle)).toEqual({ + x: 10, + y: 20, + }); + }); + }); + + describe("Test computeContainerHeightForBoundText", () => { + const params = { + width: 178, + height: 194, + }; + it("should compute container height correctly for rectangle", () => { + const element = API.createElement({ + type: "rectangle", + ...params, + }); + expect(computeContainerHeightForBoundText(element, 150)).toEqual(160); + }); + it("should compute container height correctly for ellipse", () => { + const element = API.createElement({ + type: "ellipse", + ...params, + }); + expect(computeContainerHeightForBoundText(element, 150)).toEqual(212); + }); + }); }); diff --git a/src/element/textElement.ts b/src/element/textElement.ts index c726c1c3f..ed4d27629 100644 --- a/src/element/textElement.ts +++ b/src/element/textElement.ts @@ -44,68 +44,69 @@ export const redrawTextBoundingBox = ( container: ExcalidrawElement | null, ) => { let maxWidth = undefined; - let text = textElement.text; + + const boundTextUpdates = { + x: textElement.x, + y: textElement.y, + text: textElement.text, + width: textElement.width, + height: textElement.height, + baseline: textElement.baseline, + }; + + boundTextUpdates.text = textElement.text; + if (container) { maxWidth = getMaxContainerWidth(container); - text = wrapText( + boundTextUpdates.text = wrapText( textElement.originalText, getFontString(textElement), maxWidth, ); } - const metrics = measureText(text, getFontString(textElement), maxWidth); - let coordY = textElement.y; - let coordX = textElement.x; - // Resize container and vertically center align the text + const metrics = measureText( + boundTextUpdates.text, + getFontString(textElement), + maxWidth, + ); + + boundTextUpdates.width = metrics.width; + boundTextUpdates.height = metrics.height; + boundTextUpdates.baseline = metrics.baseline; + if (container) { - if (!isArrowElement(container)) { - const containerDims = getContainerDims(container); - let nextHeight = containerDims.height; - if (textElement.verticalAlign === VERTICAL_ALIGN.TOP) { - coordY = container.y; - } else if (textElement.verticalAlign === VERTICAL_ALIGN.BOTTOM) { - coordY = - container.y + - containerDims.height - - metrics.height - - BOUND_TEXT_PADDING; - } else { - coordY = container.y + containerDims.height / 2 - metrics.height / 2; - if (metrics.height > getMaxContainerHeight(container)) { - nextHeight = metrics.height + BOUND_TEXT_PADDING * 2; - coordY = container.y + nextHeight / 2 - metrics.height / 2; - } - } - if (textElement.textAlign === TEXT_ALIGN.LEFT) { - coordX = container.x + BOUND_TEXT_PADDING; - } else if (textElement.textAlign === TEXT_ALIGN.RIGHT) { - coordX = - container.x + - containerDims.width - - metrics.width - - BOUND_TEXT_PADDING; - } else { - coordX = container.x + containerDims.width / 2 - metrics.width / 2; - } - updateOriginalContainerCache(container.id, nextHeight); - mutateElement(container, { height: nextHeight }); - } else { + if (isArrowElement(container)) { const centerX = textElement.x + textElement.width / 2; const centerY = textElement.y + textElement.height / 2; const diffWidth = metrics.width - textElement.width; const diffHeight = metrics.height - textElement.height; - coordY = centerY - (textElement.height + diffHeight) / 2; - coordX = centerX - (textElement.width + diffWidth) / 2; + boundTextUpdates.x = centerY - (textElement.height + diffHeight) / 2; + boundTextUpdates.y = centerX - (textElement.width + diffWidth) / 2; + } else { + const containerDims = getContainerDims(container); + let maxContainerHeight = getMaxContainerHeight(container); + + let nextHeight = containerDims.height; + if (metrics.height > maxContainerHeight) { + nextHeight = computeContainerHeightForBoundText( + container, + metrics.height, + ); + mutateElement(container, { height: nextHeight }); + maxContainerHeight = getMaxContainerHeight(container); + updateOriginalContainerCache(container.id, nextHeight); + } + const updatedTextElement = { + ...textElement, + ...boundTextUpdates, + } as ExcalidrawTextElementWithContainer; + const { x, y } = computeBoundTextPosition(container, updatedTextElement); + boundTextUpdates.x = x; + boundTextUpdates.y = y; } } - mutateElement(textElement, { - width: metrics.width, - height: metrics.height, - baseline: metrics.baseline, - y: coordY, - x: coordX, - text, - }); + + mutateElement(textElement, boundTextUpdates); }; export const bindTextToShapeAfterDuplication = ( @@ -197,7 +198,11 @@ export const handleBindTextResize = ( } // increase height in case text element height exceeds if (nextHeight > maxHeight) { - containerHeight = nextHeight + getBoundTextElementOffset(textElement) * 2; + containerHeight = computeContainerHeightForBoundText( + container, + nextHeight, + ); + const diff = containerHeight - containerDims.height; // fix the y coord when resizing from ne/nw/n const updatedY = @@ -217,48 +222,57 @@ export const handleBindTextResize = ( text, width: nextWidth, height: nextHeight, - baseline: nextBaseLine, }); + if (!isArrowElement(container)) { - updateBoundTextPosition( - container, - textElement as ExcalidrawTextElementWithContainer, + mutateElement( + textElement, + computeBoundTextPosition( + container, + textElement as ExcalidrawTextElementWithContainer, + ), ); } } }; -const updateBoundTextPosition = ( +const computeBoundTextPosition = ( container: ExcalidrawElement, boundTextElement: ExcalidrawTextElementWithContainer, ) => { - const containerDims = getContainerDims(container); - const boundTextElementPadding = getBoundTextElementOffset(boundTextElement); + const containerCoords = getContainerCoords(container); + const maxContainerHeight = getMaxContainerHeight(container); + const maxContainerWidth = getMaxContainerWidth(container); + const padding = container.type === "ellipse" ? 0 : BOUND_TEXT_PADDING; + + let x; let y; if (boundTextElement.verticalAlign === VERTICAL_ALIGN.TOP) { - y = container.y + boundTextElementPadding; + y = containerCoords.y + padding; } else if (boundTextElement.verticalAlign === VERTICAL_ALIGN.BOTTOM) { y = - container.y + - containerDims.height - - boundTextElement.height - - boundTextElementPadding; + containerCoords.y + + (maxContainerHeight - boundTextElement.height + padding); } else { - y = container.y + containerDims.height / 2 - boundTextElement.height / 2; + y = + containerCoords.y + + (maxContainerHeight / 2 - boundTextElement.height / 2 + padding); } - const x = - boundTextElement.textAlign === TEXT_ALIGN.LEFT - ? container.x + boundTextElementPadding - : boundTextElement.textAlign === TEXT_ALIGN.RIGHT - ? container.x + - containerDims.width - - boundTextElement.width - - boundTextElementPadding - : container.x + containerDims.width / 2 - boundTextElement.width / 2; - - mutateElement(boundTextElement, { x, y }); + if (boundTextElement.textAlign === TEXT_ALIGN.LEFT) { + x = containerCoords.x + padding; + } else if (boundTextElement.textAlign === TEXT_ALIGN.RIGHT) { + x = + containerCoords.x + + (maxContainerWidth - boundTextElement.width + padding); + } else { + x = + containerCoords.x + + (maxContainerWidth / 2 - boundTextElement.width / 2 + padding); + } + return { x, y }; }; + // https://github.com/grassator/canvas-text-editor/blob/master/lib/FontMetrics.js export const measureText = ( text: string, @@ -621,6 +635,24 @@ export const getContainerCenter = ( return { x: midSegmentMidpoint[0], y: midSegmentMidpoint[1] }; }; +export const getContainerCoords = (container: NonDeletedExcalidrawElement) => { + if (container.type === "ellipse") { + // The derivation of coordinates is explained in https://github.com/excalidraw/excalidraw/pull/6172 + const offsetX = + (container.width / 2) * (1 - Math.sqrt(2) / 2) + BOUND_TEXT_PADDING; + const offsetY = + (container.height / 2) * (1 - Math.sqrt(2) / 2) + BOUND_TEXT_PADDING; + return { + x: container.x + offsetX, + y: container.y + offsetY, + }; + } + return { + x: container.x, + y: container.y, + }; +}; + export const getTextElementAngle = (textElement: ExcalidrawTextElement) => { const container = getContainerElement(textElement); if (!container || isArrowElement(container)) { @@ -633,12 +665,13 @@ export const getBoundTextElementOffset = ( boundTextElement: ExcalidrawTextElement | null, ) => { const container = getContainerElement(boundTextElement); - if (!container) { + if (!container || !boundTextElement) { return 0; } if (isArrowElement(container)) { return BOUND_TEXT_PADDING * 8; } + return BOUND_TEXT_PADDING; }; @@ -723,3 +756,16 @@ export const isValidTextContainer = (element: ExcalidrawElement) => { isArrowElement(element) ); }; + +export const computeContainerHeightForBoundText = ( + container: NonDeletedExcalidrawElement, + boundTextElementHeight: number, +) => { + if (container.type === "ellipse") { + return Math.round((boundTextElementHeight / Math.sqrt(2)) * 2); + } + if (isArrowElement(container)) { + return boundTextElementHeight + BOUND_TEXT_PADDING * 8 * 2; + } + return boundTextElementHeight + BOUND_TEXT_PADDING * 2; +}; diff --git a/src/element/textWysiwyg.test.tsx b/src/element/textWysiwyg.test.tsx index c61d4e68b..87a6e0bf5 100644 --- a/src/element/textWysiwyg.test.tsx +++ b/src/element/textWysiwyg.test.tsx @@ -791,9 +791,7 @@ describe("textWysiwyg", () => { text = h.elements[1] as ExcalidrawTextElementWithContainer; expect(text.text).toBe("Hello \nWorld!"); expect(text.originalText).toBe("Hello World!"); - expect(text.y).toBe( - rectangle.y + rectangle.height / 2 - (APPROX_LINE_HEIGHT * 2) / 2, - ); + expect(text.y).toBe(27.5); expect(text.x).toBe(rectangle.x + BOUND_TEXT_PADDING); expect(text.height).toBe(APPROX_LINE_HEIGHT * 2); expect(text.width).toBe(rectangle.width - BOUND_TEXT_PADDING * 2); @@ -827,9 +825,7 @@ describe("textWysiwyg", () => { expect(text.text).toBe("Hello"); expect(text.originalText).toBe("Hello"); - expect(text.y).toBe( - rectangle.y + rectangle.height / 2 - APPROX_LINE_HEIGHT / 2, - ); + expect(text.y).toBe(40); expect(text.x).toBe(rectangle.x + BOUND_TEXT_PADDING); expect(text.height).toBe(APPROX_LINE_HEIGHT); expect(text.width).toBe(rectangle.width - BOUND_TEXT_PADDING * 2); @@ -1248,7 +1244,7 @@ describe("textWysiwyg", () => { expect([h.elements[1].x, h.elements[1].y]).toMatchInlineSnapshot(` Array [ 15, - 20, + 25, ] `); }); @@ -1259,7 +1255,7 @@ describe("textWysiwyg", () => { expect([h.elements[1].x, h.elements[1].y]).toMatchInlineSnapshot(` Array [ 94.5, - 20, + 25, ] `); }); @@ -1269,22 +1265,22 @@ describe("textWysiwyg", () => { fireEvent.click(screen.getByTitle("Align top")); expect([h.elements[1].x, h.elements[1].y]).toMatchInlineSnapshot(` - Array [ - 174, - 20, - ] - `); + Array [ + 174, + 25, + ] + `); }); it("when center left", async () => { fireEvent.click(screen.getByTitle("Center vertically")); fireEvent.click(screen.getByTitle("Left")); expect([h.elements[1].x, h.elements[1].y]).toMatchInlineSnapshot(` - Array [ - 15, - 25, - ] - `); + Array [ + 15, + 20, + ] + `); }); it("when center center", async () => { @@ -1292,11 +1288,11 @@ describe("textWysiwyg", () => { fireEvent.click(screen.getByTitle("Center vertically")); expect([h.elements[1].x, h.elements[1].y]).toMatchInlineSnapshot(` - Array [ - -25, - 25, - ] - `); + Array [ + -25, + 20, + ] + `); }); it("when center right", async () => { @@ -1304,11 +1300,11 @@ describe("textWysiwyg", () => { fireEvent.click(screen.getByTitle("Center vertically")); expect([h.elements[1].x, h.elements[1].y]).toMatchInlineSnapshot(` - Array [ - 174, - 25, - ] - `); + Array [ + 174, + 20, + ] + `); }); it("when bottom left", async () => { diff --git a/src/element/textWysiwyg.tsx b/src/element/textWysiwyg.tsx index 7b0098ce0..5536632b4 100644 --- a/src/element/textWysiwyg.tsx +++ b/src/element/textWysiwyg.tsx @@ -25,6 +25,7 @@ import { getApproxLineHeight, getBoundTextElementId, getBoundTextElementOffset, + getContainerCoords, getContainerDims, getContainerElement, getTextElementAngle, @@ -230,19 +231,22 @@ export const textWysiwyg = ({ // Start pushing text upward until a diff of 30px (padding) // is reached else { + const padding = + container.type === "ellipse" + ? 0 + : getBoundTextElementOffset(updatedTextElement); + const containerCoords = getContainerCoords(container); + // vertically center align the text if (verticalAlign === VERTICAL_ALIGN.MIDDLE) { if (!isArrowElement(container)) { coordY = - container.y + containerDims.height / 2 - textElementHeight / 2; + containerCoords.y + maxHeight / 2 - textElementHeight / 2; } } if (verticalAlign === VERTICAL_ALIGN.BOTTOM) { coordY = - container.y + - containerDims.height - - textElementHeight - - getBoundTextElementOffset(updatedTextElement); + containerCoords.y + (maxHeight - textElementHeight + padding); } } } From 5368ddef74570abd3889e7db3133038b04b99cc7 Mon Sep 17 00:00:00 2001 From: Aakansha Doshi Date: Wed, 22 Feb 2023 16:28:12 +0530 Subject: [PATCH 03/74] fix: improve text wrapping inside rhombus and more fixes (#6265) * fix: improve text wrapping inside rhombus * Add comments * specs * fix: shift resize and multiple element regression for ellipse and rhombus * use container width for scaling font size * fix * fix multiple resize * lint * redraw on submit * redraw only newly pasted elements * no padding when center * fix tests * fix * dont add padding in rhombus when aligning * refactor * fix * move getMaxContainerHeight and getMaxContainerWidth to textElement.ts * Add specs --- src/components/App.tsx | 9 +-- src/element/newElement.ts | 46 +----------- src/element/resizeElements.ts | 26 ++++--- src/element/textElement.test.ts | 81 +++++++++++++++++++-- src/element/textElement.ts | 99 ++++++++++++++++++++------ src/element/textWysiwyg.test.tsx | 12 ++-- src/element/textWysiwyg.tsx | 13 ++-- src/tests/linearElementEditor.test.tsx | 7 +- 8 files changed, 192 insertions(+), 101 deletions(-) diff --git a/src/components/App.tsx b/src/components/App.tsx index effa604bf..3c0ac9dc4 100644 --- a/src/components/App.tsx +++ b/src/components/App.tsx @@ -1627,6 +1627,7 @@ class App extends React.Component { oldIdToDuplicatedId.set(element.id, newElement.id); return newElement; }); + bindTextToShapeAfterDuplication(newElements, elements, oldIdToDuplicatedId); const nextElements = [ ...this.scene.getElementsIncludingDeleted(), @@ -1640,10 +1641,10 @@ class App extends React.Component { this.scene.replaceAllElements(nextElements); - nextElements.forEach((nextElement) => { - if (isTextElement(nextElement) && isBoundToContainer(nextElement)) { - const container = getContainerElement(nextElement); - redrawTextBoundingBox(nextElement, container); + newElements.forEach((newElement) => { + if (isTextElement(newElement) && isBoundToContainer(newElement)) { + const container = getContainerElement(newElement); + redrawTextBoundingBox(newElement, container); } }); diff --git a/src/element/newElement.ts b/src/element/newElement.ts index c7f33a462..6024765ec 100644 --- a/src/element/newElement.ts +++ b/src/element/newElement.ts @@ -22,15 +22,15 @@ import { getElementAbsoluteCoords } from "."; import { adjustXYWithRotation } from "../math"; import { getResizedElementAbsoluteCoords } from "./bounds"; import { - getBoundTextElement, getBoundTextElementOffset, getContainerDims, getContainerElement, measureText, normalizeText, wrapText, + getMaxContainerWidth, } from "./textElement"; -import { BOUND_TEXT_PADDING, VERTICAL_ALIGN } from "../constants"; +import { VERTICAL_ALIGN } from "../constants"; import { isArrowElement } from "./typeChecks"; type ElementConstructorOpts = MarkOptional< @@ -278,48 +278,6 @@ export const refreshTextDimensions = ( return { text, ...dimensions }; }; -export const getMaxContainerWidth = (container: ExcalidrawElement) => { - const width = getContainerDims(container).width; - if (isArrowElement(container)) { - const containerWidth = width - BOUND_TEXT_PADDING * 8 * 2; - if (containerWidth <= 0) { - const boundText = getBoundTextElement(container); - if (boundText) { - return boundText.width; - } - return BOUND_TEXT_PADDING * 8 * 2; - } - return containerWidth; - } else if (container.type === "ellipse") { - // The width of the largest rectangle inscribed inside an ellipse is - // Math.round((ellipse.width / 2) * Math.sqrt(2)) which is derived from - // equation of an ellipse -https://github.com/excalidraw/excalidraw/pull/6172 - return Math.round((width / 2) * Math.sqrt(2)) - BOUND_TEXT_PADDING * 2; - } - return width - BOUND_TEXT_PADDING * 2; -}; - -export const getMaxContainerHeight = (container: ExcalidrawElement) => { - const height = getContainerDims(container).height; - if (isArrowElement(container)) { - const containerHeight = height - BOUND_TEXT_PADDING * 8 * 2; - if (containerHeight <= 0) { - const boundText = getBoundTextElement(container); - if (boundText) { - return boundText.height; - } - return BOUND_TEXT_PADDING * 8 * 2; - } - return height; - } else if (container.type === "ellipse") { - // The height of the largest rectangle inscribed inside an ellipse is - // Math.round((ellipse.height / 2) * Math.sqrt(2)) which is derived from - // equation of an ellipse - https://github.com/excalidraw/excalidraw/pull/6172 - return Math.round((height / 2) * Math.sqrt(2)) - BOUND_TEXT_PADDING * 2; - } - return height - BOUND_TEXT_PADDING * 2; -}; - export const updateTextElement = ( textElement: ExcalidrawTextElement, { diff --git a/src/element/resizeElements.ts b/src/element/resizeElements.ts index 605ab0c2b..fe4f8e484 100644 --- a/src/element/resizeElements.ts +++ b/src/element/resizeElements.ts @@ -43,12 +43,12 @@ import { getApproxMinLineWidth, getBoundTextElement, getBoundTextElementId, - getBoundTextElementOffset, getContainerElement, handleBindTextResize, measureText, + getMaxContainerHeight, + getMaxContainerWidth, } from "./textElement"; -import { getMaxContainerWidth } from "./newElement"; export const normalizeAngle = (angle: number): number => { if (angle >= 2 * Math.PI) { @@ -427,12 +427,16 @@ export const resizeSingleElement = ( }; } if (shouldMaintainAspectRatio) { - const boundTextElementPadding = - getBoundTextElementOffset(boundTextElement); + const updatedElement = { + ...element, + width: eleNewWidth, + height: eleNewHeight, + }; + const nextFont = measureFontSizeFromWH( boundTextElement, - eleNewWidth - boundTextElementPadding * 2, - eleNewHeight - boundTextElementPadding * 2, + getMaxContainerWidth(updatedElement), + getMaxContainerHeight(updatedElement), ); if (nextFont === null) { return; @@ -697,11 +701,15 @@ const resizeMultipleElements = ( const boundTextElement = getBoundTextElement(element.latest); if (boundTextElement || isTextElement(element.orig)) { - const optionalPadding = getBoundTextElementOffset(boundTextElement) * 2; + const updatedElement = { + ...element.latest, + width, + height, + }; const textMeasurements = measureFontSizeFromWH( boundTextElement ?? (element.orig as ExcalidrawTextElement), - width - optionalPadding, - height - optionalPadding, + getMaxContainerWidth(updatedElement), + getMaxContainerHeight(updatedElement), ); if (!textMeasurements) { diff --git a/src/element/textElement.test.ts b/src/element/textElement.test.ts index 91974aca2..22086095f 100644 --- a/src/element/textElement.test.ts +++ b/src/element/textElement.test.ts @@ -3,6 +3,8 @@ import { API } from "../tests/helpers/api"; import { computeContainerHeightForBoundText, getContainerCoords, + getMaxContainerWidth, + getMaxContainerHeight, measureText, wrapText, } from "./textElement"; @@ -202,24 +204,37 @@ describe("Test measureText", () => { describe("Test getContainerCoords", () => { const params = { width: 200, height: 100, x: 10, y: 20 }; + it("should compute coords correctly when ellipse", () => { - const ellipse = API.createElement({ + const element = API.createElement({ type: "ellipse", ...params, }); - expect(getContainerCoords(ellipse)).toEqual({ + expect(getContainerCoords(element)).toEqual({ x: 44.2893218813452455, y: 39.64466094067262, }); }); + it("should compute coords correctly when rectangle", () => { - const rectangle = API.createElement({ + const element = API.createElement({ type: "rectangle", ...params, }); - expect(getContainerCoords(rectangle)).toEqual({ - x: 10, - y: 20, + expect(getContainerCoords(element)).toEqual({ + x: 15, + y: 25, + }); + }); + + it("should compute coords correctly when diamond", () => { + const element = API.createElement({ + type: "diamond", + ...params, + }); + expect(getContainerCoords(element)).toEqual({ + x: 65, + y: 50, }); }); }); @@ -229,6 +244,7 @@ describe("Test measureText", () => { width: 178, height: 194, }; + it("should compute container height correctly for rectangle", () => { const element = API.createElement({ type: "rectangle", @@ -236,6 +252,7 @@ describe("Test measureText", () => { }); expect(computeContainerHeightForBoundText(element, 150)).toEqual(160); }); + it("should compute container height correctly for ellipse", () => { const element = API.createElement({ type: "ellipse", @@ -243,5 +260,57 @@ describe("Test measureText", () => { }); expect(computeContainerHeightForBoundText(element, 150)).toEqual(212); }); + + it("should compute container height correctly for diamond", () => { + const element = API.createElement({ + type: "diamond", + ...params, + }); + expect(computeContainerHeightForBoundText(element, 150)).toEqual(300); + }); + }); + + describe("Test getMaxContainerWidth", () => { + const params = { + width: 178, + height: 194, + }; + + it("should return max width when container is rectangle", () => { + const container = API.createElement({ type: "rectangle", ...params }); + expect(getMaxContainerWidth(container)).toBe(168); + }); + + it("should return max width when container is ellipse", () => { + const container = API.createElement({ type: "ellipse", ...params }); + expect(getMaxContainerWidth(container)).toBe(116); + }); + + it("should return max width when container is diamond", () => { + const container = API.createElement({ type: "diamond", ...params }); + expect(getMaxContainerWidth(container)).toBe(79); + }); + }); + + describe("Test getMaxContainerHeight", () => { + const params = { + width: 178, + height: 194, + }; + + it("should return max height when container is rectangle", () => { + const container = API.createElement({ type: "rectangle", ...params }); + expect(getMaxContainerHeight(container)).toBe(184); + }); + + it("should return max height when container is ellipse", () => { + const container = API.createElement({ type: "ellipse", ...params }); + expect(getMaxContainerHeight(container)).toBe(127); + }); + + it("should return max height when container is diamond", () => { + const container = API.createElement({ type: "diamond", ...params }); + expect(getMaxContainerHeight(container)).toBe(87); + }); }); }); diff --git a/src/element/textElement.ts b/src/element/textElement.ts index ed4d27629..b91e9f2f6 100644 --- a/src/element/textElement.ts +++ b/src/element/textElement.ts @@ -12,7 +12,6 @@ import { BOUND_TEXT_PADDING, TEXT_ALIGN, VERTICAL_ALIGN } from "../constants"; import { MaybeTransformHandleType } from "./transformHandles"; import Scene from "../scene/Scene"; import { isTextElement } from "."; -import { getMaxContainerHeight, getMaxContainerWidth } from "./newElement"; import { isBoundToContainer, isImageElement, @@ -244,31 +243,25 @@ const computeBoundTextPosition = ( const containerCoords = getContainerCoords(container); const maxContainerHeight = getMaxContainerHeight(container); const maxContainerWidth = getMaxContainerWidth(container); - const padding = container.type === "ellipse" ? 0 : BOUND_TEXT_PADDING; let x; let y; if (boundTextElement.verticalAlign === VERTICAL_ALIGN.TOP) { - y = containerCoords.y + padding; + y = containerCoords.y; } else if (boundTextElement.verticalAlign === VERTICAL_ALIGN.BOTTOM) { - y = - containerCoords.y + - (maxContainerHeight - boundTextElement.height + padding); + y = containerCoords.y + (maxContainerHeight - boundTextElement.height); } else { y = containerCoords.y + - (maxContainerHeight / 2 - boundTextElement.height / 2 + padding); + (maxContainerHeight / 2 - boundTextElement.height / 2); } if (boundTextElement.textAlign === TEXT_ALIGN.LEFT) { - x = containerCoords.x + padding; + x = containerCoords.x; } else if (boundTextElement.textAlign === TEXT_ALIGN.RIGHT) { - x = - containerCoords.x + - (maxContainerWidth - boundTextElement.width + padding); + x = containerCoords.x + (maxContainerWidth - boundTextElement.width); } else { x = - containerCoords.x + - (maxContainerWidth / 2 - boundTextElement.width / 2 + padding); + containerCoords.x + (maxContainerWidth / 2 - boundTextElement.width / 2); } return { x, y }; }; @@ -636,20 +629,22 @@ export const getContainerCenter = ( }; export const getContainerCoords = (container: NonDeletedExcalidrawElement) => { + let offsetX = BOUND_TEXT_PADDING; + let offsetY = BOUND_TEXT_PADDING; + if (container.type === "ellipse") { // The derivation of coordinates is explained in https://github.com/excalidraw/excalidraw/pull/6172 - const offsetX = - (container.width / 2) * (1 - Math.sqrt(2) / 2) + BOUND_TEXT_PADDING; - const offsetY = - (container.height / 2) * (1 - Math.sqrt(2) / 2) + BOUND_TEXT_PADDING; - return { - x: container.x + offsetX, - y: container.y + offsetY, - }; + offsetX += (container.width / 2) * (1 - Math.sqrt(2) / 2); + offsetY += (container.height / 2) * (1 - Math.sqrt(2) / 2); + } + // The derivation of coordinates is explained in https://github.com/excalidraw/excalidraw/pull/6265 + if (container.type === "diamond") { + offsetX += container.width / 4; + offsetY += container.height / 4; } return { - x: container.x, - y: container.y, + x: container.x + offsetX, + y: container.y + offsetY, }; }; @@ -767,5 +762,63 @@ export const computeContainerHeightForBoundText = ( if (isArrowElement(container)) { return boundTextElementHeight + BOUND_TEXT_PADDING * 8 * 2; } + if (container.type === "diamond") { + return 2 * boundTextElementHeight; + } return boundTextElementHeight + BOUND_TEXT_PADDING * 2; }; + +export const getMaxContainerWidth = (container: ExcalidrawElement) => { + const width = getContainerDims(container).width; + if (isArrowElement(container)) { + const containerWidth = width - BOUND_TEXT_PADDING * 8 * 2; + if (containerWidth <= 0) { + const boundText = getBoundTextElement(container); + if (boundText) { + return boundText.width; + } + return BOUND_TEXT_PADDING * 8 * 2; + } + return containerWidth; + } + + if (container.type === "ellipse") { + // The width of the largest rectangle inscribed inside an ellipse is + // Math.round((ellipse.width / 2) * Math.sqrt(2)) which is derived from + // equation of an ellipse -https://github.com/excalidraw/excalidraw/pull/6172 + return Math.round((width / 2) * Math.sqrt(2)) - BOUND_TEXT_PADDING * 2; + } + if (container.type === "diamond") { + // The width of the largest rectangle inscribed inside a rhombus is + // Math.round(width / 2) - https://github.com/excalidraw/excalidraw/pull/6265 + return Math.round(width / 2) - BOUND_TEXT_PADDING * 2; + } + return width - BOUND_TEXT_PADDING * 2; +}; + +export const getMaxContainerHeight = (container: ExcalidrawElement) => { + const height = getContainerDims(container).height; + if (isArrowElement(container)) { + const containerHeight = height - BOUND_TEXT_PADDING * 8 * 2; + if (containerHeight <= 0) { + const boundText = getBoundTextElement(container); + if (boundText) { + return boundText.height; + } + return BOUND_TEXT_PADDING * 8 * 2; + } + return height; + } + if (container.type === "ellipse") { + // The height of the largest rectangle inscribed inside an ellipse is + // Math.round((ellipse.height / 2) * Math.sqrt(2)) which is derived from + // equation of an ellipse - https://github.com/excalidraw/excalidraw/pull/6172 + return Math.round((height / 2) * Math.sqrt(2)) - BOUND_TEXT_PADDING * 2; + } + if (container.type === "diamond") { + // The height of the largest rectangle inscribed inside a rhombus is + // Math.round(height / 2) - https://github.com/excalidraw/excalidraw/pull/6265 + return Math.round(height / 2) - BOUND_TEXT_PADDING * 2; + } + return height - BOUND_TEXT_PADDING * 2; +}; diff --git a/src/element/textWysiwyg.test.tsx b/src/element/textWysiwyg.test.tsx index 87a6e0bf5..fb41a3813 100644 --- a/src/element/textWysiwyg.test.tsx +++ b/src/element/textWysiwyg.test.tsx @@ -791,7 +791,7 @@ describe("textWysiwyg", () => { text = h.elements[1] as ExcalidrawTextElementWithContainer; expect(text.text).toBe("Hello \nWorld!"); expect(text.originalText).toBe("Hello World!"); - expect(text.y).toBe(27.5); + expect(text.y).toBe(57.5); expect(text.x).toBe(rectangle.x + BOUND_TEXT_PADDING); expect(text.height).toBe(APPROX_LINE_HEIGHT * 2); expect(text.width).toBe(rectangle.width - BOUND_TEXT_PADDING * 2); @@ -825,7 +825,7 @@ describe("textWysiwyg", () => { expect(text.text).toBe("Hello"); expect(text.originalText).toBe("Hello"); - expect(text.y).toBe(40); + expect(text.y).toBe(57.5); expect(text.x).toBe(rectangle.x + BOUND_TEXT_PADDING); expect(text.height).toBe(APPROX_LINE_HEIGHT); expect(text.width).toBe(rectangle.width - BOUND_TEXT_PADDING * 2); @@ -930,6 +930,8 @@ describe("textWysiwyg", () => { editor.select(); fireEvent.click(screen.getByTitle("Left")); + await new Promise((r) => setTimeout(r, 0)); + fireEvent.click(screen.getByTitle("Align bottom")); await new Promise((r) => setTimeout(r, 0)); @@ -1278,7 +1280,7 @@ describe("textWysiwyg", () => { expect([h.elements[1].x, h.elements[1].y]).toMatchInlineSnapshot(` Array [ 15, - 20, + 25, ] `); }); @@ -1290,7 +1292,7 @@ describe("textWysiwyg", () => { expect([h.elements[1].x, h.elements[1].y]).toMatchInlineSnapshot(` Array [ -25, - 20, + 25, ] `); }); @@ -1302,7 +1304,7 @@ describe("textWysiwyg", () => { expect([h.elements[1].x, h.elements[1].y]).toMatchInlineSnapshot(` Array [ 174, - 20, + 25, ] `); }); diff --git a/src/element/textWysiwyg.tsx b/src/element/textWysiwyg.tsx index 5536632b4..d04dfb8df 100644 --- a/src/element/textWysiwyg.tsx +++ b/src/element/textWysiwyg.tsx @@ -24,14 +24,16 @@ import { mutateElement } from "./mutateElement"; import { getApproxLineHeight, getBoundTextElementId, - getBoundTextElementOffset, getContainerCoords, getContainerDims, getContainerElement, getTextElementAngle, getTextWidth, normalizeText, + redrawTextBoundingBox, wrapText, + getMaxContainerHeight, + getMaxContainerWidth, } from "./textElement"; import { actionDecreaseFontSize, @@ -39,7 +41,6 @@ import { } from "../actions/actionProperties"; import { actionZoomIn, actionZoomOut } from "../actions/actionCanvas"; import App from "../components/App"; -import { getMaxContainerHeight, getMaxContainerWidth } from "./newElement"; import { LinearElementEditor } from "./linearElementEditor"; import { parseClipboard } from "../clipboard"; @@ -231,10 +232,6 @@ export const textWysiwyg = ({ // Start pushing text upward until a diff of 30px (padding) // is reached else { - const padding = - container.type === "ellipse" - ? 0 - : getBoundTextElementOffset(updatedTextElement); const containerCoords = getContainerCoords(container); // vertically center align the text @@ -245,8 +242,7 @@ export const textWysiwyg = ({ } } if (verticalAlign === VERTICAL_ALIGN.BOTTOM) { - coordY = - containerCoords.y + (maxHeight - textElementHeight + padding); + coordY = containerCoords.y + (maxHeight - textElementHeight); } } } @@ -616,6 +612,7 @@ export const textWysiwyg = ({ ), }); } + redrawTextBoundingBox(updateElement, container); } onSubmit({ diff --git a/src/tests/linearElementEditor.test.tsx b/src/tests/linearElementEditor.test.tsx index d4b1d9038..3e5ebb8cc 100644 --- a/src/tests/linearElementEditor.test.tsx +++ b/src/tests/linearElementEditor.test.tsx @@ -17,8 +17,11 @@ import { KEYS } from "../keys"; import { LinearElementEditor } from "../element/linearElementEditor"; import { queryByTestId, queryByText } from "@testing-library/react"; import { resize, rotate } from "./utils"; -import { getBoundTextElementPosition, wrapText } from "../element/textElement"; -import { getMaxContainerWidth } from "../element/newElement"; +import { + getBoundTextElementPosition, + wrapText, + getMaxContainerWidth, +} from "../element/textElement"; import * as textElementUtils from "../element/textElement"; import { ROUNDNESS } from "../constants"; From 1e816e87bf6229632d6d90f3468f03e166bb12b2 Mon Sep 17 00:00:00 2001 From: Hikaru Yoshino <57059705+osushicrusher@users.noreply.github.com> Date: Wed, 22 Feb 2023 20:10:29 +0900 Subject: [PATCH 04/74] fix: indenting via `tab` clashing with IME compositor (#6258) --- src/element/textWysiwyg.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/element/textWysiwyg.tsx b/src/element/textWysiwyg.tsx index d04dfb8df..1ce43b4d1 100644 --- a/src/element/textWysiwyg.tsx +++ b/src/element/textWysiwyg.tsx @@ -463,7 +463,9 @@ export const textWysiwyg = ({ event.code === CODES.BRACKET_RIGHT)) ) { event.preventDefault(); - if (event.shiftKey || event.code === CODES.BRACKET_LEFT) { + if (event.isComposing) { + return; + } else if (event.shiftKey || event.code === CODES.BRACKET_LEFT) { outdent(); } else { indent(); From e4506be3e8ef07190c8fd28ebd2750ec211d04de Mon Sep 17 00:00:00 2001 From: Excalidraw Bot <77840495+excalibot@users.noreply.github.com> Date: Wed, 22 Feb 2023 12:23:10 +0100 Subject: [PATCH 05/74] chore: Update translations from Crowdin (#6191) --- src/locales/ar-SA.json | 3 +- src/locales/bg-BG.json | 3 +- src/locales/bn-BD.json | 3 +- src/locales/ca-ES.json | 53 +++++++++++++++--------------- src/locales/cs-CZ.json | 3 +- src/locales/da-DK.json | 3 +- src/locales/de-DE.json | 3 +- src/locales/el-GR.json | 3 +- src/locales/es-ES.json | 17 +++++----- src/locales/eu-ES.json | 9 +++--- src/locales/fa-IR.json | 3 +- src/locales/fi-FI.json | 63 ++++++++++++++++++------------------ src/locales/fr-FR.json | 3 +- src/locales/gl-ES.json | 3 +- src/locales/he-IL.json | 3 +- src/locales/hi-IN.json | 3 +- src/locales/hu-HU.json | 3 +- src/locales/id-ID.json | 3 +- src/locales/it-IT.json | 3 +- src/locales/ja-JP.json | 7 ++-- src/locales/kab-KAB.json | 3 +- src/locales/kk-KZ.json | 3 +- src/locales/ko-KR.json | 3 +- src/locales/ku-TR.json | 3 +- src/locales/lt-LT.json | 3 +- src/locales/lv-LV.json | 3 +- src/locales/mr-IN.json | 3 +- src/locales/my-MM.json | 3 +- src/locales/nb-NO.json | 3 +- src/locales/nl-NL.json | 3 +- src/locales/nn-NO.json | 3 +- src/locales/oc-FR.json | 11 ++++--- src/locales/pa-IN.json | 3 +- src/locales/percentages.json | 34 +++++++++---------- src/locales/pl-PL.json | 3 +- src/locales/pt-BR.json | 23 ++++++------- src/locales/pt-PT.json | 9 +++--- src/locales/ro-RO.json | 9 +++--- src/locales/ru-RU.json | 3 +- src/locales/si-LK.json | 3 +- src/locales/sk-SK.json | 3 +- src/locales/sl-SI.json | 3 +- src/locales/sv-SE.json | 3 +- src/locales/ta-IN.json | 21 ++++++------ src/locales/tr-TR.json | 3 +- src/locales/uk-UA.json | 9 +++--- src/locales/vi-VN.json | 3 +- src/locales/zh-CN.json | 5 +-- src/locales/zh-HK.json | 3 +- src/locales/zh-TW.json | 3 +- 50 files changed, 215 insertions(+), 166 deletions(-) diff --git a/src/locales/ar-SA.json b/src/locales/ar-SA.json index 2a682ad70..6c6c0cf39 100644 --- a/src/locales/ar-SA.json +++ b/src/locales/ar-SA.json @@ -192,7 +192,8 @@ "invalidSceneUrl": "تعذر استيراد المشهد من عنوان URL المتوفر. إما أنها مشوهة، أو لا تحتوي على بيانات Excalidraw JSON صالحة.", "resetLibrary": "هذا سوف يمسح مكتبتك. هل أنت متأكد؟", "removeItemsFromsLibrary": "حذف {{count}} عنصر (عناصر) من المكتبة؟", - "invalidEncryptionKey": "مفتاح التشفير يجب أن يكون من 22 حرفاً. التعاون المباشر معطل." + "invalidEncryptionKey": "مفتاح التشفير يجب أن يكون من 22 حرفاً. التعاون المباشر معطل.", + "collabOfflineWarning": "" }, "errors": { "unsupportedFileType": "نوع الملف غير مدعوم.", diff --git a/src/locales/bg-BG.json b/src/locales/bg-BG.json index 7f59678c0..ba052783d 100644 --- a/src/locales/bg-BG.json +++ b/src/locales/bg-BG.json @@ -192,7 +192,8 @@ "invalidSceneUrl": "", "resetLibrary": "", "removeItemsFromsLibrary": "", - "invalidEncryptionKey": "" + "invalidEncryptionKey": "", + "collabOfflineWarning": "" }, "errors": { "unsupportedFileType": "Този файлов формат не се поддържа.", diff --git a/src/locales/bn-BD.json b/src/locales/bn-BD.json index 29c35eb63..47c6f02b2 100644 --- a/src/locales/bn-BD.json +++ b/src/locales/bn-BD.json @@ -192,7 +192,8 @@ "invalidSceneUrl": "সরবরাহ করা লিঙ্ক থেকে দৃশ্য লোড করা যায়নি৷ এটি হয় বিকৃত, অথবা বৈধ এক্সক্যালিড্র জেসন তথ্য নেই৷", "resetLibrary": "এটি আপনার সংগ্রহ পরিষ্কার করবে। আপনি কি নিশ্চিত?", "removeItemsFromsLibrary": "সংগ্রহ থেকে {{count}} বস্তু বিয়োগ করা হবে। আপনি কি নিশ্চিত?", - "invalidEncryptionKey": "অবৈধ এনক্রীপশন কী।" + "invalidEncryptionKey": "অবৈধ এনক্রীপশন কী।", + "collabOfflineWarning": "" }, "errors": { "unsupportedFileType": "অসমর্থিত ফাইল।", diff --git a/src/locales/ca-ES.json b/src/locales/ca-ES.json index 82415ff97..e94523b29 100644 --- a/src/locales/ca-ES.json +++ b/src/locales/ca-ES.json @@ -1,7 +1,7 @@ { "labels": { "paste": "Enganxa", - "pasteAsPlaintext": "", + "pasteAsPlaintext": "Enganxar com a text pla", "pasteCharts": "Enganxa els diagrames", "selectAll": "Selecciona-ho tot", "multiSelect": "Afegeix un element a la selecció", @@ -72,7 +72,7 @@ "layers": "Capes", "actions": "Accions", "language": "Llengua", - "liveCollaboration": "", + "liveCollaboration": "Col·laboració en directe...", "duplicateSelection": "Duplica", "untitled": "Sense títol", "name": "Nom", @@ -116,8 +116,8 @@ "label": "Enllaç" }, "lineEditor": { - "edit": "", - "exit": "" + "edit": "Editar línia", + "exit": "Sortir de l'editor de línia" }, "elementLock": { "lock": "Bloca", @@ -136,8 +136,8 @@ "buttons": { "clearReset": "Neteja el llenç", "exportJSON": "Exporta a un fitxer", - "exportImage": "", - "export": "", + "exportImage": "Exporta la imatge...", + "export": "Guardar a...", "exportToPng": "Exporta a PNG", "exportToSvg": "Exporta a SNG", "copyToClipboard": "Copia al porta-retalls", @@ -145,7 +145,7 @@ "scale": "Escala", "save": "Desa al fitxer actual", "saveAs": "Anomena i desa", - "load": "", + "load": "Obrir", "getShareableLink": "Obté l'enllaç per a compartir", "close": "Tanca", "selectLanguage": "Trieu la llengua", @@ -192,7 +192,8 @@ "invalidSceneUrl": "No s'ha pogut importar l'escena des de l'adreça URL proporcionada. Està malformada o no conté dades Excalidraw JSON vàlides.", "resetLibrary": "Això buidarà la biblioteca. N'esteu segur?", "removeItemsFromsLibrary": "Suprimir {{count}} element(s) de la biblioteca?", - "invalidEncryptionKey": "La clau d'encriptació ha de tenir 22 caràcters. La col·laboració en directe està desactivada." + "invalidEncryptionKey": "La clau d'encriptació ha de tenir 22 caràcters. La col·laboració en directe està desactivada.", + "collabOfflineWarning": "Sense connexió a internet disponible.\nEls vostres canvis no seran guardats!" }, "errors": { "unsupportedFileType": "Tipus de fitxer no suportat.", @@ -202,8 +203,8 @@ "invalidSVGString": "SVG no vàlid.", "cannotResolveCollabServer": "No ha estat possible connectar amb el servidor collab. Si us plau recarregueu la pàgina i torneu a provar.", "importLibraryError": "No s'ha pogut carregar la biblioteca", - "collabSaveFailed": "", - "collabSaveFailed_sizeExceeded": "" + "collabSaveFailed": "No s'ha pogut desar a la base de dades de fons. Si els problemes persisteixen, hauríeu de desar el fitxer localment per assegurar-vos que no perdeu el vostre treball.", + "collabSaveFailed_sizeExceeded": "No s'ha pogut desar a la base de dades de fons, sembla que el llenç és massa gran. Hauríeu de desar el fitxer localment per assegurar-vos que no perdeu el vostre treball." }, "toolBar": { "selection": "Selecció", @@ -217,10 +218,10 @@ "text": "Text", "library": "Biblioteca", "lock": "Mantenir activa l'eina seleccionada desprès de dibuixar", - "penMode": "", + "penMode": "Mode de llapis - evita tocar", "link": "Afegeix / actualitza l'enllaç per a la forma seleccionada", "eraser": "Esborrador", - "hand": "" + "hand": "Mà (eina de desplaçament)" }, "headings": { "canvasActions": "Accions del llenç", @@ -228,7 +229,7 @@ "shapes": "Formes" }, "hints": { - "canvasPanning": "", + "canvasPanning": "Per moure el llenç, manteniu premuda la roda del ratolí o la barra espaiadora mentre arrossegueu o utilitzeu l'eina manual", "linearElement": "Feu clic per a dibuixar múltiples punts; arrossegueu per a una sola línia", "freeDraw": "Feu clic i arrossegueu, deixeu anar per a finalitzar", "text": "Consell: també podeu afegir text fent doble clic en qualsevol lloc amb l'eina de selecció", @@ -239,7 +240,7 @@ "resize": "Per restringir les proporcions mentres es canvia la mida, mantenir premut el majúscul (SHIFT); per canviar la mida des del centre, mantenir premut ALT", "resizeImage": "Podeu redimensionar lliurement prement MAJÚSCULA;\nper a redimensionar des del centre, premeu ALT", "rotate": "Per restringir els angles mentre gira, mantenir premut el majúscul (SHIFT)", - "lineEditor_info": "", + "lineEditor_info": "Mantingueu premut Ctrl o Cmd i feu doble clic o premeu Ctrl o Cmd + Retorn per editar els punts", "lineEditor_pointSelected": "Premeu Suprimir per a eliminar el(s) punt(s), CtrlOrCmd+D per a duplicar-lo, o arrossegueu-lo per a moure'l", "lineEditor_nothingSelected": "Seleccioneu un punt per a editar-lo (premeu SHIFT si voleu\nselecció múltiple), o manteniu Alt i feu clic per a afegir més punts", "placeImage": "Feu clic per a col·locar la imatge o clic i arrossegar per a establir-ne la mida manualment", @@ -247,7 +248,7 @@ "bindTextToElement": "Premeu enter per a afegir-hi text", "deepBoxSelect": "Manteniu CtrlOrCmd per a selecció profunda, i per a evitar l'arrossegament", "eraserRevert": "Mantingueu premuda Alt per a revertir els elements seleccionats per a esborrar", - "firefox_clipboard_write": "" + "firefox_clipboard_write": "És probable que aquesta funció es pugui activar posant la marca \"dom.events.asyncClipboard.clipboardItem\" a \"true\". Per canviar les marques del navegador al Firefox, visiteu la pàgina \"about:config\"." }, "canvasError": { "cannotShowPreview": "No es pot mostrar la previsualització", @@ -295,7 +296,7 @@ "blog": "Llegiu el nostre blog", "click": "clic", "deepSelect": "Selecció profunda", - "deepBoxSelect": "", + "deepBoxSelect": "Seleccioneu profundament dins del quadre i eviteu arrossegar", "curvedArrow": "Fletxa corba", "curvedLine": "Línia corba", "documentation": "Documentació", @@ -316,8 +317,8 @@ "zoomToFit": "Zoom per veure tots els elements", "zoomToSelection": "Zoom per veure la selecció", "toggleElementLock": "Blocar/desblocar la selecció", - "movePageUpDown": "", - "movePageLeftRight": "" + "movePageUpDown": "Mou la pàgina cap amunt/a baix", + "movePageLeftRight": "Mou la pàgina cap a l'esquerra/dreta" }, "clearCanvasDialog": { "title": "Neteja el llenç" @@ -399,7 +400,7 @@ "fileSavedToFilename": "S'ha desat a {filename}", "canvas": "el llenç", "selection": "la selecció", - "pasteAsSingleElement": "" + "pasteAsSingleElement": "Fer servir {{shortcut}} per enganxar com un sol element,\no enganxeu-lo en un editor de text existent" }, "colors": { "ffffff": "Blanc", @@ -450,15 +451,15 @@ }, "welcomeScreen": { "app": { - "center_heading": "", - "center_heading_plus": "", - "menuHint": "" + "center_heading": "Totes les vostres dades es guarden localment al vostre navegador.", + "center_heading_plus": "Vols anar a Excalidraw+ en comptes?", + "menuHint": "Exportar, preferències, llenguatges..." }, "defaults": { - "menuHint": "", - "center_heading": "", - "toolbarHint": "", - "helpHint": "" + "menuHint": "Exportar, preferències i més...", + "center_heading": "Diagrames. Fer. Simple.", + "toolbarHint": "Selecciona una eina i comença a dibuixar!", + "helpHint": "Dreceres i ajuda" } } } diff --git a/src/locales/cs-CZ.json b/src/locales/cs-CZ.json index fa225d2a1..718d974b2 100644 --- a/src/locales/cs-CZ.json +++ b/src/locales/cs-CZ.json @@ -192,7 +192,8 @@ "invalidSceneUrl": "", "resetLibrary": "", "removeItemsFromsLibrary": "", - "invalidEncryptionKey": "" + "invalidEncryptionKey": "", + "collabOfflineWarning": "" }, "errors": { "unsupportedFileType": "", diff --git a/src/locales/da-DK.json b/src/locales/da-DK.json index 5b41ddb1e..8e541f90d 100644 --- a/src/locales/da-DK.json +++ b/src/locales/da-DK.json @@ -192,7 +192,8 @@ "invalidSceneUrl": "", "resetLibrary": "", "removeItemsFromsLibrary": "", - "invalidEncryptionKey": "" + "invalidEncryptionKey": "", + "collabOfflineWarning": "" }, "errors": { "unsupportedFileType": "", diff --git a/src/locales/de-DE.json b/src/locales/de-DE.json index a14f52859..b389fe5a4 100644 --- a/src/locales/de-DE.json +++ b/src/locales/de-DE.json @@ -192,7 +192,8 @@ "invalidSceneUrl": "Die Szene konnte nicht von der angegebenen URL importiert werden. Sie ist entweder fehlerhaft oder enthält keine gültigen Excalidraw JSON-Daten.", "resetLibrary": "Dieses löscht deine Bibliothek. Bist du sicher?", "removeItemsFromsLibrary": "{{count}} Element(e) aus der Bibliothek löschen?", - "invalidEncryptionKey": "Verschlüsselungsschlüssel muss 22 Zeichen lang sein. Die Live-Zusammenarbeit ist deaktiviert." + "invalidEncryptionKey": "Verschlüsselungsschlüssel muss 22 Zeichen lang sein. Die Live-Zusammenarbeit ist deaktiviert.", + "collabOfflineWarning": "Keine Internetverbindung verfügbar.\nDeine Änderungen werden nicht gespeichert!" }, "errors": { "unsupportedFileType": "Nicht unterstützter Dateityp.", diff --git a/src/locales/el-GR.json b/src/locales/el-GR.json index 02534c326..d478be3e7 100644 --- a/src/locales/el-GR.json +++ b/src/locales/el-GR.json @@ -192,7 +192,8 @@ "invalidSceneUrl": "Δεν ήταν δυνατή η εισαγωγή σκηνής από το URL που δώσατε. Είτε έχει λάθος μορφή, είτε δεν περιέχει έγκυρα δεδομένα JSON Excalidraw.", "resetLibrary": "Αυτό θα καθαρίσει τη βιβλιοθήκη σας. Είστε σίγουροι;", "removeItemsFromsLibrary": "Διαγραφή {{count}} αντικειμένου(ων) από τη βιβλιοθήκη;", - "invalidEncryptionKey": "Το κλειδί κρυπτογράφησης πρέπει να είναι 22 χαρακτήρες. Η ζωντανή συνεργασία είναι απενεργοποιημένη." + "invalidEncryptionKey": "Το κλειδί κρυπτογράφησης πρέπει να είναι 22 χαρακτήρες. Η ζωντανή συνεργασία είναι απενεργοποιημένη.", + "collabOfflineWarning": "Δεν υπάρχει διαθέσιμη σύνδεση στο internet.\nΟι αλλαγές σας δεν θα αποθηκευτούν!" }, "errors": { "unsupportedFileType": "Μη υποστηριζόμενος τύπος αρχείου.", diff --git a/src/locales/es-ES.json b/src/locales/es-ES.json index e70ad64e5..eceb38ae3 100644 --- a/src/locales/es-ES.json +++ b/src/locales/es-ES.json @@ -103,7 +103,7 @@ "share": "Compartir", "showStroke": "Mostrar selector de color de trazo", "showBackground": "Mostrar el selector de color de fondo", - "toggleTheme": "Alternar tema", + "toggleTheme": "Cambiar tema", "personalLib": "Biblioteca personal", "excalidrawLib": "Biblioteca Excalidraw", "decreaseFontSize": "Disminuir tamaño de letra", @@ -192,7 +192,8 @@ "invalidSceneUrl": "No se ha podido importar la escena desde la URL proporcionada. Está mal formada, o no contiene datos de Excalidraw JSON válidos.", "resetLibrary": "Esto borrará tu biblioteca. ¿Estás seguro?", "removeItemsFromsLibrary": "¿Eliminar {{count}} elemento(s) de la biblioteca?", - "invalidEncryptionKey": "La clave de cifrado debe tener 22 caracteres. La colaboración en vivo está deshabilitada." + "invalidEncryptionKey": "La clave de cifrado debe tener 22 caracteres. La colaboración en vivo está deshabilitada.", + "collabOfflineWarning": "No hay conexión a internet disponible.\n¡No se guardarán los cambios!" }, "errors": { "unsupportedFileType": "Tipo de archivo no admitido.", @@ -233,7 +234,7 @@ "freeDraw": "Haz clic y arrastra, suelta al terminar", "text": "Consejo: también puedes añadir texto haciendo doble clic en cualquier lugar con la herramienta de selección", "text_selected": "Doble clic o pulse ENTER para editar el texto", - "text_editing": "Pulse Escape o CtrlOrCmd+ENTER para terminar de editar", + "text_editing": "Pulse Escape o Ctrl/Cmd + ENTER para terminar de editar", "linearElementMulti": "Haz clic en el último punto o presiona Escape o Enter para finalizar", "lockAngle": "Puedes restringir el ángulo manteniendo presionado el botón SHIFT", "resize": "Para mantener las proporciones mantén SHIFT presionado mientras modificas el tamaño, \nmantén presionado ALT para modificar el tamaño desde el centro", @@ -314,7 +315,7 @@ "title": "Ayuda", "view": "Vista", "zoomToFit": "Ajustar la vista para mostrar todos los elementos", - "zoomToSelection": "Zoom a la selección", + "zoomToSelection": "Ampliar selección", "toggleElementLock": "Bloquear/desbloquear selección", "movePageUpDown": "Mover página hacia arriba/abajo", "movePageLeftRight": "Mover página hacia la izquierda/derecha" @@ -326,9 +327,9 @@ "title": "Publicar biblioteca", "itemName": "Nombre del artículo", "authorName": "Nombre del autor", - "githubUsername": "Nombre de usuario de Github", + "githubUsername": "Nombre de usuario de GitHub", "twitterUsername": "Nombre de usuario de Twitter", - "libraryName": "Nombre de la librería", + "libraryName": "Nombre de la biblioteca", "libraryDesc": "Descripción de la biblioteca", "website": "Sitio Web", "placeholder": { @@ -336,7 +337,7 @@ "libraryName": "Nombre de tu biblioteca", "libraryDesc": "Descripción de su biblioteca para ayudar a la gente a entender su uso", "githubHandle": "Nombre de usuario de GitHub (opcional), así podrá editar la biblioteca una vez enviada para su revisión", - "twitterHandle": "Nombre de usuario de Twitter (opcional), así que sabemos a quién acreditar cuando se promociona en Twitter", + "twitterHandle": "Nombre de usuario de Twitter (opcional), así sabemos a quién acreditar cuando se promociona en Twitter", "website": "Enlace a su sitio web personal o en cualquier otro lugar (opcional)" }, "errors": { @@ -458,7 +459,7 @@ "menuHint": "Exportar, preferencias y más...", "center_heading": "Diagramas. Hecho. Simplemente.", "toolbarHint": "¡Elige una herramienta y empieza a dibujar!", - "helpHint": "Atajos & ayuda" + "helpHint": "Atajos y ayuda" } } } diff --git a/src/locales/eu-ES.json b/src/locales/eu-ES.json index e35e8c40c..d35baf4e4 100644 --- a/src/locales/eu-ES.json +++ b/src/locales/eu-ES.json @@ -192,7 +192,8 @@ "invalidSceneUrl": "Ezin izan da eszena inportatu emandako URLtik. Gaizki eratuta dago edo ez du baliozko Excalidraw JSON daturik.", "resetLibrary": "Honek zure liburutegia garbituko du. Ziur zaude?", "removeItemsFromsLibrary": "Liburutegitik {{count}} elementu ezabatu?", - "invalidEncryptionKey": "Enkriptazio-gakoak 22 karaktere izan behar ditu. Zuzeneko lankidetza desgaituta dago." + "invalidEncryptionKey": "Enkriptazio-gakoak 22 karaktere izan behar ditu. Zuzeneko lankidetza desgaituta dago.", + "collabOfflineWarning": "Ez dago Interneteko konexiorik.\nZure aldaketak ez dira gordeko!" }, "errors": { "unsupportedFileType": "Onartu gabeko fitxategi mota.", @@ -220,7 +221,7 @@ "penMode": "Luma modua - ukipena saihestu", "link": "Gehitu / Eguneratu esteka hautatutako forma baterako", "eraser": "Borragoma", - "hand": "" + "hand": "Eskua (panoratze tresna)" }, "headings": { "canvasActions": "Canvas ekintzak", @@ -228,7 +229,7 @@ "shapes": "Formak" }, "hints": { - "canvasPanning": "", + "canvasPanning": "Oihala mugitzeko, eutsi saguaren gurpila edo zuriune-barra arrastatzean, edo erabili esku tresna", "linearElement": "Egin klik hainbat puntu hasteko, arrastatu lerro bakarrerako", "freeDraw": "Egin klik eta arrastatu, askatu amaitutakoan", "text": "Aholkua: testua gehitu dezakezu edozein lekutan klik bikoitza eginez hautapen tresnarekin", @@ -247,7 +248,7 @@ "bindTextToElement": "Sakatu Sartu testua gehitzeko", "deepBoxSelect": "Eutsi Ctrl edo Cmd sakatuta aukeraketa sakona egiteko eta arrastatzea saihesteko", "eraserRevert": "Eduki Alt sakatuta ezabatzeko markatutako elementuak leheneratzeko", - "firefox_clipboard_write": "" + "firefox_clipboard_write": "Ezaugarri hau \"dom.events.asyncClipboard.clipboardItem\" marka \"true\" gisa ezarrita gaitu daiteke. Firefox-en arakatzailearen banderak aldatzeko, bisitatu \"about:config\" orrialdera." }, "canvasError": { "cannotShowPreview": "Ezin da oihala aurreikusi", diff --git a/src/locales/fa-IR.json b/src/locales/fa-IR.json index 817ed51dd..cc7de0dee 100644 --- a/src/locales/fa-IR.json +++ b/src/locales/fa-IR.json @@ -192,7 +192,8 @@ "invalidSceneUrl": "بوم نقاشی از آدرس ارائه شده وارد نشد. این یا نادرست است، یا حاوی داده Excalidraw JSON معتبر نیست.", "resetLibrary": "ین کار کل صفحه را پاک میکند. آیا مطمئنید?", "removeItemsFromsLibrary": "حذف {{count}} آیتم(ها) از کتابخانه?", - "invalidEncryptionKey": "کلید رمزگذاری باید 22 کاراکتر باشد. همکاری زنده غیرفعال است." + "invalidEncryptionKey": "کلید رمزگذاری باید 22 کاراکتر باشد. همکاری زنده غیرفعال است.", + "collabOfflineWarning": "" }, "errors": { "unsupportedFileType": "نوع فایل پشتیبانی نشده.", diff --git a/src/locales/fi-FI.json b/src/locales/fi-FI.json index 2346a018c..6c357b167 100644 --- a/src/locales/fi-FI.json +++ b/src/locales/fi-FI.json @@ -1,7 +1,7 @@ { "labels": { "paste": "Liitä", - "pasteAsPlaintext": "", + "pasteAsPlaintext": "Liitä pelkkänä tekstinä", "pasteCharts": "Liitä kaaviot", "selectAll": "Valitse kaikki", "multiSelect": "Lisää kohde valintaan", @@ -72,7 +72,7 @@ "layers": "Tasot", "actions": "Toiminnot", "language": "Kieli", - "liveCollaboration": "", + "liveCollaboration": "Live Yhteistyö...", "duplicateSelection": "Monista", "untitled": "Nimetön", "name": "Nimi", @@ -116,14 +116,14 @@ "label": "Linkki" }, "lineEditor": { - "edit": "", - "exit": "" + "edit": "Muokkaa riviä", + "exit": "Poistu rivieditorista" }, "elementLock": { - "lock": "", - "unlock": "", - "lockAll": "", - "unlockAll": "" + "lock": "Lukitse", + "unlock": "Poista lukitus", + "lockAll": "Lukitse kaikki", + "unlockAll": "Poista lukitus kaikista" }, "statusPublished": "Julkaistu", "sidebarLock": "Pidä sivupalkki avoinna" @@ -136,8 +136,8 @@ "buttons": { "clearReset": "Tyhjennä piirtoalue", "exportJSON": "Vie tiedostoon", - "exportImage": "", - "export": "", + "exportImage": "Vie kuva...", + "export": "Tallenna nimellä...", "exportToPng": "Vie PNG-tiedostona", "exportToSvg": "Vie SVG-tiedostona", "copyToClipboard": "Kopioi leikepöydälle", @@ -145,7 +145,7 @@ "scale": "Koko", "save": "Tallenna nykyiseen tiedostoon", "saveAs": "Tallenna nimellä", - "load": "", + "load": "Avaa", "getShareableLink": "Hae jaettava linkki", "close": "Sulje", "selectLanguage": "Valitse kieli", @@ -192,7 +192,8 @@ "invalidSceneUrl": "Teosta ei voitu tuoda annetusta URL-osoitteesta. Tallenne on vioittunut, tai osoitteessa ei ole Excalidraw JSON-dataa.", "resetLibrary": "Tämä tyhjentää kirjastosi. Jatketaanko?", "removeItemsFromsLibrary": "Poista {{count}} kohdetta kirjastosta?", - "invalidEncryptionKey": "Salausavaimen on oltava 22 merkkiä pitkä. Live-yhteistyö ei ole käytössä." + "invalidEncryptionKey": "Salausavaimen on oltava 22 merkkiä pitkä. Live-yhteistyö ei ole käytössä.", + "collabOfflineWarning": "Internet-yhteyttä ei ole saatavilla.\nMuutoksiasi ei tallenneta!" }, "errors": { "unsupportedFileType": "Tiedostotyyppiä ei tueta.", @@ -201,9 +202,9 @@ "svgImageInsertError": "SVG- kuvaa ei voitu lisätä. Tiedoston SVG-sisältö näyttää virheelliseltä.", "invalidSVGString": "Virheellinen SVG.", "cannotResolveCollabServer": "Yhteyden muodostaminen collab-palvelimeen epäonnistui. Virkistä sivu ja yritä uudelleen.", - "importLibraryError": "", - "collabSaveFailed": "", - "collabSaveFailed_sizeExceeded": "" + "importLibraryError": "Kokoelman lataaminen epäonnistui", + "collabSaveFailed": "Ei voitu tallentaan palvelimen tietokantaan. Jos ongelmia esiintyy, sinun kannatta tallentaa tallentaa tiedosto paikallisesti varmistaaksesi, että et menetä työtäsi.", + "collabSaveFailed_sizeExceeded": "Ei voitu tallentaan palvelimen tietokantaan. Jos ongelmia esiintyy, sinun kannatta tallentaa tallentaa tiedosto paikallisesti varmistaaksesi, että et menetä työtäsi." }, "toolBar": { "selection": "Valinta", @@ -217,10 +218,10 @@ "text": "Teksti", "library": "Kirjasto", "lock": "Pidä valittu työkalu aktiivisena piirron jälkeen", - "penMode": "", + "penMode": "Kynätila - estä kosketus", "link": "Lisää/päivitä linkki valitulle muodolle", "eraser": "Poistotyökalu", - "hand": "" + "hand": "Käsi (panning-työkalu)" }, "headings": { "canvasActions": "Piirtoalueen toiminnot", @@ -228,7 +229,7 @@ "shapes": "Muodot" }, "hints": { - "canvasPanning": "", + "canvasPanning": "Piirtoalueen liikuttamiseksi pidä hiiren pyörää tai välilyöntiä pohjassa tai käytä käsityökalua", "linearElement": "Klikkaa piirtääksesi useampi piste, raahaa piirtääksesi yksittäinen viiva", "freeDraw": "Paina ja raahaa, päästä irti kun olet valmis", "text": "Vinkki: voit myös lisätä tekstiä kaksoisnapsauttamalla mihin tahansa valintatyökalulla", @@ -239,7 +240,7 @@ "resize": "Voit rajoittaa mittasuhteet pitämällä SHIFT-näppäintä alaspainettuna kun muutat kokoa, pidä ALT-näppäintä alaspainettuna muuttaaksesi kokoa keskipisteen suhteen", "resizeImage": "Voit muuttaa kokoa vapaasti pitämällä SHIFTiä pohjassa, pidä ALT pohjassa muuttaaksesi kokoa keskipisteen ympäri", "rotate": "Voit rajoittaa kulman pitämällä SHIFT pohjassa pyörittäessäsi", - "lineEditor_info": "", + "lineEditor_info": "Pidä CtrlOrCmd pohjassa ja kaksoisnapsauta tai paina CtrlOrCmd + Enter muokataksesi pisteitä", "lineEditor_pointSelected": "Poista piste(et) painamalla delete, monista painamalla CtrlOrCmd+D, tai liikuta raahaamalla", "lineEditor_nothingSelected": "Valitse muokattava piste (monivalinta pitämällä SHIFT pohjassa), tai paina Alt ja klikkaa lisätäksesi uusia pisteitä", "placeImage": "Klikkaa asettaaksesi kuvan, tai klikkaa ja raahaa asettaaksesi sen koon manuaalisesti", @@ -247,7 +248,7 @@ "bindTextToElement": "Lisää tekstiä painamalla enter", "deepBoxSelect": "Käytä syvävalintaa ja estä raahaus painamalla CtrlOrCmd", "eraserRevert": "Pidä Alt alaspainettuna, kumotaksesi merkittyjen elementtien poistamisen", - "firefox_clipboard_write": "" + "firefox_clipboard_write": "Tämä ominaisuus voidaan todennäköisesti ottaa käyttöön asettamalla \"dom.events.asyncClipboard.clipboardItem\" kohta \"true\":ksi. Vaihtaaksesi selaimen kohdan Firefoxissa, käy \"about:config\" sivulla." }, "canvasError": { "cannotShowPreview": "Esikatselua ei voitu näyttää", @@ -315,9 +316,9 @@ "view": "Näkymä", "zoomToFit": "Näytä kaikki elementit", "zoomToSelection": "Näytä valinta", - "toggleElementLock": "", - "movePageUpDown": "", - "movePageLeftRight": "" + "toggleElementLock": "Lukitse / poista lukitus valinta", + "movePageUpDown": "Siirrä sivua ylös/alas", + "movePageLeftRight": "Siirrä sivua vasemmalle/oikealle" }, "clearCanvasDialog": { "title": "Pyyhi piirtoalue" @@ -399,7 +400,7 @@ "fileSavedToFilename": "Tallennettiin kohteeseen {filename}", "canvas": "piirtoalue", "selection": "valinta", - "pasteAsSingleElement": "" + "pasteAsSingleElement": "Käytä {{shortcut}} liittääksesi yhtenä elementtinä,\ntai liittääksesi olemassa olevaan tekstieditoriin" }, "colors": { "ffffff": "Valkoinen", @@ -450,15 +451,15 @@ }, "welcomeScreen": { "app": { - "center_heading": "", - "center_heading_plus": "", - "menuHint": "" + "center_heading": "Kaikki tietosi on tallennettu paikallisesti selaimellesi.", + "center_heading_plus": "Haluatko sen sijaan mennä Excalidraw+:aan?", + "menuHint": "Vie, asetukset, kielet, ..." }, "defaults": { - "menuHint": "", - "center_heading": "", - "toolbarHint": "", - "helpHint": "" + "menuHint": "Vie, asetukset ja lisää...", + "center_heading": "Kaaviot. Tehty. Yksinkertaiseksi.", + "toolbarHint": "Valitse työkalu ja aloita piirtäminen!", + "helpHint": "Pikanäppäimet & ohje" } } } diff --git a/src/locales/fr-FR.json b/src/locales/fr-FR.json index 57afcf9b3..573e3fd1a 100644 --- a/src/locales/fr-FR.json +++ b/src/locales/fr-FR.json @@ -192,7 +192,8 @@ "invalidSceneUrl": "Impossible d'importer la scène depuis l'URL fournie. Elle est soit incorrecte, soit ne contient pas de données JSON Excalidraw valides.", "resetLibrary": "Cela va effacer votre bibliothèque. Êtes-vous sûr·e ?", "removeItemsFromsLibrary": "Supprimer {{count}} élément(s) de la bibliothèque ?", - "invalidEncryptionKey": "La clé de chiffrement doit comporter 22 caractères. La collaboration en direct est désactivée." + "invalidEncryptionKey": "La clé de chiffrement doit comporter 22 caractères. La collaboration en direct est désactivée.", + "collabOfflineWarning": "Aucune connexion internet disponible.\nVos modifications ne seront pas enregistrées !" }, "errors": { "unsupportedFileType": "Type de fichier non supporté.", diff --git a/src/locales/gl-ES.json b/src/locales/gl-ES.json index d7053c961..373fe4032 100644 --- a/src/locales/gl-ES.json +++ b/src/locales/gl-ES.json @@ -192,7 +192,8 @@ "invalidSceneUrl": "Non se puido importar a escena dende a URL proporcionada. Ou ben está malformada ou non contén un JSON con información válida para Excalidraw.", "resetLibrary": "Isto limpará a súa biblioteca. Está seguro?", "removeItemsFromsLibrary": "Eliminar {{count}} elemento(s) da biblioteca?", - "invalidEncryptionKey": "A clave de cifrado debe ter 22 caracteres. A colaboración en directo está desactivada." + "invalidEncryptionKey": "A clave de cifrado debe ter 22 caracteres. A colaboración en directo está desactivada.", + "collabOfflineWarning": "" }, "errors": { "unsupportedFileType": "Tipo de ficheiro non soportado.", diff --git a/src/locales/he-IL.json b/src/locales/he-IL.json index faa0013b6..9414da375 100644 --- a/src/locales/he-IL.json +++ b/src/locales/he-IL.json @@ -192,7 +192,8 @@ "invalidSceneUrl": "ייבוא המידע מן סצינה מכתובת האינטרנט נכשלה. המידע בנוי באופן משובש או שהוא אינו קובץ JSON תקין של Excalidraw.", "resetLibrary": "פעולה זו תנקה את כל הלוח. אתה בטוח?", "removeItemsFromsLibrary": "מחיקת {{count}} פריטים(ים) מתוך הספריה?", - "invalidEncryptionKey": "מפתח ההצפנה חייב להיות בן 22 תוים. השיתוף החי מבוטל." + "invalidEncryptionKey": "מפתח ההצפנה חייב להיות בן 22 תוים. השיתוף החי מבוטל.", + "collabOfflineWarning": "" }, "errors": { "unsupportedFileType": "סוג הקובץ אינו נתמך.", diff --git a/src/locales/hi-IN.json b/src/locales/hi-IN.json index c0aca8982..9e3a14d64 100644 --- a/src/locales/hi-IN.json +++ b/src/locales/hi-IN.json @@ -192,7 +192,8 @@ "invalidSceneUrl": "दिये गये युआरेल से दृश्य आयात नहीं किया जा सका. यह या तो अनुचित है, या इसमें उचित Excalidraw JSON डेटा नहीं है।", "resetLibrary": "यह पूरा संग्रह रिक्त करेगा. क्या आपको यक़ीन हैं?", "removeItemsFromsLibrary": "{{count}} वस्तु(यें) संग्रह से हटायें?", - "invalidEncryptionKey": "कूटलेखन कुंजी 22 अक्षरों की होनी चाहिये, इसलिये जीवंत सहयोग अक्षम हैं" + "invalidEncryptionKey": "कूटलेखन कुंजी 22 अक्षरों की होनी चाहिये, इसलिये जीवंत सहयोग अक्षम हैं", + "collabOfflineWarning": "कोई इंटरनेट कनेक्शन उपलब्ध नहीं है।\nआपके बदलाव सहेजे नहीं जाएंगे!" }, "errors": { "unsupportedFileType": "असमर्थित फाइल प्रकार", diff --git a/src/locales/hu-HU.json b/src/locales/hu-HU.json index faaafed90..a3cbd852c 100644 --- a/src/locales/hu-HU.json +++ b/src/locales/hu-HU.json @@ -192,7 +192,8 @@ "invalidSceneUrl": "Nem sikerült importálni a jelenetet a megadott URL-ről. Rossz formátumú, vagy nem tartalmaz érvényes Excalidraw JSON-adatokat.", "resetLibrary": "Ezzel törlöd a könyvtárát. biztos vagy ebben?", "removeItemsFromsLibrary": "{{count}} elemet törölsz a könyvtárból?", - "invalidEncryptionKey": "A titkosítási kulcsnak 22 karakterből kell állnia. Az élő együttműködés le van tiltva." + "invalidEncryptionKey": "A titkosítási kulcsnak 22 karakterből kell állnia. Az élő együttműködés le van tiltva.", + "collabOfflineWarning": "" }, "errors": { "unsupportedFileType": "Nem támogatott fájltípus.", diff --git a/src/locales/id-ID.json b/src/locales/id-ID.json index 0632a87eb..cd722c6a3 100644 --- a/src/locales/id-ID.json +++ b/src/locales/id-ID.json @@ -192,7 +192,8 @@ "invalidSceneUrl": "Tidak dapat impor pemandangan dari URL. Kemungkinan URL itu rusak atau tidak berisi data JSON Excalidraw yang valid.", "resetLibrary": "Ini akan menghapus pustaka Anda. Anda yakin?", "removeItemsFromsLibrary": "Hapus {{count}} item dari pustaka?", - "invalidEncryptionKey": "Sandi enkripsi harus 22 karakter. Kolaborasi langsung dinonaktifkan." + "invalidEncryptionKey": "Sandi enkripsi harus 22 karakter. Kolaborasi langsung dinonaktifkan.", + "collabOfflineWarning": "" }, "errors": { "unsupportedFileType": "Tipe file tidak didukung.", diff --git a/src/locales/it-IT.json b/src/locales/it-IT.json index 790c63fe6..3c6cf93a8 100644 --- a/src/locales/it-IT.json +++ b/src/locales/it-IT.json @@ -192,7 +192,8 @@ "invalidSceneUrl": "Impossibile importare la scena dall'URL fornito. Potrebbe essere malformato o non contenere dati JSON Excalidraw validi.", "resetLibrary": "Questa azione cancellerà l'intera libreria. Sei sicuro?", "removeItemsFromsLibrary": "Eliminare {{count}} elementi dalla libreria?", - "invalidEncryptionKey": "La chiave di cifratura deve essere composta da 22 caratteri. La collaborazione live è disabilitata." + "invalidEncryptionKey": "La chiave di cifratura deve essere composta da 22 caratteri. La collaborazione live è disabilitata.", + "collabOfflineWarning": "Nessuna connessione internet disponibile.\nLe tue modifiche non verranno salvate!" }, "errors": { "unsupportedFileType": "Tipo di file non supportato.", diff --git a/src/locales/ja-JP.json b/src/locales/ja-JP.json index fad9b6f10..4f7a76afe 100644 --- a/src/locales/ja-JP.json +++ b/src/locales/ja-JP.json @@ -192,7 +192,8 @@ "invalidSceneUrl": "指定された URL からシーンをインポートできませんでした。不正な形式であるか、有効な Excalidraw JSON データが含まれていません。", "resetLibrary": "ライブラリを消去します。本当によろしいですか?", "removeItemsFromsLibrary": "{{count}} 個のアイテムをライブラリから削除しますか?", - "invalidEncryptionKey": "暗号化キーは22文字でなければなりません。ライブコラボレーションは無効化されています。" + "invalidEncryptionKey": "暗号化キーは22文字でなければなりません。ライブコラボレーションは無効化されています。", + "collabOfflineWarning": "インターネットに接続されていません。\n変更は保存されません!" }, "errors": { "unsupportedFileType": "サポートされていないファイル形式です。", @@ -220,7 +221,7 @@ "penMode": "ペンモード - タッチ防止", "link": "選択した図形のリンクを追加/更新", "eraser": "消しゴム", - "hand": "" + "hand": "手 (パンニングツール)" }, "headings": { "canvasActions": "キャンバス操作", @@ -228,7 +229,7 @@ "shapes": "図形" }, "hints": { - "canvasPanning": "", + "canvasPanning": "キャンバスを移動するには、マウスホイールまたはスペースバーを押しながらドラッグするか、手ツールを使用します", "linearElement": "クリックすると複数の頂点からなる曲線を開始、ドラッグすると直線", "freeDraw": "クリックしてドラッグします。離すと終了します", "text": "ヒント: 選択ツールを使用して任意の場所をダブルクリックしてテキストを追加することもできます", diff --git a/src/locales/kab-KAB.json b/src/locales/kab-KAB.json index fdf94fa94..3603e88c7 100644 --- a/src/locales/kab-KAB.json +++ b/src/locales/kab-KAB.json @@ -192,7 +192,8 @@ "invalidSceneUrl": "Ulamek taktert n usayes seg URL i d-ittunefken. Ahat mačči d tameɣtut neɣ ur tegbir ara isefka JSON n Excalidraw.", "resetLibrary": "Ayagi ad isfeḍ tamkarḍit-inek•m. Tetḥeqqeḍ?", "removeItemsFromsLibrary": "Ad tekkseḍ {{count}} n uferdis (en) si temkarḍit?", - "invalidEncryptionKey": "Tasarut n uwgelhen isefk ad tesɛu 22 n yiekkilen. Amɛiwen srid yensa." + "invalidEncryptionKey": "Tasarut n uwgelhen isefk ad tesɛu 22 n yiekkilen. Amɛiwen srid yensa.", + "collabOfflineWarning": "" }, "errors": { "unsupportedFileType": "Anaw n ufaylu ur yettwasefrak ara.", diff --git a/src/locales/kk-KZ.json b/src/locales/kk-KZ.json index 25071aa5e..ee1e44083 100644 --- a/src/locales/kk-KZ.json +++ b/src/locales/kk-KZ.json @@ -192,7 +192,8 @@ "invalidSceneUrl": "", "resetLibrary": "", "removeItemsFromsLibrary": "", - "invalidEncryptionKey": "" + "invalidEncryptionKey": "", + "collabOfflineWarning": "" }, "errors": { "unsupportedFileType": "", diff --git a/src/locales/ko-KR.json b/src/locales/ko-KR.json index a0368663c..9c02b3ea1 100644 --- a/src/locales/ko-KR.json +++ b/src/locales/ko-KR.json @@ -192,7 +192,8 @@ "invalidSceneUrl": "제공된 URL에서 화면을 가져오는데 실패했습니다. 주소가 잘못되거나, 유효한 Excalidraw JSON 데이터를 포함하고 있지 않은 것일 수 있습니다.", "resetLibrary": "당신의 라이브러리를 초기화 합니다. 계속하시겠습니까?", "removeItemsFromsLibrary": "{{count}}개의 아이템을 라이브러리에서 삭제하시겠습니까?", - "invalidEncryptionKey": "암호화 키는 반드시 22글자여야 합니다. 실시간 협업이 비활성화됩니다." + "invalidEncryptionKey": "암호화 키는 반드시 22글자여야 합니다. 실시간 협업이 비활성화됩니다.", + "collabOfflineWarning": "" }, "errors": { "unsupportedFileType": "지원하지 않는 파일 형식 입니다.", diff --git a/src/locales/ku-TR.json b/src/locales/ku-TR.json index 636a832af..4a8ebc95c 100644 --- a/src/locales/ku-TR.json +++ b/src/locales/ku-TR.json @@ -192,7 +192,8 @@ "invalidSceneUrl": "ناتوانێت دیمەنەکە هاوردە بکات لە URL ی دابینکراو. یان نادروستە، یان داتای \"ئێکسکالیدراو\" JSON ی دروستی تێدا نییە.", "resetLibrary": "ئەمە کتێبخانەکەت خاوێن دەکاتەوە. ئایا دڵنیایت?", "removeItemsFromsLibrary": "سڕینەوەی {{count}} ئایتم(ەکان) لە کتێبخانە؟", - "invalidEncryptionKey": "کلیلی رەمزاندن دەبێت لە 22 پیت بێت. هاوکاری ڕاستەوخۆ لە کارخراوە." + "invalidEncryptionKey": "کلیلی رەمزاندن دەبێت لە 22 پیت بێت. هاوکاری ڕاستەوخۆ لە کارخراوە.", + "collabOfflineWarning": "" }, "errors": { "unsupportedFileType": "جۆری فایلی پشتگیری نەکراو.", diff --git a/src/locales/lt-LT.json b/src/locales/lt-LT.json index 6eb83b7ac..a8a8d6639 100644 --- a/src/locales/lt-LT.json +++ b/src/locales/lt-LT.json @@ -192,7 +192,8 @@ "invalidSceneUrl": "Nepavyko suimportuoti scenos iš pateiktos nuorodos (URL). Ji arba blogai suformatuota, arba savyje neturi teisingų Excalidraw JSON duomenų.", "resetLibrary": "Tai išvalys tavo biblioteką. Ar tikrai to nori?", "removeItemsFromsLibrary": "Ištrinti {{count}} elementą/-us iš bibliotekos?", - "invalidEncryptionKey": "Šifravimo raktas turi būti iš 22 simbolių. Redagavimas gyvai yra išjungtas." + "invalidEncryptionKey": "Šifravimo raktas turi būti iš 22 simbolių. Redagavimas gyvai yra išjungtas.", + "collabOfflineWarning": "" }, "errors": { "unsupportedFileType": "Nepalaikomas failo tipas.", diff --git a/src/locales/lv-LV.json b/src/locales/lv-LV.json index 7341fd3fd..ea3180149 100644 --- a/src/locales/lv-LV.json +++ b/src/locales/lv-LV.json @@ -192,7 +192,8 @@ "invalidSceneUrl": "Nevarēja importēt ainu no norādītā URL. Vai nu tas ir nederīgs, vai nesatur derīgus Excalidraw JSON datus.", "resetLibrary": "Šī funkcija iztukšos bibliotēku. Vai turpināt?", "removeItemsFromsLibrary": "Vai izņemt {{count}} vienumu(s) no bibliotēkas?", - "invalidEncryptionKey": "Šifrēšanas atslēgai jābūt 22 simbolus garai. Tiešsaistes sadarbība ir izslēgta." + "invalidEncryptionKey": "Šifrēšanas atslēgai jābūt 22 simbolus garai. Tiešsaistes sadarbība ir izslēgta.", + "collabOfflineWarning": "" }, "errors": { "unsupportedFileType": "Neatbalstīts datnes veids.", diff --git a/src/locales/mr-IN.json b/src/locales/mr-IN.json index 6e198493d..e67fc3159 100644 --- a/src/locales/mr-IN.json +++ b/src/locales/mr-IN.json @@ -192,7 +192,8 @@ "invalidSceneUrl": "दिलेल्या यू-आर-एल पासून दृश्य आणू शकलो नाही. तो एकतर बरोबार नाही आहे किंवा त्यात वैध एक्सकेलीड्रॉ जेसन डेटा नाही.", "resetLibrary": "पटल स्वच्छ होणार, तुम्हाला खात्री आहे का?", "removeItemsFromsLibrary": "संग्रहातून {{count}} तत्व (एक किव्हा अनेक) काढू?", - "invalidEncryptionKey": "कूटबद्धन कुंजी 22 अक्षरांची असणे आवश्यक आहे. थेट सहयोग अक्षम केले आहे." + "invalidEncryptionKey": "कूटबद्धन कुंजी 22 अक्षरांची असणे आवश्यक आहे. थेट सहयोग अक्षम केले आहे.", + "collabOfflineWarning": "इंटरनेट कनेक्शन उपलब्ध नाही.\nतुमचे बदल जतन केले जाणार नाहीत!" }, "errors": { "unsupportedFileType": "असमर्थित फाइल प्रकार.", diff --git a/src/locales/my-MM.json b/src/locales/my-MM.json index 8b5e6859f..e479d3702 100644 --- a/src/locales/my-MM.json +++ b/src/locales/my-MM.json @@ -192,7 +192,8 @@ "invalidSceneUrl": "", "resetLibrary": "", "removeItemsFromsLibrary": "", - "invalidEncryptionKey": "" + "invalidEncryptionKey": "", + "collabOfflineWarning": "" }, "errors": { "unsupportedFileType": "", diff --git a/src/locales/nb-NO.json b/src/locales/nb-NO.json index f8ac1e307..653779a56 100644 --- a/src/locales/nb-NO.json +++ b/src/locales/nb-NO.json @@ -192,7 +192,8 @@ "invalidSceneUrl": "Kunne ikke importere scene fra den oppgitte URL-en. Den er enten ødelagt, eller inneholder ikke gyldig Excalidraw JSON-data.", "resetLibrary": "Dette vil tømme biblioteket ditt. Er du sikker?", "removeItemsFromsLibrary": "Slett {{count}} element(er) fra biblioteket?", - "invalidEncryptionKey": "Krypteringsnøkkel må ha 22 tegn. Live-samarbeid er deaktivert." + "invalidEncryptionKey": "Krypteringsnøkkel må ha 22 tegn. Live-samarbeid er deaktivert.", + "collabOfflineWarning": "Ingen Internett-tilkobling tilgjengelig.\nEndringer dine vil ikke bli lagret!" }, "errors": { "unsupportedFileType": "Filtypen støttes ikke.", diff --git a/src/locales/nl-NL.json b/src/locales/nl-NL.json index 9152d3bbb..313c8dc47 100644 --- a/src/locales/nl-NL.json +++ b/src/locales/nl-NL.json @@ -192,7 +192,8 @@ "invalidSceneUrl": "Kan scène niet importeren vanuit de opgegeven URL. Het is onjuist of bevat geen geldige Excalidraw JSON-gegevens.", "resetLibrary": "Dit zal je bibliotheek wissen. Weet je het zeker?", "removeItemsFromsLibrary": "Verwijder {{count}} item(s) uit bibliotheek?", - "invalidEncryptionKey": "Encryptiesleutel moet 22 tekens zijn. Live samenwerking is uitgeschakeld." + "invalidEncryptionKey": "Encryptiesleutel moet 22 tekens zijn. Live samenwerking is uitgeschakeld.", + "collabOfflineWarning": "" }, "errors": { "unsupportedFileType": "Niet-ondersteund bestandstype.", diff --git a/src/locales/nn-NO.json b/src/locales/nn-NO.json index fa740d258..0311cc2fe 100644 --- a/src/locales/nn-NO.json +++ b/src/locales/nn-NO.json @@ -192,7 +192,8 @@ "invalidSceneUrl": "Kunne ikkje hente noko scene frå den URL-en. Ho er anten øydelagd eller inneheld ikkje gyldig Excalidraw JSON-data.", "resetLibrary": "Dette vil fjerne alt innhald frå biblioteket. Er du sikker?", "removeItemsFromsLibrary": "Slette {{count}} element frå biblioteket?", - "invalidEncryptionKey": "Krypteringsnøkkelen må ha 22 teikn. Sanntidssamarbeid er deaktivert." + "invalidEncryptionKey": "Krypteringsnøkkelen må ha 22 teikn. Sanntidssamarbeid er deaktivert.", + "collabOfflineWarning": "" }, "errors": { "unsupportedFileType": "Filtypen er ikkje støtta.", diff --git a/src/locales/oc-FR.json b/src/locales/oc-FR.json index c1d3ab341..51a5b9018 100644 --- a/src/locales/oc-FR.json +++ b/src/locales/oc-FR.json @@ -192,7 +192,8 @@ "invalidSceneUrl": "Importacion impossibla de la scèna a partir de l’URL provesida. Es siá mal formatada o siá conten pas cap de donada JSON Excalidraw valida.", "resetLibrary": "Aquò suprimirà vòstra bibliotèca. O volètz vertadièrament ?", "removeItemsFromsLibrary": "Suprimir {{count}} element(s) de la bibliotèca ?", - "invalidEncryptionKey": "La clau de chiframent deu conténer 22 caractèrs. La collaboracion en dirèct es desactivada." + "invalidEncryptionKey": "La clau de chiframent deu conténer 22 caractèrs. La collaboracion en dirèct es desactivada.", + "collabOfflineWarning": "Cap de connexion pas disponibla.\nVòstras modificacions seràn pas salvadas !" }, "errors": { "unsupportedFileType": "Tipe de fichièr pas pres en carga.", @@ -220,7 +221,7 @@ "penMode": "Mòde estilo - empachar lo contact", "link": "Apondre/Actualizar lo ligam per una fòrma seleccionada", "eraser": "Goma", - "hand": "" + "hand": "Man (aisina de desplaçament de la vista)" }, "headings": { "canvasActions": "Accions del canabàs", @@ -239,7 +240,7 @@ "resize": "Podètz servar las proporcions en mantenent la tòca MAJ pendent lo redimensionament,\nmantenètz la tòca ALT per redimensionar a partir del centre", "resizeImage": "Podètz retalhar liurament en quichant CTRL,\nquichatz ALT per retalhar a partir del centre", "rotate": "Podètz restrénger los angles en mantenent MAJ pendent la rotacion", - "lineEditor_info": "", + "lineEditor_info": "Tenètz quichat Ctrl o Cmd e doble clic o quichatz Ctrl o Cmd + Entrada per modificar los ponches", "lineEditor_pointSelected": "Quichar Suprimir per tirar lo(s) punt(s),\nCtrlOCmd+D per duplicar, o lisatz per desplaçar", "lineEditor_nothingSelected": "Seleccionar un punt d’editar (manténer Maj. per ne seleccionar mantun),\no manténer Alt e clicar per n’apondre de novèls", "placeImage": "Clicatz per plaçar l’imatge, o clicatz e lisatz per definir sa talha manualament", @@ -316,8 +317,8 @@ "zoomToFit": "Zoomar per veire totes los elements", "zoomToSelection": "Zoomar la seleccion", "toggleElementLock": "Verrolhar/Desverrolhar la seleccion", - "movePageUpDown": "", - "movePageLeftRight": "" + "movePageUpDown": "Desplaçar la pagina ennaut/enbàs", + "movePageLeftRight": "Desplaçar la pagina a esquèrra/drecha" }, "clearCanvasDialog": { "title": "Escafar canabàs" diff --git a/src/locales/pa-IN.json b/src/locales/pa-IN.json index d5055a917..3bf61d83a 100644 --- a/src/locales/pa-IN.json +++ b/src/locales/pa-IN.json @@ -192,7 +192,8 @@ "invalidSceneUrl": "ਦਿੱਤੀ ਗਈ URL 'ਚੋਂ ਦ੍ਰਿਸ਼ ਨੂੰ ਆਯਾਤ ਨਹੀਂ ਕਰ ਸਕੇ। ਇਹ ਜਾਂ ਤਾਂ ਖਰਾਬ ਹੈ, ਜਾਂ ਇਸ ਵਿੱਚ ਜਾਇਜ਼ Excalidraw JSON ਡਾਟਾ ਸ਼ਾਮਲ ਨਹੀਂ ਹੈ।", "resetLibrary": "ਇਹ ਤੁਹਾਡੀ ਲਾਇਬ੍ਰੇਰੀ ਨੂੰ ਸਾਫ ਕਰ ਦੇਵੇਗਾ। ਕੀ ਤੁਸੀਂ ਪੱਕਾ ਇੰਝ ਕਰਨਾ ਚਾਹੁੰਦੇ ਹੋ?", "removeItemsFromsLibrary": "ਲਾਇਬ੍ਰੇਰੀ ਵਿੱਚੋਂ {{count}} ਚੀਜ਼(-ਜ਼ਾਂ) ਮਿਟਾਉਣੀਆਂ ਹਨ?", - "invalidEncryptionKey": "" + "invalidEncryptionKey": "", + "collabOfflineWarning": "" }, "errors": { "unsupportedFileType": "", diff --git a/src/locales/percentages.json b/src/locales/percentages.json index 997a62077..29a0c1f2b 100644 --- a/src/locales/percentages.json +++ b/src/locales/percentages.json @@ -1,26 +1,26 @@ { "ar-SA": 92, "bg-BG": 54, - "bn-BD": 60, - "ca-ES": 93, - "cs-CZ": 75, - "da-DK": 33, + "bn-BD": 59, + "ca-ES": 100, + "cs-CZ": 74, + "da-DK": 32, "de-DE": 100, "el-GR": 99, "en": 100, "es-ES": 100, - "eu-ES": 99, + "eu-ES": 100, "fa-IR": 95, - "fi-FI": 92, + "fi-FI": 100, "fr-FR": 100, - "gl-ES": 100, + "gl-ES": 99, "he-IL": 89, "hi-IN": 71, - "hu-HU": 89, + "hu-HU": 88, "id-ID": 99, "it-IT": 100, - "ja-JP": 99, - "kab-KAB": 94, + "ja-JP": 100, + "kab-KAB": 93, "kk-KZ": 20, "ko-KR": 98, "ku-TR": 95, @@ -31,22 +31,22 @@ "nb-NO": 100, "nl-NL": 90, "nn-NO": 89, - "oc-FR": 97, - "pa-IN": 83, + "oc-FR": 98, + "pa-IN": 82, "pl-PL": 84, - "pt-BR": 97, - "pt-PT": 99, - "ro-RO": 99, + "pt-BR": 100, + "pt-PT": 100, + "ro-RO": 100, "ru-RU": 100, "si-LK": 8, "sk-SK": 100, "sl-SI": 100, "sv-SE": 100, - "ta-IN": 92, + "ta-IN": 94, "tr-TR": 97, "uk-UA": 96, "vi-VN": 20, "zh-CN": 100, - "zh-HK": 26, + "zh-HK": 25, "zh-TW": 100 } diff --git a/src/locales/pl-PL.json b/src/locales/pl-PL.json index 04cc86898..89a52b493 100644 --- a/src/locales/pl-PL.json +++ b/src/locales/pl-PL.json @@ -192,7 +192,8 @@ "invalidSceneUrl": "Nie udało się zaimportować sceny z podanego adresu URL. Jest ona wadliwa lub nie zawiera poprawnych danych Excalidraw w formacie JSON.", "resetLibrary": "To wyczyści twoją bibliotekę. Jesteś pewien?", "removeItemsFromsLibrary": "Usunąć {{count}} element(ów) z biblioteki?", - "invalidEncryptionKey": "Klucz szyfrowania musi składać się z 22 znaków. Współpraca na żywo jest wyłączona." + "invalidEncryptionKey": "Klucz szyfrowania musi składać się z 22 znaków. Współpraca na żywo jest wyłączona.", + "collabOfflineWarning": "" }, "errors": { "unsupportedFileType": "Nieobsługiwany typ pliku.", diff --git a/src/locales/pt-BR.json b/src/locales/pt-BR.json index dd3362137..cb7f045de 100644 --- a/src/locales/pt-BR.json +++ b/src/locales/pt-BR.json @@ -192,7 +192,8 @@ "invalidSceneUrl": "Não foi possível importar a cena da URL fornecida. Ela está incompleta ou não contém dados JSON válidos do Excalidraw.", "resetLibrary": "Isto limpará a sua biblioteca. Você tem certeza?", "removeItemsFromsLibrary": "Excluir {{count}} item(ns) da biblioteca?", - "invalidEncryptionKey": "A chave de encriptação deve ter 22 caracteres. A colaboração ao vivo está desabilitada." + "invalidEncryptionKey": "A chave de encriptação deve ter 22 caracteres. A colaboração ao vivo está desabilitada.", + "collabOfflineWarning": "Sem conexão com a internet disponível.\nSuas alterações não serão salvas!" }, "errors": { "unsupportedFileType": "Tipo de arquivo não suportado.", @@ -220,7 +221,7 @@ "penMode": "Modo caneta — impede o toque", "link": "Adicionar/Atualizar link para uma forma selecionada", "eraser": "Borracha", - "hand": "" + "hand": "Mão (ferramenta de rolagem)" }, "headings": { "canvasActions": "Ações da tela", @@ -228,7 +229,7 @@ "shapes": "Formas" }, "hints": { - "canvasPanning": "", + "canvasPanning": "Para mover a tela, segure a roda do mouse ou a barra de espaço enquanto arrasta ou use a ferramenta de mão", "linearElement": "Clique para iniciar vários pontos, arraste para uma única linha", "freeDraw": "Toque e arraste, solte quando terminar", "text": "Dica: você também pode adicionar texto clicando duas vezes em qualquer lugar com a ferramenta de seleção", @@ -247,7 +248,7 @@ "bindTextToElement": "Pressione Enter para adicionar o texto", "deepBoxSelect": "Segure Ctrl/Cmd para seleção profunda e para evitar arrastar", "eraserRevert": "Segure a tecla Alt para inverter os elementos marcados para exclusão", - "firefox_clipboard_write": "" + "firefox_clipboard_write": "Esse recurso pode ser ativado configurando a opção \"dom.events.asyncClipboard.clipboardItem\" como \"true\". Para alterar os sinalizadores do navegador no Firefox, visite a página \"about:config\"." }, "canvasError": { "cannotShowPreview": "Não é possível mostrar pré-visualização", @@ -450,15 +451,15 @@ }, "welcomeScreen": { "app": { - "center_heading": "", - "center_heading_plus": "", - "menuHint": "" + "center_heading": "Todos os dados são salvos localmente no seu navegador.", + "center_heading_plus": "Você queria ir para o Excalidraw+ em vez disso?", + "menuHint": "Exportar, preferências, idiomas..." }, "defaults": { - "menuHint": "", - "center_heading": "", - "toolbarHint": "", - "helpHint": "" + "menuHint": "Exportar, preferências e mais...", + "center_heading": "Diagramas, Feito. Simples.", + "toolbarHint": "Escolha uma ferramenta e comece a desenhar!", + "helpHint": "Atalhos e ajuda" } } } diff --git a/src/locales/pt-PT.json b/src/locales/pt-PT.json index d72c7b98f..f713b22d3 100644 --- a/src/locales/pt-PT.json +++ b/src/locales/pt-PT.json @@ -192,7 +192,8 @@ "invalidSceneUrl": "Não foi possível importar a cena a partir do URL fornecido. Ou está mal formado ou não contém dados JSON do Excalidraw válidos.", "resetLibrary": "Isto irá limpar a sua biblioteca. Tem a certeza?", "removeItemsFromsLibrary": "Apagar {{count}} item(ns) da biblioteca?", - "invalidEncryptionKey": "Chave de encriptação deve ter 22 caracteres. A colaboração ao vivo está desativada." + "invalidEncryptionKey": "Chave de encriptação deve ter 22 caracteres. A colaboração ao vivo está desativada.", + "collabOfflineWarning": "Sem ligação à internet disponível.\nAs suas alterações não serão salvas!" }, "errors": { "unsupportedFileType": "Tipo de ficheiro não suportado.", @@ -220,7 +221,7 @@ "penMode": "Modo caneta - impedir toque", "link": "Acrescentar/ Adicionar ligação para uma forma seleccionada", "eraser": "Borracha", - "hand": "" + "hand": "Mão (ferramenta de movimento da tela)" }, "headings": { "canvasActions": "Ações da área de desenho", @@ -228,7 +229,7 @@ "shapes": "Formas" }, "hints": { - "canvasPanning": "", + "canvasPanning": "Para mover a tela, carregue na roda do rato ou na barra de espaço enquanto arrasta, ou use a ferramenta da mão", "linearElement": "Clique para iniciar vários pontos, arraste para uma única linha", "freeDraw": "Clique e arraste, large quando terminar", "text": "Dica: também pode adicionar texto clicando duas vezes em qualquer lugar com a ferramenta de seleção", @@ -247,7 +248,7 @@ "bindTextToElement": "Carregue Enter para acrescentar texto", "deepBoxSelect": "Mantenha a tecla CtrlOrCmd carregada para selecção profunda, impedindo o arrastamento", "eraserRevert": "Carregue também em Alt para reverter os elementos marcados para serem apagados", - "firefox_clipboard_write": "" + "firefox_clipboard_write": "Esta função pode provavelmente ser ativada definindo a opção \"dom.events.asyncClipboard.clipboardItem\" como \"true\". Para alterar os sinalizadores do navegador no Firefox, visite a página \"about:config\"." }, "canvasError": { "cannotShowPreview": "Não é possível mostrar uma pré-visualização", diff --git a/src/locales/ro-RO.json b/src/locales/ro-RO.json index ff76eea60..8eda2385b 100644 --- a/src/locales/ro-RO.json +++ b/src/locales/ro-RO.json @@ -192,7 +192,8 @@ "invalidSceneUrl": "Scena nu a putut fi importată din URL-ul furnizat. Este fie incorect formată, fie nu conține date JSON Excalidraw valide.", "resetLibrary": "Această opțiune va elimina conținutul din bibliotecă. Confirmi?", "removeItemsFromsLibrary": "Ștergi {{count}} element(e) din bibliotecă?", - "invalidEncryptionKey": "Cheia de criptare trebuie să aibă 22 de caractere. Colaborarea în direct este dezactivată." + "invalidEncryptionKey": "Cheia de criptare trebuie să aibă 22 de caractere. Colaborarea în direct este dezactivată.", + "collabOfflineWarning": "Nu este disponibilă nicio conexiune la internet.\nModificările nu vor fi salvate!" }, "errors": { "unsupportedFileType": "Tip de fișier neacceptat.", @@ -220,7 +221,7 @@ "penMode": "Mod stilou – împiedică atingerea", "link": "Adăugare/actualizare URL pentru forma selectată", "eraser": "Radieră", - "hand": "" + "hand": "Mână (instrument de panoramare)" }, "headings": { "canvasActions": "Acțiuni pentru pânză", @@ -228,7 +229,7 @@ "shapes": "Forme" }, "hints": { - "canvasPanning": "", + "canvasPanning": "Pentru a muta pânză, ține apăsată rotița mausului sau bara de spațiu sau folosește instrumentul în formă de mână", "linearElement": "Dă clic pentru a crea mai multe puncte, glisează pentru a forma o singură linie", "freeDraw": "Dă clic pe pânză și glisează cursorul, apoi eliberează-l când ai terminat", "text": "Sfat: poți adăuga text și dând dublu clic oriunde cu instrumentul de selecție", @@ -247,7 +248,7 @@ "bindTextToElement": "Apasă tasta Enter pentru a adăuga text", "deepBoxSelect": "Ține apăsată tasta Ctrl sau Cmd pentru a efectua selectarea de adâncime și pentru a preveni glisarea", "eraserRevert": "Ține apăsată tasta Alt pentru a anula elementele marcate pentru ștergere", - "firefox_clipboard_write": "" + "firefox_clipboard_write": "Această caracteristică poate fi probabil activată prin setarea preferinței „dom.events.asyncClipboard.clipboardItem” ca „true”. Pentru a schimba preferințele navigatorului în Firefox, accesează pagina „about:config”." }, "canvasError": { "cannotShowPreview": "Nu se poate afișa previzualizarea", diff --git a/src/locales/ru-RU.json b/src/locales/ru-RU.json index ce785dc83..cd0609505 100644 --- a/src/locales/ru-RU.json +++ b/src/locales/ru-RU.json @@ -192,7 +192,8 @@ "invalidSceneUrl": "Невозможно импортировать сцену с предоставленного URL. Неверный формат, или не содержит верных Excalidraw JSON данных.", "resetLibrary": "Это очистит вашу библиотеку. Вы уверены?", "removeItemsFromsLibrary": "Удалить {{count}} объект(ов) из библиотеки?", - "invalidEncryptionKey": "Ключ шифрования должен состоять из 22 символов. Одновременное редактирование отключено." + "invalidEncryptionKey": "Ключ шифрования должен состоять из 22 символов. Одновременное редактирование отключено.", + "collabOfflineWarning": "Отсутствует интернет-соединение.\nВаши изменения не будут сохранены!" }, "errors": { "unsupportedFileType": "Неподдерживаемый тип файла.", diff --git a/src/locales/si-LK.json b/src/locales/si-LK.json index dd458b31a..ee6ad46f7 100644 --- a/src/locales/si-LK.json +++ b/src/locales/si-LK.json @@ -192,7 +192,8 @@ "invalidSceneUrl": "", "resetLibrary": "", "removeItemsFromsLibrary": "", - "invalidEncryptionKey": "" + "invalidEncryptionKey": "", + "collabOfflineWarning": "" }, "errors": { "unsupportedFileType": "", diff --git a/src/locales/sk-SK.json b/src/locales/sk-SK.json index 783de30af..2b78b4539 100644 --- a/src/locales/sk-SK.json +++ b/src/locales/sk-SK.json @@ -192,7 +192,8 @@ "invalidSceneUrl": "Nepodarilo sa načítať scénu z poskytnutej URL. Je nevalidná alebo neobsahuje žiadne validné Excalidraw JSON dáta.", "resetLibrary": "Týmto vyprázdnite vašu knižnicu. Ste si istý?", "removeItemsFromsLibrary": "Odstrániť {{count}} položiek z knižnice?", - "invalidEncryptionKey": "Šifrovací kľúč musí mať 22 znakov. Živá spolupráca je vypnutá." + "invalidEncryptionKey": "Šifrovací kľúč musí mať 22 znakov. Živá spolupráca je vypnutá.", + "collabOfflineWarning": "Internetové pripojenie nie je dostupné.\nVaše zmeny nebudú uložené!" }, "errors": { "unsupportedFileType": "Nepodporovaný typ súboru.", diff --git a/src/locales/sl-SI.json b/src/locales/sl-SI.json index 54274932f..01c8ba4ca 100644 --- a/src/locales/sl-SI.json +++ b/src/locales/sl-SI.json @@ -192,7 +192,8 @@ "invalidSceneUrl": "S priloženega URL-ja ni bilo mogoče uvoziti scene. Je napačno oblikovana ali pa ne vsebuje veljavnih podatkov Excalidraw JSON.", "resetLibrary": "To bo počistilo vašo knjižnico. Ali ste prepričani?", "removeItemsFromsLibrary": "Izbriši elemente ({{count}}) iz knjižnice?", - "invalidEncryptionKey": "Ključ za šifriranje mora vsebovati 22 znakov. Sodelovanje v živo je onemogočeno." + "invalidEncryptionKey": "Ključ za šifriranje mora vsebovati 22 znakov. Sodelovanje v živo je onemogočeno.", + "collabOfflineWarning": "Internetna povezava ni na voljo.\nVaše spremembe ne bodo shranjene!" }, "errors": { "unsupportedFileType": "Nepodprt tip datoteke.", diff --git a/src/locales/sv-SE.json b/src/locales/sv-SE.json index 30638b9e4..3cf3b0cbd 100644 --- a/src/locales/sv-SE.json +++ b/src/locales/sv-SE.json @@ -192,7 +192,8 @@ "invalidSceneUrl": "Det gick inte att importera skiss från den angivna webbadressen. Antingen har den fel format, eller så innehåller den ingen giltig Excalidraw JSON data.", "resetLibrary": "Detta kommer att rensa ditt bibliotek. Är du säker?", "removeItemsFromsLibrary": "Ta bort {{count}} objekt från biblioteket?", - "invalidEncryptionKey": "Krypteringsnyckeln måste vara 22 tecken. Livesamarbetet är inaktiverat." + "invalidEncryptionKey": "Krypteringsnyckeln måste vara 22 tecken. Livesamarbetet är inaktiverat.", + "collabOfflineWarning": "Ingen internetanslutning tillgänglig.\nDina ändringar kommer inte att sparas!" }, "errors": { "unsupportedFileType": "Filtypen stöds inte.", diff --git a/src/locales/ta-IN.json b/src/locales/ta-IN.json index 2ff76b058..493dfa560 100644 --- a/src/locales/ta-IN.json +++ b/src/locales/ta-IN.json @@ -1,7 +1,7 @@ { "labels": { "paste": "ஒட்டு", - "pasteAsPlaintext": "", + "pasteAsPlaintext": "அலங்காரமின்றி ஒட்டு", "pasteCharts": "விளக்கப்படங்களை ஒட்டு", "selectAll": "எல்லாம் தேர்ந்தெடு", "multiSelect": "உறுப்பைத் தெரிவில் சேர்", @@ -54,7 +54,7 @@ "veryLarge": "மிகப் பெரிய", "solid": "திடமான", "hachure": "மலைக்குறிக்கோடு", - "crossHatch": "", + "crossHatch": "குறுக்குகதவு", "thin": "மெல்லிய", "bold": "பட்டை", "left": "இடது", @@ -72,7 +72,7 @@ "layers": "அடுக்குகள்", "actions": "செயல்கள்", "language": "மொழி", - "liveCollaboration": "", + "liveCollaboration": "நேரடி கூட்டுப்பணி...", "duplicateSelection": "நகலாக்கு", "untitled": "தலைப்பற்றது", "name": "பெயர்", @@ -116,7 +116,7 @@ "label": "தொடுப்பு" }, "lineEditor": { - "edit": "", + "edit": "தொடுப்பைத் திருத்து", "exit": "" }, "elementLock": { @@ -137,7 +137,7 @@ "clearReset": "கித்தானை அகரமாக்கு", "exportJSON": "கோப்புக்கு ஏற்றுமதிசெய்", "exportImage": "", - "export": "", + "export": "இதில் சேமி...", "exportToPng": "PNGக்கு ஏற்றுமதிசெய்", "exportToSvg": "SVGக்கு ஏற்றுமதிசெய்", "copyToClipboard": "நகலகத்திற்கு நகலெடு", @@ -145,7 +145,7 @@ "scale": "அளவு", "save": "தற்போதைய கோப்புக்குச் சேமி", "saveAs": "இப்படி சேமி", - "load": "", + "load": "திற", "getShareableLink": "பகிரக்கூடிய தொடுப்பைப் பெறு", "close": "மூடு", "selectLanguage": "மொழியைத் தேர்ந்தெடு", @@ -192,7 +192,8 @@ "invalidSceneUrl": "வழங்கப்பட்ட உரலியிலிருந்து காட்சியை இறக்கவியலா. இது தவறான வடிவத்தில் உள்ளது, அ செல்லத்தக்க எக்ஸ்கேலிட்ரா JSON தரவைக் கொண்டில்லை.", "resetLibrary": "இது உங்கள் நுலகத்தைத் துடைக்கும். நீங்கள் உறுதியா?", "removeItemsFromsLibrary": "{{count}} உருப்படி(கள்)-ஐ உம் நூலகத்திலிருந்து அழிக்கவா?", - "invalidEncryptionKey": "மறையாக்க விசை 22 வரியுருக்கள் கொண்டிருக்கவேண்டும். நேரடி கூட்டுப்பணி முடக்கப்பட்டது." + "invalidEncryptionKey": "மறையாக்க விசை 22 வரியுருக்கள் கொண்டிருக்கவேண்டும். நேரடி கூட்டுப்பணி முடக்கப்பட்டது.", + "collabOfflineWarning": "" }, "errors": { "unsupportedFileType": "ஆதரிக்கப்படா கோப்பு வகை.", @@ -456,9 +457,9 @@ }, "defaults": { "menuHint": "", - "center_heading": "", - "toolbarHint": "", - "helpHint": "" + "center_heading": "எளிமையாக வரைபடங்கள் உருவாக்க!", + "toolbarHint": "கருவியைத் தேர்ந்தெடு & வரை!", + "helpHint": "குறுக்குவழிகள் & உதவி" } } } diff --git a/src/locales/tr-TR.json b/src/locales/tr-TR.json index 00260919c..a05de9a54 100644 --- a/src/locales/tr-TR.json +++ b/src/locales/tr-TR.json @@ -192,7 +192,8 @@ "invalidSceneUrl": "Verilen bağlantıdan çalışma alanı yüklenemedi. Dosya bozuk olabilir veya geçerli bir Excalidraw JSON verisi bulundurmuyor olabilir.", "resetLibrary": "Bu işlem kütüphanenizi sıfırlayacak. Emin misiniz?", "removeItemsFromsLibrary": "{{count}} öğe(ler) kitaplıktan kaldırılsın mı?", - "invalidEncryptionKey": "Şifreleme anahtarı 22 karakter olmalı. Canlı işbirliği devre dışı bırakıldı." + "invalidEncryptionKey": "Şifreleme anahtarı 22 karakter olmalı. Canlı işbirliği devre dışı bırakıldı.", + "collabOfflineWarning": "" }, "errors": { "unsupportedFileType": "Desteklenmeyen dosya türü.", diff --git a/src/locales/uk-UA.json b/src/locales/uk-UA.json index 73597ce9d..ac0bfcfee 100644 --- a/src/locales/uk-UA.json +++ b/src/locales/uk-UA.json @@ -72,7 +72,7 @@ "layers": "Шари", "actions": "Дії", "language": "Мова", - "liveCollaboration": "", + "liveCollaboration": "Спільна робота у режимі реального часу...", "duplicateSelection": "Дублювати", "untitled": "Без назви", "name": "Ім’я", @@ -192,7 +192,8 @@ "invalidSceneUrl": "Не вдалося імпортувати сцену з наданого URL. Він або недоформований, або не містить дійсних даних Excalidraw JSON.", "resetLibrary": "Це призведе до очищення бібліотеки. Ви впевнені?", "removeItemsFromsLibrary": "Видалити {{count}} елемент(ів) з бібліотеки?", - "invalidEncryptionKey": "Ключ шифрування повинен бути довжиною до 22 символів. Спільну роботу вимкнено." + "invalidEncryptionKey": "Ключ шифрування повинен бути довжиною до 22 символів. Спільну роботу вимкнено.", + "collabOfflineWarning": "Немає підключення до Інтернету.\nВаші зміни не будуть збережені!" }, "errors": { "unsupportedFileType": "Непідтримуваний тип файлу.", @@ -457,8 +458,8 @@ "defaults": { "menuHint": "", "center_heading": "", - "toolbarHint": "", - "helpHint": "" + "toolbarHint": "Оберіть інструмент і почніть малювати!", + "helpHint": "Гарячі клавіші і допомога" } } } diff --git a/src/locales/vi-VN.json b/src/locales/vi-VN.json index be5edd61d..1921035bc 100644 --- a/src/locales/vi-VN.json +++ b/src/locales/vi-VN.json @@ -192,7 +192,8 @@ "invalidSceneUrl": "", "resetLibrary": "", "removeItemsFromsLibrary": "", - "invalidEncryptionKey": "" + "invalidEncryptionKey": "", + "collabOfflineWarning": "" }, "errors": { "unsupportedFileType": "", diff --git a/src/locales/zh-CN.json b/src/locales/zh-CN.json index 6020688a2..2e5debd16 100644 --- a/src/locales/zh-CN.json +++ b/src/locales/zh-CN.json @@ -192,7 +192,8 @@ "invalidSceneUrl": "无法从提供的 URL 导入场景。它或者格式不正确,或者不包含有效的 Excalidraw JSON 数据。", "resetLibrary": "这将会清除你的素材库。你确定要这么做吗?", "removeItemsFromsLibrary": "确定要从素材库中删除 {{count}} 个项目吗?", - "invalidEncryptionKey": "密钥必须包含22个字符。实时协作已被禁用。" + "invalidEncryptionKey": "密钥必须包含22个字符。实时协作已被禁用。", + "collabOfflineWarning": "无网络连接。\n您的改动将不会被保存!" }, "errors": { "unsupportedFileType": "不支持的文件格式。", @@ -239,7 +240,7 @@ "resize": "您可以按住SHIFT来限制比例大小,\n按住ALT来调整中心大小", "resizeImage": "按住SHIFT可以自由缩放,\n按住ALT可以从中间缩放", "rotate": "旋转时可以按住 Shift 来约束角度", - "lineEditor_info": "按住 CtrlOrCmd 并双击或按 CtrlOrmd + Enter 来编辑点", + "lineEditor_info": "按住 CtrlOrCmd 并双击或按 CtrlOrCmd + Enter 来编辑点", "lineEditor_pointSelected": "按下 Delete 移除点,Ctrl 或 Cmd+D 以复制,拖动以移动", "lineEditor_nothingSelected": "选择要编辑的点 (按住 SHIFT 选择多个),\n或按住 Alt 并点击以添加新点", "placeImage": "点击放置图像,或者点击并拖动以手动设置图像大小", diff --git a/src/locales/zh-HK.json b/src/locales/zh-HK.json index 4ff1176be..e87a0d3e1 100644 --- a/src/locales/zh-HK.json +++ b/src/locales/zh-HK.json @@ -192,7 +192,8 @@ "invalidSceneUrl": "", "resetLibrary": "", "removeItemsFromsLibrary": "", - "invalidEncryptionKey": "" + "invalidEncryptionKey": "", + "collabOfflineWarning": "" }, "errors": { "unsupportedFileType": "", diff --git a/src/locales/zh-TW.json b/src/locales/zh-TW.json index 28ac95a26..3293fad4a 100644 --- a/src/locales/zh-TW.json +++ b/src/locales/zh-TW.json @@ -192,7 +192,8 @@ "invalidSceneUrl": "無法由提供的 URL 匯入場景。可能是發生異常,或未包含有效的 Excalidraw JSON 資料。", "resetLibrary": "這會清除您的資料庫,是否確定?", "removeItemsFromsLibrary": "從資料庫刪除 {{count}} 項?", - "invalidEncryptionKey": "加密鍵必須為22字元。即時協作已停用。" + "invalidEncryptionKey": "加密鍵必須為22字元。即時協作已停用。", + "collabOfflineWarning": "沒有可用的網路連線。\n變更無法儲存!" }, "errors": { "unsupportedFileType": "不支援的檔案類型。", From 04a8c22f3908614243273f4614a983fc04f59e24 Mon Sep 17 00:00:00 2001 From: David Luzar Date: Wed, 22 Feb 2023 15:01:23 +0100 Subject: [PATCH 06/74] fix: rerender i18n in host components on lang change (#6224) --- src/components/main-menu/DefaultItems.tsx | 33 +++++++------------ .../welcome-screen/WelcomeScreen.Center.tsx | 7 ++-- src/excalidraw-app/app-jotai.ts | 3 ++ src/excalidraw-app/collab/Collab.tsx | 12 +++---- src/excalidraw-app/collab/RoomDialog.tsx | 3 +- .../components/AppWelcomeScreen.tsx | 3 +- .../components/EncryptedIcon.tsx | 32 ++++++++++-------- .../components/ExportToExcalidrawPlus.tsx | 3 +- .../components/LanguageList.tsx | 15 +++++---- src/excalidraw-app/index.tsx | 17 +++++----- src/i18n.ts | 16 +++++++++ src/jotai.ts | 4 +-- src/packages/excalidraw/CHANGELOG.md | 2 ++ src/packages/excalidraw/index.tsx | 10 +++--- 14 files changed, 88 insertions(+), 72 deletions(-) create mode 100644 src/excalidraw-app/app-jotai.ts diff --git a/src/components/main-menu/DefaultItems.tsx b/src/components/main-menu/DefaultItems.tsx index 6e89b598c..b3cc23b90 100644 --- a/src/components/main-menu/DefaultItems.tsx +++ b/src/components/main-menu/DefaultItems.tsx @@ -1,5 +1,5 @@ import { getShortcutFromShortcutName } from "../../actions/shortcuts"; -import { t } from "../../i18n"; +import { useI18n } from "../../i18n"; import { useExcalidrawAppState, useExcalidrawSetAppState, @@ -33,9 +33,7 @@ import { useSetAtom } from "jotai"; import { activeConfirmDialogAtom } from "../ActiveConfirmDialog"; export const LoadScene = () => { - // FIXME Hack until we tie "t" to lang state - // eslint-disable-next-line - const appState = useExcalidrawAppState(); + const { t } = useI18n(); const actionManager = useExcalidrawActionManager(); if (!actionManager.isActionEnabled(actionLoadScene)) { @@ -57,9 +55,7 @@ export const LoadScene = () => { LoadScene.displayName = "LoadScene"; export const SaveToActiveFile = () => { - // FIXME Hack until we tie "t" to lang state - // eslint-disable-next-line - const appState = useExcalidrawAppState(); + const { t } = useI18n(); const actionManager = useExcalidrawActionManager(); if (!actionManager.isActionEnabled(actionSaveToActiveFile)) { @@ -80,9 +76,7 @@ SaveToActiveFile.displayName = "SaveToActiveFile"; export const SaveAsImage = () => { const setAppState = useExcalidrawSetAppState(); - // FIXME Hack until we tie "t" to lang state - // eslint-disable-next-line - const appState = useExcalidrawAppState(); + const { t } = useI18n(); return ( { SaveAsImage.displayName = "SaveAsImage"; export const Help = () => { - // FIXME Hack until we tie "t" to lang state - // eslint-disable-next-line - const appState = useExcalidrawAppState(); + const { t } = useI18n(); const actionManager = useExcalidrawActionManager(); @@ -119,9 +111,8 @@ export const Help = () => { Help.displayName = "Help"; export const ClearCanvas = () => { - // FIXME Hack until we tie "t" to lang state - // eslint-disable-next-line - const appState = useExcalidrawAppState(); + const { t } = useI18n(); + const setActiveConfirmDialog = useSetAtom(activeConfirmDialogAtom); const actionManager = useExcalidrawActionManager(); @@ -143,6 +134,7 @@ export const ClearCanvas = () => { ClearCanvas.displayName = "ClearCanvas"; export const ToggleTheme = () => { + const { t } = useI18n(); const appState = useExcalidrawAppState(); const actionManager = useExcalidrawActionManager(); @@ -175,6 +167,7 @@ export const ToggleTheme = () => { ToggleTheme.displayName = "ToggleTheme"; export const ChangeCanvasBackground = () => { + const { t } = useI18n(); const appState = useExcalidrawAppState(); const actionManager = useExcalidrawActionManager(); @@ -195,9 +188,7 @@ export const ChangeCanvasBackground = () => { ChangeCanvasBackground.displayName = "ChangeCanvasBackground"; export const Export = () => { - // FIXME Hack until we tie "t" to lang state - // eslint-disable-next-line - const appState = useExcalidrawAppState(); + const { t } = useI18n(); const setAppState = useExcalidrawSetAppState(); return ( void; isCollaborating: boolean; }) => { - // FIXME Hack until we tie "t" to lang state - // eslint-disable-next-line - const appState = useExcalidrawAppState(); + const { t } = useI18n(); return ( any; }) => { - // FIXME when we tie t() to lang state - // eslint-disable-next-line @typescript-eslint/no-unused-vars - const appState = useExcalidrawAppState(); - + const { t } = useI18n(); return ( {t("labels.liveCollaboration")} diff --git a/src/excalidraw-app/app-jotai.ts b/src/excalidraw-app/app-jotai.ts new file mode 100644 index 000000000..8c6c796f6 --- /dev/null +++ b/src/excalidraw-app/app-jotai.ts @@ -0,0 +1,3 @@ +import { unstable_createStore } from "jotai"; + +export const appJotaiStore = unstable_createStore(); diff --git a/src/excalidraw-app/collab/Collab.tsx b/src/excalidraw-app/collab/Collab.tsx index 22f748773..30c9846c8 100644 --- a/src/excalidraw-app/collab/Collab.tsx +++ b/src/excalidraw-app/collab/Collab.tsx @@ -70,7 +70,7 @@ import { decryptData } from "../../data/encryption"; import { resetBrowserStateVersions } from "../data/tabSync"; import { LocalData } from "../data/LocalData"; import { atom, useAtom } from "jotai"; -import { jotaiStore } from "../../jotai"; +import { appJotaiStore } from "../app-jotai"; export const collabAPIAtom = atom(null); export const collabDialogShownAtom = atom(false); @@ -167,7 +167,7 @@ class Collab extends PureComponent { setUsername: this.setUsername, }; - jotaiStore.set(collabAPIAtom, collabAPI); + appJotaiStore.set(collabAPIAtom, collabAPI); this.onOfflineStatusToggle(); if ( @@ -185,7 +185,7 @@ class Collab extends PureComponent { } onOfflineStatusToggle = () => { - jotaiStore.set(isOfflineAtom, !window.navigator.onLine); + appJotaiStore.set(isOfflineAtom, !window.navigator.onLine); }; componentWillUnmount() { @@ -208,10 +208,10 @@ class Collab extends PureComponent { } } - isCollaborating = () => jotaiStore.get(isCollaboratingAtom)!; + isCollaborating = () => appJotaiStore.get(isCollaboratingAtom)!; private setIsCollaborating = (isCollaborating: boolean) => { - jotaiStore.set(isCollaboratingAtom, isCollaborating); + appJotaiStore.set(isCollaboratingAtom, isCollaborating); }; private onUnload = () => { @@ -804,7 +804,7 @@ class Collab extends PureComponent { ); handleClose = () => { - jotaiStore.set(collabDialogShownAtom, false); + appJotaiStore.set(collabDialogShownAtom, false); }; setUsername = (username: string) => { diff --git a/src/excalidraw-app/collab/RoomDialog.tsx b/src/excalidraw-app/collab/RoomDialog.tsx index 2c6949aac..50f586efc 100644 --- a/src/excalidraw-app/collab/RoomDialog.tsx +++ b/src/excalidraw-app/collab/RoomDialog.tsx @@ -10,13 +10,13 @@ import { shareWindows, } from "../../components/icons"; import { ToolButton } from "../../components/ToolButton"; -import { t } from "../../i18n"; import "./RoomDialog.scss"; import Stack from "../../components/Stack"; import { AppState } from "../../types"; import { trackEvent } from "../../analytics"; import { getFrame } from "../../utils"; import DialogActionButton from "../../components/DialogActionButton"; +import { useI18n } from "../../i18n"; const getShareIcon = () => { const navigator = window.navigator as any; @@ -51,6 +51,7 @@ const RoomDialog = ({ setErrorMessage: (message: string) => void; theme: AppState["theme"]; }) => { + const { t } = useI18n(); const roomLinkInput = useRef(null); const copyRoomLink = async () => { diff --git a/src/excalidraw-app/components/AppWelcomeScreen.tsx b/src/excalidraw-app/components/AppWelcomeScreen.tsx index 9e760f734..1e34fa819 100644 --- a/src/excalidraw-app/components/AppWelcomeScreen.tsx +++ b/src/excalidraw-app/components/AppWelcomeScreen.tsx @@ -1,12 +1,13 @@ import React from "react"; import { PlusPromoIcon } from "../../components/icons"; -import { t } from "../../i18n"; +import { useI18n } from "../../i18n"; import { WelcomeScreen } from "../../packages/excalidraw/index"; import { isExcalidrawPlusSignedUser } from "../app_constants"; export const AppWelcomeScreen: React.FC<{ setCollabDialogShown: (toggle: boolean) => any; }> = React.memo((props) => { + const { t } = useI18n(); let headingContent; if (isExcalidrawPlusSignedUser) { diff --git a/src/excalidraw-app/components/EncryptedIcon.tsx b/src/excalidraw-app/components/EncryptedIcon.tsx index a3e6ff0ba..a91768917 100644 --- a/src/excalidraw-app/components/EncryptedIcon.tsx +++ b/src/excalidraw-app/components/EncryptedIcon.tsx @@ -1,17 +1,21 @@ import { shield } from "../../components/icons"; import { Tooltip } from "../../components/Tooltip"; -import { t } from "../../i18n"; +import { useI18n } from "../../i18n"; -export const EncryptedIcon = () => ( - - - {shield} - - -); +export const EncryptedIcon = () => { + const { t } = useI18n(); + + return ( + + + {shield} + + + ); +}; diff --git a/src/excalidraw-app/components/ExportToExcalidrawPlus.tsx b/src/excalidraw-app/components/ExportToExcalidrawPlus.tsx index 049a4ddf7..daf4b95c3 100644 --- a/src/excalidraw-app/components/ExportToExcalidrawPlus.tsx +++ b/src/excalidraw-app/components/ExportToExcalidrawPlus.tsx @@ -6,7 +6,7 @@ import { loadFirebaseStorage, saveFilesToFirebase } from "../data/firebase"; import { FileId, NonDeletedExcalidrawElement } from "../../element/types"; import { AppState, BinaryFileData, BinaryFiles } from "../../types"; import { nanoid } from "nanoid"; -import { t } from "../../i18n"; +import { useI18n } from "../../i18n"; import { excalidrawPlusIcon } from "./icons"; import { encryptData, generateEncryptionKey } from "../../data/encryption"; import { isInitializedImageElement } from "../../element/typeChecks"; @@ -79,6 +79,7 @@ export const ExportToExcalidrawPlus: React.FC<{ files: BinaryFiles; onError: (error: Error) => void; }> = ({ elements, appState, files, onError }) => { + const { t } = useI18n(); return (
{excalidrawPlusIcon}
diff --git a/src/excalidraw-app/components/LanguageList.tsx b/src/excalidraw-app/components/LanguageList.tsx index 1b3606b57..aaa5f2137 100644 --- a/src/excalidraw-app/components/LanguageList.tsx +++ b/src/excalidraw-app/components/LanguageList.tsx @@ -1,22 +1,23 @@ -import { useAtom } from "jotai"; +import { useSetAtom } from "jotai"; import React from "react"; -import { langCodeAtom } from ".."; -import * as i18n from "../../i18n"; +import { appLangCodeAtom } from ".."; +import { defaultLang, useI18n } from "../../i18n"; import { languages } from "../../i18n"; export const LanguageList = ({ style }: { style?: React.CSSProperties }) => { - const [langCode, setLangCode] = useAtom(langCodeAtom); + const { t, langCode } = useI18n(); + const setLangCode = useSetAtom(appLangCodeAtom); return ( onChange(option.value)} - checked={value === option.value} + {props.options.map((option) => + props.type === "button" ? ( + + ) : ( + + ), + )} ); diff --git a/src/components/icons.tsx b/src/components/icons.tsx index 046ee490b..784e81024 100644 --- a/src/components/icons.tsx +++ b/src/components/icons.tsx @@ -1008,6 +1008,13 @@ export const UngroupIcon = React.memo(({ theme }: { theme: Theme }) => ), ); +export const FillZigZagIcon = createIcon( + + + , + modifiedTablerIconProps, +); + export const FillHachureIcon = createIcon( <> Date: Wed, 12 Apr 2023 02:53:36 +0530 Subject: [PATCH 52/74] fix: fixing popover overflow on small screen (#6433) Co-authored-by: dwelle --- src/components/Popover.scss | 1 + src/components/Popover.tsx | 86 ++++++++++++++++++++++++++----------- 2 files changed, 61 insertions(+), 26 deletions(-) diff --git a/src/components/Popover.scss b/src/components/Popover.scss index 84d16e47f..9458b5026 100644 --- a/src/components/Popover.scss +++ b/src/components/Popover.scss @@ -3,5 +3,6 @@ position: absolute; z-index: 10; padding: 5px 0 5px; + outline: none; } } diff --git a/src/components/Popover.tsx b/src/components/Popover.tsx index 9a046599b..c1249f798 100644 --- a/src/components/Popover.tsx +++ b/src/components/Popover.tsx @@ -29,13 +29,15 @@ export const Popover = ({ }: Props) => { const popoverRef = useRef(null); - const container = popoverRef.current; - useEffect(() => { + const container = popoverRef.current; + if (!container) { return; } + container.focus(); + const handleKeyDown = (event: KeyboardEvent) => { if (event.key === KEYS.TAB) { const focusableElements = queryFocusableElements(container); @@ -44,15 +46,23 @@ export const Popover = ({ (element) => element === activeElement, ); - if (currentIndex === 0 && event.shiftKey) { - focusableElements[focusableElements.length - 1].focus(); + if (activeElement === container) { + if (event.shiftKey) { + focusableElements[focusableElements.length - 1]?.focus(); + } else { + focusableElements[0].focus(); + } + event.preventDefault(); + event.stopImmediatePropagation(); + } else if (currentIndex === 0 && event.shiftKey) { + focusableElements[focusableElements.length - 1]?.focus(); event.preventDefault(); event.stopImmediatePropagation(); } else if ( currentIndex === focusableElements.length - 1 && !event.shiftKey ) { - focusableElements[0].focus(); + focusableElements[0]?.focus(); event.preventDefault(); event.stopImmediatePropagation(); } @@ -62,35 +72,59 @@ export const Popover = ({ container.addEventListener("keydown", handleKeyDown); return () => container.removeEventListener("keydown", handleKeyDown); - }, [container]); + }, []); + + const lastInitializedPosRef = useRef<{ top: number; left: number } | null>( + null, + ); // ensure the popover doesn't overflow the viewport useLayoutEffect(() => { - if (fitInViewport && popoverRef.current) { - const element = popoverRef.current; - const { x, y, width, height } = element.getBoundingClientRect(); + if (fitInViewport && popoverRef.current && top != null && left != null) { + const container = popoverRef.current; + const { width, height } = container.getBoundingClientRect(); - //Position correctly when clicked on rightmost part or the bottom part of viewport - if (x + width - offsetLeft > viewportWidth) { - element.style.left = `${viewportWidth - width - 10}px`; - } - if (y + height - offsetTop > viewportHeight) { - element.style.top = `${viewportHeight - height}px`; + // hack for StrictMode so this effect only runs once for + // the same top/left position, otherwise + // we'd potentically reposition twice (once for viewport overflow) + // and once for top/left position afterwards + if ( + lastInitializedPosRef.current?.top === top && + lastInitializedPosRef.current?.left === left + ) { + return; } + lastInitializedPosRef.current = { top, left }; - //Resize to fit viewport on smaller screens - if (height >= viewportHeight) { - element.style.height = `${viewportHeight - 20}px`; - element.style.top = "10px"; - element.style.overflowY = "scroll"; - } if (width >= viewportWidth) { - element.style.width = `${viewportWidth}px`; - element.style.left = "0px"; - element.style.overflowX = "scroll"; + container.style.width = `${viewportWidth}px`; + container.style.left = "0px"; + container.style.overflowX = "scroll"; + } else if (left + width - offsetLeft > viewportWidth) { + container.style.left = `${viewportWidth - width - 10}px`; + } else { + container.style.left = `${left}px`; + } + + if (height >= viewportHeight) { + container.style.height = `${viewportHeight - 20}px`; + container.style.top = "10px"; + container.style.overflowY = "scroll"; + } else if (top + height - offsetTop > viewportHeight) { + container.style.top = `${viewportHeight - height}px`; + } else { + container.style.top = `${top}px`; } } - }, [fitInViewport, viewportWidth, viewportHeight, offsetLeft, offsetTop]); + }, [ + top, + left, + fitInViewport, + viewportWidth, + viewportHeight, + offsetLeft, + offsetTop, + ]); useEffect(() => { if (onCloseRequest) { @@ -105,7 +139,7 @@ export const Popover = ({ }, [onCloseRequest]); return ( -
+
{children}
); From 372743f59f03b2f368ea0bf704cb37c3dca60cc5 Mon Sep 17 00:00:00 2001 From: David Luzar Date: Wed, 12 Apr 2023 10:57:00 +0200 Subject: [PATCH 53/74] fix: autoredirect to plus in prod only (#6446) --- public/index.html | 2 ++ 1 file changed, 2 insertions(+) diff --git a/public/index.html b/public/index.html index 47a59624b..a8633fc4d 100644 --- a/public/index.html +++ b/public/index.html @@ -79,6 +79,7 @@ + <% if (process.env.NODE_ENV === "production") { %> + <% } %> From 13b27afe0f39cc43d80fa05232e985530d0f0600 Mon Sep 17 00:00:00 2001 From: Aakansha Doshi Date: Thu, 13 Apr 2023 11:45:58 +0530 Subject: [PATCH 54/74] fix: update coords when text unbinded from its container (#6445) * fix: update coords when text unbinded from its container * Add specs --- src/actions/actionBoundText.tsx | 6 ++- src/element/textElement.ts | 8 +++- src/element/textWysiwyg.test.tsx | 39 +++++++++++++++++ src/tests/linearElementEditor.test.tsx | 59 +++++++++++++++++++++++++- 4 files changed, 109 insertions(+), 3 deletions(-) diff --git a/src/actions/actionBoundText.tsx b/src/actions/actionBoundText.tsx index 9f9fbfc0f..990f98d41 100644 --- a/src/actions/actionBoundText.tsx +++ b/src/actions/actionBoundText.tsx @@ -2,6 +2,7 @@ import { BOUND_TEXT_PADDING, ROUNDNESS, VERTICAL_ALIGN } from "../constants"; import { getNonDeletedElements, isTextElement, newElement } from "../element"; import { mutateElement } from "../element/mutateElement"; import { + computeBoundTextPosition, computeContainerDimensionForBoundText, getBoundTextElement, measureText, @@ -33,6 +34,7 @@ export const actionUnbindText = register({ trackEvent: { category: "element" }, predicate: (elements, appState) => { const selectedElements = getSelectedElements(elements, appState); + return selectedElements.some((element) => hasBoundTextElement(element)); }, perform: (elements, appState) => { @@ -52,13 +54,15 @@ export const actionUnbindText = register({ element.id, ); resetOriginalContainerCache(element.id); - + const { x, y } = computeBoundTextPosition(element, boundTextElement); mutateElement(boundTextElement as ExcalidrawTextElement, { containerId: null, width, height, baseline, text: boundTextElement.originalText, + x, + y, }); mutateElement(element, { boundElements: element.boundElements?.filter( diff --git a/src/element/textElement.ts b/src/element/textElement.ts index 26cf91a89..38da5df5a 100644 --- a/src/element/textElement.ts +++ b/src/element/textElement.ts @@ -245,10 +245,16 @@ export const handleBindTextResize = ( } }; -const computeBoundTextPosition = ( +export const computeBoundTextPosition = ( container: ExcalidrawElement, boundTextElement: ExcalidrawTextElementWithContainer, ) => { + if (isArrowElement(container)) { + return LinearElementEditor.getBoundTextElementPosition( + container, + boundTextElement, + ); + } const containerCoords = getContainerCoords(container); const maxContainerHeight = getMaxContainerHeight(container); const maxContainerWidth = getMaxContainerWidth(container); diff --git a/src/element/textWysiwyg.test.tsx b/src/element/textWysiwyg.test.tsx index cb852cd1c..c55a9befa 100644 --- a/src/element/textWysiwyg.test.tsx +++ b/src/element/textWysiwyg.test.tsx @@ -740,6 +740,45 @@ describe("textWysiwyg", () => { expect(rectangle.boundElements).toBe(null); }); + it("should bind text to container when triggered via context menu", async () => { + expect(h.elements.length).toBe(1); + expect(h.elements[0].id).toBe(rectangle.id); + + UI.clickTool("text"); + mouse.clickAt(20, 30); + const editor = document.querySelector( + ".excalidraw-textEditorContainer > textarea", + ) as HTMLTextAreaElement; + + fireEvent.change(editor, { + target: { + value: "Excalidraw is an opensource virtual collaborative whiteboard", + }, + }); + + editor.dispatchEvent(new Event("input")); + await new Promise((cb) => setTimeout(cb, 0)); + expect(h.elements.length).toBe(2); + expect(h.elements[1].type).toBe("text"); + + API.setSelectedElements([h.elements[0], h.elements[1]]); + fireEvent.contextMenu(GlobalTestState.canvas, { + button: 2, + clientX: 20, + clientY: 30, + }); + const contextMenu = document.querySelector(".context-menu"); + fireEvent.click( + queryByText(contextMenu as HTMLElement, "Bind text to the container")!, + ); + const text = h.elements[1] as ExcalidrawTextElementWithContainer; + expect(rectangle.boundElements).toStrictEqual([ + { id: h.elements[1].id, type: "text" }, + ]); + expect(text.containerId).toBe(rectangle.id); + expect(text.verticalAlign).toBe(VERTICAL_ALIGN.MIDDLE); + }); + it("should update font family correctly on undo/redo by selecting bounded text when font family was updated", async () => { expect(h.elements.length).toBe(1); diff --git a/src/tests/linearElementEditor.test.tsx b/src/tests/linearElementEditor.test.tsx index ac4d801bc..15fd105ec 100644 --- a/src/tests/linearElementEditor.test.tsx +++ b/src/tests/linearElementEditor.test.tsx @@ -23,7 +23,7 @@ import { getMaxContainerWidth, } from "../element/textElement"; import * as textElementUtils from "../element/textElement"; -import { ROUNDNESS } from "../constants"; +import { ROUNDNESS, VERTICAL_ALIGN } from "../constants"; const renderScene = jest.spyOn(Renderer, "renderScene"); @@ -1191,5 +1191,62 @@ describe("Test Linear Elements", () => { expect(queryByTestId(container, "align-horizontal-center")).toBeNull(); expect(queryByTestId(container, "align-right")).toBeNull(); }); + + it("should update label coords when a label binded via context menu is unbinded", async () => { + createTwoPointerLinearElement("arrow"); + const text = API.createElement({ + type: "text", + text: "Hello Excalidraw", + }); + expect(text.x).toBe(0); + expect(text.y).toBe(0); + + h.elements = [h.elements[0], text]; + + const container = h.elements[0]; + API.setSelectedElements([container, text]); + fireEvent.contextMenu(GlobalTestState.canvas, { + button: 2, + clientX: 20, + clientY: 30, + }); + let contextMenu = document.querySelector(".context-menu"); + + fireEvent.click( + queryByText(contextMenu as HTMLElement, "Bind text to the container")!, + ); + expect(container.boundElements).toStrictEqual([ + { id: h.elements[1].id, type: "text" }, + ]); + expect(text.containerId).toBe(container.id); + expect(text.verticalAlign).toBe(VERTICAL_ALIGN.MIDDLE); + + mouse.reset(); + mouse.clickAt( + container.x + container.width / 2, + container.y + container.height / 2, + ); + mouse.down(); + mouse.up(); + API.setSelectedElements([h.elements[0], h.elements[1]]); + + fireEvent.contextMenu(GlobalTestState.canvas, { + button: 2, + clientX: 20, + clientY: 30, + }); + contextMenu = document.querySelector(".context-menu"); + fireEvent.click(queryByText(contextMenu as HTMLElement, "Unbind text")!); + expect(container.boundElements).toEqual([]); + expect(text).toEqual( + expect.objectContaining({ + containerId: null, + width: 160, + height: 25, + x: -40, + y: 7.5, + }), + ); + }); }); }); From ca3be2c678dfc5fae50d005fdcfe3b8c84fc2544 Mon Sep 17 00:00:00 2001 From: Aakansha Doshi Date: Thu, 13 Apr 2023 17:19:46 +0530 Subject: [PATCH 55/74] fix: exporting labelled arrows via export utils (#6443) * fix: exporting labelled arrows via export utils * add comments * lint * update changelog * fix lint * initialize scene in the utils so it can be availabe in the helper functions * fix library rendering * add comments --- src/components/LibraryUnit.tsx | 10 +++--- src/packages/excalidraw/CHANGELOG.md | 4 +++ src/packages/utils.ts | 48 ++++++++++++++++++++++++---- 3 files changed, 50 insertions(+), 12 deletions(-) diff --git a/src/components/LibraryUnit.tsx b/src/components/LibraryUnit.tsx index 6723ac800..749877cdd 100644 --- a/src/components/LibraryUnit.tsx +++ b/src/components/LibraryUnit.tsx @@ -2,7 +2,7 @@ import clsx from "clsx"; import oc from "open-color"; import { useEffect, useRef, useState } from "react"; import { useDevice } from "../components/App"; -import { exportToSvg } from "../scene/export"; +import { exportToSvg } from "../packages/utils"; import { LibraryItem } from "../types"; import "./LibraryUnit.scss"; import { CheckboxItem } from "./CheckboxItem"; @@ -36,14 +36,14 @@ export const LibraryUnit = ({ if (!elements) { return; } - const svg = await exportToSvg( + const svg = await exportToSvg({ elements, - { + appState: { exportBackground: false, viewBackgroundColor: oc.white, }, - null, - ); + files: null, + }); svg.querySelector(".style-fonts")?.remove(); node.innerHTML = svg.outerHTML; })(); diff --git a/src/packages/excalidraw/CHANGELOG.md b/src/packages/excalidraw/CHANGELOG.md index 628177a4a..c36883b8e 100644 --- a/src/packages/excalidraw/CHANGELOG.md +++ b/src/packages/excalidraw/CHANGELOG.md @@ -33,6 +33,10 @@ For more details refer to the [docs](https://docs.excalidraw.com) - The optional parameter `refreshDimensions` in [`restoreElements`](https://docs.excalidraw.com/docs/@excalidraw/excalidraw/api/utils/restore#restoreelements) has been removed and can be enabled via `opts` +### Fixes + +- Exporting labelled arrows via export utils [#6443](https://github.com/excalidraw/excalidraw/pull/6443) + ## 0.14.2 (2023-02-01) ### Features diff --git a/src/packages/utils.ts b/src/packages/utils.ts index d81995080..5161e0cab 100644 --- a/src/packages/utils.ts +++ b/src/packages/utils.ts @@ -5,7 +5,6 @@ import { import { getDefaultAppState } from "../appState"; import { AppState, BinaryFiles } from "../types"; import { ExcalidrawElement, NonDeleted } from "../element/types"; -import { getNonDeletedElements } from "../element"; import { restore } from "../data/restore"; import { MIME_TYPES } from "../constants"; import { encodePngMetadata } from "../data/image"; @@ -15,6 +14,7 @@ import { copyTextToSystemClipboard, copyToClipboard, } from "../clipboard"; +import Scene from "../scene/Scene"; export { MIME_TYPES }; @@ -44,9 +44,17 @@ export const exportToCanvas = ({ null, null, ); + // The helper methods getContainerElement and getBoundTextElement are + // dependent on Scene which will not be available + // when these pure utils are called outside Excalidraw or even if called + // from inside Excalidraw when Scene isn't available eg when using library items from store, as a result the element cannot be extracted + // hence initailizing a new scene with the elements + // so its always available to helper methods + const scene = new Scene(); + scene.replaceAllElements(restoredElements); const { exportBackground, viewBackgroundColor } = restoredAppState; return _exportToCanvas( - getNonDeletedElements(restoredElements), + scene.getNonDeletedElements(), { ...restoredAppState, offsetTop: 0, offsetLeft: 0, width: 0, height: 0 }, files || {}, { exportBackground, exportPadding, viewBackgroundColor }, @@ -114,8 +122,18 @@ export const exportToBlob = async ( }; } - const canvas = await exportToCanvas(opts); - + // The helper methods getContainerElement and getBoundTextElement are + // dependent on Scene which will not be available + // when these pure utils are called outside Excalidraw or even if called + // from inside Excalidraw when Scene isn't available eg when using library items from store, as a result the element cannot be extracted + // hence initailizing a new scene with the elements + // so its always available to helper methods + const scene = new Scene(); + scene.replaceAllElements(opts.elements); + const canvas = await exportToCanvas({ + ...opts, + elements: scene.getNonDeletedElements(), + }); quality = quality ? quality : /image\/jpe?g/.test(mimeType) ? 0.92 : 0.8; return new Promise((resolve, reject) => { @@ -132,7 +150,7 @@ export const exportToBlob = async ( blob = await encodePngMetadata({ blob, metadata: serializeAsJSON( - opts.elements, + scene.getNonDeletedElements(), opts.appState, opts.files || {}, "local", @@ -160,8 +178,16 @@ export const exportToSvg = async ({ null, null, ); + // The helper methods getContainerElement and getBoundTextElement are + // dependent on Scene which will not be available + // when these pure utils are called outside Excalidraw or even if called + // from inside Excalidraw when Scene isn't available eg when using library items from store, as a result the element cannot be extracted + // hence initailizing a new scene with the elements + // so its always available to helper methods + const scene = new Scene(); + scene.replaceAllElements(restoredElements); return _exportToSvg( - getNonDeletedElements(restoredElements), + scene.getNonDeletedElements(), { ...restoredAppState, exportPadding, @@ -177,6 +203,14 @@ export const exportToClipboard = async ( type: "png" | "svg" | "json"; }, ) => { + // The helper methods getContainerElement and getBoundTextElement are + // dependent on Scene which will not be available + // when these pure utils are called outside Excalidraw or even if called + // from inside Excalidraw when Scene isn't available eg when using library items from store, as a result the element cannot be extracted + // hence initailizing a new scene with the elements + // so its always available to helper methods + const scene = new Scene(); + scene.replaceAllElements(opts.elements); if (opts.type === "svg") { const svg = await exportToSvg(opts); await copyTextToSystemClipboard(svg.outerHTML); @@ -191,7 +225,7 @@ export const exportToClipboard = async ( ...getDefaultAppState(), ...opts.appState, }; - await copyToClipboard(opts.elements, appState, opts.files); + await copyToClipboard(scene.getNonDeletedElements(), appState, opts.files); } else { throw new Error("Invalid export type"); } From 6164b5273c39de875e136aa808f1e8a7ad62ec92 Mon Sep 17 00:00:00 2001 From: Bear <68773271+SnorfYang@users.noreply.github.com> Date: Fri, 14 Apr 2023 21:22:39 +0800 Subject: [PATCH 56/74] fix: center align text when bind to container via context menu (#6451) --- src/actions/actionBoundText.tsx | 8 +++++++- src/element/textWysiwyg.test.tsx | 7 +++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/src/actions/actionBoundText.tsx b/src/actions/actionBoundText.tsx index 990f98d41..3f240b5d8 100644 --- a/src/actions/actionBoundText.tsx +++ b/src/actions/actionBoundText.tsx @@ -1,4 +1,9 @@ -import { BOUND_TEXT_PADDING, ROUNDNESS, VERTICAL_ALIGN } from "../constants"; +import { + BOUND_TEXT_PADDING, + ROUNDNESS, + VERTICAL_ALIGN, + TEXT_ALIGN, +} from "../constants"; import { getNonDeletedElements, isTextElement, newElement } from "../element"; import { mutateElement } from "../element/mutateElement"; import { @@ -132,6 +137,7 @@ export const actionBindText = register({ mutateElement(textElement, { containerId: container.id, verticalAlign: VERTICAL_ALIGN.MIDDLE, + textAlign: TEXT_ALIGN.CENTER, }); mutateElement(container, { boundElements: (container.boundElements || []).concat({ diff --git a/src/element/textWysiwyg.test.tsx b/src/element/textWysiwyg.test.tsx index c55a9befa..4ae3f26f9 100644 --- a/src/element/textWysiwyg.test.tsx +++ b/src/element/textWysiwyg.test.tsx @@ -777,6 +777,13 @@ describe("textWysiwyg", () => { ]); expect(text.containerId).toBe(rectangle.id); expect(text.verticalAlign).toBe(VERTICAL_ALIGN.MIDDLE); + expect(text.textAlign).toBe(TEXT_ALIGN.CENTER); + expect(text.x).toBe( + h.elements[0].x + h.elements[0].width / 2 - text.width / 2, + ); + expect(text.y).toBe( + h.elements[0].y + h.elements[0].height / 2 - text.height / 2, + ); }); it("should update font family correctly on undo/redo by selecting bounded text when font family was updated", async () => { From b0b23353cfbb2def4bcceabdfbd2f0f3cf6d7f51 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lie?= <110197391+fouqueta@users.noreply.github.com> Date: Fri, 14 Apr 2023 23:34:26 +0200 Subject: [PATCH 57/74] fix: split "Edit selected shape" shortcut (#6457) Co-authored-by: David Luzar --- src/components/HelpDialog.tsx | 11 ++++++----- src/locales/en.json | 3 ++- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/components/HelpDialog.tsx b/src/components/HelpDialog.tsx index 8cb775fc5..bd2f38417 100644 --- a/src/components/HelpDialog.tsx +++ b/src/components/HelpDialog.tsx @@ -165,11 +165,12 @@ export const HelpDialog = ({ onClose }: { onClose?: () => void }) => { shortcuts={[KEYS.E, KEYS["0"]]} /> + Date: Sun, 16 Apr 2023 11:56:25 +0200 Subject: [PATCH 58/74] fix: utils leaking Scene state (#6461 * fix: utils leaking Scene state * remove debug * doc * add tests for group duplicating * tweaks --- src/element/newElement.test.ts | 403 ++++++++++++++++++---- src/element/newElement.ts | 162 ++++++++- src/packages/utils.ts | 81 +++-- src/scene/export.ts | 7 +- src/tests/packages/utils.unmocked.test.ts | 67 ++++ 5 files changed, 604 insertions(+), 116 deletions(-) create mode 100644 src/tests/packages/utils.unmocked.test.ts diff --git a/src/element/newElement.test.ts b/src/element/newElement.test.ts index 991c034e0..ba7c63ee2 100644 --- a/src/element/newElement.test.ts +++ b/src/element/newElement.test.ts @@ -1,8 +1,9 @@ -import { duplicateElement } from "./newElement"; +import { duplicateElement, duplicateElements } from "./newElement"; import { mutateElement } from "./mutateElement"; import { API } from "../tests/helpers/api"; import { FONT_FAMILY, ROUNDNESS } from "../constants"; import { isPrimitive } from "../utils"; +import { ExcalidrawLinearElement } from "./types"; const assertCloneObjects = (source: any, clone: any) => { for (const key in clone) { @@ -15,79 +16,353 @@ const assertCloneObjects = (source: any, clone: any) => { } }; -it("clones arrow element", () => { - const element = API.createElement({ - type: "arrow", - x: 0, - y: 0, - strokeColor: "#000000", - backgroundColor: "transparent", - fillStyle: "hachure", - strokeWidth: 1, - strokeStyle: "solid", - roundness: { type: ROUNDNESS.PROPORTIONAL_RADIUS }, - roughness: 1, - opacity: 100, +describe("duplicating single elements", () => { + it("clones arrow element", () => { + const element = API.createElement({ + type: "arrow", + x: 0, + y: 0, + strokeColor: "#000000", + backgroundColor: "transparent", + fillStyle: "hachure", + strokeWidth: 1, + strokeStyle: "solid", + roundness: { type: ROUNDNESS.PROPORTIONAL_RADIUS }, + roughness: 1, + opacity: 100, + }); + + // @ts-ignore + element.__proto__ = { hello: "world" }; + + mutateElement(element, { + points: [ + [1, 2], + [3, 4], + ], + }); + + const copy = duplicateElement(null, new Map(), element); + + assertCloneObjects(element, copy); + + // assert we clone the object's prototype + // @ts-ignore + expect(copy.__proto__).toEqual({ hello: "world" }); + expect(copy.hasOwnProperty("hello")).toBe(false); + + expect(copy.points).not.toBe(element.points); + expect(copy).not.toHaveProperty("shape"); + expect(copy.id).not.toBe(element.id); + expect(typeof copy.id).toBe("string"); + expect(copy.seed).not.toBe(element.seed); + expect(typeof copy.seed).toBe("number"); + expect(copy).toEqual({ + ...element, + id: copy.id, + seed: copy.seed, + }); }); - // @ts-ignore - element.__proto__ = { hello: "world" }; + it("clones text element", () => { + const element = API.createElement({ + type: "text", + x: 0, + y: 0, + strokeColor: "#000000", + backgroundColor: "transparent", + fillStyle: "hachure", + strokeWidth: 1, + strokeStyle: "solid", + roundness: null, + roughness: 1, + opacity: 100, + text: "hello", + fontSize: 20, + fontFamily: FONT_FAMILY.Virgil, + textAlign: "left", + verticalAlign: "top", + }); - mutateElement(element, { - points: [ - [1, 2], - [3, 4], - ], - }); + const copy = duplicateElement(null, new Map(), element); - const copy = duplicateElement(null, new Map(), element); + assertCloneObjects(element, copy); - assertCloneObjects(element, copy); - - // @ts-ignore - expect(copy.__proto__).toEqual({ hello: "world" }); - expect(copy.hasOwnProperty("hello")).toBe(false); - - expect(copy.points).not.toBe(element.points); - expect(copy).not.toHaveProperty("shape"); - expect(copy.id).not.toBe(element.id); - expect(typeof copy.id).toBe("string"); - expect(copy.seed).not.toBe(element.seed); - expect(typeof copy.seed).toBe("number"); - expect(copy).toEqual({ - ...element, - id: copy.id, - seed: copy.seed, + expect(copy).not.toHaveProperty("points"); + expect(copy).not.toHaveProperty("shape"); + expect(copy.id).not.toBe(element.id); + expect(typeof copy.id).toBe("string"); + expect(typeof copy.seed).toBe("number"); }); }); -it("clones text element", () => { - const element = API.createElement({ - type: "text", - x: 0, - y: 0, - strokeColor: "#000000", - backgroundColor: "transparent", - fillStyle: "hachure", - strokeWidth: 1, - strokeStyle: "solid", - roundness: null, - roughness: 1, - opacity: 100, - text: "hello", - fontSize: 20, - fontFamily: FONT_FAMILY.Virgil, - textAlign: "left", - verticalAlign: "top", +describe("duplicating multiple elements", () => { + it("duplicateElements should clone bindings", () => { + const rectangle1 = API.createElement({ + type: "rectangle", + id: "rectangle1", + boundElements: [ + { id: "arrow1", type: "arrow" }, + { id: "arrow2", type: "arrow" }, + { id: "text1", type: "text" }, + ], + }); + + const text1 = API.createElement({ + type: "text", + id: "text1", + containerId: "rectangle1", + }); + + const arrow1 = API.createElement({ + type: "arrow", + id: "arrow1", + startBinding: { + elementId: "rectangle1", + focus: 0.2, + gap: 7, + }, + }); + + const arrow2 = API.createElement({ + type: "arrow", + id: "arrow2", + endBinding: { + elementId: "rectangle1", + focus: 0.2, + gap: 7, + }, + boundElements: [{ id: "text2", type: "text" }], + }); + + const text2 = API.createElement({ + type: "text", + id: "text2", + containerId: "arrow2", + }); + + // ------------------------------------------------------------------------- + + const origElements = [rectangle1, text1, arrow1, arrow2, text2] as const; + const clonedElements = duplicateElements(origElements); + + // generic id in-equality checks + // -------------------------------------------------------------------------- + expect(origElements.map((e) => e.type)).toEqual( + clonedElements.map((e) => e.type), + ); + origElements.forEach((origElement, idx) => { + const clonedElement = clonedElements[idx]; + expect(origElement).toEqual( + expect.objectContaining({ + id: expect.not.stringMatching(clonedElement.id), + type: clonedElement.type, + }), + ); + if ("containerId" in origElement) { + expect(origElement.containerId).not.toBe( + (clonedElement as any).containerId, + ); + } + if ("endBinding" in origElement) { + if (origElement.endBinding) { + expect(origElement.endBinding.elementId).not.toBe( + (clonedElement as any).endBinding?.elementId, + ); + } else { + expect((clonedElement as any).endBinding).toBeNull(); + } + } + if ("startBinding" in origElement) { + if (origElement.startBinding) { + expect(origElement.startBinding.elementId).not.toBe( + (clonedElement as any).startBinding?.elementId, + ); + } else { + expect((clonedElement as any).startBinding).toBeNull(); + } + } + }); + // -------------------------------------------------------------------------- + + const clonedArrows = clonedElements.filter( + (e) => e.type === "arrow", + ) as ExcalidrawLinearElement[]; + + const [clonedRectangle, clonedText1, , clonedArrow2, clonedArrowLabel] = + clonedElements as any as typeof origElements; + + expect(clonedText1.containerId).toBe(clonedRectangle.id); + expect( + clonedRectangle.boundElements!.find((e) => e.id === clonedText1.id), + ).toEqual( + expect.objectContaining({ + id: clonedText1.id, + type: clonedText1.type, + }), + ); + + clonedArrows.forEach((arrow) => { + // console.log(arrow); + expect( + clonedRectangle.boundElements!.find((e) => e.id === arrow.id), + ).toEqual( + expect.objectContaining({ + id: arrow.id, + type: arrow.type, + }), + ); + + if (arrow.endBinding) { + expect(arrow.endBinding.elementId).toBe(clonedRectangle.id); + } + if (arrow.startBinding) { + expect(arrow.startBinding.elementId).toBe(clonedRectangle.id); + } + }); + + expect(clonedArrow2.boundElements).toEqual([ + { type: "text", id: clonedArrowLabel.id }, + ]); + expect(clonedArrowLabel.containerId).toBe(clonedArrow2.id); }); - const copy = duplicateElement(null, new Map(), element); + it("should remove id references of elements that aren't found", () => { + const rectangle1 = API.createElement({ + type: "rectangle", + id: "rectangle1", + boundElements: [ + // should keep + { id: "arrow1", type: "arrow" }, + // should drop + { id: "arrow-not-exists", type: "arrow" }, + // should drop + { id: "text-not-exists", type: "text" }, + ], + }); - assertCloneObjects(element, copy); + const arrow1 = API.createElement({ + type: "arrow", + id: "arrow1", + startBinding: { + elementId: "rectangle1", + focus: 0.2, + gap: 7, + }, + }); - expect(copy).not.toHaveProperty("points"); - expect(copy).not.toHaveProperty("shape"); - expect(copy.id).not.toBe(element.id); - expect(typeof copy.id).toBe("string"); - expect(typeof copy.seed).toBe("number"); + const text1 = API.createElement({ + type: "text", + id: "text1", + containerId: "rectangle-not-exists", + }); + + const arrow2 = API.createElement({ + type: "arrow", + id: "arrow2", + startBinding: { + elementId: "rectangle1", + focus: 0.2, + gap: 7, + }, + endBinding: { + elementId: "rectangle-not-exists", + focus: 0.2, + gap: 7, + }, + }); + + const arrow3 = API.createElement({ + type: "arrow", + id: "arrow2", + startBinding: { + elementId: "rectangle-not-exists", + focus: 0.2, + gap: 7, + }, + endBinding: { + elementId: "rectangle1", + focus: 0.2, + gap: 7, + }, + }); + + // ------------------------------------------------------------------------- + + const origElements = [rectangle1, text1, arrow1, arrow2, arrow3] as const; + const clonedElements = duplicateElements( + origElements, + ) as any as typeof origElements; + const [ + clonedRectangle, + clonedText1, + clonedArrow1, + clonedArrow2, + clonedArrow3, + ] = clonedElements; + + expect(clonedRectangle.boundElements).toEqual([ + { id: clonedArrow1.id, type: "arrow" }, + ]); + + expect(clonedText1.containerId).toBe(null); + + expect(clonedArrow2.startBinding).toEqual({ + ...arrow2.startBinding, + elementId: clonedRectangle.id, + }); + expect(clonedArrow2.endBinding).toBe(null); + + expect(clonedArrow3.startBinding).toBe(null); + expect(clonedArrow3.endBinding).toEqual({ + ...arrow3.endBinding, + elementId: clonedRectangle.id, + }); + }); + + describe("should duplicate all group ids", () => { + it("should regenerate all group ids and keep them consistent across elements", () => { + const rectangle1 = API.createElement({ + type: "rectangle", + groupIds: ["g1"], + }); + const rectangle2 = API.createElement({ + type: "rectangle", + groupIds: ["g2", "g1"], + }); + const rectangle3 = API.createElement({ + type: "rectangle", + groupIds: ["g2", "g1"], + }); + + const origElements = [rectangle1, rectangle2, rectangle3] as const; + const clonedElements = duplicateElements( + origElements, + ) as any as typeof origElements; + const [clonedRectangle1, clonedRectangle2, clonedRectangle3] = + clonedElements; + + expect(rectangle1.groupIds[0]).not.toBe(clonedRectangle1.groupIds[0]); + expect(rectangle2.groupIds[0]).not.toBe(clonedRectangle2.groupIds[0]); + expect(rectangle2.groupIds[1]).not.toBe(clonedRectangle2.groupIds[1]); + + expect(clonedRectangle1.groupIds[0]).toBe(clonedRectangle2.groupIds[1]); + expect(clonedRectangle2.groupIds[0]).toBe(clonedRectangle3.groupIds[0]); + expect(clonedRectangle2.groupIds[1]).toBe(clonedRectangle3.groupIds[1]); + }); + + it("should keep and regenerate ids of groups even if invalid", () => { + // lone element shouldn't be able to be grouped with itself, + // but hard to check against in a performant way so we ignore it + const rectangle1 = API.createElement({ + type: "rectangle", + groupIds: ["g1"], + }); + + const [clonedRectangle1] = duplicateElements([rectangle1]); + + expect(typeof clonedRectangle1.groupIds[0]).toBe("string"); + expect(rectangle1.groupIds[0]).not.toBe(clonedRectangle1.groupIds[0]); + }); + }); }); diff --git a/src/element/newElement.ts b/src/element/newElement.ts index b6b6cad2d..72aa54684 100644 --- a/src/element/newElement.ts +++ b/src/element/newElement.ts @@ -13,7 +13,12 @@ import { FontFamilyValues, ExcalidrawTextContainer, } from "../element/types"; -import { getFontString, getUpdatedTimestamp, isTestEnv } from "../utils"; +import { + arrayToMap, + getFontString, + getUpdatedTimestamp, + isTestEnv, +} from "../utils"; import { randomInteger, randomId } from "../random"; import { mutateElement, newElementWith } from "./mutateElement"; import { getNewGroupIdsForDuplication } from "../groups"; @@ -357,16 +362,24 @@ export const newImageElement = ( }; }; -// Simplified deep clone for the purpose of cloning ExcalidrawElement only -// (doesn't clone Date, RegExp, Map, Set, Typed arrays etc.) +// Simplified deep clone for the purpose of cloning ExcalidrawElement. +// +// Only clones plain objects and arrays. Doesn't clone Date, RegExp, Map, Set, +// Typed arrays and other non-null objects. // // Adapted from https://github.com/lukeed/klona -export const deepCopyElement = (val: any, depth: number = 0) => { +// +// The reason for `deepCopyElement()` wrapper is type safety (only allow +// passing ExcalidrawElement as the top-level argument). +const _deepCopyElement = (val: any, depth: number = 0) => { + // only clone non-primitives if (val == null || typeof val !== "object") { return val; } - if (Object.prototype.toString.call(val) === "[object Object]") { + const objectType = Object.prototype.toString.call(val); + + if (objectType === "[object Object]") { const tmp = typeof val.constructor === "function" ? Object.create(Object.getPrototypeOf(val)) @@ -378,7 +391,7 @@ export const deepCopyElement = (val: any, depth: number = 0) => { if (depth === 0 && (key === "shape" || key === "canvas")) { continue; } - tmp[key] = deepCopyElement(val[key], depth + 1); + tmp[key] = _deepCopyElement(val[key], depth + 1); } } return tmp; @@ -388,14 +401,44 @@ export const deepCopyElement = (val: any, depth: number = 0) => { let k = val.length; const arr = new Array(k); while (k--) { - arr[k] = deepCopyElement(val[k], depth + 1); + arr[k] = _deepCopyElement(val[k], depth + 1); } return arr; } + // we're not cloning non-array & non-plain-object objects because we + // don't support them on excalidraw elements yet. If we do, we need to make + // sure we start cloning them, so let's warn about it. + if (process.env.NODE_ENV === "development") { + if ( + objectType !== "[object Object]" && + objectType !== "[object Array]" && + objectType.startsWith("[object ") + ) { + console.warn( + `_deepCloneElement: unexpected object type ${objectType}. This value will not be cloned!`, + ); + } + } + return val; }; +/** + * Clones ExcalidrawElement data structure. Does not regenerate id, nonce, or + * any value. The purpose is to to break object references for immutability + * reasons, whenever we want to keep the original element, but ensure it's not + * mutated. + * + * Only clones plain objects and arrays. Doesn't clone Date, RegExp, Map, Set, + * Typed arrays and other non-null objects. + */ +export const deepCopyElement = ( + val: T, +): Mutable => { + return _deepCopyElement(val); +}; + /** * Duplicate an element, often used in the alt-drag operation. * Note that this method has gotten a bit complicated since the @@ -410,13 +453,13 @@ export const deepCopyElement = (val: any, depth: number = 0) => { * @param element Element to duplicate * @param overrides Any element properties to override */ -export const duplicateElement = >( +export const duplicateElement = ( editingGroupId: AppState["editingGroupId"], groupIdMapForOperation: Map, element: TElement, overrides?: Partial, -): TElement => { - let copy: TElement = deepCopyElement(element); +): Readonly => { + let copy = deepCopyElement(element); if (isTestEnv()) { copy.id = `${copy.id}_copy`; @@ -449,3 +492,102 @@ export const duplicateElement = >( } return copy; }; + +/** + * Clones elements, regenerating their ids (including bindings) and group ids. + * + * If bindings don't exist in the elements array, they are removed. Therefore, + * it's advised to supply the whole elements array, or sets of elements that + * are encapsulated (such as library items), if the purpose is to retain + * bindings to the cloned elements intact. + */ +export const duplicateElements = (elements: readonly ExcalidrawElement[]) => { + const clonedElements: ExcalidrawElement[] = []; + + const origElementsMap = arrayToMap(elements); + + // used for for migrating old ids to new ids + const elementNewIdsMap = new Map< + /* orig */ ExcalidrawElement["id"], + /* new */ ExcalidrawElement["id"] + >(); + + const maybeGetNewId = (id: ExcalidrawElement["id"]) => { + // if we've already migrated the element id, return the new one directly + if (elementNewIdsMap.has(id)) { + return elementNewIdsMap.get(id)!; + } + // if we haven't migrated the element id, but an old element with the same + // id exists, generate a new id for it and return it + if (origElementsMap.has(id)) { + const newId = randomId(); + elementNewIdsMap.set(id, newId); + return newId; + } + // if old element doesn't exist, return null to mark it for removal + return null; + }; + + const groupNewIdsMap = new Map(); + + for (const element of elements) { + const clonedElement: Mutable = _deepCopyElement(element); + + clonedElement.id = maybeGetNewId(element.id)!; + + if (clonedElement.groupIds) { + clonedElement.groupIds = clonedElement.groupIds.map((groupId) => { + if (!groupNewIdsMap.has(groupId)) { + groupNewIdsMap.set(groupId, randomId()); + } + return groupNewIdsMap.get(groupId)!; + }); + } + + if ("containerId" in clonedElement && clonedElement.containerId) { + const newContainerId = maybeGetNewId(clonedElement.containerId); + clonedElement.containerId = newContainerId; + } + + if ("boundElements" in clonedElement && clonedElement.boundElements) { + clonedElement.boundElements = clonedElement.boundElements.reduce( + ( + acc: Mutable>, + binding, + ) => { + const newBindingId = maybeGetNewId(binding.id); + if (newBindingId) { + acc.push({ ...binding, id: newBindingId }); + } + return acc; + }, + [], + ); + } + + if ("endBinding" in clonedElement && clonedElement.endBinding) { + const newEndBindingId = maybeGetNewId(clonedElement.endBinding.elementId); + clonedElement.endBinding = newEndBindingId + ? { + ...clonedElement.endBinding, + elementId: newEndBindingId, + } + : null; + } + if ("startBinding" in clonedElement && clonedElement.startBinding) { + const newEndBindingId = maybeGetNewId( + clonedElement.startBinding.elementId, + ); + clonedElement.startBinding = newEndBindingId + ? { + ...clonedElement.startBinding, + elementId: newEndBindingId, + } + : null; + } + + clonedElements.push(clonedElement); + } + + return clonedElements; +}; diff --git a/src/packages/utils.ts b/src/packages/utils.ts index 5161e0cab..1fb6cd3d9 100644 --- a/src/packages/utils.ts +++ b/src/packages/utils.ts @@ -15,6 +15,23 @@ import { copyToClipboard, } from "../clipboard"; import Scene from "../scene/Scene"; +import { duplicateElements } from "../element/newElement"; + +// getContainerElement and getBoundTextElement and potentially other helpers +// depend on `Scene` which will not be available when these pure utils are +// called outside initialized Excalidraw editor instance or even if called +// from inside Excalidraw if the elements were never cached by Scene (e.g. +// for library elements). +// +// As such, before passing the elements down, we need to initialize a custom +// Scene instance and assign them to it. +// +// FIXME This is a super hacky workaround and we'll need to rewrite this soon. +const passElementsSafely = (elements: readonly ExcalidrawElement[]) => { + const scene = new Scene(); + scene.replaceAllElements(duplicateElements(elements)); + return scene.getNonDeletedElements(); +}; export { MIME_TYPES }; @@ -44,17 +61,9 @@ export const exportToCanvas = ({ null, null, ); - // The helper methods getContainerElement and getBoundTextElement are - // dependent on Scene which will not be available - // when these pure utils are called outside Excalidraw or even if called - // from inside Excalidraw when Scene isn't available eg when using library items from store, as a result the element cannot be extracted - // hence initailizing a new scene with the elements - // so its always available to helper methods - const scene = new Scene(); - scene.replaceAllElements(restoredElements); const { exportBackground, viewBackgroundColor } = restoredAppState; return _exportToCanvas( - scene.getNonDeletedElements(), + passElementsSafely(restoredElements), { ...restoredAppState, offsetTop: 0, offsetLeft: 0, width: 0, height: 0 }, files || {}, { exportBackground, exportPadding, viewBackgroundColor }, @@ -122,17 +131,9 @@ export const exportToBlob = async ( }; } - // The helper methods getContainerElement and getBoundTextElement are - // dependent on Scene which will not be available - // when these pure utils are called outside Excalidraw or even if called - // from inside Excalidraw when Scene isn't available eg when using library items from store, as a result the element cannot be extracted - // hence initailizing a new scene with the elements - // so its always available to helper methods - const scene = new Scene(); - scene.replaceAllElements(opts.elements); const canvas = await exportToCanvas({ ...opts, - elements: scene.getNonDeletedElements(), + elements: passElementsSafely(opts.elements), }); quality = quality ? quality : /image\/jpe?g/.test(mimeType) ? 0.92 : 0.8; @@ -150,7 +151,10 @@ export const exportToBlob = async ( blob = await encodePngMetadata({ blob, metadata: serializeAsJSON( - scene.getNonDeletedElements(), + // NOTE as long as we're using the Scene hack, we need to ensure + // we pass the original, uncloned elements when serializing + // so that we keep ids stable + opts.elements, opts.appState, opts.files || {}, "local", @@ -178,21 +182,24 @@ export const exportToSvg = async ({ null, null, ); - // The helper methods getContainerElement and getBoundTextElement are - // dependent on Scene which will not be available - // when these pure utils are called outside Excalidraw or even if called - // from inside Excalidraw when Scene isn't available eg when using library items from store, as a result the element cannot be extracted - // hence initailizing a new scene with the elements - // so its always available to helper methods - const scene = new Scene(); - scene.replaceAllElements(restoredElements); + + const exportAppState = { + ...restoredAppState, + exportPadding, + }; + return _exportToSvg( - scene.getNonDeletedElements(), - { - ...restoredAppState, - exportPadding, - }, + passElementsSafely(restoredElements), + exportAppState, files, + { + // NOTE as long as we're using the Scene hack, we need to ensure + // we pass the original, uncloned elements when serializing + // so that we keep ids stable. Hence adding the serializeAsJSON helper + // support into the downstream exportToSvg function. + serializeAsJSON: () => + serializeAsJSON(restoredElements, exportAppState, files || {}, "local"), + }, ); }; @@ -203,14 +210,6 @@ export const exportToClipboard = async ( type: "png" | "svg" | "json"; }, ) => { - // The helper methods getContainerElement and getBoundTextElement are - // dependent on Scene which will not be available - // when these pure utils are called outside Excalidraw or even if called - // from inside Excalidraw when Scene isn't available eg when using library items from store, as a result the element cannot be extracted - // hence initailizing a new scene with the elements - // so its always available to helper methods - const scene = new Scene(); - scene.replaceAllElements(opts.elements); if (opts.type === "svg") { const svg = await exportToSvg(opts); await copyTextToSystemClipboard(svg.outerHTML); @@ -225,7 +224,7 @@ export const exportToClipboard = async ( ...getDefaultAppState(), ...opts.appState, }; - await copyToClipboard(scene.getNonDeletedElements(), appState, opts.files); + await copyToClipboard(opts.elements, appState, opts.files); } else { throw new Error("Invalid export type"); } diff --git a/src/scene/export.ts b/src/scene/export.ts index a6e4297ad..6c5712be6 100644 --- a/src/scene/export.ts +++ b/src/scene/export.ts @@ -90,6 +90,9 @@ export const exportToSvg = async ( exportEmbedScene?: boolean; }, files: BinaryFiles | null, + opts?: { + serializeAsJSON?: () => string; + }, ): Promise => { const { exportPadding = DEFAULT_EXPORT_PADDING, @@ -103,7 +106,9 @@ export const exportToSvg = async ( metadata = await ( await import(/* webpackChunkName: "image" */ "../../src/data/image") ).encodeSvgMetadata({ - text: serializeAsJSON(elements, appState, files || {}, "local"), + text: opts?.serializeAsJSON + ? opts?.serializeAsJSON?.() + : serializeAsJSON(elements, appState, files || {}, "local"), }); } catch (error: any) { console.error(error); diff --git a/src/tests/packages/utils.unmocked.test.ts b/src/tests/packages/utils.unmocked.test.ts new file mode 100644 index 000000000..28db08c49 --- /dev/null +++ b/src/tests/packages/utils.unmocked.test.ts @@ -0,0 +1,67 @@ +import { decodePngMetadata, decodeSvgMetadata } from "../../data/image"; +import { ImportedDataState } from "../../data/types"; +import * as utils from "../../packages/utils"; +import { API } from "../helpers/api"; + +// NOTE this test file is using the actual API, unmocked. Hence splitting it +// from the other test file, because I couldn't figure out how to test +// mocked and unmocked API in the same file. + +describe("embedding scene data", () => { + describe("exportToSvg", () => { + it("embedding scene data shouldn't modify them", async () => { + const rectangle = API.createElement({ type: "rectangle" }); + const ellipse = API.createElement({ type: "ellipse" }); + + const sourceElements = [rectangle, ellipse]; + + const svgNode = await utils.exportToSvg({ + elements: sourceElements, + appState: { + viewBackgroundColor: "#ffffff", + gridSize: null, + exportEmbedScene: true, + }, + files: null, + }); + + const svg = svgNode.outerHTML; + + const parsedString = await decodeSvgMetadata({ svg }); + const importedData: ImportedDataState = JSON.parse(parsedString); + + expect(sourceElements.map((x) => x.id)).toEqual( + importedData.elements?.map((el) => el.id), + ); + }); + }); + + // skipped because we can't test png encoding right now + // (canvas.toBlob not supported in jsdom) + describe.skip("exportToBlob", () => { + it("embedding scene data shouldn't modify them", async () => { + const rectangle = API.createElement({ type: "rectangle" }); + const ellipse = API.createElement({ type: "ellipse" }); + + const sourceElements = [rectangle, ellipse]; + + const blob = await utils.exportToBlob({ + mimeType: "image/png", + elements: sourceElements, + appState: { + viewBackgroundColor: "#ffffff", + gridSize: null, + exportEmbedScene: true, + }, + files: null, + }); + + const parsedString = await decodePngMetadata(blob); + const importedData: ImportedDataState = JSON.parse(parsedString); + + expect(sourceElements.map((x) => x.id)).toEqual( + importedData.elements?.map((el) => el.id), + ); + }); + }); +}); From e31230f78cc40ff40ef4b4add54131e7c427e655 Mon Sep 17 00:00:00 2001 From: David Luzar Date: Sun, 16 Apr 2023 11:57:13 +0200 Subject: [PATCH 59/74] refactor: inline `SingleLibraryItem` into `PublishLibrary` (#6462 refactor: inline `SingleLibraryItem` into `PublishLibrary` to reduce api surface area --- src/components/PublishLibrary.scss | 76 +++++++++++++++++++ src/components/PublishLibrary.tsx | 104 ++++++++++++++++++++++++-- src/components/SingleLibraryItem.scss | 79 ------------------- src/components/SingleLibraryItem.tsx | 104 -------------------------- 4 files changed, 175 insertions(+), 188 deletions(-) delete mode 100644 src/components/SingleLibraryItem.scss delete mode 100644 src/components/SingleLibraryItem.tsx diff --git a/src/components/PublishLibrary.scss b/src/components/PublishLibrary.scss index 6040ff2f4..fd7db0fe4 100644 --- a/src/components/PublishLibrary.scss +++ b/src/components/PublishLibrary.scss @@ -93,4 +93,80 @@ display: block; } } + + .single-library-item { + position: relative; + + &-status { + position: absolute; + top: 0.3rem; + left: 0.3rem; + font-size: 0.7rem; + color: $oc-red-7; + background: rgba(255, 255, 255, 0.9); + padding: 0.1rem 0.2rem; + border-radius: 0.2rem; + } + + &__svg { + background-color: $oc-white; + padding: 0.3rem; + width: 7.5rem; + height: 7.5rem; + border: 1px solid var(--button-gray-2); + svg { + width: 100%; + height: 100%; + } + } + + .ToolIcon__icon { + background-color: $oc-white; + width: auto; + height: auto; + margin: 0 0.5rem; + } + .ToolIcon, + .ToolIcon_type_button:hover { + background-color: white; + } + .required, + .error { + color: $oc-red-8; + font-weight: bold; + font-size: 1rem; + margin: 0.2rem; + } + .error { + font-weight: 500; + margin: 0; + padding: 0.3em 0; + } + + &--remove { + position: absolute; + top: 0.2rem; + right: 1rem; + + .ToolIcon__icon { + margin: 0; + } + .ToolIcon__icon { + background-color: $oc-red-6; + &:hover { + background-color: $oc-red-7; + } + &:active { + background-color: $oc-red-8; + } + } + svg { + color: $oc-white; + padding: 0.26rem; + border-radius: 0.3em; + width: 1rem; + height: 1rem; + } + } + } } diff --git a/src/components/PublishLibrary.tsx b/src/components/PublishLibrary.tsx index 0d060dfaa..b4233222d 100644 --- a/src/components/PublishLibrary.tsx +++ b/src/components/PublishLibrary.tsx @@ -1,11 +1,11 @@ -import { ReactNode, useCallback, useEffect, useState } from "react"; +import { ReactNode, useCallback, useEffect, useRef, useState } from "react"; import OpenColor from "open-color"; import { Dialog } from "./Dialog"; import { t } from "../i18n"; import { AppState, LibraryItems, LibraryItem } from "../types"; -import { exportToCanvas } from "../packages/utils"; +import { exportToCanvas, exportToSvg } from "../packages/utils"; import { EXPORT_DATA_TYPES, EXPORT_SOURCE, @@ -13,12 +13,13 @@ import { VERSIONS, } from "../constants"; import { ExportedLibraryData } from "../data/types"; - -import "./PublishLibrary.scss"; -import SingleLibraryItem from "./SingleLibraryItem"; import { canvasToBlob, resizeImageFile } from "../data/blob"; import { chunk } from "../utils"; import DialogActionButton from "./DialogActionButton"; +import { CloseIcon } from "./icons"; +import { ToolButton } from "./ToolButton"; + +import "./PublishLibrary.scss"; interface PublishLibraryDataParams { authorName: string; @@ -126,6 +127,99 @@ const generatePreviewImage = async (libraryItems: LibraryItems) => { ); }; +const SingleLibraryItem = ({ + libItem, + appState, + index, + onChange, + onRemove, +}: { + libItem: LibraryItem; + appState: AppState; + index: number; + onChange: (val: string, index: number) => void; + onRemove: (id: string) => void; +}) => { + const svgRef = useRef(null); + const inputRef = useRef(null); + + useEffect(() => { + const node = svgRef.current; + if (!node) { + return; + } + (async () => { + const svg = await exportToSvg({ + elements: libItem.elements, + appState: { + ...appState, + viewBackgroundColor: OpenColor.white, + exportBackground: true, + }, + files: null, + }); + node.innerHTML = svg.outerHTML; + })(); + }, [libItem.elements, appState]); + + return ( +
+ {libItem.status === "published" && ( + + {t("labels.statusPublished")} + + )} +
+ +
+ + {libItem.error} +
+
+ ); +}; + const PublishLibrary = ({ onClose, libraryItems, diff --git a/src/components/SingleLibraryItem.scss b/src/components/SingleLibraryItem.scss deleted file mode 100644 index 0a42992a1..000000000 --- a/src/components/SingleLibraryItem.scss +++ /dev/null @@ -1,79 +0,0 @@ -@import "../css/variables.module"; - -.excalidraw { - .single-library-item { - position: relative; - - &-status { - position: absolute; - top: 0.3rem; - left: 0.3rem; - font-size: 0.7rem; - color: $oc-red-7; - background: rgba(255, 255, 255, 0.9); - padding: 0.1rem 0.2rem; - border-radius: 0.2rem; - } - - &__svg { - background-color: $oc-white; - padding: 0.3rem; - width: 7.5rem; - height: 7.5rem; - border: 1px solid var(--button-gray-2); - svg { - width: 100%; - height: 100%; - } - } - - .ToolIcon__icon { - background-color: $oc-white; - width: auto; - height: auto; - margin: 0 0.5rem; - } - .ToolIcon, - .ToolIcon_type_button:hover { - background-color: white; - } - .required, - .error { - color: $oc-red-8; - font-weight: bold; - font-size: 1rem; - margin: 0.2rem; - } - .error { - font-weight: 500; - margin: 0; - padding: 0.3em 0; - } - - &--remove { - position: absolute; - top: 0.2rem; - right: 1rem; - - .ToolIcon__icon { - margin: 0; - } - .ToolIcon__icon { - background-color: $oc-red-6; - &:hover { - background-color: $oc-red-7; - } - &:active { - background-color: $oc-red-8; - } - } - svg { - color: $oc-white; - padding: 0.26rem; - border-radius: 0.3em; - width: 1rem; - height: 1rem; - } - } - } -} diff --git a/src/components/SingleLibraryItem.tsx b/src/components/SingleLibraryItem.tsx deleted file mode 100644 index 45959199c..000000000 --- a/src/components/SingleLibraryItem.tsx +++ /dev/null @@ -1,104 +0,0 @@ -import oc from "open-color"; -import { useEffect, useRef } from "react"; -import { t } from "../i18n"; -import { exportToSvg } from "../packages/utils"; -import { AppState, LibraryItem } from "../types"; -import { CloseIcon } from "./icons"; - -import "./SingleLibraryItem.scss"; -import { ToolButton } from "./ToolButton"; - -const SingleLibraryItem = ({ - libItem, - appState, - index, - onChange, - onRemove, -}: { - libItem: LibraryItem; - appState: AppState; - index: number; - onChange: (val: string, index: number) => void; - onRemove: (id: string) => void; -}) => { - const svgRef = useRef(null); - const inputRef = useRef(null); - - useEffect(() => { - const node = svgRef.current; - if (!node) { - return; - } - (async () => { - const svg = await exportToSvg({ - elements: libItem.elements, - appState: { - ...appState, - viewBackgroundColor: oc.white, - exportBackground: true, - }, - files: null, - }); - node.innerHTML = svg.outerHTML; - })(); - }, [libItem.elements, appState]); - - return ( -
- {libItem.status === "published" && ( - - {t("labels.statusPublished")} - - )} -
- -
- - {libItem.error} -
-
- ); -}; - -export default SingleLibraryItem; From d34cd3072f0789870a8591cafdabc0c06c08a469 Mon Sep 17 00:00:00 2001 From: zsviczian Date: Sun, 16 Apr 2023 15:33:16 +0200 Subject: [PATCH 60/74] fix: abort freedraw line if second touch is detected (#6440) --- src/components/App.tsx | 39 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 37 insertions(+), 2 deletions(-) diff --git a/src/components/App.tsx b/src/components/App.tsx index 9b4fc864d..1ebdc7713 100644 --- a/src/components/App.tsx +++ b/src/components/App.tsx @@ -3500,6 +3500,43 @@ class App extends React.Component { this.setState({ contextMenu: null }); } + this.updateGestureOnPointerDown(event); + + // if dragging element is freedraw and another pointerdown event occurs + // a second finger is on the screen + // discard the freedraw element if it is very short because it is likely + // just a spike, otherwise finalize the freedraw element when the second + // finger is lifted + if ( + event.pointerType === "touch" && + this.state.draggingElement && + this.state.draggingElement.type === "freedraw" + ) { + const element = this.state.draggingElement as ExcalidrawFreeDrawElement; + this.updateScene({ + ...(element.points.length < 10 + ? { + elements: this.scene + .getElementsIncludingDeleted() + .filter((el) => el.id !== element.id), + } + : {}), + appState: { + draggingElement: null, + editingElement: null, + startBoundElement: null, + suggestedBindings: [], + selectedElementIds: Object.keys(this.state.selectedElementIds) + .filter((key) => key !== element.id) + .reduce((obj: { [id: string]: boolean }, key) => { + obj[key] = this.state.selectedElementIds[key]; + return obj; + }, {}), + }, + }); + return; + } + // remove any active selection when we start to interact with canvas // (mainly, we care about removing selection outside the component which // would prevent our copy handling otherwise) @@ -3539,8 +3576,6 @@ class App extends React.Component { }); this.savePointer(event.clientX, event.clientY, "down"); - this.updateGestureOnPointerDown(event); - if (this.handleCanvasPanUsingWheelOrSpaceDrag(event)) { return; } From 034113772d80910b6e5335db946499d9135e46ce Mon Sep 17 00:00:00 2001 From: David Luzar Date: Sun, 16 Apr 2023 15:33:30 +0200 Subject: [PATCH 61/74] fix: color picker keyboard handling not working (#6464) --- src/components/Popover.tsx | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/components/Popover.tsx b/src/components/Popover.tsx index c1249f798..987e9fb91 100644 --- a/src/components/Popover.tsx +++ b/src/components/Popover.tsx @@ -36,7 +36,13 @@ export const Popover = ({ return; } - container.focus(); + // focus popover only if the caller didn't focus on something else nested + // within the popover, which should take precedence. Fixes cases + // like color picker listening to keydown events on containers nested + // in the popover. + if (!container.contains(document.activeElement)) { + container.focus(); + } const handleKeyDown = (event: KeyboardEvent) => { if (event.key === KEYS.TAB) { From e9064a4a87f1086238bc4c3e26bca1d0937e9517 Mon Sep 17 00:00:00 2001 From: David Luzar Date: Sun, 16 Apr 2023 17:09:51 +0200 Subject: [PATCH 62/74] fix: library ids cross-contamination on multiple insert (#6466) --- src/components/LibraryMenuItems.tsx | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/components/LibraryMenuItems.tsx b/src/components/LibraryMenuItems.tsx index f6189fcb7..7ae6517a8 100644 --- a/src/components/LibraryMenuItems.tsx +++ b/src/components/LibraryMenuItems.tsx @@ -12,6 +12,7 @@ import { MIME_TYPES } from "../constants"; import Spinner from "./Spinner"; import LibraryMenuBrowseButton from "./LibraryMenuBrowseButton"; import clsx from "clsx"; +import { duplicateElements } from "../element/newElement"; const CELLS_PER_ROW = 4; @@ -96,7 +97,14 @@ const LibraryMenuItems = ({ } else { targetElements = libraryItems.filter((item) => item.id === id); } - return targetElements; + return targetElements.map((item) => { + return { + ...item, + // duplicate each library item before inserting on canvas to confine + // ids and bindings to each library item. See #6465 + elements: duplicateElements(item.elements), + }; + }); }; const createLibraryItemCompo = (params: { From e7e54814e7fef6fbd89dbb6e740230c6df2a932f Mon Sep 17 00:00:00 2001 From: Excalidraw Bot <77840495+excalibot@users.noreply.github.com> Date: Sun, 16 Apr 2023 17:12:37 +0200 Subject: [PATCH 63/74] chore: Update translations from Crowdin (#6290) * New translations en.json (Occitan) * New translations en.json (Kabyle) * Auto commit: Calculate translation coverage * New translations en.json (Romanian) * New translations en.json (French) * New translations en.json (Slovenian) * New translations en.json (Norwegian Bokmal) * Auto commit: Calculate translation coverage * New translations en.json (Dutch) * Auto commit: Calculate translation coverage * New translations en.json (Thai) * Auto commit: Calculate translation coverage * New translations en.json (Thai) * Auto commit: Calculate translation coverage * New translations en.json (Chinese Traditional) * New translations en.json (Thai) * Auto commit: Calculate translation coverage * New translations en.json (Galician) * Auto commit: Calculate translation coverage * New translations en.json (German) * Auto commit: Calculate translation coverage * New translations en.json (Marathi) * New translations en.json (Hindi) * Auto commit: Calculate translation coverage * New translations en.json (Thai) * Auto commit: Calculate translation coverage * New translations en.json (Thai) * Auto commit: Calculate translation coverage * New translations en.json (Thai) * Auto commit: Calculate translation coverage * New translations en.json (Thai) * Auto commit: Calculate translation coverage * New translations en.json (Basque) * Auto commit: Calculate translation coverage * New translations en.json (Swedish) * Auto commit: Calculate translation coverage * New translations en.json (Spanish) * Auto commit: Calculate translation coverage * New translations en.json (Hebrew) * Auto commit: Calculate translation coverage * New translations en.json (Hebrew) * Auto commit: Calculate translation coverage * New translations en.json (Hebrew) * New translations en.json (Chinese Simplified) * Auto commit: Calculate translation coverage * New translations en.json (Slovak) * Auto commit: Calculate translation coverage * New translations en.json (Thai) * Auto commit: Calculate translation coverage * New translations en.json (Thai) * Auto commit: Calculate translation coverage * New translations en.json (Turkish) * Auto commit: Calculate translation coverage * New translations en.json (Romanian) * New translations en.json (Spanish) * New translations en.json (Catalan) * New translations en.json (Basque) * New translations en.json (Finnish) * New translations en.json (Portuguese) * New translations en.json (Slovak) * New translations en.json (Chinese Simplified) * New translations en.json (Portuguese, Brazilian) * New translations en.json (Tamil) * New translations en.json (Marathi) * New translations en.json (Hindi) * New translations en.json (French) * New translations en.json (Arabic) * New translations en.json (Bulgarian) * New translations en.json (Czech) * New translations en.json (Danish) * New translations en.json (German) * New translations en.json (Greek) * New translations en.json (Hebrew) * New translations en.json (Hungarian) * New translations en.json (Italian) * New translations en.json (Japanese) * New translations en.json (Korean) * New translations en.json (Kurdish) * New translations en.json (Lithuanian) * New translations en.json (Dutch) * New translations en.json (Punjabi) * New translations en.json (Polish) * New translations en.json (Russian) * New translations en.json (Slovenian) * New translations en.json (Swedish) * New translations en.json (Turkish) * New translations en.json (Ukrainian) * New translations en.json (Chinese Traditional) * New translations en.json (Vietnamese) * New translations en.json (Galician) * New translations en.json (Indonesian) * New translations en.json (Persian) * New translations en.json (Bengali) * New translations en.json (Norwegian Nynorsk) * New translations en.json (Kazakh) * New translations en.json (Latvian) * New translations en.json (Burmese) * New translations en.json (Chinese Traditional, Hong Kong) * New translations en.json (Sinhala) * New translations en.json (Norwegian Bokmal) * New translations en.json (Occitan) * New translations en.json (Kabyle) * New translations en.json (Thai) * Auto commit: Calculate translation coverage * New translations en.json (Chinese Simplified) * New translations en.json (Greek) * New translations en.json (Slovenian) * New translations en.json (Swedish) * New translations en.json (Norwegian Bokmal) * Auto commit: Calculate translation coverage * New translations en.json (Romanian) * Auto commit: Calculate translation coverage * New translations en.json (Chinese Traditional) * Auto commit: Calculate translation coverage * New translations en.json (Portuguese) * Auto commit: Calculate translation coverage * New translations en.json (Portuguese) * New translations en.json (German) * Auto commit: Calculate translation coverage * New translations en.json (Galician) * Auto commit: Calculate translation coverage * New translations en.json (Galician) * Auto commit: Calculate translation coverage * New translations en.json (Hebrew) * Auto commit: Calculate translation coverage * New translations en.json (Hebrew) * New translations en.json (French) * Auto commit: Calculate translation coverage * New translations en.json (Indonesian) * Auto commit: Calculate translation coverage * New translations en.json (Indonesian) * Auto commit: Calculate translation coverage * New translations en.json (Italian) * New translations en.json (Chinese Simplified) * New translations en.json (Chinese Simplified) * New translations en.json (Kabyle) * Auto commit: Calculate translation coverage * New translations en.json (Kabyle) * Auto commit: Calculate translation coverage * New translations en.json (Marathi) * New translations en.json (Hindi) * Auto commit: Calculate translation coverage * New translations en.json (Hindi) * Auto commit: Calculate translation coverage * New translations en.json (Slovak) * Auto commit: Calculate translation coverage * New translations en.json (Spanish) * Auto commit: Calculate translation coverage * New translations en.json (Thai) * Auto commit: Calculate translation coverage * New translations en.json (Thai) * Auto commit: Calculate translation coverage * New translations en.json (Japanese) * Auto commit: Calculate translation coverage * New translations en.json (Dutch) * Auto commit: Calculate translation coverage * New translations en.json (Dutch) * Auto commit: Calculate translation coverage * New translations en.json (Basque) * Auto commit: Calculate translation coverage * New translations en.json (Basque) * Auto commit: Calculate translation coverage * New translations en.json (Marathi) * Auto commit: Calculate translation coverage * New translations en.json (Marathi) * Auto commit: Calculate translation coverage * New translations en.json (Marathi) * New translations en.json (Hindi) * Auto commit: Calculate translation coverage * New translations en.json (Polish) * Auto commit: Calculate translation coverage * New translations en.json (Polish) * New translations en.json (Chinese Simplified) * New translations en.json (Vietnamese) * Auto commit: Calculate translation coverage * New translations en.json (Vietnamese) * Auto commit: Calculate translation coverage * New translations en.json (Polish) * Auto commit: Calculate translation coverage * New translations en.json (Korean) * Auto commit: Calculate translation coverage * New translations en.json (Dutch) * New translations en.json (Basque) * New translations en.json (Marathi) * New translations en.json (Hindi) * New translations en.json (Polish) * New translations en.json (Chinese Simplified) * New translations en.json (Vietnamese) * New translations en.json (Korean) * New translations en.json (Romanian) * New translations en.json (French) * New translations en.json (Spanish) * New translations en.json (Arabic) * New translations en.json (Bulgarian) * New translations en.json (Catalan) * New translations en.json (Czech) * New translations en.json (Danish) * New translations en.json (German) * New translations en.json (Greek) * New translations en.json (Finnish) * New translations en.json (Hebrew) * New translations en.json (Hungarian) * New translations en.json (Italian) * New translations en.json (Japanese) * New translations en.json (Kurdish) * New translations en.json (Lithuanian) * New translations en.json (Punjabi) * New translations en.json (Portuguese) * New translations en.json (Russian) * New translations en.json (Slovak) * New translations en.json (Slovenian) * New translations en.json (Swedish) * New translations en.json (Turkish) * New translations en.json (Ukrainian) * New translations en.json (Chinese Traditional) * New translations en.json (Galician) * New translations en.json (Portuguese, Brazilian) * New translations en.json (Indonesian) * New translations en.json (Persian) * New translations en.json (Tamil) * New translations en.json (Bengali) * New translations en.json (Thai) * New translations en.json (Norwegian Nynorsk) * New translations en.json (Kazakh) * New translations en.json (Latvian) * New translations en.json (Burmese) * New translations en.json (Chinese Traditional, Hong Kong) * New translations en.json (Sinhala) * New translations en.json (Norwegian Bokmal) * New translations en.json (Occitan) * New translations en.json (Kabyle) * Auto commit: Calculate translation coverage * New translations en.json (Slovenian) * New translations en.json (Norwegian Bokmal) * Auto commit: Calculate translation coverage * New translations en.json (Japanese) * Auto commit: Calculate translation coverage * New translations en.json (German) * Auto commit: Calculate translation coverage * New translations en.json (Chinese Traditional) * Auto commit: Calculate translation coverage * New translations en.json (Marathi) * New translations en.json (Hindi) * Auto commit: Calculate translation coverage * New translations en.json (Romanian) * Auto commit: Calculate translation coverage * New translations en.json (Italian) * Auto commit: Calculate translation coverage * New translations en.json (Turkish) * Auto commit: Calculate translation coverage --- src/locales/ar-SA.json | 21 +- src/locales/bg-BG.json | 21 +- src/locales/bn-BD.json | 21 +- src/locales/ca-ES.json | 21 +- src/locales/cs-CZ.json | 21 +- src/locales/da-DK.json | 21 +- src/locales/de-DE.json | 21 +- src/locales/el-GR.json | 21 +- src/locales/es-ES.json | 21 +- src/locales/eu-ES.json | 21 +- src/locales/fa-IR.json | 21 +- src/locales/fi-FI.json | 21 +- src/locales/fr-FR.json | 21 +- src/locales/gl-ES.json | 23 +- src/locales/he-IL.json | 307 +++++++++++----------- src/locales/hi-IN.json | 21 +- src/locales/hu-HU.json | 21 +- src/locales/id-ID.json | 23 +- src/locales/it-IT.json | 21 +- src/locales/ja-JP.json | 21 +- src/locales/kab-KAB.json | 33 ++- src/locales/kk-KZ.json | 21 +- src/locales/ko-KR.json | 37 ++- src/locales/ku-TR.json | 21 +- src/locales/lt-LT.json | 21 +- src/locales/lv-LV.json | 21 +- src/locales/mr-IN.json | 21 +- src/locales/my-MM.json | 21 +- src/locales/nb-NO.json | 21 +- src/locales/nl-NL.json | 47 ++-- src/locales/nn-NO.json | 21 +- src/locales/oc-FR.json | 21 +- src/locales/pa-IN.json | 21 +- src/locales/percentages.json | 79 +++--- src/locales/pl-PL.json | 77 +++--- src/locales/pt-BR.json | 21 +- src/locales/pt-PT.json | 21 +- src/locales/ro-RO.json | 21 +- src/locales/ru-RU.json | 21 +- src/locales/si-LK.json | 21 +- src/locales/sk-SK.json | 21 +- src/locales/sl-SI.json | 21 +- src/locales/sv-SE.json | 21 +- src/locales/ta-IN.json | 21 +- src/locales/th-TH.json | 482 +++++++++++++++++++++++++++++++++++ src/locales/tr-TR.json | 31 ++- src/locales/uk-UA.json | 21 +- src/locales/vi-VN.json | 267 ++++++++++--------- src/locales/zh-CN.json | 27 +- src/locales/zh-HK.json | 21 +- src/locales/zh-TW.json | 21 +- 51 files changed, 1784 insertions(+), 468 deletions(-) create mode 100644 src/locales/th-TH.json diff --git a/src/locales/ar-SA.json b/src/locales/ar-SA.json index 6c6c0cf39..25a32f222 100644 --- a/src/locales/ar-SA.json +++ b/src/locales/ar-SA.json @@ -110,6 +110,7 @@ "increaseFontSize": "تكبير حجم الخط", "unbindText": "فك ربط النص", "bindText": "ربط النص بالحاوية", + "createContainerFromText": "", "link": { "edit": "تعديل الرابط", "create": "إنشاء رابط", @@ -204,7 +205,22 @@ "cannotResolveCollabServer": "تعذر الاتصال بخادم التعاون. الرجاء إعادة تحميل الصفحة والمحاولة مرة أخرى.", "importLibraryError": "تعذر تحميل المكتبة", "collabSaveFailed": "تعذر الحفظ في قاعدة البيانات. إذا استمرت المشاكل، يفضل أن تحفظ ملفك محليا كي لا تفقد عملك.", - "collabSaveFailed_sizeExceeded": "تعذر الحفظ في قاعدة البيانات، يبدو أن القماش كبير للغاية، يفضّل حفظ الملف محليا كي لا تفقد عملك." + "collabSaveFailed_sizeExceeded": "تعذر الحفظ في قاعدة البيانات، يبدو أن القماش كبير للغاية، يفضّل حفظ الملف محليا كي لا تفقد عملك.", + "brave_measure_text_error": { + "start": "", + "aggressive_block_fingerprint": "", + "setting_enabled": "", + "break": "", + "text_elements": "", + "in_your_drawings": "", + "strongly_recommend": "", + "steps": "", + "how": "", + "disable_setting": "", + "issue": "", + "write": "", + "discord": "" + } }, "toolBar": { "selection": "تحديد", @@ -303,7 +319,8 @@ "doubleClick": "انقر مرتين", "drag": "اسحب", "editor": "المحرر", - "editSelectedShape": "تعديل الشكل المحدد (النص/السهم/الخط)", + "editLineArrowPoints": "", + "editText": "", "github": "عثرت على مشكلة؟ إرسال", "howto": "اتبع التعليمات", "or": "أو", diff --git a/src/locales/bg-BG.json b/src/locales/bg-BG.json index ba052783d..501ce7399 100644 --- a/src/locales/bg-BG.json +++ b/src/locales/bg-BG.json @@ -110,6 +110,7 @@ "increaseFontSize": "", "unbindText": "", "bindText": "", + "createContainerFromText": "", "link": { "edit": "", "create": "", @@ -204,7 +205,22 @@ "cannotResolveCollabServer": "", "importLibraryError": "", "collabSaveFailed": "", - "collabSaveFailed_sizeExceeded": "" + "collabSaveFailed_sizeExceeded": "", + "brave_measure_text_error": { + "start": "", + "aggressive_block_fingerprint": "", + "setting_enabled": "", + "break": "", + "text_elements": "", + "in_your_drawings": "", + "strongly_recommend": "", + "steps": "", + "how": "", + "disable_setting": "", + "issue": "", + "write": "", + "discord": "" + } }, "toolBar": { "selection": "Селекция", @@ -303,7 +319,8 @@ "doubleClick": "", "drag": "плъзнете", "editor": "Редактор", - "editSelectedShape": "", + "editLineArrowPoints": "", + "editText": "", "github": "Намерихте проблем? Изпратете", "howto": "Следвайте нашите ръководства", "or": "или", diff --git a/src/locales/bn-BD.json b/src/locales/bn-BD.json index 47c6f02b2..a5d9dec0f 100644 --- a/src/locales/bn-BD.json +++ b/src/locales/bn-BD.json @@ -110,6 +110,7 @@ "increaseFontSize": "লেখনীর মাত্রা বাড়ান", "unbindText": "", "bindText": "", + "createContainerFromText": "", "link": { "edit": "লিঙ্ক সংশোধন", "create": "লিঙ্ক তৈরী", @@ -204,7 +205,22 @@ "cannotResolveCollabServer": "কোল্যাব সার্ভারের সাথে সংযোগ করা যায়নি। পৃষ্ঠাটি পুনরায় লোড করে আবার চেষ্টা করুন।", "importLibraryError": "সংগ্রহ লোড করা যায়নি", "collabSaveFailed": "", - "collabSaveFailed_sizeExceeded": "" + "collabSaveFailed_sizeExceeded": "", + "brave_measure_text_error": { + "start": "", + "aggressive_block_fingerprint": "", + "setting_enabled": "", + "break": "", + "text_elements": "", + "in_your_drawings": "", + "strongly_recommend": "", + "steps": "", + "how": "", + "disable_setting": "", + "issue": "", + "write": "", + "discord": "" + } }, "toolBar": { "selection": "বাছাই", @@ -303,7 +319,8 @@ "doubleClick": "", "drag": "", "editor": "", - "editSelectedShape": "", + "editLineArrowPoints": "", + "editText": "", "github": "", "howto": "", "or": "অথবা", diff --git a/src/locales/ca-ES.json b/src/locales/ca-ES.json index e94523b29..ae45e764d 100644 --- a/src/locales/ca-ES.json +++ b/src/locales/ca-ES.json @@ -110,6 +110,7 @@ "increaseFontSize": "Augmenta la mida de la lletra", "unbindText": "Desvincular el text", "bindText": "Ajusta el text al contenidor", + "createContainerFromText": "", "link": { "edit": "Edita l'enllaç", "create": "Crea un enllaç", @@ -204,7 +205,22 @@ "cannotResolveCollabServer": "No ha estat possible connectar amb el servidor collab. Si us plau recarregueu la pàgina i torneu a provar.", "importLibraryError": "No s'ha pogut carregar la biblioteca", "collabSaveFailed": "No s'ha pogut desar a la base de dades de fons. Si els problemes persisteixen, hauríeu de desar el fitxer localment per assegurar-vos que no perdeu el vostre treball.", - "collabSaveFailed_sizeExceeded": "No s'ha pogut desar a la base de dades de fons, sembla que el llenç és massa gran. Hauríeu de desar el fitxer localment per assegurar-vos que no perdeu el vostre treball." + "collabSaveFailed_sizeExceeded": "No s'ha pogut desar a la base de dades de fons, sembla que el llenç és massa gran. Hauríeu de desar el fitxer localment per assegurar-vos que no perdeu el vostre treball.", + "brave_measure_text_error": { + "start": "", + "aggressive_block_fingerprint": "", + "setting_enabled": "", + "break": "", + "text_elements": "", + "in_your_drawings": "", + "strongly_recommend": "", + "steps": "", + "how": "", + "disable_setting": "", + "issue": "", + "write": "", + "discord": "" + } }, "toolBar": { "selection": "Selecció", @@ -303,7 +319,8 @@ "doubleClick": "doble clic", "drag": "arrossega", "editor": "Editor", - "editSelectedShape": "Edita la forma seleccionada (text, fletxa o línia)", + "editLineArrowPoints": "", + "editText": "", "github": "Hi heu trobat un problema? Informeu-ne", "howto": "Seguiu les nostres guies", "or": "o", diff --git a/src/locales/cs-CZ.json b/src/locales/cs-CZ.json index 718d974b2..d57a8837d 100644 --- a/src/locales/cs-CZ.json +++ b/src/locales/cs-CZ.json @@ -110,6 +110,7 @@ "increaseFontSize": "Zvětšit písmo", "unbindText": "Zrušit vazbu textu", "bindText": "Vázat text s kontejnerem", + "createContainerFromText": "", "link": { "edit": "Upravit odkaz", "create": "Vytvořit odkaz", @@ -204,7 +205,22 @@ "cannotResolveCollabServer": "", "importLibraryError": "", "collabSaveFailed": "", - "collabSaveFailed_sizeExceeded": "" + "collabSaveFailed_sizeExceeded": "", + "brave_measure_text_error": { + "start": "", + "aggressive_block_fingerprint": "", + "setting_enabled": "", + "break": "", + "text_elements": "", + "in_your_drawings": "", + "strongly_recommend": "", + "steps": "", + "how": "", + "disable_setting": "", + "issue": "", + "write": "", + "discord": "" + } }, "toolBar": { "selection": "Výběr", @@ -303,7 +319,8 @@ "doubleClick": "dvojklik", "drag": "tažení", "editor": "Editor", - "editSelectedShape": "", + "editLineArrowPoints": "", + "editText": "", "github": "", "howto": "", "or": "nebo", diff --git a/src/locales/da-DK.json b/src/locales/da-DK.json index 8e541f90d..c8b5ad6e3 100644 --- a/src/locales/da-DK.json +++ b/src/locales/da-DK.json @@ -110,6 +110,7 @@ "increaseFontSize": "", "unbindText": "", "bindText": "", + "createContainerFromText": "", "link": { "edit": "", "create": "", @@ -204,7 +205,22 @@ "cannotResolveCollabServer": "", "importLibraryError": "", "collabSaveFailed": "", - "collabSaveFailed_sizeExceeded": "" + "collabSaveFailed_sizeExceeded": "", + "brave_measure_text_error": { + "start": "", + "aggressive_block_fingerprint": "", + "setting_enabled": "", + "break": "", + "text_elements": "", + "in_your_drawings": "", + "strongly_recommend": "", + "steps": "", + "how": "", + "disable_setting": "", + "issue": "", + "write": "", + "discord": "" + } }, "toolBar": { "selection": "", @@ -303,7 +319,8 @@ "doubleClick": "", "drag": "", "editor": "", - "editSelectedShape": "", + "editLineArrowPoints": "", + "editText": "", "github": "", "howto": "", "or": "", diff --git a/src/locales/de-DE.json b/src/locales/de-DE.json index b389fe5a4..bdf30a371 100644 --- a/src/locales/de-DE.json +++ b/src/locales/de-DE.json @@ -110,6 +110,7 @@ "increaseFontSize": "Schrift vergrößern", "unbindText": "Text lösen", "bindText": "Text an Container binden", + "createContainerFromText": "Text in Container einbetten", "link": { "edit": "Link bearbeiten", "create": "Link erstellen", @@ -204,7 +205,22 @@ "cannotResolveCollabServer": "Konnte keine Verbindung zum Collab-Server herstellen. Bitte lade die Seite neu und versuche es erneut.", "importLibraryError": "Bibliothek konnte nicht geladen werden", "collabSaveFailed": "Keine Speicherung in der Backend-Datenbank möglich. Wenn die Probleme weiterhin bestehen, solltest Du Deine Datei lokal speichern, um sicherzustellen, dass Du Deine Arbeit nicht verlierst.", - "collabSaveFailed_sizeExceeded": "Keine Speicherung in der Backend-Datenbank möglich, die Zeichenfläche scheint zu groß zu sein. Du solltest Deine Datei lokal speichern, um sicherzustellen, dass Du Deine Arbeit nicht verlierst." + "collabSaveFailed_sizeExceeded": "Keine Speicherung in der Backend-Datenbank möglich, die Zeichenfläche scheint zu groß zu sein. Du solltest Deine Datei lokal speichern, um sicherzustellen, dass Du Deine Arbeit nicht verlierst.", + "brave_measure_text_error": { + "start": "Sieht so aus, als ob du den Brave Browser benutzt mit der", + "aggressive_block_fingerprint": "\"Fingerprinting aggressiv blockieren\"", + "setting_enabled": "Einstellung aktiviert", + "break": "Dies könnte zur inkorrekten Darstellung der", + "text_elements": "Textelemente", + "in_your_drawings": "in deinen Zeichnungen führen", + "strongly_recommend": "Wir empfehlen dringend, diese Einstellung zu deaktivieren. Du kannst", + "steps": "diesen Schritten entsprechend", + "how": "folgen", + "disable_setting": " Wenn die Deaktivierung dieser Einstellung nicht zu einer korrekten Textdarstellung führt, öffne bitte einen", + "issue": "Issue", + "write": "auf GitHub, oder schreibe uns auf", + "discord": "Discord" + } }, "toolBar": { "selection": "Auswahl", @@ -303,7 +319,8 @@ "doubleClick": "doppelklicken", "drag": "ziehen", "editor": "Editor", - "editSelectedShape": "Ausgewählte Form bearbeiten (Text/Pfeil/Linie)", + "editLineArrowPoints": "Linien-/Pfeil-Punkte bearbeiten", + "editText": "Text bearbeiten / Label hinzufügen", "github": "Ein Problem gefunden? Informiere uns", "howto": "Folge unseren Anleitungen", "or": "oder", diff --git a/src/locales/el-GR.json b/src/locales/el-GR.json index d478be3e7..888c39568 100644 --- a/src/locales/el-GR.json +++ b/src/locales/el-GR.json @@ -110,6 +110,7 @@ "increaseFontSize": "Αύξηση μεγέθους γραμματοσειράς", "unbindText": "Αποσύνδεση κειμένου", "bindText": "Δέσμευση κειμένου στο δοχείο", + "createContainerFromText": "", "link": { "edit": "Επεξεργασία συνδέσμου", "create": "Δημιουργία συνδέσμου", @@ -204,7 +205,22 @@ "cannotResolveCollabServer": "Αδυναμία σύνδεσης με τον διακομιστή συνεργασίας. Παρακαλώ ανανεώστε τη σελίδα και προσπαθήστε ξανά.", "importLibraryError": "Αδυναμία φόρτωσης βιβλιοθήκης", "collabSaveFailed": "Η αποθήκευση στη βάση δεδομένων δεν ήταν δυνατή. Αν το προβλήματα παραμείνει, θα πρέπει να αποθηκεύσετε το αρχείο σας τοπικά για να βεβαιωθείτε ότι δεν χάνετε την εργασία σας.", - "collabSaveFailed_sizeExceeded": "Η αποθήκευση στη βάση δεδομένων δεν ήταν δυνατή, ο καμβάς φαίνεται να είναι πολύ μεγάλος. Θα πρέπει να αποθηκεύσετε το αρχείο τοπικά για να βεβαιωθείτε ότι δεν θα χάσετε την εργασία σας." + "collabSaveFailed_sizeExceeded": "Η αποθήκευση στη βάση δεδομένων δεν ήταν δυνατή, ο καμβάς φαίνεται να είναι πολύ μεγάλος. Θα πρέπει να αποθηκεύσετε το αρχείο τοπικά για να βεβαιωθείτε ότι δεν θα χάσετε την εργασία σας.", + "brave_measure_text_error": { + "start": "Φαίνεται ότι χρησιμοποιείτε το Brave browser με το", + "aggressive_block_fingerprint": "Αποκλεισμός \"Δακτυλικών Αποτυπωμάτων\"", + "setting_enabled": "ρύθμιση ενεργοποιημένη", + "break": "Αυτό θα μπορούσε να σπάσει το", + "text_elements": "Στοιχεία Κειμένου", + "in_your_drawings": "στα σχέδιά σας", + "strongly_recommend": "Συνιστούμε να απενεργοποιήσετε αυτή τη ρύθμιση. Μπορείτε να ακολουθήσετε", + "steps": "αυτά τα βήματα", + "how": "για το πώς να το κάνετε", + "disable_setting": " Εάν η απενεργοποίηση αυτής της ρύθμισης δεν διορθώνει την εμφάνιση των στοιχείων κειμένου, παρακαλώ ανοίξτε ένα", + "issue": "πρόβλημα", + "write": "στο GitHub, ή γράψτε μας στο", + "discord": "Discord" + } }, "toolBar": { "selection": "Επιλογή", @@ -303,7 +319,8 @@ "doubleClick": "διπλό κλικ", "drag": "σύρε", "editor": "Επεξεργαστής", - "editSelectedShape": "Επεξεργασία επιλεγμένου σχήματος (κείμενο/βέλος/γραμμή)", + "editLineArrowPoints": "", + "editText": "", "github": "Βρήκατε πρόβλημα; Υποβάλετε το", "howto": "Ακολουθήστε τους οδηγούς μας", "or": "ή", diff --git a/src/locales/es-ES.json b/src/locales/es-ES.json index eceb38ae3..67a110293 100644 --- a/src/locales/es-ES.json +++ b/src/locales/es-ES.json @@ -110,6 +110,7 @@ "increaseFontSize": "Aumentar el tamaño de letra", "unbindText": "Desvincular texto", "bindText": "Vincular texto al contenedor", + "createContainerFromText": "Envolver el texto en un contenedor", "link": { "edit": "Editar enlace", "create": "Crear enlace", @@ -204,7 +205,22 @@ "cannotResolveCollabServer": "No se pudo conectar al servidor colaborador. Por favor, vuelva a cargar la página y vuelva a intentarlo.", "importLibraryError": "No se pudo cargar la librería", "collabSaveFailed": "No se pudo guardar en la base de datos del backend. Si los problemas persisten, debería guardar su archivo localmente para asegurarse de que no pierde su trabajo.", - "collabSaveFailed_sizeExceeded": "No se pudo guardar en la base de datos del backend, el lienzo parece ser demasiado grande. Debería guardar el archivo localmente para asegurarse de que no pierde su trabajo." + "collabSaveFailed_sizeExceeded": "No se pudo guardar en la base de datos del backend, el lienzo parece ser demasiado grande. Debería guardar el archivo localmente para asegurarse de que no pierde su trabajo.", + "brave_measure_text_error": { + "start": "", + "aggressive_block_fingerprint": "", + "setting_enabled": "ajuste activado", + "break": "Esto podría resultar en romper los", + "text_elements": "Elementos de texto", + "in_your_drawings": "en tus dibujos", + "strongly_recommend": "Recomendamos desactivar esta configuración. Puedes seguir", + "steps": "estos pasos", + "how": "sobre cómo hacerlo", + "disable_setting": " Si deshabilitar esta opción no arregla la visualización de elementos de texto, por favor abre un", + "issue": "issue", + "write": "en GitHub, o escríbenos en", + "discord": "Discord" + } }, "toolBar": { "selection": "Selección", @@ -303,7 +319,8 @@ "doubleClick": "doble clic", "drag": "arrastrar", "editor": "Editor", - "editSelectedShape": "Editar la forma seleccionada (texto/flecha/línea)", + "editLineArrowPoints": "", + "editText": "", "github": "¿Ha encontrado un problema? Envíelo", "howto": "Siga nuestras guías", "or": "o", diff --git a/src/locales/eu-ES.json b/src/locales/eu-ES.json index d35baf4e4..1aec330cb 100644 --- a/src/locales/eu-ES.json +++ b/src/locales/eu-ES.json @@ -110,6 +110,7 @@ "increaseFontSize": "Handitu letra tamaina", "unbindText": "Askatu testua", "bindText": "Lotu testua edukiontziari", + "createContainerFromText": "Bilatu testua edukiontzi batean", "link": { "edit": "Editatu esteka", "create": "Sortu esteka", @@ -204,7 +205,22 @@ "cannotResolveCollabServer": "Ezin izan da elkarlaneko zerbitzarira konektatu. Mesedez, berriro kargatu orria eta saiatu berriro.", "importLibraryError": "Ezin izan da liburutegia kargatu", "collabSaveFailed": "Ezin izan da backend datu-basean gorde. Arazoak jarraitzen badu, zure fitxategia lokalean gorde beharko zenuke zure lana ez duzula galtzen ziurtatzeko.", - "collabSaveFailed_sizeExceeded": "Ezin izan da backend datu-basean gorde, ohiala handiegia dela dirudi. Fitxategia lokalean gorde beharko zenuke zure lana galtzen ez duzula ziurtatzeko." + "collabSaveFailed_sizeExceeded": "Ezin izan da backend datu-basean gorde, ohiala handiegia dela dirudi. Fitxategia lokalean gorde beharko zenuke zure lana galtzen ez duzula ziurtatzeko.", + "brave_measure_text_error": { + "start": "Brave nabigatzailea erabiltzen ari zarela dirudi", + "aggressive_block_fingerprint": "Aggressively Block Fingerprinting", + "setting_enabled": "ezarpena gaituta", + "break": "Honek honen haustea eragin dezake", + "text_elements": "Testu-elementuak", + "in_your_drawings": "zure marrazkietan", + "strongly_recommend": "Ezarpen hau desgaitzea gomendatzen dugu. Jarrai dezakezu", + "steps": "urrats hauek", + "how": "jakiteko nola egin", + "disable_setting": " Ezarpen hau desgaitzeak testu-elementuen bistaratzea konpontzen ez badu, ireki", + "issue": "eskaera (issue) bat", + "write": "gure Github-en edo idatz iezaguzu", + "discord": "Discord-en" + } }, "toolBar": { "selection": "Hautapena", @@ -303,7 +319,8 @@ "doubleClick": "klik bikoitza", "drag": "arrastatu", "editor": "Editorea", - "editSelectedShape": "Editatu hautatutako forma (testua/gezia/lerroa)", + "editLineArrowPoints": "", + "editText": "", "github": "Arazorik izan al duzu? Eman horren berri", "howto": "Jarraitu gure gidak", "or": "edo", diff --git a/src/locales/fa-IR.json b/src/locales/fa-IR.json index cc7de0dee..44cf7ae00 100644 --- a/src/locales/fa-IR.json +++ b/src/locales/fa-IR.json @@ -110,6 +110,7 @@ "increaseFontSize": "افزایش دادن اندازه فونت", "unbindText": "بازکردن نوشته", "bindText": "بستن نوشته", + "createContainerFromText": "", "link": { "edit": "ویرایش لینک", "create": "ایجاد پیوند", @@ -204,7 +205,22 @@ "cannotResolveCollabServer": "به سرور collab متصل نشد. لطفا صفحه را مجددا بارگذاری کنید و دوباره تلاش کنید.", "importLibraryError": "داده‌ها بارگذاری نشدند", "collabSaveFailed": "", - "collabSaveFailed_sizeExceeded": "" + "collabSaveFailed_sizeExceeded": "", + "brave_measure_text_error": { + "start": "", + "aggressive_block_fingerprint": "", + "setting_enabled": "", + "break": "", + "text_elements": "", + "in_your_drawings": "", + "strongly_recommend": "", + "steps": "", + "how": "", + "disable_setting": "", + "issue": "", + "write": "", + "discord": "" + } }, "toolBar": { "selection": "گزینش", @@ -303,7 +319,8 @@ "doubleClick": "دابل کلیک", "drag": "کشیدن", "editor": "ویرایشگر", - "editSelectedShape": "ویرایش شکل انتخاب شده (متن/فلش/خط)", + "editLineArrowPoints": "", + "editText": "", "github": "اشکالی می بینید؟ گزارش دهید", "howto": "راهنمای ما را دنبال کنید", "or": "یا", diff --git a/src/locales/fi-FI.json b/src/locales/fi-FI.json index 6c357b167..e0701f2d2 100644 --- a/src/locales/fi-FI.json +++ b/src/locales/fi-FI.json @@ -110,6 +110,7 @@ "increaseFontSize": "Kasvata kirjasinkokoa", "unbindText": "Irroita teksti", "bindText": "Kiinnitä teksti säiliöön", + "createContainerFromText": "", "link": { "edit": "Muokkaa linkkiä", "create": "Luo linkki", @@ -204,7 +205,22 @@ "cannotResolveCollabServer": "Yhteyden muodostaminen collab-palvelimeen epäonnistui. Virkistä sivu ja yritä uudelleen.", "importLibraryError": "Kokoelman lataaminen epäonnistui", "collabSaveFailed": "Ei voitu tallentaan palvelimen tietokantaan. Jos ongelmia esiintyy, sinun kannatta tallentaa tallentaa tiedosto paikallisesti varmistaaksesi, että et menetä työtäsi.", - "collabSaveFailed_sizeExceeded": "Ei voitu tallentaan palvelimen tietokantaan. Jos ongelmia esiintyy, sinun kannatta tallentaa tallentaa tiedosto paikallisesti varmistaaksesi, että et menetä työtäsi." + "collabSaveFailed_sizeExceeded": "Ei voitu tallentaan palvelimen tietokantaan. Jos ongelmia esiintyy, sinun kannatta tallentaa tallentaa tiedosto paikallisesti varmistaaksesi, että et menetä työtäsi.", + "brave_measure_text_error": { + "start": "", + "aggressive_block_fingerprint": "", + "setting_enabled": "", + "break": "", + "text_elements": "", + "in_your_drawings": "", + "strongly_recommend": "", + "steps": "", + "how": "", + "disable_setting": "", + "issue": "", + "write": "", + "discord": "" + } }, "toolBar": { "selection": "Valinta", @@ -303,7 +319,8 @@ "doubleClick": "kaksoisnapsautus", "drag": "vedä", "editor": "Muokkausohjelma", - "editSelectedShape": "Muokkaa valittua muotoa (teksti/nuoli/viiva)", + "editLineArrowPoints": "", + "editText": "", "github": "Löysitkö ongelman? Kerro meille", "howto": "Seuraa oppaitamme", "or": "tai", diff --git a/src/locales/fr-FR.json b/src/locales/fr-FR.json index 573e3fd1a..49135c3b6 100644 --- a/src/locales/fr-FR.json +++ b/src/locales/fr-FR.json @@ -110,6 +110,7 @@ "increaseFontSize": "Augmenter la taille de la police", "unbindText": "Dissocier le texte", "bindText": "Associer le texte au conteneur", + "createContainerFromText": "Encadrer le texte dans un conteneur", "link": { "edit": "Modifier le lien", "create": "Ajouter un lien", @@ -204,7 +205,22 @@ "cannotResolveCollabServer": "Impossible de se connecter au serveur collaboratif. Veuillez recharger la page et réessayer.", "importLibraryError": "Impossible de charger la bibliothèque", "collabSaveFailed": "Impossible d'enregistrer dans la base de données en arrière-plan. Si des problèmes persistent, vous devriez enregistrer votre fichier localement pour vous assurer de ne pas perdre votre travail.", - "collabSaveFailed_sizeExceeded": "Impossible d'enregistrer dans la base de données en arrière-plan, le tableau semble trop grand. Vous devriez enregistrer le fichier localement pour vous assurer de ne pas perdre votre travail." + "collabSaveFailed_sizeExceeded": "Impossible d'enregistrer dans la base de données en arrière-plan, le tableau semble trop grand. Vous devriez enregistrer le fichier localement pour vous assurer de ne pas perdre votre travail.", + "brave_measure_text_error": { + "start": "Il semble que vous utilisiez le navigateur Brave avec le", + "aggressive_block_fingerprint": "blocage d'empreinte agressif", + "setting_enabled": "activé", + "break": "Ceci pourrait avoir pour conséquence de « casser » les", + "text_elements": "éléments texte", + "in_your_drawings": "dans vos dessins", + "strongly_recommend": "Nous recommandons fortement de désactiver ce paramètre. Vous pouvez suivre", + "steps": "ces étapes", + "how": "sur la manière de procéder", + "disable_setting": " Si la désactivation de ce paramètre ne résout pas l'affichage des éléments texte, veuillez ouvrir un", + "issue": "ticket", + "write": "sur notre GitHub, ou nous écrire sur", + "discord": "Discord" + } }, "toolBar": { "selection": "Sélection", @@ -303,7 +319,8 @@ "doubleClick": "double-clic", "drag": "glisser", "editor": "Éditeur", - "editSelectedShape": "Modifier la forme sélectionnée (texte/flèche/ligne)", + "editLineArrowPoints": "", + "editText": "", "github": "Problème trouvé ? Soumettre", "howto": "Suivez nos guides", "or": "ou", diff --git a/src/locales/gl-ES.json b/src/locales/gl-ES.json index 373fe4032..5571f3f15 100644 --- a/src/locales/gl-ES.json +++ b/src/locales/gl-ES.json @@ -110,6 +110,7 @@ "increaseFontSize": "Aumentar o tamaño da fonte", "unbindText": "Desvincular texto", "bindText": "Ligar o texto ao contedor", + "createContainerFromText": "Envolver o texto nun contedor", "link": { "edit": "Editar ligazón", "create": "Crear ligazón", @@ -193,7 +194,7 @@ "resetLibrary": "Isto limpará a súa biblioteca. Está seguro?", "removeItemsFromsLibrary": "Eliminar {{count}} elemento(s) da biblioteca?", "invalidEncryptionKey": "A clave de cifrado debe ter 22 caracteres. A colaboración en directo está desactivada.", - "collabOfflineWarning": "" + "collabOfflineWarning": "Non hai conexión a Internet dispoñible.\nOs teus cambios non serán gardados!" }, "errors": { "unsupportedFileType": "Tipo de ficheiro non soportado.", @@ -204,7 +205,22 @@ "cannotResolveCollabServer": "Non se puido conectar ao servidor de colaboración. Por favor recargue a páxina e probe de novo.", "importLibraryError": "Non se puido cargar a biblioteca", "collabSaveFailed": "Non se puido gardar na base de datos. Se o problema persiste, deberías gardar o teu arquivo de maneira local para asegurarte de non perdelo teu traballo.", - "collabSaveFailed_sizeExceeded": "Non se puido gardar na base de datos, o lenzo semella demasiado grande. Deberías gardar o teu arquivo de maneira local para asegurarte de non perdelo teu traballo." + "collabSaveFailed_sizeExceeded": "Non se puido gardar na base de datos, o lenzo semella demasiado grande. Deberías gardar o teu arquivo de maneira local para asegurarte de non perdelo teu traballo.", + "brave_measure_text_error": { + "start": "Semella que estás usando o navegador Brave coa opción", + "aggressive_block_fingerprint": "Aggressively Block Fingerprinting", + "setting_enabled": "activada", + "break": "Isto podería provocar unha ruptura dos", + "text_elements": "Elementos de Texto", + "in_your_drawings": "nos seus debuxos", + "strongly_recommend": "Recomendámoslle encarecidamente que desactive esa opción. Pode seguir", + "steps": "estes pasos", + "how": "sobre como facelo", + "disable_setting": " Se ao desactivar esta opción non se arranxa o problema ao mostrar os elementos de texto, por favor abra unha", + "issue": "issue", + "write": "no noso GitHub, ou escríbenos ao", + "discord": "Discord" + } }, "toolBar": { "selection": "Selección", @@ -303,7 +319,8 @@ "doubleClick": "dobre-clic", "drag": "arrastrar", "editor": "Editor", - "editSelectedShape": "Editar a forma seleccionada (texto/frecha/liña)", + "editLineArrowPoints": "", + "editText": "", "github": "Encontrou un problema? Envíeo", "howto": "Sigue as nosas normas", "or": "ou", diff --git a/src/locales/he-IL.json b/src/locales/he-IL.json index 9414da375..810fc1776 100644 --- a/src/locales/he-IL.json +++ b/src/locales/he-IL.json @@ -1,18 +1,18 @@ { "labels": { "paste": "הדבק", - "pasteAsPlaintext": "", + "pasteAsPlaintext": "הדבק ללא עיצוב", "pasteCharts": "הדבק גרפים", "selectAll": "בחר הכל", - "multiSelect": "הוסף אובייקט לבחירה", + "multiSelect": "הוסף רכיב לבחירה", "moveCanvas": "הזז את הקנבס", - "cut": "חתוך", + "cut": "גזור", "copy": "העתק", "copyAsPng": "העתק ללוח כ PNG", "copyAsSvg": "העתק ללוח כ SVG", - "copyText": "העתק ללוח כ-PNG", + "copyText": "העתק ללוח כטקסט", "bringForward": "הבא שכבה קדימה", - "sendToBack": "העבר לסוף", + "sendToBack": "שלח אחורה", "bringToFront": "העבר לחזית", "sendBackward": "העבר שכבה אחורה", "delete": "מחק", @@ -26,7 +26,7 @@ "strokeStyle_solid": "מלא", "strokeStyle_dashed": "מקווקו", "strokeStyle_dotted": "מנוקד", - "sloppiness": "סגנון", + "sloppiness": "רישול", "opacity": "אטימות", "textAlign": "יישור טקסט", "edges": "קצוות", @@ -35,57 +35,57 @@ "arrowheads": "ראשי חצים", "arrowhead_none": "ללא", "arrowhead_arrow": "חץ", - "arrowhead_bar": "שורה", + "arrowhead_bar": "קצה אנכי", "arrowhead_dot": "נקודה", "arrowhead_triangle": "משולש", "fontSize": "גודל גופן", - "fontFamily": "סוג הגופן", + "fontFamily": "גופן", "onlySelected": "רק מה שנבחר", "withBackground": "רקע", "exportEmbedScene": "הטמעה של מידע הסצנה", - "exportEmbedScene_details": "מידע התצוגה יישמר לקובץ המיוצא מסוג PNG/SVG כך שיהיה ניתן לשחזרה ממנו.\nהפעולה תגדיל את גודל הקובץ המיוצא.", + "exportEmbedScene_details": "הייצוא יבוצע לקובץ מסוג PNG/SVG כדי שהמידע על הסצנה ישמר בו וניתן יהיה לבצע שחזור ממנו.\nיגדיל את גודל הקובץ של הייצוא.", "addWatermark": "הוסף \"נוצר באמצעות Excalidraw\"", - "handDrawn": "כתב יד", + "handDrawn": "ציור יד", "normal": "רגיל", "code": "קוד", "small": "קטן", - "medium": "בנוני", + "medium": "בינוני", "large": "גדול", - "veryLarge": "ענק", + "veryLarge": "גדול מאוד", "solid": "מוצק", - "hachure": "קווים משופעים", - "crossHatch": "קווים מוצלבים", + "hachure": "קווים מקבילים קצרים להצגת כיוון וחדות שיפוע במפה", + "crossHatch": "קווים מוצלבים שתי וערב", "thin": "דק", "bold": "מודגש", "left": "שמאל", "center": "מרכז", "right": "ימין", - "extraBold": "עבה", + "extraBold": "מודגש במיוחד", "architect": "ארכיטקט", "artist": "אמן", "cartoonist": "קריקטוריסט", "fileTitle": "שם קובץ", - "colorPicker": "בחירת צבע", + "colorPicker": "בוחר צבעים", "canvasColors": "בשימוש בקנבס", - "canvasBackground": "רקע הלוח", - "drawingCanvas": "לוח ציור", + "canvasBackground": "רקע קנבס", + "drawingCanvas": "קנבס ציור", "layers": "שכבות", "actions": "פעולות", "language": "שפה", - "liveCollaboration": "", + "liveCollaboration": "התחל שיתוף חי...", "duplicateSelection": "שכפל", "untitled": "ללא כותרת", "name": "שם", - "yourName": "שם", + "yourName": "שמך", "madeWithExcalidraw": "נוצר באמצעות Excalidraw", - "group": "אחד לקבוצה", + "group": "קבץ", "ungroup": "פרק קבוצה", "collaborators": "שותפים", "showGrid": "הצג רשת", "addToLibrary": "הוסף לספריה", "removeFromLibrary": "הסר מספריה", "libraryLoadingMessage": "טוען ספריה…", - "libraries": "דפדף בספריות", + "libraries": "עיין בספריות", "loadingScene": "טוען תצוגה…", "align": "יישר", "alignTop": "יישר למעלה", @@ -96,28 +96,29 @@ "centerHorizontally": "מרכז אופקית", "distributeHorizontally": "חלוקה אופקית", "distributeVertically": "חלוקה אנכית", - "flipHorizontal": "סובב אופקית", - "flipVertical": "סובב אנכית", + "flipHorizontal": "הפוך אופקית", + "flipVertical": "הפוך אנכית", "viewMode": "מצב תצוגה", - "toggleExportColorScheme": "שנה את ערכת צבעי הייצוא", + "toggleExportColorScheme": "מתג ערכת צבעים לייצוא", "share": "שתף", - "showStroke": "הצג צבעי קו מתאר", - "showBackground": "הצג צבעי רקע", + "showStroke": "הצג בוחר צבע מברשת", + "showBackground": "הצג בוחר צבע רקע", "toggleTheme": "שינוי ערכת העיצוב", "personalLib": "ספריה פרטית", "excalidrawLib": "הספריה של Excalidraw", "decreaseFontSize": "הקטן את גודל הגופן", "increaseFontSize": "הגדל את גודל הגופן", "unbindText": "ביטול קיבוע הטקסט", - "bindText": "קיבוע הטקסט לאוגד", + "bindText": "קיבוע הטקסט למיכל", + "createContainerFromText": "ארוז טקסט במיכל", "link": { "edit": "עריכת קישור", "create": "יצירת קישור", "label": "קישור" }, "lineEditor": { - "edit": "", - "exit": "" + "edit": "ערוך קו", + "exit": "צא מעורך הקו" }, "elementLock": { "lock": "נעילה", @@ -125,16 +126,16 @@ "lockAll": "לנעול הכל", "unlockAll": "שחרור הכול" }, - "statusPublished": "", - "sidebarLock": "" + "statusPublished": "פורסם", + "sidebarLock": "שמור את סרגל הצד פתוח" }, "library": { - "noItems": "", - "hint_emptyLibrary": "", - "hint_emptyPrivateLibrary": "" + "noItems": "עוד לא הוספת דברים...", + "hint_emptyLibrary": "בחר משהו בקנבס כדי להוסיף אותו לכאן, או שתתקין ספריה מהספריה הציבורית מטה.", + "hint_emptyPrivateLibrary": "בחר משהו בקנבס כדי להוסיף אותו לכאן." }, "buttons": { - "clearReset": "אפס את הלוח", + "clearReset": "אפס את הקנבאס", "exportJSON": "ייצא לקובץ", "exportImage": "ייצוא התמונה...", "export": "שמור ל...", @@ -143,7 +144,7 @@ "copyToClipboard": "העתק ללוח", "copyPngToClipboard": "העתק PNG ללוח", "scale": "קנה מידה", - "save": "שמירת קובץ נוכחי", + "save": "שמור לקובץ נוכחי", "saveAs": "שמירה בשם", "load": "פתח", "getShareableLink": "קבל קישור לשיתוף", @@ -159,58 +160,73 @@ "undo": "בטל", "redo": "בצע מחדש", "resetLibrary": "איפוס ספריה", - "createNewRoom": "צור חדר", + "createNewRoom": "צור חדר חדש", "fullScreen": "מסך מלא", "darkMode": "מצב כהה", "lightMode": "מצב בהיר", "zenMode": "מצב זן", - "exitZenMode": "צא ממצב תפריט מרחף", + "exitZenMode": "צא ממצב זן", "cancel": "ביטול", "clear": "ניקוי", - "remove": "מחיקה", - "publishLibrary": "פירסום", + "remove": "הסר", + "publishLibrary": "פרסום", "submit": "שליחה", - "confirm": "לאשר" + "confirm": "אשר" }, "alerts": { - "clearReset": "פעולה זו תנקה את כל הלוח. אתה בטוח?", - "couldNotCreateShareableLink": "לא ניתן לייצר לינק לשיתוף.", - "couldNotCreateShareableLinkTooBig": "לא הצלחנו לייצר קישור לשיתוף: התצוגה גדולה מדי", - "couldNotLoadInvalidFile": "לא ניתן לטעון קובץ שאיננו תואם", + "clearReset": "פעולה זו תנקה את כל הקנבס. אתה בטוח?", + "couldNotCreateShareableLink": "יצירת קישור לשיתוף נכשל.", + "couldNotCreateShareableLinkTooBig": "יצירת קישור לשיתוף נכשל: התצוגה גדולה מדי", + "couldNotLoadInvalidFile": "טעינת קובץ לא תקין נכשלה", "importBackendFailed": "ייבוא מהשרת נכשל.", - "cannotExportEmptyCanvas": "לא ניתן לייצא לוח ריק.", - "couldNotCopyToClipboard": "לא ניתן היה להעתיק ללוח", - "decryptFailed": "לא ניתן לפענח מידע.", - "uploadedSecurly": "ההעלאה הוצפנה מקצה לקצה, ולכן שרת Excalidraw וצד שלישי לא יכולים לקרוא את התוכן.", + "cannotExportEmptyCanvas": "לא ניתן לייצא קנבאס ריק.", + "couldNotCopyToClipboard": "לא ניתן היה להעתיק ללוח.", + "decryptFailed": "פיענוח ההצפנה של המידע נכשל.", + "uploadedSecurly": "ההעלאה אובטחה באמצעות הצפנה מקצה לקצה, פירוש הדבר שהשרת של Excalidraw וגורמי צד ג׳ לא יכולים לקרוא את התוכן.", "loadSceneOverridePrompt": "טעינה של ציור חיצוני תחליף את התוכן הקיים שלך. האם תרצה להמשיך?", - "collabStopOverridePrompt": "עצירת השיתוף תוביל למחיקת התרשימים השמורים בדפדפן. האם את/ה בטוח/ה?\n(אם תרצה לשמור את התרשימים הקיימים, תוכל לסגור את הדפדפן מבלי לסיים את השיתוף.)", + "collabStopOverridePrompt": "עצירת השיתוף תוביל למחיקת הציור הקודם ששמור מקומית בדפדפן. האם אתה בטוח?\n\n(אם תרצה לשמור את הציור המקומי, סגור את הטאב של הדפדפן במקום.)", "errorAddingToLibrary": "לא ניתן להוסיף פריט לספרייה", - "errorRemovingFromLibrary": "לא ניתן למחוק פריט מהספריה", - "confirmAddLibrary": "הפעולה תוסיף {{numShapes}} צורה(ות) לספריה שלך. האם אתה בטוח?", + "errorRemovingFromLibrary": "לא ניתן להסיר פריט מהספריה", + "confirmAddLibrary": "זה יוסיף {{numShapes}} צורה(ות) לספריה שלך. האם אתה בטוח?", "imageDoesNotContainScene": "נראה שהתמונה לא מכילה מידע על הסצינה. האם אפשרת הטמעת מידע הסצינה בעת השמירה?", - "cannotRestoreFromImage": "לא הצלחנו לשחזר את התצוגה מקובץ התמונה", - "invalidSceneUrl": "ייבוא המידע מן סצינה מכתובת האינטרנט נכשלה. המידע בנוי באופן משובש או שהוא אינו קובץ JSON תקין של Excalidraw.", - "resetLibrary": "פעולה זו תנקה את כל הלוח. אתה בטוח?", - "removeItemsFromsLibrary": "מחיקת {{count}} פריטים(ים) מתוך הספריה?", - "invalidEncryptionKey": "מפתח ההצפנה חייב להיות בן 22 תוים. השיתוף החי מבוטל.", - "collabOfflineWarning": "" + "cannotRestoreFromImage": "לא הצלחנו לשחזר את הסצנה מקובץ התמונה", + "invalidSceneUrl": "ייבוא מידע סצנה מהקישור שסופק כשל. או שהוא משובש, או שאינו מכיל מידע של Excalidraw בפורמט JSON.", + "resetLibrary": "פעולה זו תנקה את כל הספריה שלך. אתה בטוח?", + "removeItemsFromsLibrary": "מחק {{count}} פריט(ים) מהספריה?", + "invalidEncryptionKey": "מפתח ההצפנה חייב להיות בן 22 תוים. השיתוף החי מנוטרל.", + "collabOfflineWarning": "אין חיבור זמין לאינטרנט.\nהשינויים שלך לא ישמרו!" }, "errors": { "unsupportedFileType": "סוג הקובץ אינו נתמך.", - "imageInsertError": "לא ניתן היה להטמיע את התמונה, אנא נסו שוב מאוחר יותר...", - "fileTooBig": "הקובץ כבד מדי. הגודל המקסימלי המותר הוא {{maxSize}}.", - "svgImageInsertError": "לא ניתן היה להטמיע את תמונת ה-SVG. קידוד ה-SVG אינו תקני.", - "invalidSVGString": "SVG בלתי תקני.", - "cannotResolveCollabServer": "", + "imageInsertError": "לא ניתן היה להוסיף את התמונה. אנא נסה שוב מאוחר יותר...", + "fileTooBig": "הקובץ גדול מדי. הגודל המירבי המותר הינו {{maxSize}}.", + "svgImageInsertError": "לא ניתן היה להוסיף את תמונת ה-SVG. הסימונים בתוך קובץ ה-SVG עשויים להיות שגויים.", + "invalidSVGString": "SVG שגוי.", + "cannotResolveCollabServer": "לא הצלחתי להתחבר לשרת השיתוף. אנא רענן את הדף ונסה שוב.", "importLibraryError": "לא ניתן היה לטעון את הספריה", - "collabSaveFailed": "", - "collabSaveFailed_sizeExceeded": "" + "collabSaveFailed": "לא הצלחתי להתחבר למסד הנתונים האחורי. אם הבעיה ממשיכה, כדאי שתשמור את הקובץ מקומית כדי לוודא שלא תאבד את העבודה שלך.", + "collabSaveFailed_sizeExceeded": "לא הצלחתי לשמור למסד הנתונים האחורי, נראה שהקנבס שלך גדול מדי. כדאי שתשמור את הקובץ מקומית כדי לוודא שלא תאבד את העבודה שלך.", + "brave_measure_text_error": { + "start": "נראה שאתה משתמש בדפדפן Brave עם ה-", + "aggressive_block_fingerprint": "חסימת Fingerprinting אגרסיבית", + "setting_enabled": "ההגדרה מופעלת", + "break": "זה יכול לגרום לבעיה ב-", + "text_elements": "רכיבי טקסט", + "in_your_drawings": "בציורים שלך", + "strongly_recommend": "אנו ממליצים בחום לנטרל את ההגדרה הזו. אתה יכול לעקוב", + "steps": "הצעדים הבאים", + "how": "כיצד לעשות את זה", + "disable_setting": " אם נטרול ההגדרה לא מתקן את תצוגת רכיבי הטקסט, אנא פתח", + "issue": "בעיה", + "write": "ב- GitHub שלנו, או כתוב לנו ב-", + "discord": "Discord" + } }, "toolBar": { "selection": "בחירה", "image": "הוספת תמונה", - "rectangle": "מרובע", - "diamond": "מעוין", + "rectangle": "מלבן", + "diamond": "יהלום", "ellipse": "אליפסה", "arrow": "חץ", "line": "קו", @@ -218,79 +234,79 @@ "text": "טקסט", "library": "ספריה", "lock": "השאר את הכלי הנבחר פעיל גם לאחר סיום הציור", - "penMode": "", - "link": "הוספה/עדכון של קישור עבור הצורה הנבחרת", + "penMode": "מצב עט - מנע נגיעה", + "link": "הוספה/עדכון קישור של הצורה שנבחרה", "eraser": "מחק", - "hand": "" + "hand": "יד (כלי הזזה)" }, "headings": { - "canvasActions": "פעולות הלוח", - "selectedShapeActions": "פעולות צורה שנבחרה", + "canvasActions": "פעולות קנבאס", + "selectedShapeActions": "פעולות על הצורות שנבחרו", "shapes": "צורות" }, "hints": { - "canvasPanning": "", - "linearElement": "הקלק בשביל לבחור נקודות מרובות, גרור בשביל קו בודד", + "canvasPanning": "כדי להזיז את הקנבס, החזק את גלגל העכבר לחוץ או את מקש הרווח לחוץ תוך כדי גרירה, או השתמש בכלי היד", + "linearElement": "לחץ להתחלת מספר נקודות, גרור לקו יחיד", "freeDraw": "לחץ וגרור, שחרר כשסיימת", "text": "טיפ: אפשר להוסיף טקסט על ידי לחיצה כפולה בכל מקום עם כלי הבחירה", - "text_selected": "לחץ לחיצה כפולה או אנטר לעריכת הנקודות", - "text_editing": "כדי לסיים את העריכה לחצו על מקש Escape או על Ctrl ומקש Enter (Cmd במחשבי אפל)", + "text_selected": "לחץ לחיצה כפולה או הקש על אנטר לעריכת הטקסט", + "text_editing": "כדי לסיים את העריכה לחץ על מקש Escape או על Ctrl (Cmd במחשבי אפל) ומקש Enter", "linearElementMulti": "הקלק על הנקודה האחרונה או הקש Escape או Enter לסיום", - "lockAngle": "אתה יכול להגביל זווית ע״י לחיצה על SHIFT", + "lockAngle": "ניתן להגביל את הזוויות על ידי החזקה של מקש ה- SHIFT", "resize": "ניתן להגביל פרופורציות על ידי לחיצה על SHIFT תוך כדי שינוי גודל,\nהחזק ALT בשביל לשנות גודל ביחס למרכז", - "resizeImage": "", + "resizeImage": "אתה יכול לשנות את הגודל בחופשיות על ידי החזקת מקש SHIFT,\nהחזק את מקש ALT כדי לבצע שינוי גודל מהמרכז", "rotate": "ניתן להגביל זוויות על ידי לחיצה על SHIFT תוך כדי סיבוב", - "lineEditor_info": "", - "lineEditor_pointSelected": "", - "lineEditor_nothingSelected": "", - "placeImage": "", - "publishLibrary": "פירסום ספריה אישית", - "bindTextToElement": "יש להקיש Enter כדי להוסיף טקסט", - "deepBoxSelect": "", - "eraserRevert": "", - "firefox_clipboard_write": "" + "lineEditor_info": "החזק Ctrl / Cmd ובצע לחיצה כפולה או לחץ Ctrl / Cmd + Enter לעריכת נקודות", + "lineEditor_pointSelected": "לחץ Delete למחיקת נקודה/ות,\nCtrl / Cmd + D לשכפול, או גרור להזזה", + "lineEditor_nothingSelected": "בחר נקודה כדי לערוך (החזק SHIFT לבחירת כמה),\nאו החזק Alt והקלק להוספת נקודות חדשות", + "placeImage": "הקלק להנחת התמונה, או הקלק וגרור להגדרת הגודל שלו ידנית", + "publishLibrary": "פרסם ספריה משלך", + "bindTextToElement": "הקש Enter כדי להוספת טקסט", + "deepBoxSelect": "החזק Ctrl / Cmd לבחירה עמוקה ולמניעת גרירה", + "eraserRevert": "החזק Alt להחזרת רכיבים מסומנים למחיקה", + "firefox_clipboard_write": "יכולות זה ניתנת להפעלה על ידי שינוי הדגל של \"dom.events.asyncClipboard.clipboardItem\" למצב \"true\". כדי לשנות את הדגל בדפדפן Firefox, בקר בעמוד ״about:config״." }, "canvasError": { - "cannotShowPreview": "לא הצלחנו להציג את התצוגה המקדימה", + "cannotShowPreview": "לא ניתן להראות תצוגה מקדימה", "canvasTooBig": "הקנבס עלול להיות גדול מדי.", - "canvasTooBigTip": "טיפ: נסה להזיז את האלמנטים הרחוקים ביותר מעט קרוב יותר יחד." + "canvasTooBigTip": "טיפ: נסה להזיז את הרכיבים הרחוקים ביותר מעט קרוב יותר האחד לשני." }, "errorSplash": { "headingMain_pre": "אירעה שגיאה. נסה ", "headingMain_button": "טוען את העמוד מחדש.", "clearCanvasMessage": "אם טעינה מחדש לא עובדת, נסה ", - "clearCanvasMessage_button": "מנקה את הלוח.", - "clearCanvasCaveat": " זה יביא לאובדן עבודה ", - "trackedToSentry_pre": "שגיאה עם מזהה ", + "clearCanvasMessage_button": "מנקה את הקנבאס.", + "clearCanvasCaveat": " זה יגרום לאובדן העבודה ", + "trackedToSentry_pre": "השגיאה עם מזהה ", "trackedToSentry_post": " נמצאה במערכת שלנו.", - "openIssueMessage_pre": "נזהרנו מאוד שלא לכלול מידע שלך בשגיאה. אם המידע איננו אישי, בבקשה עקוב אחר ", + "openIssueMessage_pre": "נזהרנו מאוד שלא לכלול מידע מהקנבאס שלך בשגיאה. אם המידע בקנבאס אינו אישי, שקול לבצע מעקב אחר הטיפול שלנו ", "openIssueMessage_button": "מעקב באגים.", - "openIssueMessage_post": " בבקשה כלול את המידע למטה באמצעות העתקה והדבקה בנושא ב GitHub.", - "sceneContent": "תוכן הלוח:" + "openIssueMessage_post": " בבקשה כלול את המידע מטה באמצעות העתקתה שלו, והדבקה שלו ב- GitHub Issue.", + "sceneContent": "תוכן הקנבאס:" }, "roomDialog": { - "desc_intro": "אתה יכול להזמין אנשים ללוח הנוכחי שלך בכדי לשתף פעולה.", + "desc_intro": "אתה יכול להזמין אנשים לקנבאס הנוכחי שלך לעבודה משותפת.", "desc_privacy": "אל דאגה, השיתוף מוצפן מקצה לקצה, כך שכל מה שתצייר ישאר פרטי. אפילו השרתים שלנו לא יוכלו לראות את מה שאתה ממציא.", "button_startSession": "התחל שיתוף", "button_stopSession": "הפסק שיתוף", - "desc_inProgressIntro": "שיתוף חי כרגע בפעולה.", + "desc_inProgressIntro": "שיתוף חי פעיל כרגע.", "desc_shareLink": "שתף את הקישור עם כל מי שאתה מעוניין לעבוד אתו:", - "desc_exitSession": "עצירת השיתוף תנתק אותך מהחדר, אבל עדיין תוכל להמשיך לעבוד על הלוח, מקומית. שים לב שזה לא ישפיע על אנשים אחרים, והם עדיין יוכלו לשתף פעולה עם הגירסה שלהם.", - "shareTitle": "הצטרף לסשן שיתוף בזמן אמת של Excalidraw" + "desc_exitSession": "עצירת השיתוף תנתק אותך מהחדר, אבל עדיין תוכל להמשיך לעבוד על הקנבאס, מקומית. שים לב שזה לא ישפיע על אנשים אחרים, והם עדיין יוכלו לבצע שיתוף עם הגרסה שלהם.", + "shareTitle": "הצטרף לשיתוף לעבודה משותפת חיה, בזמן אמת, על גבי Excalidraw" }, "errorDialog": { "title": "שגיאה" }, "exportDialog": { "disk_title": "שמור לכונן", - "disk_details": "ייצוא מידע הסצינה לקובץ אותו ניתן יהיה לייבא בהמשך.", + "disk_details": "ייצא מידע של הקנבאס לקובץ שתוכל לייבא אחר כך.", "disk_button": "שמירה לקובץ", - "link_title": "העתקת קישור לשיתוף", + "link_title": "קבל קישור לשיתוף", "link_details": "ייצוא כקישור לקריאה בלבד.", - "link_button": "ייצוא כקישור", - "excalidrawplus_description": "שמור את המפה לסביבת העבודה שלך ב-Excalidraw+.", + "link_button": "ייצוא לקישור", + "excalidrawplus_description": "שמור את הקנבאס לסביבת העבודה שלך ב- +Excalidraw.", "excalidrawplus_button": "ייצוא", - "excalidrawplus_exportError": "הייצוא ל-Excalidraw+ לא הצליח לעת עתה..." + "excalidrawplus_exportError": "לא הצלחתי לייצא ל- +Excalidraw כרגע..." }, "helpDialog": { "blog": "קרא את הבלוג שלנו", @@ -301,30 +317,31 @@ "curvedLine": "קו מעוגל", "documentation": "תיעוד", "doubleClick": "לחיצה כפולה", - "drag": "לגרור", + "drag": "גרור", "editor": "עורך", - "editSelectedShape": "ערוך את הצורה הנבחרת (טקסט/חץ/קו)", + "editLineArrowPoints": "", + "editText": "", "github": "מצאת בעיה? דווח", "howto": "עקוב אחר המדריכים שלנו", "or": "או", "preventBinding": "למנוע נעיצת חיצים", "tools": "כלים", "shortcuts": "קיצורי מקלדת", - "textFinish": "סיים עריכה (טקסט)", - "textNewLine": "הוסף שורה חדשה (טקסט)", + "textFinish": "סיים עריכה (עורך טקסט)", + "textNewLine": "הוסף שורה חדשה (עורך טקסט)", "title": "עזרה", "view": "תצוגה", - "zoomToFit": "גלילה להצגת כל האלמנטים במסך", + "zoomToFit": "זום להתאמת כל האלמנטים למסך", "zoomToSelection": "התמקד בבחירה", "toggleElementLock": "נעילה/ביטול הנעילה של הרכיבים הנבחרים", - "movePageUpDown": "", - "movePageLeftRight": "" + "movePageUpDown": "זוז עמוד למעלה/למטה", + "movePageLeftRight": "זוז עמוד שמאלה/ימינה" }, "clearCanvasDialog": { "title": "ניקוי הקנבס" }, "publishDialog": { - "title": "פרסום ספריה", + "title": "פרסם ספריה", "itemName": "שם הפריט", "authorName": "שם היוצר", "githubUsername": "שם המשתמש שלך ב-GitHub", @@ -333,11 +350,11 @@ "libraryDesc": "תיאור הספריה", "website": "אתר", "placeholder": { - "authorName": "שם או שם משתמש", + "authorName": "שמך או שם המשתמש שלך", "libraryName": "תנו שם לספריה", "libraryDesc": "תיאור של הספריה שלך כדי לסייע למשתמשים להבין את השימוש בה", - "githubHandle": "", - "twitterHandle": "", + "githubHandle": "כינוי GitHub (לא חובה), כדי שתוכל לערוך את הספרית לאחר שנשלחה לבדיקה", + "twitterHandle": "שם משתמש טוויטר (לא חובה), כדי שנדע למי לתת קרדיט כשאנחנו מפרסמים בטוויטר", "website": "קישור לאתר הפרטי שלך או לכל מקום אחר (אופציונאלי)" }, "errors": { @@ -345,44 +362,44 @@ "website": "הזינו כתובת URL תקינה" }, "noteDescription": { - "pre": "להציע את הספריה שלך להיות כלולה ב", - "link": "מאגר הספריה הציבורי", - "post": "כך שאחרים יוכלו לעשות שימוש בציורים שלהם." + "pre": "הגש את הספריה שלך להכללתה ב ", + "link": "מאגר הספריה הציבורית", + "post": "לשימושם של אנשים אחרים בציורים שלהם." }, "noteGuidelines": { - "pre": "הספריה צריכה לקבל אישור ידני. אנא קרא את ", + "pre": "הספריה צריכה לקבל אישור ידני קודם לכן. אנא קרא את ", "link": "הנחיות", - "post": "" + "post": " לפני השליחה. אתה תצטרך לחשבון GitHub כדי לתקשר ולבצע שינויים אם תתבקש, אבל זה לא דרישה הכרחית." }, "noteLicense": { - "pre": "", + "pre": "על ידי שליחה, אתה מסכים שהסיפריה תפורסם תחת ה- ", "link": "רישיון MIT, ", - "post": "שאומר בקצרה שכל אחד יכול לעשות בהם שימוש ללא מגבלות." + "post": "שאומר בקצרה, שכל אחד יכול לעשות בהם שימוש ללא מגבלות." }, - "noteItems": "", - "atleastOneLibItem": "", - "republishWarning": "" + "noteItems": "לכל פריט בסיפריה חייב להיות שם כדי שאפשר יהיה לסנן. הפריטי סיפריה הבאים יהיו כלולים:", + "atleastOneLibItem": "אנא בחר לפחות פריט אחד מספריה כדי להתחיל", + "republishWarning": "הערה: חלק מהפריטים שבחרת מסומנים ככאלו שכבר פורסמו/נשלחו. אתה צריך לשלוח פריטים מחדש כאשר אתה מעדכן ספריה או הגשה קיימים." }, "publishSuccessDialog": { - "title": "הספריה נשלחה", + "title": "הספריה הוגשה", "content": "תודה {{authorName}}. הספריה שלך נשלחה לבחינה. תוכל לעקוב אחרי סטטוס הפרסום", "link": "כאן" }, "confirmDialog": { "resetLibrary": "איפוס ספריה", - "removeItemsFromLib": "להסיר את הפריטים הנבחרים מהספריה" + "removeItemsFromLib": "הסר את הפריטים הנבחרים מהספריה" }, "encrypted": { - "tooltip": "הרישומים שלך מוצפנים מקצה לקצה כך שהשרתים של Excalidraw לא יראו אותם לעולם.", + "tooltip": "הציורים שלך מוצפנים מקצה לקצה כך שהשרתים של Excalidraw לא יראו אותם לעולם.", "link": "פוסט בבלוג על הצפנה מקצה לקצב ב-Excalidraw" }, "stats": { "angle": "זווית", - "element": "אלמנט", - "elements": "אלמנטים", + "element": "רכיב", + "elements": "רכיבים", "height": "גובה", "scene": "תצוגה", - "selected": "נבחר/ים", + "selected": "נבחר", "storage": "אחסון", "title": "סטטיסטיקות לחנונים", "total": "סה״כ", @@ -393,14 +410,14 @@ }, "toast": { "addedToLibrary": "נוסף לספריה", - "copyStyles": "העתק סגנונות.", - "copyToClipboard": "הועתק אל הלוח.", + "copyStyles": "סגנונות הועתקו.", + "copyToClipboard": "הועתק ללוח.", "copyToClipboardAsPng": "{{exportSelection}} הועתקה ללוח כ-PNG\n({{exportColorScheme}})", "fileSaved": "קובץ נשמר.", "fileSavedToFilename": "נשמר לקובץ {filename}", - "canvas": "משטח ציור", + "canvas": "קנבאס", "selection": "בחירה", - "pasteAsSingleElement": "" + "pasteAsSingleElement": "השתמש ב- {{shortcut}} כדי להדביק כפריט יחיד,\nאו הדבק לתוך עורך טקסט קיים" }, "colors": { "ffffff": "לבן", @@ -443,23 +460,23 @@ "364fc7": "כחול כהה 9", "1864ab": "כחול 9", "0b7285": "טורקיז 9", - "087f5b": "ירקרק 9", + "087f5b": "ירוק-כחול 9", "2b8a3e": "ירוק 9", "5c940d": "ליים 9", - "e67700": "ירוק 9", + "e67700": "צהוב 9", "d9480f": "כתום 9" }, "welcomeScreen": { "app": { - "center_heading": "", - "center_heading_plus": "", - "menuHint": "" + "center_heading": "כל המידע שלח נשמר מקומית בדפדפן.", + "center_heading_plus": "אתה רוצה ללכת אל Excalidraw+ במקום?", + "menuHint": "ייצוא, העדפות, שפות, ..." }, "defaults": { - "menuHint": "", - "center_heading": "", - "toolbarHint": "", - "helpHint": "" + "menuHint": "ייצוא, העדפות, ועוד...", + "center_heading": "איורים. נעשים. פשוטים.", + "toolbarHint": "בחר כלי & והתחל לצייר!", + "helpHint": "קיצורים & עזרה" } } } diff --git a/src/locales/hi-IN.json b/src/locales/hi-IN.json index 9e3a14d64..77d6dae2d 100644 --- a/src/locales/hi-IN.json +++ b/src/locales/hi-IN.json @@ -110,6 +110,7 @@ "increaseFontSize": "फ़ॉन्ट आकार बढ़ाएँ", "unbindText": "", "bindText": "लेखन को कोश से जोड़े", + "createContainerFromText": "मूलपाठ कंटेनर में मोड के दिखाए", "link": { "edit": "", "create": "", @@ -204,7 +205,22 @@ "cannotResolveCollabServer": "कॉलेब सर्वर से कनेक्शन नहीं हो पा रहा. कृपया पृष्ठ को पुनः लाने का प्रयास करे.", "importLibraryError": "संग्रह प्रतिष्ठापित नहीं किया जा सका", "collabSaveFailed": "किसी कारण वश अंदरूनी डेटाबेस में सहेजा नहीं जा सका। यदि समस्या बनी रहती है, तो किये काम को खोने न देने के लिये अपनी फ़ाइल को स्थानीय रूप से सहेजे।", - "collabSaveFailed_sizeExceeded": "लगता है कि पृष्ठ तल काफ़ी बड़ा है, इस्कारण अंदरूनी डेटाबेस में सहेजा नहीं जा सका। किये काम को खोने न देने के लिये अपनी फ़ाइल को स्थानीय रूप से सहेजे।" + "collabSaveFailed_sizeExceeded": "लगता है कि पृष्ठ तल काफ़ी बड़ा है, इस्कारण अंदरूनी डेटाबेस में सहेजा नहीं जा सका। किये काम को खोने न देने के लिये अपनी फ़ाइल को स्थानीय रूप से सहेजे।", + "brave_measure_text_error": { + "start": "ऐसा लगता है कि आप ब्रेव ब्राउज़र का उपयोग कर रहे है, उसके साथ", + "aggressive_block_fingerprint": "उग्रतापूर्वक उंगलियों के निशान रोकने की", + "setting_enabled": "सेटिंग सक्रिय की गई है।", + "break": "इसके परिणाम स्वरूम टूट सकता है यह", + "text_elements": "मूल पाठ अवयव टूट सकते हैं", + "in_your_drawings": "आपके चित्र में उपस्थित", + "strongly_recommend": "हमारा शशक्त अनुरोध इस सेटिंग्स को अक्षम करने का है. आप", + "steps": "इन दिए हुए स्टेप्स को अनुकरीत करके देख सकते है कि", + "how": "ये कैसे करे", + "disable_setting": " यदि इन सेटिंग्स को निष्क्रिय करने पर भी, पाठ्य दिखना ठीक न हो तो", + "issue": "समस्या", + "write": "हमारे \"GitHub\" पर, अथवा", + "discord": "\"Discord\" पर लिख सकते हैं." + } }, "toolBar": { "selection": "चयन", @@ -303,7 +319,8 @@ "doubleClick": "", "drag": "खींचें", "editor": "संपादक", - "editSelectedShape": "", + "editLineArrowPoints": "रेखा/तीर बिंदु सम्पादित करे", + "editText": "पाठ्य सम्पादित करे/ लेबल जोड़े", "github": "मुद्दा मिला? प्रस्तुत करें", "howto": "हमारे गाइड का पालन करें", "or": "या", diff --git a/src/locales/hu-HU.json b/src/locales/hu-HU.json index a3cbd852c..d514520ed 100644 --- a/src/locales/hu-HU.json +++ b/src/locales/hu-HU.json @@ -110,6 +110,7 @@ "increaseFontSize": "Betűméret növelése", "unbindText": "Szövegkötés feloldása", "bindText": "", + "createContainerFromText": "", "link": { "edit": "Hivatkozás szerkesztése", "create": "Hivatkozás létrehozása", @@ -204,7 +205,22 @@ "cannotResolveCollabServer": "", "importLibraryError": "", "collabSaveFailed": "", - "collabSaveFailed_sizeExceeded": "" + "collabSaveFailed_sizeExceeded": "", + "brave_measure_text_error": { + "start": "", + "aggressive_block_fingerprint": "", + "setting_enabled": "", + "break": "", + "text_elements": "", + "in_your_drawings": "", + "strongly_recommend": "", + "steps": "", + "how": "", + "disable_setting": "", + "issue": "", + "write": "", + "discord": "" + } }, "toolBar": { "selection": "Kijelölés", @@ -303,7 +319,8 @@ "doubleClick": "dupla kattintás", "drag": "vonszolás", "editor": "Szerkesztő", - "editSelectedShape": "Kijelölt alakzat szerkesztése (szöveg/nyíl/vonal)", + "editLineArrowPoints": "", + "editText": "", "github": "Hibát találtál? Küld be", "howto": "Kövesd az útmutatóinkat", "or": "vagy", diff --git a/src/locales/id-ID.json b/src/locales/id-ID.json index cd722c6a3..01b510fcd 100644 --- a/src/locales/id-ID.json +++ b/src/locales/id-ID.json @@ -110,6 +110,7 @@ "increaseFontSize": "Besarkan ukuran font", "unbindText": "Lepas teks", "bindText": "Kunci teks ke kontainer", + "createContainerFromText": "", "link": { "edit": "Edit tautan", "create": "Buat tautan", @@ -193,7 +194,7 @@ "resetLibrary": "Ini akan menghapus pustaka Anda. Anda yakin?", "removeItemsFromsLibrary": "Hapus {{count}} item dari pustaka?", "invalidEncryptionKey": "Sandi enkripsi harus 22 karakter. Kolaborasi langsung dinonaktifkan.", - "collabOfflineWarning": "" + "collabOfflineWarning": "Tidak ada koneksi internet.\nPerubahan tidak akan disimpan!" }, "errors": { "unsupportedFileType": "Tipe file tidak didukung.", @@ -204,7 +205,22 @@ "cannotResolveCollabServer": "Tidak dapat terhubung ke server kolab. Muat ulang laman dan coba lagi.", "importLibraryError": "Tidak dapat memuat pustaka", "collabSaveFailed": "Tidak dapat menyimpan ke dalam basis data server. Jika masih berlanjut, Anda sebaiknya simpan berkas Anda secara lokal untuk memastikan pekerjaan Anda tidak hilang.", - "collabSaveFailed_sizeExceeded": "Tidak dapat menyimpan ke dalam basis data server, tampaknya ukuran kanvas terlalu besar. Anda sebaiknya simpan berkas Anda secara lokal untuk memastikan pekerjaan Anda tidak hilang." + "collabSaveFailed_sizeExceeded": "Tidak dapat menyimpan ke dalam basis data server, tampaknya ukuran kanvas terlalu besar. Anda sebaiknya simpan berkas Anda secara lokal untuk memastikan pekerjaan Anda tidak hilang.", + "brave_measure_text_error": { + "start": "Sepertinya kamu menggunakan browser Brave dengan", + "aggressive_block_fingerprint": "", + "setting_enabled": "pengaturan diaktifkan", + "break": "", + "text_elements": "Elemen Teks", + "in_your_drawings": "dalam gambar anda", + "strongly_recommend": "Kami sangat menyarankan untuk mematikan pengaturan ini. Kamu dapat mengikuti", + "steps": "langkah-langkah ini", + "how": "dalam bagaimana melakukan itu", + "disable_setting": " Jika pengaturan ini dimatikan tidak mengatasi masalah tampilan dari elemen teks, silahkan buka", + "issue": "isu", + "write": "di GitHub kami, atau tulis kami di", + "discord": "Discord" + } }, "toolBar": { "selection": "Pilihan", @@ -303,7 +319,8 @@ "doubleClick": "klik-ganda", "drag": "seret", "editor": "Editor", - "editSelectedShape": "Edit bentuk yang dipilih (teks/panah/garis)", + "editLineArrowPoints": "", + "editText": "", "github": "Menemukan masalah? Kirimkan", "howto": "Ikuti panduan kami", "or": "atau", diff --git a/src/locales/it-IT.json b/src/locales/it-IT.json index 3c6cf93a8..8380fd8e5 100644 --- a/src/locales/it-IT.json +++ b/src/locales/it-IT.json @@ -110,6 +110,7 @@ "increaseFontSize": "Aumenta la dimensione dei caratteri", "unbindText": "Scollega testo", "bindText": "Associa il testo al container", + "createContainerFromText": "", "link": { "edit": "Modifica link", "create": "Crea link", @@ -204,7 +205,22 @@ "cannotResolveCollabServer": "Impossibile connettersi al server di collab. Ricarica la pagina e riprova.", "importLibraryError": "Impossibile caricare la libreria", "collabSaveFailed": "Impossibile salvare nel database di backend. Se i problemi persistono, dovresti salvare il tuo file localmente per assicurarti di non perdere il tuo lavoro.", - "collabSaveFailed_sizeExceeded": "Impossibile salvare nel database di backend, la tela sembra essere troppo grande. Dovresti salvare il file localmente per assicurarti di non perdere il tuo lavoro." + "collabSaveFailed_sizeExceeded": "Impossibile salvare nel database di backend, la tela sembra essere troppo grande. Dovresti salvare il file localmente per assicurarti di non perdere il tuo lavoro.", + "brave_measure_text_error": { + "start": "Sembra che tu stia usando il browser Brave con il", + "aggressive_block_fingerprint": "Blocco Aggressivamente Impronte Digitali", + "setting_enabled": "impostazioni abilitate", + "break": "Questo potrebbe portare a rompere gli", + "text_elements": "Elementi Di Testo", + "in_your_drawings": "nei tuoi disegni", + "strongly_recommend": "Si consiglia vivamente di disabilitare questa impostazione. È possibile seguire", + "steps": "questi passaggi", + "how": "su come fare", + "disable_setting": " Se la disabilitazione di questa impostazione non corregge la visualizzazione degli elementi di testo, apri un", + "issue": "problema", + "write": "sul nostro GitHub, o scrivici su", + "discord": "Discord" + } }, "toolBar": { "selection": "Selezione", @@ -303,7 +319,8 @@ "doubleClick": "doppio-click", "drag": "trascina", "editor": "Editor", - "editSelectedShape": "Modifica la forma selezionata (testo/freccia/linea)", + "editLineArrowPoints": "", + "editText": "Modifica testo / aggiungi etichetta", "github": "Trovato un problema? Segnalalo", "howto": "Segui le nostre guide", "or": "oppure", diff --git a/src/locales/ja-JP.json b/src/locales/ja-JP.json index 4f7a76afe..53333aea3 100644 --- a/src/locales/ja-JP.json +++ b/src/locales/ja-JP.json @@ -110,6 +110,7 @@ "increaseFontSize": "フォントサイズを拡大", "unbindText": "テキストのバインド解除", "bindText": "テキストをコンテナにバインド", + "createContainerFromText": "", "link": { "edit": "リンクを編集", "create": "リンクを作成", @@ -204,7 +205,22 @@ "cannotResolveCollabServer": "コラボレーションサーバに接続できませんでした。ページを再読み込みして、もう一度お試しください。", "importLibraryError": "ライブラリを読み込めませんでした。", "collabSaveFailed": "バックエンドデータベースに保存できませんでした。問題が解決しない場合は、作業を失わないようにローカルにファイルを保存してください。", - "collabSaveFailed_sizeExceeded": "キャンバスが大きすぎるため、バックエンドデータベースに保存できませんでした。問題が解決しない場合は、作業を失わないようにローカルにファイルを保存してください。" + "collabSaveFailed_sizeExceeded": "キャンバスが大きすぎるため、バックエンドデータベースに保存できませんでした。問題が解決しない場合は、作業を失わないようにローカルにファイルを保存してください。", + "brave_measure_text_error": { + "start": "", + "aggressive_block_fingerprint": "", + "setting_enabled": "設定が有効化されました", + "break": "", + "text_elements": "テキスト要素", + "in_your_drawings": "", + "strongly_recommend": "", + "steps": "", + "how": "", + "disable_setting": "", + "issue": "", + "write": "", + "discord": "Discord" + } }, "toolBar": { "selection": "選択", @@ -303,7 +319,8 @@ "doubleClick": "ダブルクリック", "drag": "ドラッグ", "editor": "エディタ", - "editSelectedShape": "選択した図形の編集 (テキスト/矢印/線)", + "editLineArrowPoints": "", + "editText": "テキストの編集 / ラベルの追加", "github": "不具合報告はこちら", "howto": "ヘルプ・マニュアル", "or": "または", diff --git a/src/locales/kab-KAB.json b/src/locales/kab-KAB.json index 3603e88c7..ba6a3de7e 100644 --- a/src/locales/kab-KAB.json +++ b/src/locales/kab-KAB.json @@ -110,6 +110,7 @@ "increaseFontSize": "Sali tiddi n tsefsit", "unbindText": "Serreḥ iweḍris", "bindText": "Arez aḍris s anagbar", + "createContainerFromText": "", "link": { "edit": "Ẓreg aseɣwen", "create": "Snulfu-d aseɣwen", @@ -193,7 +194,7 @@ "resetLibrary": "Ayagi ad isfeḍ tamkarḍit-inek•m. Tetḥeqqeḍ?", "removeItemsFromsLibrary": "Ad tekkseḍ {{count}} n uferdis (en) si temkarḍit?", "invalidEncryptionKey": "Tasarut n uwgelhen isefk ad tesɛu 22 n yiekkilen. Amɛiwen srid yensa.", - "collabOfflineWarning": "" + "collabOfflineWarning": "Ulac tuqqna n internet.\nIbedilen-ik ur ttwaklasen ara!" }, "errors": { "unsupportedFileType": "Anaw n ufaylu ur yettwasefrak ara.", @@ -204,7 +205,22 @@ "cannotResolveCollabServer": "Ulamek tuqqna s aqeddac n umyalel. Ma ulac uɣilif ales asali n usebter sakin eɛreḍ tikkelt-nniḍen.", "importLibraryError": "Ur d-ssalay ara tamkarḍit", "collabSaveFailed": "Ulamek asekles deg uzadur n yisefka deg ugilal. Ma ikemmel wugur, isefk ad teskelseḍ afaylu s wudem adigan akken ad tetḥeqqeḍ ur tesruḥuyeḍ ara amahil-inek•inem.", - "collabSaveFailed_sizeExceeded": "Ulamek asekles deg uzadur n yisefka deg ugilal, taɣzut n usuneɣ tettban-d temqer aṭas. Isefk ad teskelseḍ afaylu s wudem adigan akken ad tetḥeqqeḍ ur tesruḥuyeḍ ara amahil-inek•inem." + "collabSaveFailed_sizeExceeded": "Ulamek asekles deg uzadur n yisefka deg ugilal, taɣzut n usuneɣ tettban-d temqer aṭas. Isefk ad teskelseḍ afaylu s wudem adigan akken ad tetḥeqqeḍ ur tesruḥuyeḍ ara amahil-inek•inem.", + "brave_measure_text_error": { + "start": "Ittban-d am wakken tsseqdaceḍ iminig Brave akked", + "aggressive_block_fingerprint": "", + "setting_enabled": "yermed", + "break": "Ayagi yezmer ad d-iseglu s truẓi n", + "text_elements": "Iferdisen iḍrisen", + "in_your_drawings": "deg wunuɣen-inek", + "strongly_recommend": "", + "steps": "isurifen-agi", + "how": "", + "disable_setting": "", + "issue": "", + "write": "di GitHub inek neɣ aru-yaɣ-d di", + "discord": "" + } }, "toolBar": { "selection": "Tafrayt", @@ -229,7 +245,7 @@ "shapes": "Talɣiwin" }, "hints": { - "canvasPanning": "", + "canvasPanning": "Akken ad tesmuttiḍ taɣzut n usuneɣ, ṭṭef ṛṛuda n umumed, neɣ seqdec afecku Afus", "linearElement": "Ssit akken ad tebduḍ aṭas n tenqiḍin, zuɣer i yiwen n yizirig", "freeDraw": "Ssit yerna zuɣer, serreḥ ticki tfukeḍ", "text": "Tixidest: tzemreḍ daɣen ad ternuḍ aḍris s usiti snat n tikkal anida tebɣiḍ s ufecku n tefrayt", @@ -240,7 +256,7 @@ "resize": "Tzemreḍ ad tḥettemeḍ assaɣ s tuṭṭfa n tqeffalt SHIFT mi ara tettbeddileḍ tiddi,\nma teṭṭfeḍ ALT abeddel n tiddi ad yili si tlemmast", "resizeImage": "Tzemreḍ ad talseḍ tiddi s tilelli s tuṭṭfa n SHIFT,\nṭṭef ALT akken ad talseḍ tiddi si tlemmast", "rotate": "Tzemreḍ ad tḥettemeḍ tiɣemmar s tuṭṭfa n SHIFT di tuzzya", - "lineEditor_info": "", + "lineEditor_info": "Ssed ɣef CtrlOrCmd yerna ssit snat n tikkal neɣ ssed ɣef CtrlOrCmd + Kcem akken ad tẓergeḍ tineqqiḍin", "lineEditor_pointSelected": "Ssed taqeffalt kkes akken ad tekkseḍ tanqiḍ (tinqiḍin),\nCtrlOrCmd+D akken ad tsiselgeḍ, neɣ zuɣer akken ad tesmuttiḍ", "lineEditor_nothingSelected": "Fren tanqiḍt akken ad tẓergeḍ (ṭṭef SHIFT akken ad tferneḍ aṭas),\nneɣ ṭṭef Alt akken ad ternuḍ tinqiḍin timaynutin", "placeImage": "Ssit akken ad tserseḍ tugna, neɣ ssit u zuɣer akken ad tesbaduḍ tiddi-ines s ufus", @@ -303,7 +319,8 @@ "doubleClick": "ssit snat n tikkal", "drag": "zuɣer", "editor": "Amaẓrag", - "editSelectedShape": "Ẓreg talɣa yettwafernen (aḍris/taneccabt/izirig)", + "editLineArrowPoints": "", + "editText": "", "github": "Tufiḍ-d ugur? Azen-aɣ-d", "howto": "Ḍfer imniren-nneɣ", "or": "neɣ", @@ -452,11 +469,11 @@ "welcomeScreen": { "app": { "center_heading": "", - "center_heading_plus": "", - "menuHint": "" + "center_heading_plus": "Tebɣiḍ ad tedduḍ ɣer Excalidraw+ deg umḍiq?", + "menuHint": "Asifeḍ, ismenyifen, tutlayin, ..." }, "defaults": { - "menuHint": "", + "menuHint": "Asifeḍ, ismenyifen, d wayen-nniḍen...", "center_heading": "", "toolbarHint": "", "helpHint": "" diff --git a/src/locales/kk-KZ.json b/src/locales/kk-KZ.json index ee1e44083..97a9063fa 100644 --- a/src/locales/kk-KZ.json +++ b/src/locales/kk-KZ.json @@ -110,6 +110,7 @@ "increaseFontSize": "", "unbindText": "", "bindText": "", + "createContainerFromText": "", "link": { "edit": "", "create": "", @@ -204,7 +205,22 @@ "cannotResolveCollabServer": "", "importLibraryError": "", "collabSaveFailed": "", - "collabSaveFailed_sizeExceeded": "" + "collabSaveFailed_sizeExceeded": "", + "brave_measure_text_error": { + "start": "", + "aggressive_block_fingerprint": "", + "setting_enabled": "", + "break": "", + "text_elements": "", + "in_your_drawings": "", + "strongly_recommend": "", + "steps": "", + "how": "", + "disable_setting": "", + "issue": "", + "write": "", + "discord": "" + } }, "toolBar": { "selection": "", @@ -303,7 +319,8 @@ "doubleClick": "қос шерту", "drag": "апару", "editor": "Өңдеу", - "editSelectedShape": "Таңдалған пішінді өңдеу (мәтін/нұсқар/сызық)", + "editLineArrowPoints": "", + "editText": "", "github": "Қате таптыңыз ба? Жолдаңыз", "howto": "Біздің нұсқаулықтарды орындаңыз", "or": "немесе", diff --git a/src/locales/ko-KR.json b/src/locales/ko-KR.json index 9c02b3ea1..f170e4cbf 100644 --- a/src/locales/ko-KR.json +++ b/src/locales/ko-KR.json @@ -110,6 +110,7 @@ "increaseFontSize": "폰트 사이즈 키우기", "unbindText": "텍스트 분리", "bindText": "텍스트를 컨테이너에 결합", + "createContainerFromText": "텍스트를 컨테이너에 담기", "link": { "edit": "링크 수정하기", "create": "링크 만들기", @@ -193,7 +194,7 @@ "resetLibrary": "당신의 라이브러리를 초기화 합니다. 계속하시겠습니까?", "removeItemsFromsLibrary": "{{count}}개의 아이템을 라이브러리에서 삭제하시겠습니까?", "invalidEncryptionKey": "암호화 키는 반드시 22글자여야 합니다. 실시간 협업이 비활성화됩니다.", - "collabOfflineWarning": "" + "collabOfflineWarning": "인터넷에 연결되어 있지 않습니다.\n변경 사항들이 저장되지 않습니다!" }, "errors": { "unsupportedFileType": "지원하지 않는 파일 형식 입니다.", @@ -204,7 +205,22 @@ "cannotResolveCollabServer": "협업 서버에 접속하는데 실패했습니다. 페이지를 새로고침하고 다시 시도해보세요.", "importLibraryError": "라이브러리를 불러오지 못했습니다.", "collabSaveFailed": "데이터베이스에 저장하지 못했습니다. 문제가 계속 된다면, 작업 내용을 잃지 않도록 로컬 저장소에 저장해 주세요.", - "collabSaveFailed_sizeExceeded": "데이터베이스에 저장하지 못했습니다. 캔버스가 너무 큰 거 같습니다. 문제가 계속 된다면, 작업 내용을 잃지 않도록 로컬 저장소에 저장해 주세요." + "collabSaveFailed_sizeExceeded": "데이터베이스에 저장하지 못했습니다. 캔버스가 너무 큰 거 같습니다. 문제가 계속 된다면, 작업 내용을 잃지 않도록 로컬 저장소에 저장해 주세요.", + "brave_measure_text_error": { + "start": "귀하께서는", + "aggressive_block_fingerprint": "강력한 지문 차단", + "setting_enabled": "설정이 활성화된 Brave browser를 사용하고 계신 것 같습니다", + "break": "이 기능으로 인해 화이트보드의", + "text_elements": "텍스트 요소들이", + "in_your_drawings": "손상될 수 있습니다", + "strongly_recommend": "해당 기능을 설정에서 비활성화하는 것을 강력히 권장 드립니다. 비활성화 방법에 대해서는", + "steps": "이 게시글을", + "how": "참고해주세요.", + "disable_setting": " 만약 이 설정을 껐음에도 텍스트 요소들이 올바르게 표시되지 않는다면, 저희", + "issue": "Github에 이슈를", + "write": "올려주시거나", + "discord": "Discord에 제보해주세요" + } }, "toolBar": { "selection": "선택", @@ -221,7 +237,7 @@ "penMode": "펜 모드 - 터치 방지", "link": "선택한 도형에 대해서 링크를 추가/업데이트", "eraser": "지우개", - "hand": "" + "hand": "손 (패닝 도구)" }, "headings": { "canvasActions": "캔버스 동작", @@ -229,7 +245,7 @@ "shapes": "모양" }, "hints": { - "canvasPanning": "", + "canvasPanning": "캔버스를 옮기려면 마우스 휠이나 스페이스바를 누르고 드래그하거나, 손 도구를 사용하기", "linearElement": "여러 점을 연결하려면 클릭하고, 직선을 그리려면 바로 드래그하세요.", "freeDraw": "클릭 후 드래그하세요. 완료되면 놓으세요.", "text": "팁: 선택 툴로 아무 곳이나 더블 클릭해 텍스트를 추가할 수도 있습니다.", @@ -248,7 +264,7 @@ "bindTextToElement": "Enter 키를 눌러서 텍스트 추가하기", "deepBoxSelect": "CtrlOrCmd 키를 눌러서 깊게 선택하고, 드래그하지 않도록 하기", "eraserRevert": "Alt를 눌러서 삭제하도록 지정된 요소를 되돌리기", - "firefox_clipboard_write": "" + "firefox_clipboard_write": "이 기능은 설정에서 \"dom.events.asyncClipboard.clipboardItem\" 플래그를 \"true\"로 설정하여 활성화할 수 있습니다. Firefox에서 브라우저 플래그를 수정하려면, \"about:config\" 페이지에 접속하세요." }, "canvasError": { "cannotShowPreview": "미리보기를 볼 수 없습니다", @@ -303,7 +319,8 @@ "doubleClick": "더블 클릭", "drag": "드래그", "editor": "에디터", - "editSelectedShape": "선택한 도형 편집하기(텍스트/화살표/라인)", + "editLineArrowPoints": "", + "editText": "", "github": "문제 제보하기", "howto": "가이드 참고하기", "or": "또는", @@ -451,14 +468,14 @@ }, "welcomeScreen": { "app": { - "center_heading": "당신의 모든 데이터는 브라우저에 저장되었습니다.", + "center_heading": "모든 데이터는 브라우저에 안전하게 저장됩니다.", "center_heading_plus": "대신 Excalidraw+로 이동하시겠습니까?", "menuHint": "내보내기, 설정, 언어, ..." }, "defaults": { - "menuHint": "내보내기, 설정, 더 보기...", - "center_heading": "", - "toolbarHint": "도구 선택 & 그리기 시작", + "menuHint": "내보내기, 설정, 등등...", + "center_heading": "간단하게 만드는 다이어그램.", + "toolbarHint": "도구를 선택하고, 그리세요!", "helpHint": "단축키 & 도움말" } } diff --git a/src/locales/ku-TR.json b/src/locales/ku-TR.json index 4a8ebc95c..76b5086e8 100644 --- a/src/locales/ku-TR.json +++ b/src/locales/ku-TR.json @@ -110,6 +110,7 @@ "increaseFontSize": "زایدکردنی قەبارەی فۆنت", "unbindText": "دەقەکە جیابکەرەوە", "bindText": "دەقەکە ببەستەوە بە کۆنتەینەرەکەوە", + "createContainerFromText": "", "link": { "edit": "دەستکاریکردنی بەستەر", "create": "دروستکردنی بەستەر", @@ -204,7 +205,22 @@ "cannotResolveCollabServer": "ناتوانێت پەیوەندی بکات بە سێرڤەری کۆلاب. تکایە لاپەڕەکە دووبارە باربکەوە و دووبارە هەوڵ بدەوە.", "importLibraryError": "نەیتوانی کتێبخانە بار بکات", "collabSaveFailed": "", - "collabSaveFailed_sizeExceeded": "" + "collabSaveFailed_sizeExceeded": "", + "brave_measure_text_error": { + "start": "", + "aggressive_block_fingerprint": "", + "setting_enabled": "", + "break": "", + "text_elements": "", + "in_your_drawings": "", + "strongly_recommend": "", + "steps": "", + "how": "", + "disable_setting": "", + "issue": "", + "write": "", + "discord": "" + } }, "toolBar": { "selection": "دەستنیشانکردن", @@ -303,7 +319,8 @@ "doubleClick": "دوو گرتە", "drag": "راکێشان", "editor": "دەستکاریکەر", - "editSelectedShape": "دەستکاریکردنی شێوەی هەڵبژێردراو (دەق/تیر/هێڵ)", + "editLineArrowPoints": "", + "editText": "", "github": "کێشەیەکت دۆزیەوە؟ پێشکەشکردن", "howto": "شوێن ڕینماییەکانمان بکەوە", "or": "یان", diff --git a/src/locales/lt-LT.json b/src/locales/lt-LT.json index a8a8d6639..d80739f09 100644 --- a/src/locales/lt-LT.json +++ b/src/locales/lt-LT.json @@ -110,6 +110,7 @@ "increaseFontSize": "", "unbindText": "", "bindText": "", + "createContainerFromText": "", "link": { "edit": "Redeguoti nuorodą", "create": "Sukurti nuorodą", @@ -204,7 +205,22 @@ "cannotResolveCollabServer": "Nepavyko prisijungti prie serverio bendradarbiavimui. Perkrauk puslapį ir pabandyk prisijungti dar kartą.", "importLibraryError": "Nepavyko įkelti bibliotekos", "collabSaveFailed": "", - "collabSaveFailed_sizeExceeded": "" + "collabSaveFailed_sizeExceeded": "", + "brave_measure_text_error": { + "start": "", + "aggressive_block_fingerprint": "", + "setting_enabled": "", + "break": "", + "text_elements": "", + "in_your_drawings": "", + "strongly_recommend": "", + "steps": "", + "how": "", + "disable_setting": "", + "issue": "", + "write": "", + "discord": "" + } }, "toolBar": { "selection": "Žymėjimas", @@ -303,7 +319,8 @@ "doubleClick": "dvigubas paspaudimas", "drag": "vilkti", "editor": "Redaktorius", - "editSelectedShape": "", + "editLineArrowPoints": "", + "editText": "", "github": "Radai klaidą? Pateik", "howto": "Vadovaukis mūsų gidu", "or": "arba", diff --git a/src/locales/lv-LV.json b/src/locales/lv-LV.json index ea3180149..4a311f7cd 100644 --- a/src/locales/lv-LV.json +++ b/src/locales/lv-LV.json @@ -110,6 +110,7 @@ "increaseFontSize": "Palielināt fonta izmēru", "unbindText": "Atdalīt tekstu", "bindText": "Piesaistīt tekstu figūrai", + "createContainerFromText": "", "link": { "edit": "Rediģēt saiti", "create": "Izveidot saiti", @@ -204,7 +205,22 @@ "cannotResolveCollabServer": "Nevarēja savienoties ar sadarbošanās serveri. Lūdzu, pārlādējiet lapu un mēģiniet vēlreiz.", "importLibraryError": "Nevarēja ielādēt bibliotēku", "collabSaveFailed": "Darbs nav saglabāts datubāzē. Ja problēma turpinās, saglabājiet datni lokālajā krātuvē, lai nodrošinātos pret darba pazaudēšanu.", - "collabSaveFailed_sizeExceeded": "Darbs nav saglabāts datubāzē, šķiet, ka tāfele ir pārāk liela. Saglabājiet datni lokālajā krātuvē, lai nodrošinātos pret darba pazaudēšanu." + "collabSaveFailed_sizeExceeded": "Darbs nav saglabāts datubāzē, šķiet, ka tāfele ir pārāk liela. Saglabājiet datni lokālajā krātuvē, lai nodrošinātos pret darba pazaudēšanu.", + "brave_measure_text_error": { + "start": "", + "aggressive_block_fingerprint": "", + "setting_enabled": "", + "break": "", + "text_elements": "", + "in_your_drawings": "", + "strongly_recommend": "", + "steps": "", + "how": "", + "disable_setting": "", + "issue": "", + "write": "", + "discord": "" + } }, "toolBar": { "selection": "Atlase", @@ -303,7 +319,8 @@ "doubleClick": "dubultklikšķis", "drag": "vilkt", "editor": "Redaktors", - "editSelectedShape": "Rediģēt atlasīto figūru (tekstu/bultu/līniju)", + "editLineArrowPoints": "", + "editText": "", "github": "Sastapāt kļūdu? Ziņot", "howto": "Sekojiet mūsu instrukcijām", "or": "vai", diff --git a/src/locales/mr-IN.json b/src/locales/mr-IN.json index e67fc3159..4f58009c9 100644 --- a/src/locales/mr-IN.json +++ b/src/locales/mr-IN.json @@ -110,6 +110,7 @@ "increaseFontSize": "अक्षर आकार मोठा करा", "unbindText": "लेखन संबंध संपवा", "bindText": "शब्द समूह ला पात्रात घ्या", + "createContainerFromText": "मजकूर कंटेनर मधे मोडून दाखवा", "link": { "edit": "दुवा संपादन", "create": "दुवा तयार करा", @@ -204,7 +205,22 @@ "cannotResolveCollabServer": "कॉलेब-सर्वर हे पोहोचत नाही आहे. पान परत लोड करायचा प्रयत्न करावे.", "importLibraryError": "संग्रह प्रतिस्थापित नाही करता आला", "collabSaveFailed": "काही कारणा निमित्त आतल्या डेटाबेसमध्ये जतन करू शकत नाही। समस्या तशिस राहिल्यास, तुम्ही तुमचे काम गमावणार नाही याची खात्री करण्यासाठी तुम्ही तुमची फाइल स्थानिक जतन करावी.", - "collabSaveFailed_sizeExceeded": "लगता है कि पृष्ठ तल काफ़ी बड़ा है, इस्कारण अंदरूनी डेटाबेस में सहेजा नहीं जा सका। किये काम को खोने न देने के लिये अपनी फ़ाइल को स्थानीय रूप से सहेजे।\n\nबॅकएंड डेटाबेसमध्ये जतन करू शकत नाही, कॅनव्हास खूप मोठा असल्याचे दिसते. तुम्ही तुमचे काम गमावणार नाही याची खात्री करण्यासाठी तुम्ही फाइल स्थानिक पातळीवर जतन करावी." + "collabSaveFailed_sizeExceeded": "लगता है कि पृष्ठ तल काफ़ी बड़ा है, इस्कारण अंदरूनी डेटाबेस में सहेजा नहीं जा सका। किये काम को खोने न देने के लिये अपनी फ़ाइल को स्थानीय रूप से सहेजे।\n\nबॅकएंड डेटाबेसमध्ये जतन करू शकत नाही, कॅनव्हास खूप मोठा असल्याचे दिसते. तुम्ही तुमचे काम गमावणार नाही याची खात्री करण्यासाठी तुम्ही फाइल स्थानिक पातळीवर जतन करावी.", + "brave_measure_text_error": { + "start": "असं वाटते की तुम्हीं \"Brave\" \"Browser\" वापरत आहात, त्या बरोबार", + "aggressive_block_fingerprint": "बोटांचे ठसे उग्रतेने थाम्बवाचे", + "setting_enabled": "सेटिंग्स सक्रिय केले आहेत", + "break": "ह्या कारणानिं", + "text_elements": "पाठ अवयव तुटु शकतात", + "in_your_drawings": "तुमच्या चित्रिकराणतले", + "strongly_recommend": "आमचा ज़ोरदार सल्ला असा कि सेटिंग्स निष्क्रिय करावे. तुम्हीं", + "steps": "ह्या स्टेप्स", + "how": "घेउ शकतात", + "disable_setting": " जर सेटिंग्स निष्क्रिय करून पाठ्य दिसणे ठीक नसेल होत तर", + "issue": "मुद्दा", + "write": "आमच्या Github वर, किव्हा आम्हाला", + "discord": "\"Discord\" वर लिहां" + } }, "toolBar": { "selection": "निवड", @@ -303,7 +319,8 @@ "doubleClick": "दुहेरी क्लिक", "drag": "ओढा", "editor": "संपादक", - "editSelectedShape": "निवडलेला प्रकार संपादित करा (मजकूर/बाण/रेघ)", + "editLineArrowPoints": "रेघ/तीर बिंदु सम्पादित करा", + "editText": "पाठ्य सम्पादित करा/ लेबल जोडा", "github": "समस्या मिळाली? प्रस्तुत करा", "howto": "आमच्या मार्गदर्शकाचे अनुसरण करा", "or": "किंवा", diff --git a/src/locales/my-MM.json b/src/locales/my-MM.json index e479d3702..efc874218 100644 --- a/src/locales/my-MM.json +++ b/src/locales/my-MM.json @@ -110,6 +110,7 @@ "increaseFontSize": "", "unbindText": "", "bindText": "", + "createContainerFromText": "", "link": { "edit": "", "create": "", @@ -204,7 +205,22 @@ "cannotResolveCollabServer": "", "importLibraryError": "", "collabSaveFailed": "", - "collabSaveFailed_sizeExceeded": "" + "collabSaveFailed_sizeExceeded": "", + "brave_measure_text_error": { + "start": "", + "aggressive_block_fingerprint": "", + "setting_enabled": "", + "break": "", + "text_elements": "", + "in_your_drawings": "", + "strongly_recommend": "", + "steps": "", + "how": "", + "disable_setting": "", + "issue": "", + "write": "", + "discord": "" + } }, "toolBar": { "selection": "ရွေးချယ်", @@ -303,7 +319,8 @@ "doubleClick": "", "drag": "", "editor": "", - "editSelectedShape": "", + "editLineArrowPoints": "", + "editText": "", "github": "", "howto": "", "or": "", diff --git a/src/locales/nb-NO.json b/src/locales/nb-NO.json index 653779a56..27e717d2a 100644 --- a/src/locales/nb-NO.json +++ b/src/locales/nb-NO.json @@ -110,6 +110,7 @@ "increaseFontSize": "Øk skriftstørrelse", "unbindText": "Avbind tekst", "bindText": "Bind tekst til beholderen", + "createContainerFromText": "La tekst flyte i en beholder", "link": { "edit": "Rediger lenke", "create": "Opprett lenke", @@ -204,7 +205,22 @@ "cannotResolveCollabServer": "Kunne ikke koble til samarbeidsserveren. Vennligst oppdater siden og prøv på nytt.", "importLibraryError": "Kunne ikke laste bibliotek", "collabSaveFailed": "Kan ikke lagre i backend-databasen. Hvis problemer vedvarer, bør du lagre filen lokalt for å sikre at du ikke mister arbeidet.", - "collabSaveFailed_sizeExceeded": "Kunne ikke lagre til backend-databasen, lerretet ser ut til å være for stort. Du bør lagre filen lokalt for å sikre at du ikke mister arbeidet ditt." + "collabSaveFailed_sizeExceeded": "Kunne ikke lagre til backend-databasen, lerretet ser ut til å være for stort. Du bør lagre filen lokalt for å sikre at du ikke mister arbeidet ditt.", + "brave_measure_text_error": { + "start": "Ser ut som du bruker Brave nettleser med", + "aggressive_block_fingerprint": "Blokker fingeravtrykk aggressivt", + "setting_enabled": "innstilling aktivert", + "break": "Dette kan føre til at den bryter", + "text_elements": "Tekstelementer", + "in_your_drawings": "i tegningene dine", + "strongly_recommend": "Vi anbefaler på det sterkeste å deaktivere denne innstillingen. Du kan følge dette", + "steps": "disse trinnene", + "how": "om hvordan det skal gjøres", + "disable_setting": " Hvis deaktivering av denne innstillingen ikke fikser visningen av tekstelementer, kan du åpne en", + "issue": "sak", + "write": "på vår GitHub, eller skriv oss på", + "discord": "Discord" + } }, "toolBar": { "selection": "Velg", @@ -303,7 +319,8 @@ "doubleClick": "dobbeltklikk", "drag": "dra", "editor": "Redigeringsvisning", - "editSelectedShape": "Rediger valgt figur (tekst/pil/linje)", + "editLineArrowPoints": "Rediger linje/pilpunkter", + "editText": "Rediger tekst / legg til etikett", "github": "Funnet et problem? Send inn", "howto": "Følg våre veiledninger", "or": "eller", diff --git a/src/locales/nl-NL.json b/src/locales/nl-NL.json index 313c8dc47..7c2bb105b 100644 --- a/src/locales/nl-NL.json +++ b/src/locales/nl-NL.json @@ -110,6 +110,7 @@ "increaseFontSize": "Letters vergroten", "unbindText": "Ontkoppel tekst", "bindText": "Koppel tekst aan de container", + "createContainerFromText": "", "link": { "edit": "Wijzig link", "create": "Maak link", @@ -193,7 +194,7 @@ "resetLibrary": "Dit zal je bibliotheek wissen. Weet je het zeker?", "removeItemsFromsLibrary": "Verwijder {{count}} item(s) uit bibliotheek?", "invalidEncryptionKey": "Encryptiesleutel moet 22 tekens zijn. Live samenwerking is uitgeschakeld.", - "collabOfflineWarning": "" + "collabOfflineWarning": "Geen internetverbinding beschikbaar.\nJe wijzigingen worden niet opgeslagen!" }, "errors": { "unsupportedFileType": "Niet-ondersteund bestandstype.", @@ -204,7 +205,22 @@ "cannotResolveCollabServer": "Kan geen verbinding maken met de collab server. Herlaad de pagina en probeer het opnieuw.", "importLibraryError": "Kon bibliotheek niet laden", "collabSaveFailed": "Kan niet opslaan in de backend database. Als de problemen blijven bestaan, moet u het bestand lokaal opslaan om ervoor te zorgen dat u uw werk niet verliest.", - "collabSaveFailed_sizeExceeded": "Kan de backend database niet opslaan, het canvas lijkt te groot te zijn. U moet het bestand lokaal opslaan om ervoor te zorgen dat u uw werk niet verliest." + "collabSaveFailed_sizeExceeded": "Kan de backend database niet opslaan, het canvas lijkt te groot te zijn. U moet het bestand lokaal opslaan om ervoor te zorgen dat u uw werk niet verliest.", + "brave_measure_text_error": { + "start": "Het ziet er uit dat u de Brave browser gebruikt met de", + "aggressive_block_fingerprint": "", + "setting_enabled": "instelling ingeschakeld", + "break": "Dit kan leiden tot het breken van de", + "text_elements": "Tekst Elementen", + "in_your_drawings": "in je tekeningen", + "strongly_recommend": "We raden u ten zeerste aan deze instelling uit te schakelen. U kunt dit volgen", + "steps": "deze stappen", + "how": "over hoe dit te doen", + "disable_setting": " Indien het uitschakelen van deze instelling de weergave van tekst elementen niet wijzigt, open dan een", + "issue": "probleem", + "write": "", + "discord": "Discord" + } }, "toolBar": { "selection": "Selectie", @@ -219,7 +235,7 @@ "library": "Bibliotheek", "lock": "Geselecteerde tool actief houden na tekenen", "penMode": "Pen modus - Blokkeer aanraken", - "link": "", + "link": "Link toevoegen / bijwerken voor een geselecteerde vorm", "eraser": "Gum", "hand": "" }, @@ -229,7 +245,7 @@ "shapes": "Vormen" }, "hints": { - "canvasPanning": "", + "canvasPanning": "Om de canvas te verplaatsen, houd muiswiel of spatiebalk ingedrukt tijdens slepen, of gebruik het handgereedschap", "linearElement": "Klik om meerdere punten te starten, sleep voor één lijn", "freeDraw": "Klik en sleep, laat los als je klaar bent", "text": "Tip: je kunt tekst toevoegen door ergens dubbel te klikken met de selectietool", @@ -245,7 +261,7 @@ "lineEditor_nothingSelected": "", "placeImage": "", "publishLibrary": "Publiceer je eigen bibliotheek", - "bindTextToElement": "", + "bindTextToElement": "Druk op enter om tekst toe te voegen", "deepBoxSelect": "", "eraserRevert": "", "firefox_clipboard_write": "" @@ -295,7 +311,7 @@ "helpDialog": { "blog": "Lees onze blog", "click": "klik", - "deepSelect": "", + "deepSelect": "Deep selecteer", "deepBoxSelect": "", "curvedArrow": "Gebogen pijl", "curvedLine": "Kromme lijn", @@ -303,7 +319,8 @@ "doubleClick": "dubbelklikken", "drag": "slepen", "editor": "Editor", - "editSelectedShape": "Bewerk geselecteerde vorm (tekst/pijl/lijn)", + "editLineArrowPoints": "", + "editText": "", "github": "Probleem gevonden? Verzenden", "howto": "Volg onze handleidingen", "or": "of", @@ -364,13 +381,13 @@ "republishWarning": "" }, "publishSuccessDialog": { - "title": "", + "title": "Bibliotheek ingediend", "content": "", "link": "Hier" }, "confirmDialog": { - "resetLibrary": "", - "removeItemsFromLib": "" + "resetLibrary": "Reset bibliotheek", + "removeItemsFromLib": "Verwijder geselecteerde items uit bibliotheek" }, "encrypted": { "tooltip": "Je tekeningen zijn beveiligd met end-to-end encryptie, dus Excalidraw's servers zullen nooit zien wat je tekent.", @@ -392,7 +409,7 @@ "width": "Breedte" }, "toast": { - "addedToLibrary": "", + "addedToLibrary": "Toegevoegd aan bibliotheek", "copyStyles": "Stijlen gekopieerd.", "copyToClipboard": "Gekopieerd naar het klembord.", "copyToClipboardAsPng": "{{exportSelection}} naar klembord gekopieerd als PNG\n({{exportColorScheme}})", @@ -456,10 +473,10 @@ "menuHint": "" }, "defaults": { - "menuHint": "", - "center_heading": "", - "toolbarHint": "", - "helpHint": "" + "menuHint": "Exporteren, voorkeuren en meer...", + "center_heading": "Diagrammen. Eenvoudig. Gemaakt.", + "toolbarHint": "Kies een tool & begin met tekenen!", + "helpHint": "Snelkoppelingen en hulp" } } } diff --git a/src/locales/nn-NO.json b/src/locales/nn-NO.json index 0311cc2fe..1733b84c4 100644 --- a/src/locales/nn-NO.json +++ b/src/locales/nn-NO.json @@ -110,6 +110,7 @@ "increaseFontSize": "Gjer skriftstorleik større", "unbindText": "Avbind tekst", "bindText": "", + "createContainerFromText": "", "link": { "edit": "Rediger lenke", "create": "Lag lenke", @@ -204,7 +205,22 @@ "cannotResolveCollabServer": "Kunne ikkje kople til samarbeidsserveren. Ver vennleg å oppdatere inn sida og prøv på nytt.", "importLibraryError": "", "collabSaveFailed": "", - "collabSaveFailed_sizeExceeded": "" + "collabSaveFailed_sizeExceeded": "", + "brave_measure_text_error": { + "start": "", + "aggressive_block_fingerprint": "", + "setting_enabled": "", + "break": "", + "text_elements": "", + "in_your_drawings": "", + "strongly_recommend": "", + "steps": "", + "how": "", + "disable_setting": "", + "issue": "", + "write": "", + "discord": "" + } }, "toolBar": { "selection": "Vel", @@ -303,7 +319,8 @@ "doubleClick": "dobbelklikk", "drag": "drag", "editor": "Redigering", - "editSelectedShape": "Rediger valt form (tekst/pil/linje)", + "editLineArrowPoints": "", + "editText": "", "github": "Funne eit problem? Send inn", "howto": "Følg vegleiinga vår", "or": "eller", diff --git a/src/locales/oc-FR.json b/src/locales/oc-FR.json index 51a5b9018..a9ca70a84 100644 --- a/src/locales/oc-FR.json +++ b/src/locales/oc-FR.json @@ -110,6 +110,7 @@ "increaseFontSize": "Aumentar talha polissa", "unbindText": "Dessociar lo tèxte", "bindText": "Ligar lo tèxt al contenidor", + "createContainerFromText": "", "link": { "edit": "Modificar lo ligam", "create": "Crear un ligam", @@ -204,7 +205,22 @@ "cannotResolveCollabServer": "Connexion impossibla al servidor collab. Mercés de recargar la pagina e tornar ensajar.", "importLibraryError": "Impossible de cargar la bibliotèca", "collabSaveFailed": "", - "collabSaveFailed_sizeExceeded": "" + "collabSaveFailed_sizeExceeded": "", + "brave_measure_text_error": { + "start": "", + "aggressive_block_fingerprint": "", + "setting_enabled": "", + "break": "", + "text_elements": "", + "in_your_drawings": "", + "strongly_recommend": "", + "steps": "", + "how": "", + "disable_setting": "", + "issue": "", + "write": "", + "discord": "" + } }, "toolBar": { "selection": "Seleccion", @@ -303,7 +319,8 @@ "doubleClick": "doble clic", "drag": "lisar", "editor": "Editor", - "editSelectedShape": "Modificar la fòrma seleccionada (tèxt/sageta/linha)", + "editLineArrowPoints": "", + "editText": "", "github": "Problèma trobat ? Senhalatz-lo", "howto": "Seguissètz nòstras guidas", "or": "o", diff --git a/src/locales/pa-IN.json b/src/locales/pa-IN.json index 3bf61d83a..31c11ceb8 100644 --- a/src/locales/pa-IN.json +++ b/src/locales/pa-IN.json @@ -110,6 +110,7 @@ "increaseFontSize": "ਫੌਂਟ ਦਾ ਅਕਾਰ ਵਧਾਓ", "unbindText": "", "bindText": "ਪਾਠ ਨੂੰ ਕੰਟੇਨਰ ਨਾਲ ਬੰਨ੍ਹੋ", + "createContainerFromText": "", "link": { "edit": "ਕੜੀ ਸੋਧੋ", "create": "ਕੜੀ ਬਣਾਓ", @@ -204,7 +205,22 @@ "cannotResolveCollabServer": "", "importLibraryError": "", "collabSaveFailed": "", - "collabSaveFailed_sizeExceeded": "" + "collabSaveFailed_sizeExceeded": "", + "brave_measure_text_error": { + "start": "", + "aggressive_block_fingerprint": "", + "setting_enabled": "", + "break": "", + "text_elements": "", + "in_your_drawings": "", + "strongly_recommend": "", + "steps": "", + "how": "", + "disable_setting": "", + "issue": "", + "write": "", + "discord": "" + } }, "toolBar": { "selection": "ਚੋਣਕਾਰ", @@ -303,7 +319,8 @@ "doubleClick": "ਡਬਲ-ਕਲਿੱਕ", "drag": "ਘਸੀਟੋ", "editor": "ਸੋਧਕ", - "editSelectedShape": "ਚੁਣਿਆ ਰੂਪ ਸੋਧੋ (ਪਾਠ/ਤੀਰ/ਲਾਈਨ)", + "editLineArrowPoints": "", + "editText": "", "github": "ਕੋਈ ਸਮੱਸਿਆ ਲੱਭੀ? ਜਮ੍ਹਾਂ ਕਰਵਾਓ", "howto": "ਸਾਡੀਆਂ ਗਾਈਡਾਂ ਦੀ ਪਾਲਣਾ ਕਰੋ", "or": "ਜਾਂ", diff --git a/src/locales/percentages.json b/src/locales/percentages.json index 29a0c1f2b..b28b6c259 100644 --- a/src/locales/percentages.json +++ b/src/locales/percentages.json @@ -1,52 +1,53 @@ { - "ar-SA": 92, - "bg-BG": 54, - "bn-BD": 59, - "ca-ES": 100, - "cs-CZ": 74, - "da-DK": 32, + "ar-SA": 89, + "bg-BG": 52, + "bn-BD": 57, + "ca-ES": 96, + "cs-CZ": 72, + "da-DK": 31, "de-DE": 100, - "el-GR": 99, + "el-GR": 98, "en": 100, - "es-ES": 100, - "eu-ES": 100, - "fa-IR": 95, - "fi-FI": 100, - "fr-FR": 100, + "es-ES": 99, + "eu-ES": 99, + "fa-IR": 91, + "fi-FI": 96, + "fr-FR": 99, "gl-ES": 99, - "he-IL": 89, - "hi-IN": 71, - "hu-HU": 88, - "id-ID": 99, - "it-IT": 100, - "ja-JP": 100, + "he-IL": 99, + "hi-IN": 73, + "hu-HU": 85, + "id-ID": 98, + "it-IT": 99, + "ja-JP": 97, "kab-KAB": 93, - "kk-KZ": 20, - "ko-KR": 98, - "ku-TR": 95, - "lt-LT": 63, - "lv-LV": 97, + "kk-KZ": 19, + "ko-KR": 99, + "ku-TR": 91, + "lt-LT": 61, + "lv-LV": 93, "mr-IN": 100, - "my-MM": 41, + "my-MM": 40, "nb-NO": 100, - "nl-NL": 90, - "nn-NO": 89, - "oc-FR": 98, - "pa-IN": 82, - "pl-PL": 84, - "pt-BR": 100, - "pt-PT": 100, + "nl-NL": 92, + "nn-NO": 86, + "oc-FR": 94, + "pa-IN": 79, + "pl-PL": 87, + "pt-BR": 96, + "pt-PT": 99, "ro-RO": 100, - "ru-RU": 100, + "ru-RU": 96, "si-LK": 8, - "sk-SK": 100, + "sk-SK": 99, "sl-SI": 100, - "sv-SE": 100, - "ta-IN": 94, - "tr-TR": 97, - "uk-UA": 96, - "vi-VN": 20, - "zh-CN": 100, + "sv-SE": 99, + "ta-IN": 90, + "th-TH": 39, + "tr-TR": 98, + "uk-UA": 93, + "vi-VN": 52, + "zh-CN": 99, "zh-HK": 25, "zh-TW": 100 } diff --git a/src/locales/pl-PL.json b/src/locales/pl-PL.json index 89a52b493..1abaa038f 100644 --- a/src/locales/pl-PL.json +++ b/src/locales/pl-PL.json @@ -1,7 +1,7 @@ { "labels": { "paste": "Wklej", - "pasteAsPlaintext": "", + "pasteAsPlaintext": "Wklej jako zwykły tekst", "pasteCharts": "Wklej wykresy", "selectAll": "Zaznacz wszystko", "multiSelect": "Dodaj element do zaznaczenia", @@ -72,7 +72,7 @@ "layers": "Warstwy", "actions": "Akcje", "language": "Język", - "liveCollaboration": "", + "liveCollaboration": "Współpraca w czasie rzeczywistym...", "duplicateSelection": "Powiel", "untitled": "Bez tytułu", "name": "Nazwa", @@ -96,8 +96,8 @@ "centerHorizontally": "Wyśrodkuj w poziomie", "distributeHorizontally": "Rozłóż poziomo", "distributeVertically": "Rozłóż pionowo", - "flipHorizontal": "Odbij w poziomie", - "flipVertical": "Odbij w pionie", + "flipHorizontal": "Odwróć w poziomie", + "flipVertical": "Odwróć w pionie", "viewMode": "Tryb widoku", "toggleExportColorScheme": "Przełącz schemat kolorów przy eksporcie", "share": "Udostępnij", @@ -106,46 +106,47 @@ "toggleTheme": "Przełącz motyw", "personalLib": "Biblioteka prywatna", "excalidrawLib": "Biblioteka Excalidraw", - "decreaseFontSize": "", - "increaseFontSize": "", + "decreaseFontSize": "Zmniejsz rozmiar czcionki", + "increaseFontSize": "Zwiększ rozmiar czcionki", "unbindText": "", "bindText": "", + "createContainerFromText": "", "link": { - "edit": "", - "create": "", - "label": "" + "edit": "Edytuj łącze", + "create": "Utwórz łącze", + "label": "Łącze" }, "lineEditor": { "edit": "", "exit": "" }, "elementLock": { - "lock": "", - "unlock": "", - "lockAll": "", - "unlockAll": "" + "lock": "Zablokuj", + "unlock": "Odblokuj", + "lockAll": "Zablokuj wszystko", + "unlockAll": "Odblokuj wszystko" }, - "statusPublished": "", - "sidebarLock": "" + "statusPublished": "Opublikowano", + "sidebarLock": "Panel boczny zawsze otwarty" }, "library": { - "noItems": "", + "noItems": "Nie dodano jeszcze żadnych elementów...", "hint_emptyLibrary": "", - "hint_emptyPrivateLibrary": "" + "hint_emptyPrivateLibrary": "Wybierz element, aby dodać go tutaj." }, "buttons": { "clearReset": "Wyczyść dokument i zresetuj kolor dokumentu", "exportJSON": "Eksportuj do pliku", - "exportImage": "", - "export": "", + "exportImage": "Eksportuj obraz...", + "export": "Zapisz jako...", "exportToPng": "Zapisz jako PNG", "exportToSvg": "Zapisz jako SVG", "copyToClipboard": "Skopiuj do schowka", "copyPngToClipboard": "Skopiuj do schowka jako plik PNG", "scale": "Skala", - "save": "", + "save": "Zapisz do bieżącego pliku", "saveAs": "Zapisz jako", - "load": "", + "load": "Otwórz", "getShareableLink": "Udostępnij", "close": "Zamknij", "selectLanguage": "Wybierz język", @@ -179,11 +180,11 @@ "couldNotLoadInvalidFile": "Nie udało się otworzyć pliku. Wybrany plik jest nieprawidłowy.", "importBackendFailed": "Wystąpił błąd podczas importowania pliku.", "cannotExportEmptyCanvas": "Najpierw musisz coś narysować, aby zapisać dokument.", - "couldNotCopyToClipboard": "", + "couldNotCopyToClipboard": "Nie udało się skopiować do schowka.", "decryptFailed": "Nie udało się odszyfrować danych.", "uploadedSecurly": "By zapewnić Ci prywatność, udostępnianie projektu jest zabezpieczone szyfrowaniem end-to-end, co oznacza, że poza tobą i osobą z którą podzielisz się linkiem, nikt nie ma dostępu do tego co udostępniasz.", "loadSceneOverridePrompt": "Wczytanie zewnętrznego rysunku zastąpi istniejącą zawartość. Czy chcesz kontynuować?", - "collabStopOverridePrompt": "Zatrzymanie sesji nadpisze poprzedni, zapisany lokalnie rysunk. Jesteś pewien?\n\n(Jeśli chcesz zachować swój lokalny rysunek, po prostu zamknij zakładkę przeglądarki.)", + "collabStopOverridePrompt": "Zatrzymanie sesji nadpisze poprzedni, zapisany lokalnie rysunek. Czy jesteś pewien?\n\n(Jeśli chcesz zachować swój lokalny rysunek, po prostu zamknij zakładkę przeglądarki.)", "errorAddingToLibrary": "Nie udało się dodać elementu do biblioteki", "errorRemovingFromLibrary": "Nie udało się usunąć elementu z biblioteki", "confirmAddLibrary": "To doda {{numShapes}} kształtów do twojej biblioteki. Jesteś pewien?", @@ -193,7 +194,7 @@ "resetLibrary": "To wyczyści twoją bibliotekę. Jesteś pewien?", "removeItemsFromsLibrary": "Usunąć {{count}} element(ów) z biblioteki?", "invalidEncryptionKey": "Klucz szyfrowania musi składać się z 22 znaków. Współpraca na żywo jest wyłączona.", - "collabOfflineWarning": "" + "collabOfflineWarning": "Brak połączenia z Internetem.\nTwoje zmiany nie zostaną zapisane!" }, "errors": { "unsupportedFileType": "Nieobsługiwany typ pliku.", @@ -201,10 +202,25 @@ "fileTooBig": "Plik jest zbyt duży. Maksymalny dozwolony rozmiar to {{maxSize}}.", "svgImageInsertError": "Nie udało się wstawić obrazu SVG. Znacznik SVG wygląda na nieprawidłowy.", "invalidSVGString": "Nieprawidłowy SVG.", - "cannotResolveCollabServer": "", - "importLibraryError": "", - "collabSaveFailed": "", - "collabSaveFailed_sizeExceeded": "" + "cannotResolveCollabServer": "Nie można połączyć się z serwerem współpracy w czasie rzeczywistym. Proszę odświeżyć stronę i spróbować ponownie.", + "importLibraryError": "Wystąpił błąd w trakcie ładowania biblioteki", + "collabSaveFailed": "Nie udało się zapisać w bazie danych. Jeśli problemy nie ustąpią, zapisz plik lokalnie, aby nie utracić swojej pracy.", + "collabSaveFailed_sizeExceeded": "Nie udało się zapisać w bazie danych — dokument jest za duży. Zapisz plik lokalnie, aby nie utracić swojej pracy.", + "brave_measure_text_error": { + "start": "", + "aggressive_block_fingerprint": "", + "setting_enabled": "", + "break": "", + "text_elements": "", + "in_your_drawings": "", + "strongly_recommend": "", + "steps": "", + "how": "", + "disable_setting": "", + "issue": "problem", + "write": "na naszym GitHubie lub napisz do nas na", + "discord": "Discord" + } }, "toolBar": { "selection": "Zaznaczenie", @@ -303,7 +319,8 @@ "doubleClick": "podwójne kliknięcie", "drag": "przeciągnij", "editor": "Edytor", - "editSelectedShape": "Edytuj wybrany kształt (tekst/strzałka/linia)", + "editLineArrowPoints": "", + "editText": "", "github": "Znalazłeś problem? Prześlij", "howto": "Skorzystaj z instrukcji", "or": "lub", @@ -317,7 +334,7 @@ "zoomToFit": "Powiększ, aby wyświetlić wszystkie elementy", "zoomToSelection": "Przybliż do zaznaczenia", "toggleElementLock": "", - "movePageUpDown": "", + "movePageUpDown": "Przesuń stronę w górę/w dół", "movePageLeftRight": "" }, "clearCanvasDialog": { diff --git a/src/locales/pt-BR.json b/src/locales/pt-BR.json index cb7f045de..d790c5db5 100644 --- a/src/locales/pt-BR.json +++ b/src/locales/pt-BR.json @@ -110,6 +110,7 @@ "increaseFontSize": "Aumentar o tamanho da fonte", "unbindText": "Desvincular texto", "bindText": "Vincular texto ao contêiner", + "createContainerFromText": "", "link": { "edit": "Editar link", "create": "Criar link", @@ -204,7 +205,22 @@ "cannotResolveCollabServer": "Não foi possível conectar-se ao servidor colaborativo. Por favor, recarregue a página e tente novamente.", "importLibraryError": "Não foi possível carregar a biblioteca", "collabSaveFailed": "Não foi possível salvar no banco de dados do servidor. Se os problemas persistirem, salve o arquivo localmente para garantir que não perca o seu trabalho.", - "collabSaveFailed_sizeExceeded": "Não foi possível salvar no banco de dados do servidor, a tela parece ser muito grande. Se os problemas persistirem, salve o arquivo localmente para garantir que não perca o seu trabalho." + "collabSaveFailed_sizeExceeded": "Não foi possível salvar no banco de dados do servidor, a tela parece ser muito grande. Se os problemas persistirem, salve o arquivo localmente para garantir que não perca o seu trabalho.", + "brave_measure_text_error": { + "start": "", + "aggressive_block_fingerprint": "", + "setting_enabled": "", + "break": "", + "text_elements": "", + "in_your_drawings": "", + "strongly_recommend": "", + "steps": "", + "how": "", + "disable_setting": "", + "issue": "", + "write": "", + "discord": "" + } }, "toolBar": { "selection": "Seleção", @@ -303,7 +319,8 @@ "doubleClick": "clique duplo", "drag": "arrastar", "editor": "Editor", - "editSelectedShape": "Editar forma selecionada (texto/seta/linha)", + "editLineArrowPoints": "", + "editText": "", "github": "Encontrou algum problema? Nos informe", "howto": "Siga nossos guias", "or": "ou", diff --git a/src/locales/pt-PT.json b/src/locales/pt-PT.json index f713b22d3..7ce28f00f 100644 --- a/src/locales/pt-PT.json +++ b/src/locales/pt-PT.json @@ -110,6 +110,7 @@ "increaseFontSize": "Aumentar o tamanho do tipo de letra", "unbindText": "Desvincular texto", "bindText": "Ligar texto ao recipiente", + "createContainerFromText": "Envolver texto num recipiente", "link": { "edit": "Editar ligação", "create": "Criar ligação", @@ -204,7 +205,22 @@ "cannotResolveCollabServer": "Não foi possível fazer a ligação ao servidor colaborativo. Por favor, volte a carregar a página e tente novamente.", "importLibraryError": "Não foi possível carregar a biblioteca", "collabSaveFailed": "Não foi possível guardar na base de dados de backend. Se os problemas persistirem, guarde o ficheiro localmente para garantir que não perde o seu trabalho.", - "collabSaveFailed_sizeExceeded": "Não foi possível guardar na base de dados de backend, o ecrã parece estar muito grande. Deve guardar o ficheiro localmente para garantir que não perde o seu trabalho." + "collabSaveFailed_sizeExceeded": "Não foi possível guardar na base de dados de backend, o ecrã parece estar muito grande. Deve guardar o ficheiro localmente para garantir que não perde o seu trabalho.", + "brave_measure_text_error": { + "start": "Parece que está a usar o navegador Brave com o", + "aggressive_block_fingerprint": "Bloqueio Agressivo via Impressão Digital", + "setting_enabled": "activo", + "break": "Isso pode desconfigurar os", + "text_elements": "Elementos de Texto", + "in_your_drawings": "nos seus desenhos", + "strongly_recommend": "Recomendamos fortemente a desactivação desta configuração. Pode seguir", + "steps": "os seguintes passos", + "how": "para saber como o fazer", + "disable_setting": " Se desactivar esta configuração não consertar a exibição de elementos de texto, por favor, abra um", + "issue": "problema", + "write": "no nosso GitHub ou então escreva-nos no", + "discord": "Discord" + } }, "toolBar": { "selection": "Seleção", @@ -303,7 +319,8 @@ "doubleClick": "clique duplo", "drag": "arrastar", "editor": "Editor", - "editSelectedShape": "Editar forma selecionada (texto/seta/linha)", + "editLineArrowPoints": "", + "editText": "", "github": "Encontrou algum problema? Informe-nos", "howto": "Siga os nossos guias", "or": "ou", diff --git a/src/locales/ro-RO.json b/src/locales/ro-RO.json index 8eda2385b..a0e3b5883 100644 --- a/src/locales/ro-RO.json +++ b/src/locales/ro-RO.json @@ -110,6 +110,7 @@ "increaseFontSize": "Mărește dimensiunea fontului", "unbindText": "Deconectare text", "bindText": "Legare text de container", + "createContainerFromText": "Încadrare text într-un container", "link": { "edit": "Editare URL", "create": "Creare URL", @@ -204,7 +205,22 @@ "cannotResolveCollabServer": "Nu a putut fi realizată conexiunea la serverul de colaborare. Reîncarcă pagina și încearcă din nou.", "importLibraryError": "Biblioteca nu a putut fi încărcată", "collabSaveFailed": "Nu s-a putut salva în baza de date la nivel de server. Dacă problemele persistă, ar trebui să salvezi fișierul la nivel local pentru a te asigura că nu îți pierzi munca.", - "collabSaveFailed_sizeExceeded": "Nu s-a putut salva în baza de date la nivel de server, întrucât se pare că pânza este prea mare. Ar trebui să salvezi fișierul la nivel local pentru a te asigura că nu îți pierzi munca." + "collabSaveFailed_sizeExceeded": "Nu s-a putut salva în baza de date la nivel de server, întrucât se pare că pânza este prea mare. Ar trebui să salvezi fișierul la nivel local pentru a te asigura că nu îți pierzi munca.", + "brave_measure_text_error": { + "start": "Se pare că folosești navigatorul Brave cu", + "aggressive_block_fingerprint": "setarea „Blocați amprentarea” în modul strict", + "setting_enabled": "activată", + "break": "Acest lucru ar putea duce la întreruperea", + "text_elements": "elementelor de text", + "in_your_drawings": "din desenele tale", + "strongly_recommend": "Recomandăm insistent dezactivarea acestei setări. Poți urma", + "steps": "acești pași", + "how": "pentru efectuarea procedurii", + "disable_setting": " Dacă dezactivarea acestei setări nu remediază afișarea elementelor de text, deschide", + "issue": "un tichet cu probleme", + "write": "pe pagina noastră de GitHub sau scrie-ne pe", + "discord": "Discord" + } }, "toolBar": { "selection": "Selecție", @@ -303,7 +319,8 @@ "doubleClick": "dublu clic", "drag": "glisare", "editor": "Editor", - "editSelectedShape": "Editează forma selectată (text/săgeată/linie)", + "editLineArrowPoints": "Editare puncte de săgeată/rând", + "editText": "Editare text/adăugare etichetă", "github": "Ai întâmpinat o problemă? Trimite un raport", "howto": "Urmărește ghidurile noastre", "or": "sau", diff --git a/src/locales/ru-RU.json b/src/locales/ru-RU.json index cd0609505..d4030897c 100644 --- a/src/locales/ru-RU.json +++ b/src/locales/ru-RU.json @@ -110,6 +110,7 @@ "increaseFontSize": "Увеличить шрифт", "unbindText": "Отвязать текст", "bindText": "Привязать текст к контейнеру", + "createContainerFromText": "", "link": { "edit": "Редактировать ссылку", "create": "Создать ссылку", @@ -204,7 +205,22 @@ "cannotResolveCollabServer": "Не удалось подключиться к серверу совместного редактирования. Перезагрузите страницу и повторите попытку.", "importLibraryError": "Не удалось загрузить библиотеку", "collabSaveFailed": "Не удалось сохранить в базу данных. Если проблема повторится, нужно будет сохранить файл локально, чтобы быть уверенным, что вы не потеряете вашу работу.", - "collabSaveFailed_sizeExceeded": "Не удалось сохранить в базу данных. Похоже, что холст слишком большой. Нужно сохранить файл локально, чтобы быть уверенным, что вы не потеряете вашу работу." + "collabSaveFailed_sizeExceeded": "Не удалось сохранить в базу данных. Похоже, что холст слишком большой. Нужно сохранить файл локально, чтобы быть уверенным, что вы не потеряете вашу работу.", + "brave_measure_text_error": { + "start": "", + "aggressive_block_fingerprint": "", + "setting_enabled": "", + "break": "", + "text_elements": "", + "in_your_drawings": "", + "strongly_recommend": "", + "steps": "", + "how": "", + "disable_setting": "", + "issue": "", + "write": "", + "discord": "" + } }, "toolBar": { "selection": "Выделение области", @@ -303,7 +319,8 @@ "doubleClick": "двойной клик", "drag": "перетащить", "editor": "Редактор", - "editSelectedShape": "Редактировать выбранную фигуру (текст/стрелка/линия)", + "editLineArrowPoints": "", + "editText": "", "github": "Нашли проблему? Отправьте", "howto": "Следуйте нашим инструкциям", "or": "или", diff --git a/src/locales/si-LK.json b/src/locales/si-LK.json index ee6ad46f7..b84ad567f 100644 --- a/src/locales/si-LK.json +++ b/src/locales/si-LK.json @@ -110,6 +110,7 @@ "increaseFontSize": "", "unbindText": "", "bindText": "", + "createContainerFromText": "", "link": { "edit": "", "create": "", @@ -204,7 +205,22 @@ "cannotResolveCollabServer": "", "importLibraryError": "", "collabSaveFailed": "", - "collabSaveFailed_sizeExceeded": "" + "collabSaveFailed_sizeExceeded": "", + "brave_measure_text_error": { + "start": "", + "aggressive_block_fingerprint": "", + "setting_enabled": "", + "break": "", + "text_elements": "", + "in_your_drawings": "", + "strongly_recommend": "", + "steps": "", + "how": "", + "disable_setting": "", + "issue": "", + "write": "", + "discord": "" + } }, "toolBar": { "selection": "", @@ -303,7 +319,8 @@ "doubleClick": "", "drag": "", "editor": "", - "editSelectedShape": "", + "editLineArrowPoints": "", + "editText": "", "github": "", "howto": "", "or": "", diff --git a/src/locales/sk-SK.json b/src/locales/sk-SK.json index 2b78b4539..d6322ac1c 100644 --- a/src/locales/sk-SK.json +++ b/src/locales/sk-SK.json @@ -110,6 +110,7 @@ "increaseFontSize": "Zväčšiť veľkosť písma", "unbindText": "Zrušiť previazanie textu", "bindText": "Previazať text s kontajnerom", + "createContainerFromText": "Zabaliť text do kontajneru", "link": { "edit": "Upraviť odkaz", "create": "Vytvoriť odkaz", @@ -204,7 +205,22 @@ "cannotResolveCollabServer": "Nepodarilo sa pripojiť ku kolaboračnému serveru. Prosím obnovte stránku a skúste to znovu.", "importLibraryError": "Nepodarilo sa načítať knižnicu", "collabSaveFailed": "Uloženie do databázy sa nepodarilo. Ak tento problém pretrváva uložte si váš súbor lokálne aby ste nestratili vašu prácu.", - "collabSaveFailed_sizeExceeded": "Uloženie do databázy sa nepodarilo, pretože veľkosť plátna je príliš veľká. Uložte si váš súbor lokálne aby ste nestratili vašu prácu." + "collabSaveFailed_sizeExceeded": "Uloženie do databázy sa nepodarilo, pretože veľkosť plátna je príliš veľká. Uložte si váš súbor lokálne aby ste nestratili vašu prácu.", + "brave_measure_text_error": { + "start": "Vyzerá to, že používate prehliadač Brave s", + "aggressive_block_fingerprint": "agresívnym blokovaním sledovania", + "setting_enabled": "zapnutým", + "break": "Toto môže znemožniť používanie", + "text_elements": "textových prvkov", + "in_your_drawings": "vo vašich kresbách", + "strongly_recommend": "Odporúčame vypnutie tohto nastavenia. Postupuje podľa", + "steps": "týchto krokov", + "how": "pre jeho vypnutie", + "disable_setting": " Ak sa vypnutím tohto nastavenia problém zobrazenia textových prvkov neodstráni, prosím vytvorte", + "issue": "issue", + "write": "na našom Github-e alebo nám to oznámte na", + "discord": "Discord" + } }, "toolBar": { "selection": "Výber", @@ -303,7 +319,8 @@ "doubleClick": "dvojklik", "drag": "potiahnutie", "editor": "Editovanie", - "editSelectedShape": "Editovať zvolený tvar (text/šípka/čiara)", + "editLineArrowPoints": "", + "editText": "", "github": "Objavili ste problém? Nahláste ho", "howto": "Postupujte podľa naších návodov", "or": "alebo", diff --git a/src/locales/sl-SI.json b/src/locales/sl-SI.json index 01c8ba4ca..57199c813 100644 --- a/src/locales/sl-SI.json +++ b/src/locales/sl-SI.json @@ -110,6 +110,7 @@ "increaseFontSize": "Povečaj velikost pisave", "unbindText": "Veži besedilo", "bindText": "Veži besedilo na element", + "createContainerFromText": "Zavij besedilo v vsebnik", "link": { "edit": "Uredi povezavo", "create": "Ustvari povezavo", @@ -204,7 +205,22 @@ "cannotResolveCollabServer": "Povezave s strežnikom za sodelovanje ni bilo mogoče vzpostaviti. Ponovno naložite stran in poskusite znova.", "importLibraryError": "Nalaganje knjižnice ni uspelo", "collabSaveFailed": "Ni bilo mogoče shraniti v zaledno bazo podatkov. Če se težave nadaljujejo, shranite datoteko lokalno, da ne boste izgubili svojega dela.", - "collabSaveFailed_sizeExceeded": "Ni bilo mogoče shraniti v zaledno bazo podatkov, zdi se, da je platno preveliko. Datoteko shranite lokalno, da ne izgubite svojega dela." + "collabSaveFailed_sizeExceeded": "Ni bilo mogoče shraniti v zaledno bazo podatkov, zdi se, da je platno preveliko. Datoteko shranite lokalno, da ne izgubite svojega dela.", + "brave_measure_text_error": { + "start": "Videti je, da uporabljate brskalnik Brave z omogočeno nastavitvijo", + "aggressive_block_fingerprint": "Agresivno blokiranje prstnih odtisov", + "setting_enabled": " ", + "break": "To bi lahko povzročilo motnje v obnašanju", + "text_elements": "besedilnih elementov", + "in_your_drawings": "v vaših risbah", + "strongly_recommend": "Močno priporočamo, da onemogočite to nastavitev. Sledite", + "steps": "tem korakom,", + "how": "kako to storiti", + "disable_setting": " Če onemogočanje te nastavitve ne popravi prikaza besedilnih elementov, odprite", + "issue": "vprašanje", + "write": "na našem GitHubu ali nam pišite na", + "discord": "Discord" + } }, "toolBar": { "selection": "Izbor", @@ -303,7 +319,8 @@ "doubleClick": "dvojni klik", "drag": "vleci", "editor": "Urejevalnik", - "editSelectedShape": "Uredi izbrano obliko (besedilo/puščica/črta)", + "editLineArrowPoints": "Uredi črto/točke puščice", + "editText": "Uredi besedilo / dodaj oznako", "github": "Ste našli težavo? Pošljite", "howto": "Sledite našim vodičem", "or": "ali", diff --git a/src/locales/sv-SE.json b/src/locales/sv-SE.json index 3cf3b0cbd..d0392b119 100644 --- a/src/locales/sv-SE.json +++ b/src/locales/sv-SE.json @@ -110,6 +110,7 @@ "increaseFontSize": "Öka fontstorleken", "unbindText": "Koppla bort text", "bindText": "Bind texten till behållaren", + "createContainerFromText": "Radbryt text i en avgränsad yta", "link": { "edit": "Redigera länk", "create": "Skapa länk", @@ -204,7 +205,22 @@ "cannotResolveCollabServer": "Det gick inte att ansluta till samarbets-servern. Ladda om sidan och försök igen.", "importLibraryError": "Kunde inte ladda bibliotek", "collabSaveFailed": "Det gick inte att spara i backend-databasen. Om problemen kvarstår bör du spara filen lokalt för att se till att du inte förlorar ditt arbete.", - "collabSaveFailed_sizeExceeded": "Det gick inte att spara till backend-databasen, whiteboarden verkar vara för stor. Du bör spara filen lokalt för att du inte ska förlora ditt arbete." + "collabSaveFailed_sizeExceeded": "Det gick inte att spara till backend-databasen, whiteboarden verkar vara för stor. Du bör spara filen lokalt för att du inte ska förlora ditt arbete.", + "brave_measure_text_error": { + "start": "Det ser ut som att du använder webbläsaren Brave med", + "aggressive_block_fingerprint": "Blockera \"Fingerprinting\" aggressivt", + "setting_enabled": "inställningen aktiverad", + "break": "Detta kan resultera i att ha sönder", + "text_elements": "Textelement", + "in_your_drawings": "i dina skisser", + "strongly_recommend": "Vi rekommenderar starkt att inaktivera denna inställning. Du kan följa", + "steps": "dessa steg", + "how": "om hur man gör det", + "disable_setting": " Om inaktivering av den här inställningen inte åtgärdar visningen av textelement, vänligen skapa ett", + "issue": "ärende", + "write": "på vår GitHub, eller skriv till oss på", + "discord": "Discord" + } }, "toolBar": { "selection": "Markering", @@ -303,7 +319,8 @@ "doubleClick": "dubbelklicka", "drag": "dra", "editor": "Redigerare", - "editSelectedShape": "Redigera markerad form (text/pil/linje)", + "editLineArrowPoints": "", + "editText": "", "github": "Hittat ett problem? Rapportera", "howto": "Följ våra guider", "or": "eller", diff --git a/src/locales/ta-IN.json b/src/locales/ta-IN.json index 493dfa560..6dc865766 100644 --- a/src/locales/ta-IN.json +++ b/src/locales/ta-IN.json @@ -110,6 +110,7 @@ "increaseFontSize": "எழுத்துரு அளவை அதிகரி", "unbindText": "உரையைப் பிணைவவிழ்", "bindText": "", + "createContainerFromText": "", "link": { "edit": "தொடுப்பைத் திருத்து", "create": "தொடுப்பைப் படை", @@ -204,7 +205,22 @@ "cannotResolveCollabServer": "", "importLibraryError": "நூலகத்தை ஏற்ற முடியவில்லை", "collabSaveFailed": "", - "collabSaveFailed_sizeExceeded": "" + "collabSaveFailed_sizeExceeded": "", + "brave_measure_text_error": { + "start": "", + "aggressive_block_fingerprint": "", + "setting_enabled": "", + "break": "", + "text_elements": "", + "in_your_drawings": "", + "strongly_recommend": "", + "steps": "", + "how": "", + "disable_setting": "", + "issue": "", + "write": "", + "discord": "" + } }, "toolBar": { "selection": "தெரிவு", @@ -303,7 +319,8 @@ "doubleClick": "இரு-சொடுக்கு", "drag": "பிடித்திழு", "editor": "திருத்தி", - "editSelectedShape": "தேர்ந்த வடிவத்தைத் திருத்து (உரை/அம்பு/வரி)", + "editLineArrowPoints": "", + "editText": "", "github": "சிக்கலைக் கண்டீரா? சமர்ப்பி", "howto": "எங்கள் கையேடுகளைப் பின்பற்றுக", "or": "அ", diff --git a/src/locales/th-TH.json b/src/locales/th-TH.json new file mode 100644 index 000000000..2dcb9cb43 --- /dev/null +++ b/src/locales/th-TH.json @@ -0,0 +1,482 @@ +{ + "labels": { + "paste": "วาง", + "pasteAsPlaintext": "วางโดยไม่มีการจัดรูปแบบ", + "pasteCharts": "วางแผนภูมิ", + "selectAll": "เลือกทั้งหมด", + "multiSelect": "", + "moveCanvas": "", + "cut": "ตัด", + "copy": "คัดลอก", + "copyAsPng": "คัดลองไปยังคลิปบอร์ดเป็น PNG", + "copyAsSvg": "คัดลองไปยังคลิปบอร์ดเป็น SVG", + "copyText": "คัดลองไปยังคลิปบอร์ดเป็นข้อความ", + "bringForward": "นำขึ้นข้างบน", + "sendToBack": "ย้ายไปข้างล่าง", + "bringToFront": "นำขึ้นข้างหน้า", + "sendBackward": "ย้ายไปข้างหลัง", + "delete": "ลบ", + "copyStyles": "คัดลอกรูปแบบ", + "pasteStyles": "วางรูปแบบ", + "stroke": "เส้นขอบ", + "background": "พื้นหลัง", + "fill": "เติมสี", + "strokeWidth": "น้ำหนักเส้นขอบ", + "strokeStyle": "", + "strokeStyle_solid": "", + "strokeStyle_dashed": "", + "strokeStyle_dotted": "", + "sloppiness": "ความเลอะเทอะ", + "opacity": "ความทึบแสง", + "textAlign": "จัดข้อความ", + "edges": "ขอบ", + "sharp": "", + "round": "", + "arrowheads": "", + "arrowhead_none": "", + "arrowhead_arrow": "", + "arrowhead_bar": "", + "arrowhead_dot": "", + "arrowhead_triangle": "", + "fontSize": "ขนาดตัวอักษร", + "fontFamily": "แบบตัวอักษร", + "onlySelected": "เฉพาะที่เลือก", + "withBackground": "พื้นหลัง", + "exportEmbedScene": "", + "exportEmbedScene_details": "", + "addWatermark": "เพิ่มลายน้ำ \"สร้างด้วย Excalidraw\"", + "handDrawn": "ลายมือ", + "normal": "ปกติ", + "code": "โค้ด", + "small": "เล็ก", + "medium": "กลาง", + "large": "ใหญ่", + "veryLarge": "ใหญ่มาก", + "solid": "", + "hachure": "", + "crossHatch": "", + "thin": "บาง", + "bold": "หนา", + "left": "ซ้าย", + "center": "กลาง", + "right": "ขวา", + "extraBold": "หนาพิเศษ", + "architect": "", + "artist": "", + "cartoonist": "", + "fileTitle": "ชื่อไฟล์", + "colorPicker": "เลือกสีที่กำหนดเอง", + "canvasColors": "", + "canvasBackground": "", + "drawingCanvas": "", + "layers": "", + "actions": "การกระทำ", + "language": "ภาษา", + "liveCollaboration": "", + "duplicateSelection": "ทำสำเนา", + "untitled": "ไม่มีชื่อ", + "name": "ชื่อ", + "yourName": "ชื่อของคุณ", + "madeWithExcalidraw": "", + "group": "จัดกลุ่ม", + "ungroup": "ยกเลิกการจัดกลุ่ม", + "collaborators": "", + "showGrid": "แสดงเส้นตาราง", + "addToLibrary": "เพิ่มไปในคลัง", + "removeFromLibrary": "นำออกจากคลัง", + "libraryLoadingMessage": "กำลังโหลดคลัง...", + "libraries": "", + "loadingScene": "กำลังโหลดฉาก", + "align": "จัดตำแหน่ง", + "alignTop": "จัดชิดด้านบน", + "alignBottom": "จัดชิดด้านล่าง", + "alignLeft": "จัดชิดซ้าย", + "alignRight": "จัดชิดขวา", + "centerVertically": "กึ่งกลางแนวตั้ง", + "centerHorizontally": "กึ่งกลางแนวนอน", + "distributeHorizontally": "กระจายแนวนอน", + "distributeVertically": "กระจายแนวตั้ง", + "flipHorizontal": "พลิกแนวนอน", + "flipVertical": "พลิกแนวตั้ง", + "viewMode": "โหมดมุมมอง", + "toggleExportColorScheme": "", + "share": "แชร์", + "showStroke": "", + "showBackground": "", + "toggleTheme": "สลับธีม", + "personalLib": "คลังของฉัน", + "excalidrawLib": "คลังของ Excalidraw", + "decreaseFontSize": "ลดขนาดตัวอักษร", + "increaseFontSize": "เพิ่มขนาดตัวอักษร", + "unbindText": "", + "bindText": "", + "createContainerFromText": "", + "link": { + "edit": "แก้ไขลิงก์", + "create": "สร้างลิงค์", + "label": "ลิงค์" + }, + "lineEditor": { + "edit": "แก้ไขเส้น", + "exit": "" + }, + "elementLock": { + "lock": "ล็อก", + "unlock": "ปลดล็อก", + "lockAll": "ล็อกทั้งหมด", + "unlockAll": "ปลดล็อกทั้งหมด" + }, + "statusPublished": "เผยแพร่", + "sidebarLock": "" + }, + "library": { + "noItems": "", + "hint_emptyLibrary": "", + "hint_emptyPrivateLibrary": "" + }, + "buttons": { + "clearReset": "", + "exportJSON": "ส่งออกไปยังไฟล์", + "exportImage": "ส่งออกเป็นรูปภาพ", + "export": "บันทึกไปยัง", + "exportToPng": "ส่งออกไปเป็น PNG", + "exportToSvg": "ส่งออกไปเป็น SVG", + "copyToClipboard": "คัดลอกไปยังคลิปบอร์ด", + "copyPngToClipboard": "คัดลอก PNG ไปยังคลิปบอร์ด", + "scale": "อัตราส่วน", + "save": "", + "saveAs": "", + "load": "เปิด", + "getShareableLink": "สร้างลิงค์ที่แชร์ได้", + "close": "ปิด", + "selectLanguage": "เลือกภาษา", + "scrollBackToContent": "เลื่อนกลับไปด้านบน", + "zoomIn": "ซูมเข้า", + "zoomOut": "ซูมออก", + "resetZoom": "รีเซ็ตการซูม", + "menu": "เมนู", + "done": "เสร็จสิ้น", + "edit": "แก้ไข", + "undo": "เลิกทำ", + "redo": "ทำซ้ำ", + "resetLibrary": "รีเซ็ตคลัง", + "createNewRoom": "สร้างห้องใหม่", + "fullScreen": "เต็มหน้าจอ", + "darkMode": "โหมดกลางคืน", + "lightMode": "โหมดกลางวัน", + "zenMode": "โหมด Zen", + "exitZenMode": "ออกจากโหมด Zen", + "cancel": "ยกเลิก", + "clear": "เคลียร์", + "remove": "ลบ", + "publishLibrary": "เผยแพร่", + "submit": "ตกลง", + "confirm": "ยืนยัน" + }, + "alerts": { + "clearReset": "", + "couldNotCreateShareableLink": "", + "couldNotCreateShareableLinkTooBig": "", + "couldNotLoadInvalidFile": "ไม่สามารถโหลดไฟล์ที่ผิดพลาดได้", + "importBackendFailed": "", + "cannotExportEmptyCanvas": "", + "couldNotCopyToClipboard": "ไม่สามารถคัดลอกไปยังคลิปบอร์ดได้", + "decryptFailed": "ไม่สามารถถอดรหัสข้อมูลได้", + "uploadedSecurly": "การอัพโหลดได้ถูกเข้ารหัสแบบ end-to-end หมายความว่าเซิร์ฟเวอร์ของ Excalidraw และบุคคลอื่นไม่สามารถอ่านข้อมูลได้", + "loadSceneOverridePrompt": "", + "collabStopOverridePrompt": "", + "errorAddingToLibrary": "ไม่สามารถเพิ่มรายการเข้าไปในคลังได้", + "errorRemovingFromLibrary": "ไม่สามารถลบรายการนี้ออกจากคลังได้", + "confirmAddLibrary": "", + "imageDoesNotContainScene": "", + "cannotRestoreFromImage": "", + "invalidSceneUrl": "", + "resetLibrary": "", + "removeItemsFromsLibrary": "", + "invalidEncryptionKey": "", + "collabOfflineWarning": "" + }, + "errors": { + "unsupportedFileType": "ไม่รองรับชนิดของไฟล์นี้", + "imageInsertError": "ไม่สามารถเพิ่มรูปภาพได้ ลองอีกครั้งในภายหลัง", + "fileTooBig": "", + "svgImageInsertError": "", + "invalidSVGString": "ไฟล์ SVG ผิดพลาด", + "cannotResolveCollabServer": "ไม่สามารถเชื่อต่อกับ collab เซิร์ฟเวอร์ได้ โปรดลองโหลดหน้านี้ใหม่และลองอีกครั้ง", + "importLibraryError": "", + "collabSaveFailed": "", + "collabSaveFailed_sizeExceeded": "", + "brave_measure_text_error": { + "start": "", + "aggressive_block_fingerprint": "", + "setting_enabled": "", + "break": "", + "text_elements": "", + "in_your_drawings": "", + "strongly_recommend": "", + "steps": "", + "how": "", + "disable_setting": "", + "issue": "", + "write": "", + "discord": "Discord" + } + }, + "toolBar": { + "selection": "", + "image": "", + "rectangle": "สี่เหลี่ยมผืนผ้า", + "diamond": "", + "ellipse": "", + "arrow": "", + "line": "", + "freedraw": "", + "text": "ข้อความ", + "library": "คลัง", + "lock": "", + "penMode": "", + "link": "", + "eraser": "ยางลบ", + "hand": "" + }, + "headings": { + "canvasActions": "", + "selectedShapeActions": "", + "shapes": "รูปร่าง" + }, + "hints": { + "canvasPanning": "", + "linearElement": "", + "freeDraw": "", + "text": "", + "text_selected": "คลิกสองครั้งหรือกด ENTER เพื่อแก้ไขข้อความ", + "text_editing": "กดปุ่ม Esc หรือกด Ctrl, Cmd + Enter เพื่อเสร็จการแก้ไข", + "linearElementMulti": "คลิกที่จุดสุดท้ายหรือกด Escape หรือ Enter เพื่อเสร็จสิ้น", + "lockAngle": "", + "resize": "", + "resizeImage": "", + "rotate": "", + "lineEditor_info": "", + "lineEditor_pointSelected": "กดปุ่ม Delete เพื่อลบจุด\nกด Ctrl หรือ Cmd + D เพื่อทำซ้ำหรือลากเพื่อเคลื่อนย้าย", + "lineEditor_nothingSelected": "", + "placeImage": "", + "publishLibrary": "", + "bindTextToElement": "", + "deepBoxSelect": "", + "eraserRevert": "", + "firefox_clipboard_write": "" + }, + "canvasError": { + "cannotShowPreview": "", + "canvasTooBig": "", + "canvasTooBigTip": "" + }, + "errorSplash": { + "headingMain_pre": "", + "headingMain_button": "กำลังรีโหลดหน้า", + "clearCanvasMessage": "ถ้าโหลดไม่ได้ ให้ลอง ", + "clearCanvasMessage_button": "เคลียร์ผืนผ้าใบ", + "clearCanvasCaveat": "", + "trackedToSentry_pre": "", + "trackedToSentry_post": "", + "openIssueMessage_pre": "", + "openIssueMessage_button": "", + "openIssueMessage_post": "", + "sceneContent": "" + }, + "roomDialog": { + "desc_intro": "", + "desc_privacy": "", + "button_startSession": "เริ่มเซสชัน", + "button_stopSession": "หยุดเซสชัน", + "desc_inProgressIntro": "", + "desc_shareLink": "", + "desc_exitSession": "", + "shareTitle": "" + }, + "errorDialog": { + "title": "" + }, + "exportDialog": { + "disk_title": "", + "disk_details": "", + "disk_button": "", + "link_title": "", + "link_details": "", + "link_button": "", + "excalidrawplus_description": "", + "excalidrawplus_button": "", + "excalidrawplus_exportError": "ไม่สามารถส่งออกไปที่ Excalidraw+ ได้ในขณะนี้" + }, + "helpDialog": { + "blog": "อ่านบล็อกของพวกเรา", + "click": "คลิก", + "deepSelect": "", + "deepBoxSelect": "", + "curvedArrow": "", + "curvedLine": "", + "documentation": "", + "doubleClick": "ดับเบิลคลิก", + "drag": "ลาก", + "editor": "", + "editLineArrowPoints": "", + "editText": "", + "github": "", + "howto": "", + "or": "", + "preventBinding": "", + "tools": "", + "shortcuts": "", + "textFinish": "", + "textNewLine": "", + "title": "ช่วยเหลือ", + "view": "ดู", + "zoomToFit": "", + "zoomToSelection": "", + "toggleElementLock": "", + "movePageUpDown": "", + "movePageLeftRight": "ย้ายหน้าไปด้าน ซ้าย/ขวา" + }, + "clearCanvasDialog": { + "title": "" + }, + "publishDialog": { + "title": "", + "itemName": "", + "authorName": "ชื่อเจ้าของ", + "githubUsername": "ชื่อผู้ใช้ GitHub", + "twitterUsername": "ชื่อผู้ใช้ Twitter", + "libraryName": "", + "libraryDesc": "", + "website": "", + "placeholder": { + "authorName": "", + "libraryName": "", + "libraryDesc": "", + "githubHandle": "", + "twitterHandle": "", + "website": "" + }, + "errors": { + "required": "", + "website": "" + }, + "noteDescription": { + "pre": "", + "link": "", + "post": "" + }, + "noteGuidelines": { + "pre": "", + "link": "", + "post": "" + }, + "noteLicense": { + "pre": "", + "link": "", + "post": "" + }, + "noteItems": "", + "atleastOneLibItem": "", + "republishWarning": "" + }, + "publishSuccessDialog": { + "title": "", + "content": "", + "link": "" + }, + "confirmDialog": { + "resetLibrary": "", + "removeItemsFromLib": "" + }, + "encrypted": { + "tooltip": "", + "link": "" + }, + "stats": { + "angle": "", + "element": "", + "elements": "", + "height": "", + "scene": "", + "selected": "", + "storage": "", + "title": "", + "total": "", + "version": "", + "versionCopy": "", + "versionNotAvailable": "", + "width": "" + }, + "toast": { + "addedToLibrary": "", + "copyStyles": "", + "copyToClipboard": "", + "copyToClipboardAsPng": "", + "fileSaved": "", + "fileSavedToFilename": "", + "canvas": "", + "selection": "", + "pasteAsSingleElement": "" + }, + "colors": { + "ffffff": "สีขาว", + "f8f9fa": "สีเทา 0", + "f1f3f5": "สีเทา 1", + "fff5f5": "สีแดง 0", + "fff0f6": "สีชมพู 0", + "f8f0fc": "", + "f3f0ff": "", + "edf2ff": "", + "e7f5ff": "", + "e3fafc": "", + "e6fcf5": "", + "ebfbee": "", + "f4fce3": "", + "fff9db": "", + "fff4e6": "", + "transparent": "", + "ced4da": "สีเทา 4", + "868e96": "สีเทา 6", + "fa5252": "สีแดง 6", + "e64980": "สีชมพู 6", + "be4bdb": "", + "7950f2": "", + "4c6ef5": "", + "228be6": "", + "15aabf": "", + "12b886": "", + "40c057": "", + "82c91e": "", + "fab005": "", + "fd7e14": "", + "000000": "", + "343a40": "", + "495057": "", + "c92a2a": "", + "a61e4d": "", + "862e9c": "", + "5f3dc4": "", + "364fc7": "", + "1864ab": "", + "0b7285": "", + "087f5b": "", + "2b8a3e": "", + "5c940d": "", + "e67700": "", + "d9480f": "" + }, + "welcomeScreen": { + "app": { + "center_heading": "", + "center_heading_plus": "", + "menuHint": "" + }, + "defaults": { + "menuHint": "", + "center_heading": "", + "toolbarHint": "", + "helpHint": "" + } + } +} diff --git a/src/locales/tr-TR.json b/src/locales/tr-TR.json index a05de9a54..78dd9329a 100644 --- a/src/locales/tr-TR.json +++ b/src/locales/tr-TR.json @@ -66,7 +66,7 @@ "cartoonist": "Karikatürist", "fileTitle": "Dosya adı", "colorPicker": "Renk seçici", - "canvasColors": "Tuvallerin üzerinde kullanıldı", + "canvasColors": "Tuvalin üzerinde kullanıldı", "canvasBackground": "Tuval arka planı", "drawingCanvas": "Çizim tuvali", "layers": "Katmanlar", @@ -110,6 +110,7 @@ "increaseFontSize": "Yazı Tipi Boyutunu Büyült", "unbindText": "Metni çöz", "bindText": "Metni taşıyıcıya bağla", + "createContainerFromText": "", "link": { "edit": "Bağlantıyı düzenle", "create": "Bağlantı oluştur", @@ -204,7 +205,22 @@ "cannotResolveCollabServer": "İş birliği sunucusuna bağlanılamıyor. Lütfen sayfayı yenileyip tekrar deneyin.", "importLibraryError": "Kütüphane yüklenemedi", "collabSaveFailed": "Backend veritabanına kaydedilemedi. Eğer problem devam ederse, çalışmanızı korumak için dosyayı yerel olarak kaydetmelisiniz.", - "collabSaveFailed_sizeExceeded": "Backend veritabanına kaydedilemedi; tuval çok büyük. Çalışmanızı korumak için dosyayı yerel olarak kaydetmelisiniz." + "collabSaveFailed_sizeExceeded": "Backend veritabanına kaydedilemedi; tuval çok büyük. Çalışmanızı korumak için dosyayı yerel olarak kaydetmelisiniz.", + "brave_measure_text_error": { + "start": "Görünüşe göre Brave gezginini", + "aggressive_block_fingerprint": "Agresif parmakizi bloklama", + "setting_enabled": "ayarları etkin şeklide kullanıyor gibisiniz", + "break": "Bu bir takım sorunlara yol açabilir", + "text_elements": "Metin elementlerinde bozulma", + "in_your_drawings": "çizimlerde bozulma gibi", + "strongly_recommend": "Bu ayarı devre dışı bırakmanızı şiddetle öneririz. Şu adımları", + "steps": "takip ederek", + "how": "nasıl yapılacağını", + "disable_setting": " Yapabilirsiniz. Eğer devre dışı bırakmak işe yaramazsa, lütfen", + "issue": "konu hakkında", + "write": "github'da sorun belirtin, ya da bize", + "discord": "Discord üzerinden iletin" + } }, "toolBar": { "selection": "Seçme", @@ -303,7 +319,8 @@ "doubleClick": "çift-tıklama", "drag": "sürükle", "editor": "Düzenleyici", - "editSelectedShape": "Seçili şekli düzenle (metin/ok/çizgi)", + "editLineArrowPoints": "Çizgi/ok noktalarını düzenle", + "editText": "Etiket / metin düzenle", "github": "Bir hata mı buldun? Bildir", "howto": "Rehberlerimizi takip edin", "or": "veya", @@ -453,13 +470,13 @@ "app": { "center_heading": "", "center_heading_plus": "", - "menuHint": "" + "menuHint": "Dışa aktar, seçenekler, diller, ..." }, "defaults": { - "menuHint": "", + "menuHint": "Dışa aktar, seçenekler, ve daha fazlası...", "center_heading": "", - "toolbarHint": "", - "helpHint": "" + "toolbarHint": "Bir araç seçin ve çizime başlayın!", + "helpHint": "Kısayollar & yardım" } } } diff --git a/src/locales/uk-UA.json b/src/locales/uk-UA.json index ac0bfcfee..ed1a01e68 100644 --- a/src/locales/uk-UA.json +++ b/src/locales/uk-UA.json @@ -110,6 +110,7 @@ "increaseFontSize": "Збільшити розмір шрифту", "unbindText": "Відв'язати текст", "bindText": "Прив’язати текст до контейнера", + "createContainerFromText": "", "link": { "edit": "Редагування посилання", "create": "Створити посилання", @@ -204,7 +205,22 @@ "cannotResolveCollabServer": "Не вдалося приєднатися до сервера. Перезавантажте сторінку та повторіть спробу.", "importLibraryError": "Не вдалося завантажити бібліотеку", "collabSaveFailed": "", - "collabSaveFailed_sizeExceeded": "" + "collabSaveFailed_sizeExceeded": "", + "brave_measure_text_error": { + "start": "", + "aggressive_block_fingerprint": "", + "setting_enabled": "", + "break": "", + "text_elements": "", + "in_your_drawings": "", + "strongly_recommend": "", + "steps": "", + "how": "", + "disable_setting": "", + "issue": "", + "write": "", + "discord": "" + } }, "toolBar": { "selection": "Виділення", @@ -303,7 +319,8 @@ "doubleClick": "подвійний клік", "drag": "перетягнути", "editor": "Редактор", - "editSelectedShape": "Змінити вибрану фігуру (текст/стрілку/рядок)", + "editLineArrowPoints": "", + "editText": "", "github": "Знайшли помилку? Повідомте", "howto": "Дотримуйтесь наших інструкцій", "or": "або", diff --git a/src/locales/vi-VN.json b/src/locales/vi-VN.json index 1921035bc..887c32e3e 100644 --- a/src/locales/vi-VN.json +++ b/src/locales/vi-VN.json @@ -5,7 +5,7 @@ "pasteCharts": "Dán biểu đồ", "selectAll": "Chọn tất cả", "multiSelect": "Thêm mới vào Select", - "moveCanvas": "Di chuyển Canvas", + "moveCanvas": "Di chuyển canvas", "cut": "Cắt", "copy": "Sao chép", "copyAsPng": "Sao chép vào bộ nhớ tạm dưới dạng PNG", @@ -60,11 +60,11 @@ "left": "Trái", "center": "Giữa", "right": "Phải", - "extraBold": "", + "extraBold": "Nét siêu đậm", "architect": "Kiến trúc sư", "artist": "Nghệ sỹ", "cartoonist": "Hoạt hình", - "fileTitle": "", + "fileTitle": "Tên tập tin", "colorPicker": "Chọn màu", "canvasColors": "Đã dùng trên canvas", "canvasBackground": "Nền canvas", @@ -72,28 +72,28 @@ "layers": "Lớp", "actions": "Chức năng", "language": "Ngôn ngữ", - "liveCollaboration": "", + "liveCollaboration": "Hợp tác trực tiếp...", "duplicateSelection": "Tạo bản sao", "untitled": "Không có tiêu đề", "name": "Tên", "yourName": "Tên của bạn", "madeWithExcalidraw": "Làm với Excalidraw", - "group": "", - "ungroup": "", + "group": "Gộp nhóm lại lựa chọn", + "ungroup": "Tách nhóm lựa chọn", "collaborators": "Cộng tác viên", - "showGrid": "", - "addToLibrary": "", - "removeFromLibrary": "", - "libraryLoadingMessage": "", - "libraries": "", + "showGrid": "Hiển thị lưới", + "addToLibrary": "Thêm vào thư viện", + "removeFromLibrary": "Xóa khỏi thư viện", + "libraryLoadingMessage": "Đang tải thư viện…", + "libraries": "Xem thư viện", "loadingScene": "", - "align": "", - "alignTop": "", - "alignBottom": "", - "alignLeft": "", - "alignRight": "", - "centerVertically": "", - "centerHorizontally": "", + "align": "Căn chỉnh", + "alignTop": "Căn trên", + "alignBottom": "Căn dưới", + "alignLeft": "Canh trái", + "alignRight": "Canh phải", + "centerVertically": "Giữa theo chiều dọc", + "centerHorizontally": "Giữa theo chiều ngang", "distributeHorizontally": "Phân bố theo chiều ngang", "distributeVertically": "Phân bố theo chiều dọc", "flipHorizontal": "Lật ngang", @@ -105,42 +105,43 @@ "showBackground": "Hiện thị chọn màu nền", "toggleTheme": "", "personalLib": "", - "excalidrawLib": "", - "decreaseFontSize": "", - "increaseFontSize": "", + "excalidrawLib": "Thư viện Excalidraw", + "decreaseFontSize": "Giảm cỡ chữ", + "increaseFontSize": "Tăng cỡ chữ", "unbindText": "", "bindText": "", + "createContainerFromText": "", "link": { - "edit": "", - "create": "", - "label": "" + "edit": "Sửa liên kết", + "create": "Tạo liên kết", + "label": "Liên kết" }, "lineEditor": { - "edit": "", - "exit": "" + "edit": "Điều chỉnh nét", + "exit": "Thoát chỉnh nét" }, "elementLock": { - "lock": "", - "unlock": "", - "lockAll": "", - "unlockAll": "" + "lock": "Khoá", + "unlock": "Mở khoá", + "lockAll": "Khóa tất cả", + "unlockAll": "Mở khóa tất cả" }, - "statusPublished": "", - "sidebarLock": "" + "statusPublished": "Đã đăng tải", + "sidebarLock": "Giữ thanh bên luôn mở" }, "library": { - "noItems": "", - "hint_emptyLibrary": "", - "hint_emptyPrivateLibrary": "" + "noItems": "Chưa có món nào...", + "hint_emptyLibrary": "Chọn một món trên canvas để thêm nó vào đây, hoặc cài đặt thư viện từ kho lưu trữ công cộng, ở bên dưới.", + "hint_emptyPrivateLibrary": "Chọn một món trên canvas để thêm nó vào đây." }, "buttons": { - "clearReset": "", - "exportJSON": "", - "exportImage": "", - "export": "", - "exportToPng": "", - "exportToSvg": "", - "copyToClipboard": "", + "clearReset": "Reset canvas", + "exportJSON": "Xuất ra tập tin", + "exportImage": "Xuất file ảnh...", + "export": "Lưu vào...", + "exportToPng": "Xuất ra tập tin PNG", + "exportToSvg": "Xuất ra tập tin SVG", + "copyToClipboard": "Sao chép vào bộ nhớ tạm", "copyPngToClipboard": "", "scale": "", "save": "", @@ -160,84 +161,99 @@ "redo": "", "resetLibrary": "", "createNewRoom": "", - "fullScreen": "", - "darkMode": "", - "lightMode": "", - "zenMode": "", - "exitZenMode": "", - "cancel": "", - "clear": "", - "remove": "", - "publishLibrary": "", - "submit": "", - "confirm": "" + "fullScreen": "Toàn màn hình", + "darkMode": "Chế độ tối", + "lightMode": "Chế độ sáng", + "zenMode": "Chế độ zen", + "exitZenMode": "Thoát chể độ zen", + "cancel": "Hủy", + "clear": "Làm sạch", + "remove": "Xóa", + "publishLibrary": "Đăng tải", + "submit": "Gửi", + "confirm": "Xác nhận" }, "alerts": { - "clearReset": "", - "couldNotCreateShareableLink": "", - "couldNotCreateShareableLinkTooBig": "", - "couldNotLoadInvalidFile": "", + "clearReset": "Điều này sẽ dọn hết canvas. Bạn có chắc không?", + "couldNotCreateShareableLink": "Không thể tạo đường dẫn chia sẻ.", + "couldNotCreateShareableLinkTooBig": "Không thể tạo đường dẫn chia sẻ: bản vẽ quá lớn", + "couldNotLoadInvalidFile": "Không thể load tập tin không hợp lệ", "importBackendFailed": "", - "cannotExportEmptyCanvas": "", + "cannotExportEmptyCanvas": "Không thể xuất canvas trống.", "couldNotCopyToClipboard": "", "decryptFailed": "", "uploadedSecurly": "", "loadSceneOverridePrompt": "", - "collabStopOverridePrompt": "", - "errorAddingToLibrary": "", - "errorRemovingFromLibrary": "", - "confirmAddLibrary": "", - "imageDoesNotContainScene": "", + "collabStopOverridePrompt": "Dừng phiên sẽ ghi đè lên bản vẽ được lưu trữ cục bộ trước đó của bạn. Bạn có chắc không?\n\n(Nếu bạn muốn giữ bản vẽ cục bộ của mình, chỉ cần đóng tab trình duyệt.)", + "errorAddingToLibrary": "Không thể thêm món vào thư viện", + "errorRemovingFromLibrary": "Không thể xoá món khỏi thư viện", + "confirmAddLibrary": "Hình {{numShapes}} sẽ được thêm vào thư viện. Bạn chắc chứ?", + "imageDoesNotContainScene": "Hình ảnh này dường như không chứa bất kỳ dữ liệu cảnh nào. Bạn đã bật tính năng nhúng cảnh khi xuất chưa?", "cannotRestoreFromImage": "", "invalidSceneUrl": "", "resetLibrary": "", - "removeItemsFromsLibrary": "", - "invalidEncryptionKey": "", - "collabOfflineWarning": "" + "removeItemsFromsLibrary": "Xoá {{count}} món từ thư viện?", + "invalidEncryptionKey": "Khóa mã hóa phải có 22 ký tự. Hợp tác trực tiếp bị vô hiệu hóa.", + "collabOfflineWarning": "Không có kết nối internet.\nThay đổi của bạn sẽ không được lưu!" }, "errors": { - "unsupportedFileType": "", - "imageInsertError": "", - "fileTooBig": "", - "svgImageInsertError": "", - "invalidSVGString": "", - "cannotResolveCollabServer": "", - "importLibraryError": "", - "collabSaveFailed": "", - "collabSaveFailed_sizeExceeded": "" + "unsupportedFileType": "Loại tập tin không được hỗ trợ.", + "imageInsertError": "Không thể thêm ảnh. Hãy thử lại sau...", + "fileTooBig": "Tệp tin quá lớn. Dung lượng tối đa cho phép là {{maxSize}}.", + "svgImageInsertError": "Không thể thêm ảnh SVG. Mã SVG có vẻ sai.", + "invalidSVGString": "SVG không hợp lệ.", + "cannotResolveCollabServer": "Không thể kết nối với máy chủ hợp tác. Hãy tải lại trang và thử lại.", + "importLibraryError": "Không thể tải thư viện", + "collabSaveFailed": "Không thể lưu vào cơ sở dữ liệu. Nếu vấn đề tiếp tục xảy ra, bạn nên lưu tệp vào máy để đảm bảo bạn không bị mất công việc.", + "collabSaveFailed_sizeExceeded": "Không thể lưu vào cơ sở dữ liệu, canvas có vẻ quá lớn. Bạn nên lưu tệp cục bộ để đảm bảo bạn không bị mất công việc.", + "brave_measure_text_error": { + "start": "Có vẻ bạn đang sử dụng trình duyện Brave với chức năng", + "aggressive_block_fingerprint": "Aggressively Block Fingerprinting", + "setting_enabled": "được bật", + "break": "Điều này có thể xảy ra lỗi các", + "text_elements": "Yếu Tố Chữ", + "in_your_drawings": "trong bản vẽ của bạn", + "strongly_recommend": "Chúng tôi khuyên rằng bạn nên tắt chức năng này. Bạn có thể theo", + "steps": "các bước sau đây", + "how": "để tắt nó", + "disable_setting": " Nếu tắt chức năng này vẫn không sửa lại lỗi hiện thị các yếu tố chữ, hảy mở", + "issue": "issue", + "write": "trên trang GitHUb của chúng tôi, hoặc nhắn chúng tôi tại", + "discord": "Discord" + } }, "toolBar": { - "selection": "", - "image": "", - "rectangle": "", - "diamond": "", - "ellipse": "", - "arrow": "", - "line": "", - "freedraw": "", - "text": "", - "library": "", - "lock": "", - "penMode": "", - "link": "", - "eraser": "", - "hand": "" + "selection": "Lựa chọn", + "image": "Chèn ảnh", + "rectangle": "Hình chữ nhật", + "diamond": "Kim cương", + "ellipse": "Hình elíp", + "arrow": "Mũi tên", + "line": "Đường kẻ", + "freedraw": "Vẽ", + "text": "Văn bản", + "library": "Thư viện", + "lock": "Giữ dụng cũ hiện tại sau khi vẽ", + "penMode": "Chế độ bút vẽ - ngăn ngừa chạm nhầm", + "link": "Thêm/ Chỉnh sửa liên kết cho hình được chọn", + "eraser": "Xóa", + "hand": "Tay kéo" }, "headings": { - "canvasActions": "", - "selectedShapeActions": "", - "shapes": "" + "canvasActions": "Hành động canvas", + "selectedShapeActions": "Các hành động cho hình dạng đã chọn", + "shapes": "Các hình khối" }, "hints": { - "canvasPanning": "", - "linearElement": "", - "freeDraw": "", - "text": "", - "text_selected": "", - "text_editing": "", - "linearElementMulti": "", - "lockAngle": "", - "resize": "", + "canvasPanning": "Để di chuyển canvas, giữ con lăn chuột hoặc phím cách trong khi kéo, hoặc sử dụng công cụ cầm tay", + "linearElement": "Ấn để bắt đầu nhiểm điểm vẽ, kéo để vẽ một đường thẳng", + "freeDraw": "Ấn bà kéo, thả khi bạn xong", + "text": "Mẹo: bạn có thể thêm văn bản tại bất cứ đâu bằng cách ấn hai lần bằng tool lựa chọn", + "text_selected": "Ấn 2 lần hoặc nhấn ENTER để chỉnh văn bản", + "text_editing": "Nhấn Escape hoặc Ctrl/Cmd+ENTER để hoàn thành chỉnh sửa", + "linearElementMulti": "Nhấn vào điểm cuối hoặc nhấn Escape hoặc Enter để kết thúc", + "lockAngle": "Bạn có thể chỉnh lại góc bằng cách giữ phím SHIFT", + "resize": "Bạn có thể chỉnh tỷ lệ bằng cách giữ SHIFT khi chỉnh kích cỡ,\ngiữ ALT để chỉnh kích cỡ từ trung tâm", "resizeImage": "", "rotate": "", "lineEditor_info": "", @@ -248,19 +264,19 @@ "bindTextToElement": "", "deepBoxSelect": "", "eraserRevert": "", - "firefox_clipboard_write": "" + "firefox_clipboard_write": "Tính năng này có thể được bật bằng cách đặt cờ \"dom.events.asyncClipboard.clipboardItem\" thành \"true\". Để thay đổi cờ trình duyệt trong Firefox, hãy truy cập trang \"about:config\"." }, "canvasError": { - "cannotShowPreview": "", - "canvasTooBig": "", - "canvasTooBigTip": "" + "cannotShowPreview": "Không thể xem trước", + "canvasTooBig": "Canvas này có thể hơi lớn.", + "canvasTooBigTip": "Mẹo: hãy thử di chuyển các elements nhất lại gần nhau hơn một chút." }, "errorSplash": { "headingMain_pre": "", "headingMain_button": "", - "clearCanvasMessage": "", - "clearCanvasMessage_button": "", - "clearCanvasCaveat": "", + "clearCanvasMessage": "Nếu không tải lại được, hãy thử ", + "clearCanvasMessage_button": "dọn canvas.", + "clearCanvasCaveat": " Điều này sẽ dẫn đến mất dữ liệu bạn đã làm ", "trackedToSentry_pre": "", "trackedToSentry_post": "", "openIssueMessage_pre": "", @@ -303,7 +319,8 @@ "doubleClick": "", "drag": "", "editor": "", - "editSelectedShape": "", + "editLineArrowPoints": "", + "editText": "", "github": "", "howto": "", "or": "", @@ -321,11 +338,11 @@ "movePageLeftRight": "" }, "clearCanvasDialog": { - "title": "" + "title": "Dọn canvas" }, "publishDialog": { "title": "", - "itemName": "", + "itemName": "Tên món", "authorName": "", "githubUsername": "", "twitterUsername": "", @@ -359,9 +376,9 @@ "link": "", "post": "" }, - "noteItems": "", - "atleastOneLibItem": "", - "republishWarning": "" + "noteItems": "Từng món trong thư viện phải có tên riêng để có thể lọc. Các món thư viện sau đây sẽ thêm:", + "atleastOneLibItem": "Vui lòng chọn ít nhất một món thư viện để bắt đầu", + "republishWarning": "Lưu ý: một số món đã chọn được đánh dấu là đã xuất bản/đã gửi. Bạn chỉ nên gửi lại các món khi cập nhật thư viện hiện có hoặc gửi." }, "publishSuccessDialog": { "title": "", @@ -370,7 +387,7 @@ }, "confirmDialog": { "resetLibrary": "", - "removeItemsFromLib": "" + "removeItemsFromLib": "Xóa món đã chọn khỏi thư viện" }, "encrypted": { "tooltip": "", @@ -398,7 +415,7 @@ "copyToClipboardAsPng": "", "fileSaved": "", "fileSavedToFilename": "", - "canvas": "", + "canvas": "canvas", "selection": "", "pasteAsSingleElement": "" }, @@ -440,14 +457,14 @@ "a61e4d": "", "862e9c": "", "5f3dc4": "", - "364fc7": "", - "1864ab": "", - "0b7285": "", - "087f5b": "", - "2b8a3e": "", - "5c940d": "", - "e67700": "", - "d9480f": "" + "364fc7": "Chàm 9", + "1864ab": "Xanh Dương 9", + "0b7285": "Lục Lam 9", + "087f5b": "Xanh Mòng Két 9", + "2b8a3e": "Xanh Lá 9", + "5c940d": "Chanh Xanh 9", + "e67700": "Vàng 9", + "d9480f": "Cam 9" }, "welcomeScreen": { "app": { diff --git a/src/locales/zh-CN.json b/src/locales/zh-CN.json index 2e5debd16..5863f5ceb 100644 --- a/src/locales/zh-CN.json +++ b/src/locales/zh-CN.json @@ -35,12 +35,12 @@ "arrowheads": "端点", "arrowhead_none": "无", "arrowhead_arrow": "箭头", - "arrowhead_bar": "条", + "arrowhead_bar": "条状", "arrowhead_dot": "圆点", "arrowhead_triangle": "三角箭头", "fontSize": "字体大小", "fontFamily": "字体", - "onlySelected": "仅被选中", + "onlySelected": "仅选中", "withBackground": "背景", "exportEmbedScene": "包含画布数据", "exportEmbedScene_details": "画布数据将被保存到导出的 PNG/SVG 文件,以便恢复。\n将会增加导出的文件大小。", @@ -110,6 +110,7 @@ "increaseFontSize": "放大字体大小", "unbindText": "取消文本绑定", "bindText": "将文本绑定到容器", + "createContainerFromText": "将文本包围在容器中", "link": { "edit": "编辑链接", "create": "新建链接", @@ -204,7 +205,22 @@ "cannotResolveCollabServer": "无法连接到实时协作服务器。请重新加载页面并重试。", "importLibraryError": "无法加载素材库", "collabSaveFailed": "无法保存到后端数据库。如果问题持续存在,您应该保存文件到本地,以确保您的工作不会丢失。", - "collabSaveFailed_sizeExceeded": "无法保存到后端数据库,画布似乎过大。您应该保存文件到本地,以确保您的工作不会丢失。" + "collabSaveFailed_sizeExceeded": "无法保存到后端数据库,画布似乎过大。您应该保存文件到本地,以确保您的工作不会丢失。", + "brave_measure_text_error": { + "start": "看起来您正在使用 Brave 浏览器并启用了", + "aggressive_block_fingerprint": "积极阻止指纹识别", + "setting_enabled": "设置", + "break": "这可能会破坏绘图中的", + "text_elements": "文本元素", + "in_your_drawings": " ", + "strongly_recommend": "我们强烈建议禁用此设置。您可以按照", + "steps": "这些步骤", + "how": "来设置", + "disable_setting": " 如果禁用此设置无法修复文本元素的显示,请在 GitHub 提交一个", + "issue": "issue", + "write": ",或者通过", + "discord": "Discord 反馈" + } }, "toolBar": { "selection": "选择", @@ -248,7 +264,7 @@ "bindTextToElement": "按下 Enter 以添加文本", "deepBoxSelect": "按住 CtrlOrCmd 以深度选择,并避免拖拽", "eraserRevert": "按住 Alt 以反选被标记删除的元素", - "firefox_clipboard_write": "将高级配置首选项“dom.events.asyncClipboard.lipboarditem”设置为“true”可以启用此功能。要更改 Firefox 的高级配置首选项,请前往“about:config”页面。" + "firefox_clipboard_write": "将高级配置首选项“dom.events.asyncClipboard.clipboardItem”设置为“true”可以启用此功能。要更改 Firefox 的高级配置首选项,请前往“about:config”页面。" }, "canvasError": { "cannotShowPreview": "无法显示预览", @@ -303,7 +319,8 @@ "doubleClick": "双击", "drag": "拖动", "editor": "编辑器", - "editSelectedShape": "编辑选中的形状 (文本、箭头或线条)", + "editLineArrowPoints": "", + "editText": "", "github": "提交问题", "howto": "帮助文档", "or": "或", diff --git a/src/locales/zh-HK.json b/src/locales/zh-HK.json index e87a0d3e1..bbf23f7f7 100644 --- a/src/locales/zh-HK.json +++ b/src/locales/zh-HK.json @@ -110,6 +110,7 @@ "increaseFontSize": "", "unbindText": "", "bindText": "", + "createContainerFromText": "", "link": { "edit": "", "create": "", @@ -204,7 +205,22 @@ "cannotResolveCollabServer": "", "importLibraryError": "", "collabSaveFailed": "", - "collabSaveFailed_sizeExceeded": "" + "collabSaveFailed_sizeExceeded": "", + "brave_measure_text_error": { + "start": "", + "aggressive_block_fingerprint": "", + "setting_enabled": "", + "break": "", + "text_elements": "", + "in_your_drawings": "", + "strongly_recommend": "", + "steps": "", + "how": "", + "disable_setting": "", + "issue": "", + "write": "", + "discord": "" + } }, "toolBar": { "selection": "", @@ -303,7 +319,8 @@ "doubleClick": "", "drag": "", "editor": "", - "editSelectedShape": "", + "editLineArrowPoints": "", + "editText": "", "github": "", "howto": "", "or": "", diff --git a/src/locales/zh-TW.json b/src/locales/zh-TW.json index 3293fad4a..f4462842c 100644 --- a/src/locales/zh-TW.json +++ b/src/locales/zh-TW.json @@ -110,6 +110,7 @@ "increaseFontSize": "放大文字", "unbindText": "取消綁定文字", "bindText": "結合文字至容器", + "createContainerFromText": "將文字包於容器中", "link": { "edit": "編輯連結", "create": "建立連結", @@ -204,7 +205,22 @@ "cannotResolveCollabServer": "無法連結至 collab 伺服器。請重新整理後再試一次。", "importLibraryError": "無法載入資料庫", "collabSaveFailed": "無法儲存至後端資料庫。若此問題持續發生,請將檔案儲存於本機以確保資料不會遺失。", - "collabSaveFailed_sizeExceeded": "無法儲存至後端資料庫,可能的原因為畫布尺寸過大。請將檔案儲存於本機以確保資料不會遺失。" + "collabSaveFailed_sizeExceeded": "無法儲存至後端資料庫,可能的原因為畫布尺寸過大。請將檔案儲存於本機以確保資料不會遺失。", + "brave_measure_text_error": { + "start": "您似乎正在使用 Brave 瀏覽器並且將", + "aggressive_block_fingerprint": "\"Aggressively Block Fingerprinting\" 設定", + "setting_enabled": "設為開啟", + "break": "這可能導致破壞", + "text_elements": "文字元素", + "in_your_drawings": "在您的繪圖中", + "strongly_recommend": "我們強烈建議您關掉此設定。您可以參考", + "steps": "這些步驟", + "how": "來變更", + "disable_setting": " 若關閉此設定無法修正文字元素的顯示問題,請回報", + "issue": "問題", + "write": "至我們的 GitHub,或反應在我們的", + "discord": "Discord" + } }, "toolBar": { "selection": "選取", @@ -303,7 +319,8 @@ "doubleClick": "雙擊", "drag": "拖曳", "editor": "編輯器", - "editSelectedShape": "編輯選定的形狀(文字/箭號/線條)", + "editLineArrowPoints": "編輯線/箭頭控制點", + "editText": "編輯文字/增加標籤", "github": "發現異常?回報問題", "howto": "參照我們的說明", "or": "或", From f640ddc2aa1320e674c7d8aed06e78572bf61213 Mon Sep 17 00:00:00 2001 From: David Luzar Date: Sun, 16 Apr 2023 17:22:16 +0200 Subject: [PATCH 64/74] fix: incorrectly duplicating items on paste/library insert (#6467 * fix: incorrectly duplicating items on paste/library insert * fix: deduplicate element ids on restore * tests --- src/components/App.tsx | 35 +++---- src/data/restore.ts | 7 ++ src/element/newElement.ts | 43 +++++---- .../regressionTests.test.tsx.snap | 12 +-- src/tests/helpers/api.ts | 5 +- src/tests/library.test.tsx | 94 +++++++++++++++++++ 6 files changed, 153 insertions(+), 43 deletions(-) diff --git a/src/components/App.tsx b/src/components/App.tsx index 1ebdc7713..be5561995 100644 --- a/src/components/App.tsx +++ b/src/components/App.tsx @@ -127,7 +127,11 @@ import { } from "../element/binding"; import { LinearElementEditor } from "../element/linearElementEditor"; import { mutateElement, newElementWith } from "../element/mutateElement"; -import { deepCopyElement, newFreeDrawElement } from "../element/newElement"; +import { + deepCopyElement, + duplicateElements, + newFreeDrawElement, +} from "../element/newElement"; import { hasBoundTextElement, isArrowElement, @@ -1625,35 +1629,22 @@ class App extends React.Component { const dx = x - elementsCenterX; const dy = y - elementsCenterY; - const groupIdMap = new Map(); const [gridX, gridY] = getGridPoint(dx, dy, this.state.gridSize); - const oldIdToDuplicatedId = new Map(); - const newElements = elements.map((element) => { - const newElement = duplicateElement( - this.state.editingGroupId, - groupIdMap, - element, - { + const newElements = duplicateElements( + elements.map((element) => { + return newElementWith(element, { x: element.x + gridX - minX, y: element.y + gridY - minY, - }, - ); - oldIdToDuplicatedId.set(element.id, newElement.id); - return newElement; - }); + }); + }), + ); - bindTextToShapeAfterDuplication(newElements, elements, oldIdToDuplicatedId); const nextElements = [ ...this.scene.getElementsIncludingDeleted(), ...newElements, ]; - fixBindingsAfterDuplication(nextElements, elements, oldIdToDuplicatedId); - - if (opts.files) { - this.files = { ...this.files, ...opts.files }; - } this.scene.replaceAllElements(nextElements); @@ -1664,6 +1655,10 @@ class App extends React.Component { } }); + if (opts.files) { + this.files = { ...this.files, ...opts.files }; + } + this.history.resumeRecording(); this.setState( diff --git a/src/data/restore.ts b/src/data/restore.ts index 2735d91d2..fcf5fa132 100644 --- a/src/data/restore.ts +++ b/src/data/restore.ts @@ -369,6 +369,9 @@ export const restoreElements = ( localElements: readonly ExcalidrawElement[] | null | undefined, opts?: { refreshDimensions?: boolean; repairBindings?: boolean } | undefined, ): ExcalidrawElement[] => { + // used to detect duplicate top-level element ids + const existingIds = new Set(); + const localElementsMap = localElements ? arrayToMap(localElements) : null; const restoredElements = (elements || []).reduce((elements, element) => { // filtering out selection, which is legacy, no longer kept in elements, @@ -383,6 +386,10 @@ export const restoreElements = ( if (localElement && localElement.version > migratedElement.version) { migratedElement = bumpVersion(migratedElement, localElement.version); } + if (existingIds.has(migratedElement.id)) { + migratedElement = { ...migratedElement, id: randomId() }; + } + existingIds.add(migratedElement.id); elements.push(migratedElement); } } diff --git a/src/element/newElement.ts b/src/element/newElement.ts index 72aa54684..e3b25e848 100644 --- a/src/element/newElement.ts +++ b/src/element/newElement.ts @@ -439,6 +439,29 @@ export const deepCopyElement = ( return _deepCopyElement(val); }; +/** + * utility wrapper to generate new id. In test env it reuses the old + postfix + * for test assertions. + */ +const regenerateId = ( + /** supply null if no previous id exists */ + previousId: string | null, +) => { + if (isTestEnv() && previousId) { + let nextId = `${previousId}_copy`; + // `window.h` may not be defined in some unit tests + if ( + window.h?.app + ?.getSceneElementsIncludingDeleted() + .find((el) => el.id === nextId) + ) { + nextId += "_copy"; + } + return nextId; + } + return randomId(); +}; + /** * Duplicate an element, often used in the alt-drag operation. * Note that this method has gotten a bit complicated since the @@ -461,19 +484,7 @@ export const duplicateElement = ( ): Readonly => { let copy = deepCopyElement(element); - if (isTestEnv()) { - copy.id = `${copy.id}_copy`; - // `window.h` may not be defined in some unit tests - if ( - window.h?.app - ?.getSceneElementsIncludingDeleted() - .find((el) => el.id === copy.id) - ) { - copy.id += "_copy"; - } - } else { - copy.id = randomId(); - } + copy.id = regenerateId(copy.id); copy.boundElements = null; copy.updated = getUpdatedTimestamp(); copy.seed = randomInteger(); @@ -482,7 +493,7 @@ export const duplicateElement = ( editingGroupId, (groupId) => { if (!groupIdMapForOperation.has(groupId)) { - groupIdMapForOperation.set(groupId, randomId()); + groupIdMapForOperation.set(groupId, regenerateId(groupId)); } return groupIdMapForOperation.get(groupId)!; }, @@ -520,7 +531,7 @@ export const duplicateElements = (elements: readonly ExcalidrawElement[]) => { // if we haven't migrated the element id, but an old element with the same // id exists, generate a new id for it and return it if (origElementsMap.has(id)) { - const newId = randomId(); + const newId = regenerateId(id); elementNewIdsMap.set(id, newId); return newId; } @@ -538,7 +549,7 @@ export const duplicateElements = (elements: readonly ExcalidrawElement[]) => { if (clonedElement.groupIds) { clonedElement.groupIds = clonedElement.groupIds.map((groupId) => { if (!groupNewIdsMap.has(groupId)) { - groupNewIdsMap.set(groupId, randomId()); + groupNewIdsMap.set(groupId, regenerateId(groupId)); } return groupNewIdsMap.get(groupId)!; }); diff --git a/src/tests/__snapshots__/regressionTests.test.tsx.snap b/src/tests/__snapshots__/regressionTests.test.tsx.snap index 35f9eb7c8..967a0cf69 100644 --- a/src/tests/__snapshots__/regressionTests.test.tsx.snap +++ b/src/tests/__snapshots__/regressionTests.test.tsx.snap @@ -13431,7 +13431,7 @@ Object { "boundElements": null, "fillStyle": "hachure", "groupIds": Array [ - "id6", + "id4_copy", ], "height": 10, "id": "id0_copy", @@ -13464,7 +13464,7 @@ Object { "boundElements": null, "fillStyle": "hachure", "groupIds": Array [ - "id6", + "id4_copy", ], "height": 10, "id": "id1_copy", @@ -13497,7 +13497,7 @@ Object { "boundElements": null, "fillStyle": "hachure", "groupIds": Array [ - "id6", + "id4_copy", ], "height": 10, "id": "id2_copy", @@ -13981,7 +13981,7 @@ Object { "boundElements": null, "fillStyle": "hachure", "groupIds": Array [ - "id6", + "id4_copy", ], "height": 10, "id": "id0_copy", @@ -14011,7 +14011,7 @@ Object { "boundElements": null, "fillStyle": "hachure", "groupIds": Array [ - "id6", + "id4_copy", ], "height": 10, "id": "id1_copy", @@ -14041,7 +14041,7 @@ Object { "boundElements": null, "fillStyle": "hachure", "groupIds": Array [ - "id6", + "id4_copy", ], "height": 10, "id": "id2_copy", diff --git a/src/tests/helpers/api.ts b/src/tests/helpers/api.ts index bc8bfc8a9..705180d6a 100644 --- a/src/tests/helpers/api.ts +++ b/src/tests/helpers/api.ts @@ -211,7 +211,10 @@ export class API { type, startArrowhead: null, endArrowhead: null, - points: rest.points ?? [], + points: rest.points ?? [ + [0, 0], + [100, 100], + ], }); break; case "image": diff --git a/src/tests/library.test.tsx b/src/tests/library.test.tsx index 6b3ff5dd7..86847aeee 100644 --- a/src/tests/library.test.tsx +++ b/src/tests/library.test.tsx @@ -72,6 +72,100 @@ describe("library", () => { }); }); + it("should regenerate ids but retain bindings on library insert", async () => { + const rectangle = API.createElement({ + id: "rectangle1", + type: "rectangle", + boundElements: [ + { type: "text", id: "text1" }, + { type: "arrow", id: "arrow1" }, + ], + }); + const text = API.createElement({ + id: "text1", + type: "text", + text: "ola", + containerId: "rectangle1", + }); + const arrow = API.createElement({ + id: "arrow1", + type: "arrow", + endBinding: { elementId: "rectangle1", focus: -1, gap: 0 }, + }); + + await API.drop( + new Blob( + [ + serializeLibraryAsJSON([ + { + id: "item1", + status: "published", + elements: [rectangle, text, arrow], + created: 1, + }, + ]), + ], + { + type: MIME_TYPES.excalidrawlib, + }, + ), + ); + + await waitFor(() => { + expect(h.elements).toEqual([ + expect.objectContaining({ + id: "rectangle1_copy", + boundElements: expect.arrayContaining([ + { type: "text", id: "text1_copy" }, + { type: "arrow", id: "arrow1_copy" }, + ]), + }), + expect.objectContaining({ + id: "text1_copy", + containerId: "rectangle1_copy", + }), + expect.objectContaining({ + id: "arrow1_copy", + endBinding: expect.objectContaining({ elementId: "rectangle1_copy" }), + }), + ]); + }); + }); + + it("should fix duplicate ids between items on insert", async () => { + // note, we're not testing for duplicate group ids and such because + // deduplication of that happens upstream in the library component + // which would be very hard to orchestrate in this test + + const elem1 = API.createElement({ + id: "elem1", + type: "rectangle", + }); + const item1: LibraryItem = { + id: "item1", + status: "published", + elements: [elem1], + created: 1, + }; + + await API.drop( + new Blob([serializeLibraryAsJSON([item1, item1])], { + type: MIME_TYPES.excalidrawlib, + }), + ); + + await waitFor(() => { + expect(h.elements).toEqual([ + expect.objectContaining({ + id: "elem1_copy", + }), + expect.objectContaining({ + id: expect.not.stringMatching(/^(elem1_copy|elem1)$/), + }), + ]); + }); + }); + it("inserting library item should revert to selection tool", async () => { UI.clickTool("rectangle"); expect(h.elements).toEqual([]); From c3e8ddaf580f395366ff1fded0c9592a4485c701 Mon Sep 17 00:00:00 2001 From: David Luzar Date: Mon, 17 Apr 2023 11:41:27 +0200 Subject: [PATCH 65/74] fix: improperly cache-busting on canvas scale instead of zoom (#6473) --- src/renderer/renderElement.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/renderer/renderElement.ts b/src/renderer/renderElement.ts index 486e52bdf..21de70cb9 100644 --- a/src/renderer/renderElement.ts +++ b/src/renderer/renderElement.ts @@ -88,6 +88,7 @@ export interface ExcalidrawElementWithCanvas { canvas: HTMLCanvasElement; theme: RenderConfig["theme"]; scale: number; + zoomValue: RenderConfig["zoom"]["value"]; canvasOffsetX: number; canvasOffsetY: number; boundTextElementVersion: number | null; @@ -202,6 +203,7 @@ const generateElementCanvas = ( canvas, theme: renderConfig.theme, scale, + zoomValue: zoom.value, canvasOffsetX, canvasOffsetY, boundTextElementVersion: getBoundTextElement(element)?.version || null, @@ -712,7 +714,7 @@ const generateElementWithCanvas = ( const prevElementWithCanvas = elementWithCanvasCache.get(element); const shouldRegenerateBecauseZoom = prevElementWithCanvas && - prevElementWithCanvas.scale !== zoom.value && + prevElementWithCanvas.zoomValue !== zoom.value && !renderConfig?.shouldCacheIgnoreZoom; const boundTextElementVersion = getBoundTextElement(element)?.version || null; From 21726e22cce04aa2d3bdac7f270e2748156ca40e Mon Sep 17 00:00:00 2001 From: Aakansha Doshi Date: Tue, 18 Apr 2023 18:42:48 +0530 Subject: [PATCH 66/74] fix: mark more props as optional for element (#6448) * fix: mark more props as optional for element * fix --- src/appState.ts | 17 +++++----- src/charts.ts | 18 +---------- src/components/App.tsx | 2 -- src/constants.ts | 23 ++++++++++++- src/element/newElement.ts | 68 +++++++++++++++++++++++++++------------ 5 files changed, 80 insertions(+), 48 deletions(-) diff --git a/src/appState.ts b/src/appState.ts index f02d5943c..6f4db7557 100644 --- a/src/appState.ts +++ b/src/appState.ts @@ -1,5 +1,6 @@ import oc from "open-color"; import { + DEFAULT_ELEMENT_PROPS, DEFAULT_FONT_FAMILY, DEFAULT_FONT_SIZE, DEFAULT_TEXT_ALIGN, @@ -23,18 +24,18 @@ export const getDefaultAppState = (): Omit< theme: THEME.LIGHT, collaborators: new Map(), currentChartType: "bar", - currentItemBackgroundColor: "transparent", + currentItemBackgroundColor: DEFAULT_ELEMENT_PROPS.backgroundColor, currentItemEndArrowhead: "arrow", - currentItemFillStyle: "hachure", + currentItemFillStyle: DEFAULT_ELEMENT_PROPS.fillStyle, currentItemFontFamily: DEFAULT_FONT_FAMILY, currentItemFontSize: DEFAULT_FONT_SIZE, - currentItemOpacity: 100, - currentItemRoughness: 1, + currentItemOpacity: DEFAULT_ELEMENT_PROPS.opacity, + currentItemRoughness: DEFAULT_ELEMENT_PROPS.roughness, currentItemStartArrowhead: null, - currentItemStrokeColor: oc.black, + currentItemStrokeColor: DEFAULT_ELEMENT_PROPS.strokeColor, currentItemRoundness: "round", - currentItemStrokeStyle: "solid", - currentItemStrokeWidth: 1, + currentItemStrokeStyle: DEFAULT_ELEMENT_PROPS.strokeStyle, + currentItemStrokeWidth: DEFAULT_ELEMENT_PROPS.strokeWidth, currentItemTextAlign: DEFAULT_TEXT_ALIGN, cursorButton: "up", draggingElement: null, @@ -44,7 +45,7 @@ export const getDefaultAppState = (): Omit< activeTool: { type: "selection", customType: null, - locked: false, + locked: DEFAULT_ELEMENT_PROPS.locked, lastActiveTool: null, }, penMode: false, diff --git a/src/charts.ts b/src/charts.ts index e8980db6c..c3b0950d1 100644 --- a/src/charts.ts +++ b/src/charts.ts @@ -1,10 +1,5 @@ import colors from "./colors"; -import { - DEFAULT_FONT_FAMILY, - DEFAULT_FONT_SIZE, - ENV, - VERTICAL_ALIGN, -} from "./constants"; +import { DEFAULT_FONT_SIZE, ENV } from "./constants"; import { newElement, newLinearElement, newTextElement } from "./element"; import { NonDeletedExcalidrawElement } from "./element/types"; import { randomId } from "./random"; @@ -166,17 +161,7 @@ const bgColors = colors.elementBackground.slice( // Put all the common properties here so when the whole chart is selected // the properties dialog shows the correct selected values const commonProps = { - fillStyle: "hachure", - fontFamily: DEFAULT_FONT_FAMILY, - fontSize: DEFAULT_FONT_SIZE, - opacity: 100, - roughness: 1, strokeColor: colors.elementStroke[0], - roundness: null, - strokeStyle: "solid", - strokeWidth: 1, - verticalAlign: VERTICAL_ALIGN.MIDDLE, - locked: false, } as const; const getChartDimentions = (spreadsheet: Spreadsheet) => { @@ -323,7 +308,6 @@ const chartBaseElements = ( x: x + chartWidth / 2, y: y - BAR_HEIGHT - BAR_GAP * 2 - DEFAULT_FONT_SIZE, roundness: null, - strokeStyle: "solid", textAlign: "center", }) : null; diff --git a/src/components/App.tsx b/src/components/App.tsx index be5561995..fcbcca873 100644 --- a/src/components/App.tsx +++ b/src/components/App.tsx @@ -2732,7 +2732,6 @@ class App extends React.Component { strokeStyle: this.state.currentItemStrokeStyle, roughness: this.state.currentItemRoughness, opacity: this.state.currentItemOpacity, - roundness: null, text: "", fontSize, fontFamily, @@ -2744,7 +2743,6 @@ class App extends React.Component { : DEFAULT_VERTICAL_ALIGN, containerId: shouldBindToContainer ? container?.id : undefined, groupIds: container?.groupIds ?? [], - locked: false, lineHeight, }); diff --git a/src/constants.ts b/src/constants.ts index ef563e4a4..23fefa6e5 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -1,6 +1,7 @@ import cssVariables from "./css/variables.module.scss"; import { AppProps } from "./types"; -import { FontFamilyValues } from "./element/types"; +import { ExcalidrawElement, FontFamilyValues } from "./element/types"; +import oc from "open-color"; export const isDarwin = /Mac|iPod|iPhone|iPad/.test(navigator.platform); export const isWindows = /^Win/.test(navigator.platform); @@ -254,3 +255,23 @@ export const ROUNDNESS = { /** key containt id of precedeing elemnt id we use in reconciliation during * collaboration */ export const PRECEDING_ELEMENT_KEY = "__precedingElement__"; + +export const DEFAULT_ELEMENT_PROPS: { + strokeColor: ExcalidrawElement["strokeColor"]; + backgroundColor: ExcalidrawElement["backgroundColor"]; + fillStyle: ExcalidrawElement["fillStyle"]; + strokeWidth: ExcalidrawElement["strokeWidth"]; + strokeStyle: ExcalidrawElement["strokeStyle"]; + roughness: ExcalidrawElement["roughness"]; + opacity: ExcalidrawElement["opacity"]; + locked: ExcalidrawElement["locked"]; +} = { + strokeColor: oc.black, + backgroundColor: "transparent", + fillStyle: "hachure", + strokeWidth: 1, + strokeStyle: "solid", + roughness: 1, + opacity: 100, + locked: false, +}; diff --git a/src/element/newElement.ts b/src/element/newElement.ts index e3b25e848..36c8cc0e0 100644 --- a/src/element/newElement.ts +++ b/src/element/newElement.ts @@ -36,7 +36,14 @@ import { getMaxContainerWidth, getDefaultLineHeight, } from "./textElement"; -import { VERTICAL_ALIGN } from "../constants"; +import { + DEFAULT_ELEMENT_PROPS, + DEFAULT_FONT_FAMILY, + DEFAULT_FONT_SIZE, + DEFAULT_TEXT_ALIGN, + DEFAULT_VERTICAL_ALIGN, + VERTICAL_ALIGN, +} from "../constants"; import { isArrowElement } from "./typeChecks"; import { MarkOptional, Merge, Mutable } from "../utility-types"; @@ -51,6 +58,15 @@ type ElementConstructorOpts = MarkOptional< | "version" | "versionNonce" | "link" + | "strokeStyle" + | "fillStyle" + | "strokeColor" + | "backgroundColor" + | "roughness" + | "strokeWidth" + | "roundness" + | "locked" + | "opacity" >; const _newElementBase = ( @@ -58,13 +74,13 @@ const _newElementBase = ( { x, y, - strokeColor, - backgroundColor, - fillStyle, - strokeWidth, - strokeStyle, - roughness, - opacity, + strokeColor = DEFAULT_ELEMENT_PROPS.strokeColor, + backgroundColor = DEFAULT_ELEMENT_PROPS.backgroundColor, + fillStyle = DEFAULT_ELEMENT_PROPS.fillStyle, + strokeWidth = DEFAULT_ELEMENT_PROPS.strokeWidth, + strokeStyle = DEFAULT_ELEMENT_PROPS.strokeStyle, + roughness = DEFAULT_ELEMENT_PROPS.roughness, + opacity = DEFAULT_ELEMENT_PROPS.opacity, width = 0, height = 0, angle = 0, @@ -72,7 +88,7 @@ const _newElementBase = ( roundness = null, boundElements = null, link = null, - locked, + locked = DEFAULT_ELEMENT_PROPS.locked, ...rest }: ElementConstructorOpts & Omit, "type">, ) => { @@ -138,27 +154,39 @@ const getTextElementPositionOffsets = ( export const newTextElement = ( opts: { text: string; - fontSize: number; - fontFamily: FontFamilyValues; - textAlign: TextAlign; - verticalAlign: VerticalAlign; + fontSize?: number; + fontFamily?: FontFamilyValues; + textAlign?: TextAlign; + verticalAlign?: VerticalAlign; containerId?: ExcalidrawTextContainer["id"]; lineHeight?: ExcalidrawTextElement["lineHeight"]; + strokeWidth?: ExcalidrawTextElement["strokeWidth"]; } & ElementConstructorOpts, ): NonDeleted => { - const lineHeight = opts.lineHeight || getDefaultLineHeight(opts.fontFamily); + const fontFamily = opts.fontFamily || DEFAULT_FONT_FAMILY; + const fontSize = opts.fontSize || DEFAULT_FONT_SIZE; + const lineHeight = opts.lineHeight || getDefaultLineHeight(fontFamily); const text = normalizeText(opts.text); - const metrics = measureText(text, getFontString(opts), lineHeight); - const offsets = getTextElementPositionOffsets(opts, metrics); + const metrics = measureText( + text, + getFontString({ fontFamily, fontSize }), + lineHeight, + ); + const textAlign = opts.textAlign || DEFAULT_TEXT_ALIGN; + const verticalAlign = opts.verticalAlign || DEFAULT_VERTICAL_ALIGN; + const offsets = getTextElementPositionOffsets( + { textAlign, verticalAlign }, + metrics, + ); const textElement = newElementWith( { ..._newElementBase("text", opts), text, - fontSize: opts.fontSize, - fontFamily: opts.fontFamily, - textAlign: opts.textAlign, - verticalAlign: opts.verticalAlign, + fontSize, + fontFamily, + textAlign, + verticalAlign, x: opts.x - offsets.x, y: opts.y - offsets.y, width: metrics.width, From 801412bf6bd15b81403ec976a2089f2e303fff45 Mon Sep 17 00:00:00 2001 From: Aakansha Doshi Date: Tue, 18 Apr 2023 18:50:25 +0530 Subject: [PATCH 67/74] fix: restore original container height when unbinding text which was binded via context menu (#6444) * fix: restore original container height when unbinding text which was binded via context menu * remove flag * comment --- src/actions/actionBoundText.tsx | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/actions/actionBoundText.tsx b/src/actions/actionBoundText.tsx index 3f240b5d8..830af3733 100644 --- a/src/actions/actionBoundText.tsx +++ b/src/actions/actionBoundText.tsx @@ -16,6 +16,7 @@ import { import { getOriginalContainerHeightFromCache, resetOriginalContainerCache, + updateOriginalContainerCache, } from "../element/textWysiwyg"; import { hasBoundTextElement, @@ -145,7 +146,11 @@ export const actionBindText = register({ id: textElement.id, }), }); + const originalContainerHeight = container.height; redrawTextBoundingBox(textElement, container); + // overwritting the cache with original container height so + // it can be restored when unbind + updateOriginalContainerCache(container.id, originalContainerHeight); return { elements: pushTextAboveContainer(elements, container, textElement), From 4d0d844e390e8b41d059f1fe646783c0465d1a41 Mon Sep 17 00:00:00 2001 From: David Luzar Date: Tue, 18 Apr 2023 15:27:51 +0200 Subject: [PATCH 68/74] feat: constrain export dialog preview size (#6475) --- src/components/ImageExportDialog.tsx | 24 +++++++++++------------- src/packages/utils.ts | 6 +++++- 2 files changed, 16 insertions(+), 14 deletions(-) diff --git a/src/components/ImageExportDialog.tsx b/src/components/ImageExportDialog.tsx index fb2c1ec81..0e4eff365 100644 --- a/src/components/ImageExportDialog.tsx +++ b/src/components/ImageExportDialog.tsx @@ -4,7 +4,6 @@ import { canvasToBlob } from "../data/blob"; import { NonDeletedExcalidrawElement } from "../element/types"; import { t } from "../i18n"; import { getSelectedElements, isSomeElementSelected } from "../scene"; -import { exportToCanvas } from "../scene/export"; import { AppState, BinaryFiles } from "../types"; import { Dialog } from "./Dialog"; import { clipboard } from "./icons"; @@ -15,6 +14,7 @@ import { CheckboxItem } from "./CheckboxItem"; import { DEFAULT_EXPORT_PADDING, isFirefox } from "../constants"; import { nativeFileSystemSupported } from "../data/filesystem"; import { ActionManager } from "../actions/manager"; +import { exportToCanvas } from "../packages/utils"; const supportsContextFilters = "filter" in document.createElement("canvas").getContext("2d")!; @@ -83,7 +83,6 @@ const ImageExportModal = ({ const someElementIsSelected = isSomeElementSelected(elements, appState); const [exportSelected, setExportSelected] = useState(someElementIsSelected); const previewRef = useRef(null); - const { exportBackground, viewBackgroundColor } = appState; const [renderError, setRenderError] = useState(null); const exportedElements = exportSelected @@ -99,10 +98,16 @@ const ImageExportModal = ({ if (!previewNode) { return; } - exportToCanvas(exportedElements, appState, files, { - exportBackground, - viewBackgroundColor, + const maxWidth = previewNode.offsetWidth; + if (!maxWidth) { + return; + } + exportToCanvas({ + elements: exportedElements, + appState, + files, exportPadding, + maxWidthOrHeight: maxWidth, }) .then((canvas) => { setRenderError(null); @@ -116,14 +121,7 @@ const ImageExportModal = ({ console.error(error); setRenderError(error); }); - }, [ - appState, - files, - exportedElements, - exportBackground, - exportPadding, - viewBackgroundColor, - ]); + }, [appState, files, exportedElements, exportPadding]); return (
diff --git a/src/packages/utils.ts b/src/packages/utils.ts index 1fb6cd3d9..560fa13ca 100644 --- a/src/packages/utils.ts +++ b/src/packages/utils.ts @@ -79,7 +79,11 @@ export const exportToCanvas = ({ const max = Math.max(width, height); - const scale = maxWidthOrHeight / max; + // if content is less then maxWidthOrHeight, fallback on supplied scale + const scale = + maxWidthOrHeight < max + ? maxWidthOrHeight / max + : appState?.exportScale ?? 1; canvas.width = width * scale; canvas.height = height * scale; From 979312f779b675b9e6ad917ae0b04512fcfbd783 Mon Sep 17 00:00:00 2001 From: Aakansha Doshi Date: Tue, 18 Apr 2023 19:44:14 +0530 Subject: [PATCH 69/74] fix: center align text when wrapped in container via context menu (#6480) * rename action to wrapTextInContainer * fix: center align text when wrapped in container via context menu * revert translation key * fix tests --- src/actions/actionBoundText.tsx | 5 +++-- src/actions/types.ts | 2 +- src/components/App.tsx | 4 ++-- src/element/textWysiwyg.test.tsx | 2 +- src/tests/__snapshots__/contextmenu.test.tsx.snap | 10 +++++----- src/tests/binding.test.tsx | 4 ++-- 6 files changed, 14 insertions(+), 13 deletions(-) diff --git a/src/actions/actionBoundText.tsx b/src/actions/actionBoundText.tsx index 830af3733..658bdf8ce 100644 --- a/src/actions/actionBoundText.tsx +++ b/src/actions/actionBoundText.tsx @@ -196,8 +196,8 @@ const pushContainerBelowText = ( return updatedElements; }; -export const actionCreateContainerFromText = register({ - name: "createContainerFromText", +export const actionWrapTextInContainer = register({ + name: "wrapTextInContainer", contextItemLabel: "labels.createContainerFromText", trackEvent: { category: "element" }, predicate: (elements, appState) => { @@ -286,6 +286,7 @@ export const actionCreateContainerFromText = register({ containerId: container.id, verticalAlign: VERTICAL_ALIGN.MIDDLE, boundElements: null, + textAlign: TEXT_ALIGN.CENTER, }, false, ); diff --git a/src/actions/types.ts b/src/actions/types.ts index e46cd2ab8..b03e1053b 100644 --- a/src/actions/types.ts +++ b/src/actions/types.ts @@ -115,7 +115,7 @@ export type ActionName = | "toggleLinearEditor" | "toggleEraserTool" | "toggleHandTool" - | "createContainerFromText"; + | "wrapTextInContainer"; export type PanelComponentProps = { elements: readonly ExcalidrawElement[]; diff --git a/src/components/App.tsx b/src/components/App.tsx index fcbcca873..20def468a 100644 --- a/src/components/App.tsx +++ b/src/components/App.tsx @@ -295,7 +295,7 @@ import { } from "../actions/actionCanvas"; import { jotaiStore } from "../jotai"; import { activeConfirmDialogAtom } from "./ActiveConfirmDialog"; -import { actionCreateContainerFromText } from "../actions/actionBoundText"; +import { actionWrapTextInContainer } from "../actions/actionBoundText"; import BraveMeasureTextError from "./BraveMeasureTextError"; const deviceContextInitialValue = { @@ -6364,7 +6364,7 @@ class App extends React.Component { actionGroup, actionUnbindText, actionBindText, - actionCreateContainerFromText, + actionWrapTextInContainer, actionUngroup, CONTEXT_MENU_SEPARATOR, actionAddToLibrary, diff --git a/src/element/textWysiwyg.test.tsx b/src/element/textWysiwyg.test.tsx index 4ae3f26f9..71c75c5a0 100644 --- a/src/element/textWysiwyg.test.tsx +++ b/src/element/textWysiwyg.test.tsx @@ -1506,7 +1506,7 @@ describe("textWysiwyg", () => { expect.objectContaining({ text: "Excalidraw is an opensource virtual collaborative whiteboard", verticalAlign: VERTICAL_ALIGN.MIDDLE, - textAlign: TEXT_ALIGN.LEFT, + textAlign: TEXT_ALIGN.CENTER, boundElements: null, }), ); diff --git a/src/tests/__snapshots__/contextmenu.test.tsx.snap b/src/tests/__snapshots__/contextmenu.test.tsx.snap index 18656edd1..c5b61e42f 100644 --- a/src/tests/__snapshots__/contextmenu.test.tsx.snap +++ b/src/tests/__snapshots__/contextmenu.test.tsx.snap @@ -121,7 +121,7 @@ Object { }, Object { "contextItemLabel": "labels.createContainerFromText", - "name": "createContainerFromText", + "name": "wrapTextInContainer", "perform": [Function], "predicate": [Function], "trackEvent": Object { @@ -4518,7 +4518,7 @@ Object { }, Object { "contextItemLabel": "labels.createContainerFromText", - "name": "createContainerFromText", + "name": "wrapTextInContainer", "perform": [Function], "predicate": [Function], "trackEvent": Object { @@ -5068,7 +5068,7 @@ Object { }, Object { "contextItemLabel": "labels.createContainerFromText", - "name": "createContainerFromText", + "name": "wrapTextInContainer", "perform": [Function], "predicate": [Function], "trackEvent": Object { @@ -5917,7 +5917,7 @@ Object { }, Object { "contextItemLabel": "labels.createContainerFromText", - "name": "createContainerFromText", + "name": "wrapTextInContainer", "perform": [Function], "predicate": [Function], "trackEvent": Object { @@ -6263,7 +6263,7 @@ Object { }, Object { "contextItemLabel": "labels.createContainerFromText", - "name": "createContainerFromText", + "name": "wrapTextInContainer", "perform": [Function], "predicate": [Function], "trackEvent": Object { diff --git a/src/tests/binding.test.tsx b/src/tests/binding.test.tsx index c615eb925..07af36569 100644 --- a/src/tests/binding.test.tsx +++ b/src/tests/binding.test.tsx @@ -4,7 +4,7 @@ import { UI, Pointer, Keyboard } from "./helpers/ui"; import { getTransformHandles } from "../element/transformHandles"; import { API } from "./helpers/api"; import { KEYS } from "../keys"; -import { actionCreateContainerFromText } from "../actions/actionBoundText"; +import { actionWrapTextInContainer } from "../actions/actionBoundText"; const { h } = window; @@ -277,7 +277,7 @@ describe("element binding", () => { expect(h.state.selectedElementIds[text1.id]).toBe(true); - h.app.actionManager.executeAction(actionCreateContainerFromText); + h.app.actionManager.executeAction(actionWrapTextInContainer); // new text container will be placed before the text element const container = h.elements.at(-2)!; From c9c79646c535dbca2342c5f3de26c24c6aadce19 Mon Sep 17 00:00:00 2001 From: Aakansha Doshi Date: Tue, 18 Apr 2023 20:48:30 +0530 Subject: [PATCH 70/74] =?UTF-8?q?docs:=20release=20@excalidraw/excalidraw@?= =?UTF-8?q?0.15.0=20=20=F0=9F=8E=89=20(#6481)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/packages/excalidraw/CHANGELOG.md | 150 ++++++++++++++++++++++++++- src/packages/excalidraw/package.json | 2 +- 2 files changed, 150 insertions(+), 2 deletions(-) diff --git a/src/packages/excalidraw/CHANGELOG.md b/src/packages/excalidraw/CHANGELOG.md index c36883b8e..5143bda31 100644 --- a/src/packages/excalidraw/CHANGELOG.md +++ b/src/packages/excalidraw/CHANGELOG.md @@ -11,7 +11,7 @@ The change should be grouped under one of the below section and must contain PR Please add the latest change on the top under the correct section. --> -## Unreleased +## 0.15.0 (2023-04-18) ### Features @@ -37,6 +37,154 @@ For more details refer to the [docs](https://docs.excalidraw.com) - Exporting labelled arrows via export utils [#6443](https://github.com/excalidraw/excalidraw/pull/6443) +## Excalidraw Library + +**_This section lists the updates made to the excalidraw library and will not affect the integration._** + +### Features + +- Constrain export dialog preview size [#6475](https://github.com/excalidraw/excalidraw/pull/6475) + +- Zigzag fill easter egg [#6439](https://github.com/excalidraw/excalidraw/pull/6439) + +- Add container to multiple text elements [#6428](https://github.com/excalidraw/excalidraw/pull/6428) + +- Starting migration from GA to Matomo for better privacy [#6398](https://github.com/excalidraw/excalidraw/pull/6398) + +- Add line height attribute to text element [#6360](https://github.com/excalidraw/excalidraw/pull/6360) + +- Add thai lang support [#6314](https://github.com/excalidraw/excalidraw/pull/6314) + +- Create bound container from text [#6301](https://github.com/excalidraw/excalidraw/pull/6301) + +- Improve text measurements in bound containers [#6187](https://github.com/excalidraw/excalidraw/pull/6187) + +- Bind text to container if double clicked on filled shape or stroke [#6250](https://github.com/excalidraw/excalidraw/pull/6250) + +- Make repair and refreshDimensions configurable in restoreElements [#6238](https://github.com/excalidraw/excalidraw/pull/6238) + +- Show error message when not connected to internet while collabo… [#6165](https://github.com/excalidraw/excalidraw/pull/6165) + +- Shortcut for clearCanvas confirmDialog [#6114](https://github.com/excalidraw/excalidraw/pull/6114) + +- Disable canvas smoothing (antialiasing) for right-angled elements [#6186](https://github.com/excalidraw/excalidraw/pull/6186)Co-authored-by: Ignacio Cuadra <67276174+ignacio-cuadra@users.noreply.github.com> + +### Fixes + +- Center align text when wrapped in container via context menu [#6480](https://github.com/excalidraw/excalidraw/pull/6480) + +- Restore original container height when unbinding text which was binded via context menu [#6444](https://github.com/excalidraw/excalidraw/pull/6444) + +- Mark more props as optional for element [#6448](https://github.com/excalidraw/excalidraw/pull/6448) + +- Improperly cache-busting on canvas scale instead of zoom [#6473](https://github.com/excalidraw/excalidraw/pull/6473) + +- Incorrectly duplicating items on paste/library insert [#6467](https://github.com/excalidraw/excalidraw/pull/6467) + +- Library ids cross-contamination on multiple insert [#6466](https://github.com/excalidraw/excalidraw/pull/6466) + +- Color picker keyboard handling not working [#6464](https://github.com/excalidraw/excalidraw/pull/6464) + +- Abort freedraw line if second touch is detected [#6440](https://github.com/excalidraw/excalidraw/pull/6440) + +- Utils leaking Scene state [#6461](https://github.com/excalidraw/excalidraw/pull/6461) + +- Split "Edit selected shape" shortcut [#6457](https://github.com/excalidraw/excalidraw/pull/6457) + +- Center align text when bind to container via context menu [#6451](https://github.com/excalidraw/excalidraw/pull/6451) + +- Update coords when text unbinded from its container [#6445](https://github.com/excalidraw/excalidraw/pull/6445) + +- Autoredirect to plus in prod only [#6446](https://github.com/excalidraw/excalidraw/pull/6446) + +- Fixing popover overflow on small screen [#6433](https://github.com/excalidraw/excalidraw/pull/6433) + +- Introduce baseline to fix the layout shift when switching to text editor [#6397](https://github.com/excalidraw/excalidraw/pull/6397) + +- Don't refresh dimensions for deleted text elements [#6438](https://github.com/excalidraw/excalidraw/pull/6438) + +- Element vanishes when zoomed in [#6417](https://github.com/excalidraw/excalidraw/pull/6417) + +- Don't jump text to end when out of viewport in safari [#6416](https://github.com/excalidraw/excalidraw/pull/6416) + +- GetDefaultLineHeight should return default font family line height for unknown font [#6399](https://github.com/excalidraw/excalidraw/pull/6399) + +- Revert use `ideographic` textBaseline to improve layout shift when editing text" [#6400](https://github.com/excalidraw/excalidraw/pull/6400) + +- Call stack size exceeded when paste large text [#6373](https://github.com/excalidraw/excalidraw/pull/6373) (#6396) + +- Use `ideographic` textBaseline to improve layout shift when editing text [#6384](https://github.com/excalidraw/excalidraw/pull/6384) + +- Chrome crashing when embedding scene on chrome arm [#6383](https://github.com/excalidraw/excalidraw/pull/6383) + +- Division by zero in findFocusPointForEllipse leads to infinite loop in wrapText freezing Excalidraw [#6377](https://github.com/excalidraw/excalidraw/pull/6377) + +- Containerizing text incorrectly updates arrow bindings [#6369](https://github.com/excalidraw/excalidraw/pull/6369) + +- Ensure export preview is centered [#6337](https://github.com/excalidraw/excalidraw/pull/6337) + +- Hide text align for labelled arrows [#6339](https://github.com/excalidraw/excalidraw/pull/6339) + +- Refresh dimensions when elements loaded from shareable link and blob [#6333](https://github.com/excalidraw/excalidraw/pull/6333) + +- Show error message when measureText API breaks in brave [#6336](https://github.com/excalidraw/excalidraw/pull/6336) + +- Add an offset of 0.5px for text editor in containers [#6328](https://github.com/excalidraw/excalidraw/pull/6328) + +- Move utility types out of `.d.ts` file to fix exported declaration files [#6315](https://github.com/excalidraw/excalidraw/pull/6315) + +- More jotai scopes missing [#6313](https://github.com/excalidraw/excalidraw/pull/6313) + +- Provide HelpButton title prop [#6209](https://github.com/excalidraw/excalidraw/pull/6209) + +- Respect text align when wrapping in a container [#6310](https://github.com/excalidraw/excalidraw/pull/6310) + +- Compute bounding box correctly for text element when multiple element resizing [#6307](https://github.com/excalidraw/excalidraw/pull/6307) + +- Use jotai scope for editor-specific atoms [#6308](https://github.com/excalidraw/excalidraw/pull/6308) + +- Consider arrow for bound text element [#6297](https://github.com/excalidraw/excalidraw/pull/6297) + +- Text never goes beyond max width for unbound text elements [#6288](https://github.com/excalidraw/excalidraw/pull/6288) + +- Svg text baseline [#6285](https://github.com/excalidraw/excalidraw/pull/6273) + +- Compute container height from bound text correctly [#6273](https://github.com/excalidraw/excalidraw/pull/6273) + +- Fit mobile toolbar and make scrollable [#6270](https://github.com/excalidraw/excalidraw/pull/6270) + +- Indenting via `tab` clashing with IME compositor [#6258](https://github.com/excalidraw/excalidraw/pull/6258) + +- Improve text wrapping inside rhombus and more fixes [#6265](https://github.com/excalidraw/excalidraw/pull/6265) + +- Improve text wrapping in ellipse and alignment [#6172](https://github.com/excalidraw/excalidraw/pull/6172) + +- Don't allow blank space in collab name [#6211](https://github.com/excalidraw/excalidraw/pull/6211) + +- Docker build architecture:linux/amd64 error occur on linux/arm64 instance [#6197](https://github.com/excalidraw/excalidraw/pull/6197) + +- Sort bound text elements to fix text duplication z-index error [#5130](https://github.com/excalidraw/excalidraw/pull/5130) + +- Hide welcome screen on mobile once user interacts [#6185](https://github.com/excalidraw/excalidraw/pull/6185) + +- Edit link in docs [#6182](https://github.com/excalidraw/excalidraw/pull/6182) + +### Refactor + +- Inline `SingleLibraryItem` into `PublishLibrary` [#6462](https://github.com/excalidraw/excalidraw/pull/6462) + +- Make the example React app reusable without duplication [#6188](https://github.com/excalidraw/excalidraw/pull/6188) + +### Performance + +- Break early if the line width <= max width of the container [#6347](https://github.com/excalidraw/excalidraw/pull/6347) + +### Build + +- Move TS and types to devDependencies [#6346](https://github.com/excalidraw/excalidraw/pull/6346) + +--- + ## 0.14.2 (2023-02-01) ### Features diff --git a/src/packages/excalidraw/package.json b/src/packages/excalidraw/package.json index be4e61d27..2351ee6a5 100644 --- a/src/packages/excalidraw/package.json +++ b/src/packages/excalidraw/package.json @@ -1,6 +1,6 @@ { "name": "@excalidraw/excalidraw", - "version": "0.14.2", + "version": "0.15.0", "main": "main.js", "types": "types/packages/excalidraw/index.d.ts", "files": [ From 1d0653ce50cfc56b92235bf793cfa638f39144c4 Mon Sep 17 00:00:00 2001 From: Aakansha Doshi Date: Tue, 18 Apr 2023 21:03:17 +0530 Subject: [PATCH 71/74] docs: update docs for next version (#6251) * update docs for opts param inr estore utils * docs: add docs for `useI18n` hook (#6269) Co-authored-by: Aakansha Doshi * upgrade excal --------- Co-authored-by: David Luzar --- .../excalidraw/api/utils/restore.mdx | 28 +++++++++--- .../excalidraw/api/utils/utils-intro.md | 44 +++++++++++++++++++ dev-docs/package.json | 2 +- dev-docs/src/theme/ReactLiveScope/index.js | 1 + dev-docs/yarn.lock | 8 ++-- 5 files changed, 73 insertions(+), 10 deletions(-) diff --git a/dev-docs/docs/@excalidraw/excalidraw/api/utils/restore.mdx b/dev-docs/docs/@excalidraw/excalidraw/api/utils/restore.mdx index 198626eec..665a1ef9f 100644 --- a/dev-docs/docs/@excalidraw/excalidraw/api/utils/restore.mdx +++ b/dev-docs/docs/@excalidraw/excalidraw/api/utils/restore.mdx @@ -31,10 +31,29 @@ You can pass `null` / `undefined` if not applicable. restoreElements( elements: ImportedDataState["elements"],
  localElements: ExcalidrawElement[] | null | undefined): ExcalidrawElement[],
  - refreshDimensions?: boolean
+ opts: { refreshDimensions?: boolean, repairBindings?: boolean }
) +| Prop | Type | Description | +| ---- | ---- | ---- | +| `elements` | ImportedDataState["elements"] | The `elements` to be restored | +| [`localElements`](#localelements) | ExcalidrawElement[] | null | undefined | When `localElements` are supplied, they are used to ensure that existing restored elements reuse `version` (and increment it), and regenerate `versionNonce`. | +| [`opts`](#opts) | `Object` | The extra optional parameter to configure restored elements + +#### localElements + +When `localElements` are supplied, they are used to ensure that existing restored elements reuse `version` (and increment it), and regenerate `versionNonce`. +Use this when you `import` elements which may already be present in the scene to ensure that you do not disregard the newly imported elements if you're using element version to detect the update + +#### opts +The extra optional parameter to configure restored elements. It has the following attributes + +| Prop | Type | Description| +| --- | --- | ------| +| `refreshDimensions` | `boolean` | Indicates whether we should also `recalculate` text element dimensions. Since this is a potentially costly operation, you may want to disable it if you restore elements in tight loops, such as during collaboration. | +| `repairBindings` |`boolean` | Indicates whether the `bindings` for the elements should be repaired. This is to make sure there are no containers with non existent bound text element id and no bound text elements with non existent container id. | + **_How to use_** ```js @@ -43,9 +62,6 @@ import { restoreElements } from "@excalidraw/excalidraw"; This function will make sure all properties of element is correctly set and if any attribute is missing, it will be set to its default value. -When `localElements` are supplied, they are used to ensure that existing restored elements reuse `version` (and increment it), and regenerate `versionNonce`. -Use this when you import elements which may already be present in the scene to ensure that you do not disregard the newly imported elements if you're using element version to detect the updates. - Parameter `refreshDimensions` indicates whether we should also `recalculate` text element dimensions. Defaults to `false`. Since this is a potentially costly operation, you may want to disable it if you restore elements in tight loops, such as during collaboration. ### restore @@ -56,7 +72,9 @@ Parameter `refreshDimensions` indicates whether we should also `recalculate` tex restore( data: ImportedDataState,
  localAppState: Partial<AppState> | null | undefined,
  - localElements: ExcalidrawElement[] | null | undefined
): DataState + localElements: ExcalidrawElement[] | null | undefined
): DataState
+ opts: { refreshDimensions?: boolean, repairBindings?: boolean }
+ ) diff --git a/dev-docs/docs/@excalidraw/excalidraw/api/utils/utils-intro.md b/dev-docs/docs/@excalidraw/excalidraw/api/utils/utils-intro.md index c21592382..4d2745c09 100644 --- a/dev-docs/docs/@excalidraw/excalidraw/api/utils/utils-intro.md +++ b/dev-docs/docs/@excalidraw/excalidraw/api/utils/utils-intro.md @@ -339,3 +339,47 @@ The `device` has the following `attributes` | `isMobile` | `boolean` | Set to `true` when the device is `mobile` | | `isTouchScreen` | `boolean` | Set to `true` for `touch` devices | | `canDeviceFitSidebar` | `boolean` | Implies whether there is enough space to fit the `sidebar` | + +### i18n + +To help with localization, we export the following. + +| name | type | +| --- | --- | +| `defaultLang` | `string` | +| `languages` | [`Language[]`](https://github.com/excalidraw/excalidraw/blob/master/src/i18n.ts#L15) | +| `useI18n` | [`() => { langCode, t }`](https://github.com/excalidraw/excalidraw/blob/master/src/i18n.ts#L15) | + +```js +import { defaultLang, languages, useI18n } from "@excalidraw/excalidraw"; +``` + +#### defaultLang + +Default language code, `en`. + +#### languages + +List of supported language codes. You can pass any of these to `Excalidraw`'s [`langCode` prop](/docs/@excalidraw/excalidraw/api/props/#langcode). + +#### useI18n + +A hook that returns the current language code and translation helper function. You can use this to translate strings in the components you render as children of ``. + +```jsx live +function App() { + const { t } = useI18n(); + return ( +
+ + + +
+ ); +} +``` diff --git a/dev-docs/package.json b/dev-docs/package.json index dd3c45872..1e8745910 100644 --- a/dev-docs/package.json +++ b/dev-docs/package.json @@ -18,7 +18,7 @@ "@docusaurus/core": "2.2.0", "@docusaurus/preset-classic": "2.2.0", "@docusaurus/theme-live-codeblock": "2.2.0", - "@excalidraw/excalidraw": "0.14.2", + "@excalidraw/excalidraw": "0.15.0", "@mdx-js/react": "^1.6.22", "clsx": "^1.2.1", "docusaurus-plugin-sass": "0.2.3", diff --git a/dev-docs/src/theme/ReactLiveScope/index.js b/dev-docs/src/theme/ReactLiveScope/index.js index a282ad6f0..e5263e1db 100644 --- a/dev-docs/src/theme/ReactLiveScope/index.js +++ b/dev-docs/src/theme/ReactLiveScope/index.js @@ -24,6 +24,7 @@ const ExcalidrawScope = { Sidebar: ExcalidrawComp.Sidebar, exportToCanvas: ExcalidrawComp.exportToCanvas, initialData, + useI18n: ExcalidrawComp.useI18n, }; export default ExcalidrawScope; diff --git a/dev-docs/yarn.lock b/dev-docs/yarn.lock index 041f39b55..ee3d50cbf 100644 --- a/dev-docs/yarn.lock +++ b/dev-docs/yarn.lock @@ -1631,10 +1631,10 @@ url-loader "^4.1.1" webpack "^5.73.0" -"@excalidraw/excalidraw@0.14.2": - version "0.14.2" - resolved "https://registry.yarnpkg.com/@excalidraw/excalidraw/-/excalidraw-0.14.2.tgz#150cb4b7a1bf0d11cd64295936c930e7e0db8375" - integrity sha512-8LdjpTBWEK5waDWB7Bt/G9YBI4j0OxkstUhvaDGz7dwQGfzF6FW5CXBoYHNEoX0qmb+Fg/NPOlZ7FrKsrSVCqg== +"@excalidraw/excalidraw@0.15.0": + version "0.15.0" + resolved "https://registry.yarnpkg.com/@excalidraw/excalidraw/-/excalidraw-0.15.0.tgz#47170de8d3ff006e9d09dfede2815682b0d4485b" + integrity sha512-PJmh1VcuRHG4l+Zgt9qhezxrJ16tYCZFZ8if5IEfmTL9A/7c5mXxY/qrPTqiGlVC7jYs+ciePXQ0YUDzfOfbzw== "@hapi/hoek@^9.0.0": version "9.3.0" From 89304c9f66ac9949edb47d254ae3d99602b2dff4 Mon Sep 17 00:00:00 2001 From: Aakansha Doshi Date: Tue, 18 Apr 2023 21:23:47 +0530 Subject: [PATCH 72/74] fix: add readme back to npm package which was mistakenly removed (#6484) * fix: remove update readme script from release * update docs * remove * fix --- scripts/release.js | 20 -------------------- src/packages/excalidraw/CHANGELOG.md | 6 ++++++ 2 files changed, 6 insertions(+), 20 deletions(-) diff --git a/scripts/release.js b/scripts/release.js index 986eadc2a..24ac89c6b 100644 --- a/scripts/release.js +++ b/scripts/release.js @@ -1,22 +1,9 @@ -const fs = require("fs"); const { execSync } = require("child_process"); const excalidrawDir = `${__dirname}/../src/packages/excalidraw`; const excalidrawPackage = `${excalidrawDir}/package.json`; const pkg = require(excalidrawPackage); -const originalReadMe = fs.readFileSync(`${excalidrawDir}/README.md`, "utf8"); - -const updateReadme = () => { - const excalidrawIndex = originalReadMe.indexOf("### Excalidraw"); - - // remove note for stable readme - const data = originalReadMe.slice(excalidrawIndex); - - // update readme - fs.writeFileSync(`${excalidrawDir}/README.md`, data, "utf8"); -}; - const publish = () => { try { execSync(`yarn --frozen-lockfile`); @@ -30,15 +17,8 @@ const publish = () => { }; const release = () => { - updateReadme(); - console.info("Note for stable readme removed"); - publish(); console.info(`Published ${pkg.version}!`); - - // revert readme after release - fs.writeFileSync(`${excalidrawDir}/README.md`, originalReadMe, "utf8"); - console.info("Readme reverted"); }; release(); diff --git a/src/packages/excalidraw/CHANGELOG.md b/src/packages/excalidraw/CHANGELOG.md index 5143bda31..b2ac3a682 100644 --- a/src/packages/excalidraw/CHANGELOG.md +++ b/src/packages/excalidraw/CHANGELOG.md @@ -11,6 +11,12 @@ The change should be grouped under one of the below section and must contain PR Please add the latest change on the top under the correct section. --> +## Unreleased + +### Docs + +- Add the readme back to the package which was mistakenly removed [#6484](https://github.com/excalidraw/excalidraw/pull/6484) + ## 0.15.0 (2023-04-18) ### Features From b64beaf5ba42ab55302fb9f5b752b06b28c0c213 Mon Sep 17 00:00:00 2001 From: Aakansha Doshi Date: Tue, 18 Apr 2023 21:32:33 +0530 Subject: [PATCH 73/74] =?UTF-8?q?docs:=20release=20@excalidraw/excalidraw@?= =?UTF-8?q?0.15.1=20=20=F0=9F=8E=89=20(#6485)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/packages/excalidraw/CHANGELOG.md | 8 +++++++- src/packages/excalidraw/package.json | 2 +- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/packages/excalidraw/CHANGELOG.md b/src/packages/excalidraw/CHANGELOG.md index b2ac3a682..f285349f0 100644 --- a/src/packages/excalidraw/CHANGELOG.md +++ b/src/packages/excalidraw/CHANGELOG.md @@ -11,12 +11,18 @@ The change should be grouped under one of the below section and must contain PR Please add the latest change on the top under the correct section. --> -## Unreleased +## 0.15.1 (2023-04-18) ### Docs - Add the readme back to the package which was mistakenly removed [#6484](https://github.com/excalidraw/excalidraw/pull/6484) +## Excalidraw Library + +**_This section lists the updates made to the excalidraw library and will not affect the integration._** + +--- + ## 0.15.0 (2023-04-18) ### Features diff --git a/src/packages/excalidraw/package.json b/src/packages/excalidraw/package.json index 2351ee6a5..a59837454 100644 --- a/src/packages/excalidraw/package.json +++ b/src/packages/excalidraw/package.json @@ -1,6 +1,6 @@ { "name": "@excalidraw/excalidraw", - "version": "0.15.0", + "version": "0.15.1", "main": "main.js", "types": "types/packages/excalidraw/index.d.ts", "files": [ From ff3c2e5a160d2982d48657091f1fbcaacf82a6e2 Mon Sep 17 00:00:00 2001 From: Aakansha Doshi Date: Tue, 18 Apr 2023 21:52:08 +0530 Subject: [PATCH 74/74] docs: fix docs link in readme (#6486) * docs: fix docs link in readme * update changelog --- src/packages/excalidraw/CHANGELOG.md | 6 ++++++ src/packages/excalidraw/README.md | 4 ++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/packages/excalidraw/CHANGELOG.md b/src/packages/excalidraw/CHANGELOG.md index f285349f0..e69f05d0a 100644 --- a/src/packages/excalidraw/CHANGELOG.md +++ b/src/packages/excalidraw/CHANGELOG.md @@ -11,6 +11,12 @@ The change should be grouped under one of the below section and must contain PR Please add the latest change on the top under the correct section. --> +## Unreleased + +### Docs + +- Fix docs link in readme [#6486](https://github.com/excalidraw/excalidraw/pull/6486) + ## 0.15.1 (2023-04-18) ### Docs diff --git a/src/packages/excalidraw/README.md b/src/packages/excalidraw/README.md index eaeef4b0c..d650885df 100644 --- a/src/packages/excalidraw/README.md +++ b/src/packages/excalidraw/README.md @@ -38,8 +38,8 @@ Excalidraw takes _100%_ of `width` and `height` of the containing block so make ## Integration -Head over to the [docs](https://docs.excalidraw.com/docs/package/integration) +Head over to the [docs](https://docs.excalidraw.com/docs/@excalidraw/excalidraw/integration) ## API -Head over to the [docs](https://docs.excalidraw.com/docs/package/api) +Head over to the [docs](https://docs.excalidraw.com/docs/@excalidraw/excalidraw/api)