Compare commits

..

90 Commits

Author SHA1 Message Date
dwelle fe973e3513 purify getDefaultAppState by removing name 2021-01-03 23:53:30 +01:00
Aakansha Doshi ade2565f49 feat: add langCode and renderFooter props (#2644)
Co-authored-by: Aakansha Doshi <aakansha1216@gmail.com>
Co-authored-by: dwelle <luzar.david@gmail.com>
2021-01-03 21:51:52 +01:00
dependabot[bot] c35d983fef chore(deps): bump @testing-library/jest-dom from 5.11.6 to 5.11.8 (#2702)
Bumps [@testing-library/jest-dom](https://github.com/testing-library/jest-dom) from 5.11.6 to 5.11.8.
- [Release notes](https://github.com/testing-library/jest-dom/releases)
- [Changelog](https://github.com/testing-library/jest-dom/blob/master/CHANGELOG.md)
- [Commits](https://github.com/testing-library/jest-dom/compare/v5.11.6...v5.11.8)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-01-03 10:52:02 +01:00
Lipis 69878167c2 improvement: Make dialogs look more like dialogs (#2686)
Co-authored-by: Jed Fox <git@jedfox.com>
Co-authored-by: dwelle <luzar.david@gmail.com>
2021-01-03 10:50:41 +01:00
dependabot[bot] eb1f717d35 chore(deps-dev): bump ts-loader in /src/packages/utils (#2700)
Bumps [ts-loader](https://github.com/TypeStrong/ts-loader) from 8.0.12 to 8.0.13.
- [Release notes](https://github.com/TypeStrong/ts-loader/releases)
- [Changelog](https://github.com/TypeStrong/ts-loader/blob/master/CHANGELOG.md)
- [Commits](https://github.com/TypeStrong/ts-loader/compare/v8.0.12...v8.0.13)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-01-03 15:20:28 +05:30
dependabot[bot] 8e9af5c51b chore(deps-dev): bump webpack in /src/packages/excalidraw (#2698)
Bumps [webpack](https://github.com/webpack/webpack) from 5.11.0 to 5.11.1.
- [Release notes](https://github.com/webpack/webpack/releases)
- [Commits](https://github.com/webpack/webpack/compare/v5.11.0...v5.11.1)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-01-03 15:20:06 +05:30
dependabot[bot] afe0c760f6 chore(deps-dev): bump webpack-cli in /src/packages/utils (#2701)
Bumps [webpack-cli](https://github.com/webpack/webpack-cli) from 4.2.0 to 4.3.1.
- [Release notes](https://github.com/webpack/webpack-cli/releases)
- [Changelog](https://github.com/webpack/webpack-cli/blob/master/CHANGELOG.md)
- [Commits](https://github.com/webpack/webpack-cli/compare/webpack-cli@4.2.0...webpack-cli@4.3.1)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-01-03 15:14:21 +05:30
dependabot[bot] a231cefac0 chore(deps-dev): bump webpack-cli in /src/packages/excalidraw (#2697)
Bumps [webpack-cli](https://github.com/webpack/webpack-cli) from 4.2.0 to 4.3.1.
- [Release notes](https://github.com/webpack/webpack-cli/releases)
- [Changelog](https://github.com/webpack/webpack-cli/blob/master/CHANGELOG.md)
- [Commits](https://github.com/webpack/webpack-cli/compare/webpack-cli@4.2.0...webpack-cli@4.3.1)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-01-03 15:14:01 +05:30
dependabot[bot] cb4c9d16fc chore(deps-dev): bump webpack in /src/packages/utils (#2699)
Bumps [webpack](https://github.com/webpack/webpack) from 5.11.0 to 5.11.1.
- [Release notes](https://github.com/webpack/webpack/releases)
- [Commits](https://github.com/webpack/webpack/compare/v5.11.0...v5.11.1)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-01-03 15:02:27 +05:30
dependabot[bot] 7366f089ba chore(packages/excalidraw): bump ts-loader from 8.0.12 to 8.0.13. (#2696)
Bumps [ts-loader](https://github.com/TypeStrong/ts-loader) from 8.0.12 to 8.0.13.
- [Release notes](https://github.com/TypeStrong/ts-loader/releases)
- [Changelog](https://github.com/TypeStrong/ts-loader/blob/master/CHANGELOG.md)
- [Commits](https://github.com/TypeStrong/ts-loader/compare/v8.0.12...v8.0.13)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-01-03 15:01:57 +05:30
Benja Kugler 7c3513b9df feat: browse libraries styles fixed (#2694)
* feat: browse libraries styles fixed

* simplify jsx & css

* remove justify-content

* fix padding/margin

* Update src/components/LayerUI.scss

Co-authored-by: benjamin.kugler <benjamin.kugler@elliemae.com>
Co-authored-by: dwelle <luzar.david@gmail.com>
2021-01-02 17:13:48 +01:00
David Luzar aef3644c93 fix: scene not initialized properly when tab not focused (#2677)
Co-authored-by: Lipis <lipiridis@gmail.com>
2020-12-29 21:03:34 +01:00
David Luzar 0cf58adb4c chore: Update portal URL (#2689) 2020-12-28 13:14:22 +01:00
David Luzar 3aab81bc35 docs: add tsdoc for certain element props (#2673)
Co-authored-by: Panayiotis Lipiridis <lipiridis@gmail.com>
2020-12-28 00:17:27 +02:00
Jed Fox 3b0fb1562d feat: Require use of a preset dialog size; adjust dialog sizing (#2684) 2020-12-28 00:07:05 +02:00
Jed Fox 0488b7b5c6 fix(css): Fix compile error (#2685) 2020-12-27 23:42:23 +02:00
Jed Fox b8d13c98b5 refactor: Media queries (#2680) 2020-12-27 23:27:25 +02:00
Lipis 6f82a88b79 chore: Cleanup unused labels (#2682) 2020-12-27 18:51:47 +02:00
Lipis 022f349dc6 feat: Add line chart and paste dialog selection (#2670)
Co-authored-by: dwelle <luzar.david@gmail.com>
Co-authored-by: Jed Fox <git@jedfox.com>
2020-12-27 18:26:30 +02:00
dependabot[bot] c1e2146d78 chore(deps-dev): bump firebase-tools from 9.0.1 to 9.1.0 (#2676)
Bumps [firebase-tools](https://github.com/firebase/firebase-tools) from 9.0.1 to 9.1.0.
- [Release notes](https://github.com/firebase/firebase-tools/releases)
- [Commits](https://github.com/firebase/firebase-tools/compare/v9.0.1...v9.1.0)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2020-12-27 13:51:54 +02:00
Harshil Parmar 8091ac6c08 style: media query for hiding shortcuts for mobile view (#2667)
Co-authored-by: Lipis <lipiridis@gmail.com>
2020-12-26 21:23:51 +02:00
Luo bc414ccaaf feat: tweak editing behavior (#2668)
* feat: tweak editing behavior

* fix tests

Co-authored-by: dwelle <luzar.david@gmail.com>
2020-12-25 19:34:47 +01:00
Kostas Bariotis 0cf5f1ac1f chore: update Sentry (#2669) 2020-12-25 20:02:12 +02:00
Lipis e9cb7ee77c chore: New Crowdin updates (#2620)
Co-authored-by: Kostas Bariotis <konmpar@gmail.com>
2020-12-25 12:15:34 +02:00
Lipis 86c036505b chore: Remove unused cursorX, cursorY from AppState (#2665) 2020-12-24 22:05:22 +01:00
dependabot[bot] 39e7b8cf4f chore(deps-dev): bump firebase-tools from 8.19.0 to 9.0.1 (#2629)
Bumps [firebase-tools](https://github.com/firebase/firebase-tools) from 8.19.0 to 9.0.1.
- [Release notes](https://github.com/firebase/firebase-tools/releases)
- [Changelog](https://github.com/firebase/firebase-tools/blob/master/CHANGELOG.md)
- [Commits](https://github.com/firebase/firebase-tools/compare/v8.19.0...v9.0.1)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2020-12-22 11:20:57 +01:00
dependabot[bot] e0ece680a6 chore(deps): bump firebase from 8.2.0 to 8.2.1 (#2631)
Bumps [firebase](https://github.com/firebase/firebase-js-sdk) from 8.2.0 to 8.2.1.
- [Release notes](https://github.com/firebase/firebase-js-sdk/releases)
- [Changelog](https://github.com/firebase/firebase-js-sdk/blob/master/CHANGELOG.md)
- [Commits](https://github.com/firebase/firebase-js-sdk/commits)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2020-12-22 11:01:03 +01:00
David Luzar 8dfea49ec1 improvement: change hint for 2-point lines on resize (#2655) 2020-12-22 11:00:51 +01:00
Lipis d7f314cda8 refactor: Remove duplicate entry from en.json (#2654) 2020-12-22 11:42:01 +02:00
Lipis 6adb45ef5a feat: Change title to Excalidraw after a timeout (#2656)
* feat: Change title to Excalidraw after a timeout

* clear timeout
2020-12-22 10:34:06 +01:00
Lipis b0eeb8e6e6 refactor: Remove the word toggle from labels (#2648) 2020-12-21 14:58:43 +01:00
dependabot[bot] c3c20b6087 chore(deps): bump browser-nativefs from 0.11.2 to 0.12.0 (#2634)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2020-12-21 12:35:22 +02:00
Rene 0faec7efb6 feat: Checkmark to toggle context-menu-items (#2645) 2020-12-21 00:20:03 +02:00
Rene aff817c667 align items in context menu (#2640) 2020-12-20 12:15:40 -08:00
Christopher Chedeau 9a3a3ecb44 fix: Center zoom on iPhone and iPad (#2642)
My last attempt removed the gesture handler altogether but broke macbooks. This one keeps it working on macbook but makes sure it zooms properly on iPhone and iPad.
2020-12-20 12:13:15 -08:00
Christopher Chedeau 81f8039ec7 fix: Don't break zoom when zooming in on UI (#2638)
If you start the gesture on the chrome, gesture is not defined and the zoom will be set to NaN and you will have to refresh.

Typescript was actually right and we shouldn't have overridden the bang ;)

Repro:
- On iPhone touch down on a shape, touch down on the chrome, start moving around
- See that the selection is off, but the zoom is not being modified like crazy.

https://user-images.githubusercontent.com/197597/102722206-6c182400-42b4-11eb-9865-6ae1cd0af9be.MP4
2020-12-20 12:07:58 -08:00
Lipis 14759d5b72 chore: Remove changelog check and graphql (#2639) 2020-12-20 21:52:21 +02:00
David Luzar b997e69ebc improvement: Tweak error message on image import (#2619)
* improvement: tweak error message on image import

* tweak copy
2020-12-20 16:11:44 +01:00
dependabot[bot] 4a89aba682 chore(deps-dev): bump webpack in /src/packages/excalidraw (#2625)
Bumps [webpack](https://github.com/webpack/webpack) from 5.10.1 to 5.11.0.
- [Release notes](https://github.com/webpack/webpack/releases)
- [Commits](https://github.com/webpack/webpack/compare/v5.10.1...v5.11.0)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2020-12-20 20:35:49 +05:30
David Luzar 34dcf998bd docs: excalidraw package usage example tweaks (#2608)
Co-authored-by: Aakansha Doshi <monstershome@gmail.com>
2020-12-20 16:04:12 +01:00
Aakansha Doshi 325d1bec91 feat: Add onExportToBackend prop so host can handle it (#2612)
Co-authored-by: dwelle <luzar.david@gmail.com>
2020-12-20 15:14:04 +01:00
Lipis b917e42694 fix: Consistent case for export locale strings (#2622) 2020-12-20 14:40:11 +02:00
Christopher Chedeau eb9e67e36a improvement: Support numbers with commas in them (#2636) 2020-12-20 14:08:22 +02:00
dependabot[bot] f14ae52e94 chore(deps-dev): bump @babel/preset-env in /src/packages/excalidraw (#2624)
Bumps [@babel/preset-env](https://github.com/babel/babel/tree/HEAD/packages/babel-preset-env) from 7.12.10 to 7.12.11.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.12.11/packages/babel-preset-env)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2020-12-20 16:49:20 +05:30
dependabot[bot] f93eb658d6 chore(deps-dev): bump webpack-bundle-analyzer (#2623)
Bumps [webpack-bundle-analyzer](https://github.com/webpack-contrib/webpack-bundle-analyzer) from 4.2.0 to 4.3.0.
- [Release notes](https://github.com/webpack-contrib/webpack-bundle-analyzer/releases)
- [Changelog](https://github.com/webpack-contrib/webpack-bundle-analyzer/blob/master/CHANGELOG.md)
- [Commits](https://github.com/webpack-contrib/webpack-bundle-analyzer/compare/v4.2.0...v4.3.0)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2020-12-20 16:46:37 +05:30
Aakansha Doshi 9bac44ee75 fix(appstate.ts): Remove unnecessary console.error as it was polluting Sentry (#2637) 2020-12-20 16:28:46 +05:30
dependabot[bot] 91b4109f67 chore(deps-dev): bump webpack-bundle-analyzer in /src/packages/utils (#2626)
Bumps [webpack-bundle-analyzer](https://github.com/webpack-contrib/webpack-bundle-analyzer) from 4.2.0 to 4.3.0.
- [Release notes](https://github.com/webpack-contrib/webpack-bundle-analyzer/releases)
- [Changelog](https://github.com/webpack-contrib/webpack-bundle-analyzer/blob/master/CHANGELOG.md)
- [Commits](https://github.com/webpack-contrib/webpack-bundle-analyzer/compare/v4.2.0...v4.3.0)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2020-12-20 16:21:17 +05:30
dependabot[bot] 6e45cb95db chore(deps-dev): bump webpack in /src/packages/utils (#2627)
Bumps [webpack](https://github.com/webpack/webpack) from 5.10.1 to 5.11.0.
- [Release notes](https://github.com/webpack/webpack/releases)
- [Commits](https://github.com/webpack/webpack/compare/v5.10.1...v5.11.0)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2020-12-20 15:20:35 +05:30
dependabot[bot] 5edf82898b chore(deps-dev): bump @babel/preset-env in /src/packages/utils (#2628)
Bumps [@babel/preset-env](https://github.com/babel/babel/tree/HEAD/packages/babel-preset-env) from 7.12.10 to 7.12.11.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.12.11/packages/babel-preset-env)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2020-12-20 15:19:20 +05:30
dependabot[bot] 60b82e3055 chore(deps-dev): bump eslint-config-prettier from 7.0.0 to 7.1.0 (#2632)
Bumps [eslint-config-prettier](https://github.com/prettier/eslint-config-prettier) from 7.0.0 to 7.1.0.
- [Release notes](https://github.com/prettier/eslint-config-prettier/releases)
- [Changelog](https://github.com/prettier/eslint-config-prettier/blob/main/CHANGELOG.md)
- [Commits](https://github.com/prettier/eslint-config-prettier/compare/v7.0.0...v7.1.0)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2020-12-20 03:16:05 +02:00
Lipis 5d6590c200 ci: Update the coverage report for i18n PRs (#2592)
Co-authored-by: Kostas Bariotis <konmpar@gmail.com>
2020-12-19 22:44:01 +02:00
Lipis d3bebbc68d ci: Better locale coverage comment (#2616)
Co-authored-by: kbariotis <konmpar@gmail.com>
2020-12-19 20:35:03 +02:00
Kostas Bariotis 4ff8f3b006 add commenting on translations PRs, resolves #2587 (#2615) 2020-12-19 13:52:50 +00:00
Aakansha Doshi abbc756887 ci: Add semantic pr title action (#2610) 2020-12-18 21:32:37 +05:30
Lipis 10e07e434c chore: Remove support for deprecated Excalidraw for Desktop (#2465) 2020-12-18 14:02:29 +02:00
Andrés Rivera fb582b45db fix: Fix centering element on init (#2445)
Co-authored-by: Andrés Rivera <andres@MBP.local>
Co-authored-by: Lipis <lipiridis@gmail.com>
Co-authored-by: dwelle <luzar.david@gmail.com>
2020-12-16 18:17:39 +01:00
Aakansha Doshi 23f21434ff Fix changelog workflow (#2604)
Fix the workflow to make sure it runs on pull requests against forked branches and also don't run on l10_master and dependabot branches
2020-12-16 20:19:05 +05:30
Jaiwanth 7e9fdf85a0 fix: Dropdown for Arrowheads overflow (#2596)
Co-authored-by: Lipis <lipiridis@gmail.com>
Co-authored-by: dwelle <luzar.david@gmail.com>
2020-12-16 13:06:55 +01:00
David Luzar 98c26642d0 fix: hide collab button when onCollabButtonClick not supplied (#2598) 2020-12-15 16:21:14 +01:00
Lipis 2b434db062 chore: Don't run docker build on PRs (#2601) 2020-12-15 14:24:00 +01:00
Kartik Prajapati f919907855 Enhance delete button in context menu (#2591) 2020-12-15 00:59:00 +02:00
Aakansha Doshi bfeb3c7dfd fix(changelog-check.yml): ignore l10n_master and dependabot branches in changelog workflow (#2594) 2020-12-15 02:25:45 +05:30
Aakansha Doshi 8729ab3c54 chore(package/utils): update deps (#2593) 2020-12-15 02:13:19 +05:30
Aakansha Doshi 37f53bccbf Revert the changelog check of excluding the branches (#2590)
* temp commit

* fix

* fix

* Update actionAlign.tsx
2020-12-14 22:41:00 +02:00
Aakansha Doshi c783763307 chore(package/excalidraw): update deps (#2589) 2020-12-14 19:21:22 +01:00
dependabot[bot] f151f45df8 Bump webpack from 4.42.0 to 4.44.2 in /src/packages/utils (#2554)
Bumps [webpack](https://github.com/webpack/webpack) from 4.42.0 to 4.44.2.
- [Release notes](https://github.com/webpack/webpack/releases)
- [Commits](https://github.com/webpack/webpack/compare/v4.42.0...v4.44.2)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2020-12-14 21:08:06 +05:30
dependabot[bot] f33880e005 Bump @babel/plugin-transform-arrow-functions in /src/packages/utils (#2558)
Bumps [@babel/plugin-transform-arrow-functions](https://github.com/babel/babel/tree/HEAD/packages/babel-plugin-transform-arrow-functions) from 7.8.3 to 7.12.1.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.12.1/packages/babel-plugin-transform-arrow-functions)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2020-12-14 21:02:52 +05:30
dependabot[bot] 29ed50f6ea Bump webpack-cli from 3.3.11 to 4.2.0 in /src/packages/excalidraw (#2571)
Bumps [webpack-cli](https://github.com/webpack/webpack-cli) from 3.3.11 to 4.2.0.
- [Release notes](https://github.com/webpack/webpack-cli/releases)
- [Changelog](https://github.com/webpack/webpack-cli/blob/master/CHANGELOG.md)
- [Commits](https://github.com/webpack/webpack-cli/compare/v3.3.11...webpack-cli@4.2.0)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Aakansha Doshi <aakansha1216@gmail.com>
2020-12-14 21:00:06 +05:30
Lipis ce52c18382 fix: Add width/height for the lines in charts (#2586)
Co-authored-by: David Luzar <luzar.david@gmail.com>
2020-12-14 16:29:39 +01:00
Thomas Steiner 7c3e1d8d1b Add declarative link capturing
See https://github.com/WICG/sw-launch/blob/master/declarative_link_capturing.md#user-content-proposal:~:text=new_client,-%E2%80%9D%20%E2%80%94 for context.
2020-12-14 16:25:48 +01:00
dependabot[bot] 0d15934a96 Bump firebase-tools from 8.17.0 to 8.19.0 (#2574)
Bumps [firebase-tools](https://github.com/firebase/firebase-tools) from 8.17.0 to 8.19.0.
- [Release notes](https://github.com/firebase/firebase-tools/releases)
- [Commits](https://github.com/firebase/firebase-tools/compare/v8.17.0...v8.19.0)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2020-12-14 17:15:38 +02:00
dependabot[bot] a0069d04f0 Bump firebase from 8.1.2 to 8.2.0 (#2580)
Bumps [firebase](https://github.com/firebase/firebase-js-sdk) from 8.1.2 to 8.2.0.
- [Release notes](https://github.com/firebase/firebase-js-sdk/releases)
- [Changelog](https://github.com/firebase/firebase-js-sdk/blob/master/CHANGELOG.md)
- [Commits](https://github.com/firebase/firebase-js-sdk/commits)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2020-12-14 17:15:16 +02:00
Lipis 3b86944365 chore: New Crowdin updates (#2480) 2020-12-14 16:49:01 +02:00
João Forja 4c7b1a2269 fix: Visibility and zooming when canvas offset is not zero (#2534) 2020-12-14 15:14:56 +01:00
Kartik Prajapati 60864ace54 feat: Add tooltip with icon for embedding scenes (#2532)
Co-authored-by: dwelle <luzar.david@gmail.com>
2020-12-14 14:24:54 +01:00
Daishi Kato 4b32c03994 feat: export exportToCanvas in utils (#2583) 2020-12-14 14:11:48 +01:00
dependabot[bot] 222dbdcc00 Bump @types/jest from 26.0.16 to 26.0.19 (#2578)
Bumps [@types/jest](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/jest) from 26.0.16 to 26.0.19.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/jest)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2020-12-14 07:59:56 +02:00
dependabot[bot] 1346227d30 Bump @sentry/browser from 5.28.0 to 5.29.0 (#2561)
Bumps [@sentry/browser](https://github.com/getsentry/sentry-javascript) from 5.28.0 to 5.29.0.
- [Release notes](https://github.com/getsentry/sentry-javascript/releases)
- [Changelog](https://github.com/getsentry/sentry-javascript/blob/master/CHANGELOG.md)
- [Commits](https://github.com/getsentry/sentry-javascript/compare/5.28.0...5.29.0)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2020-12-14 07:59:48 +02:00
dependabot[bot] ecbddd214c Bump pepjs from 0.5.2 to 0.5.3 (#2550)
Bumps [pepjs](https://github.com/jquery/PEP) from 0.5.2 to 0.5.3.
- [Release notes](https://github.com/jquery/PEP/releases)
- [Commits](https://github.com/jquery/PEP/commits/0.5.3)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2020-12-14 05:29:51 +02:00
dependabot[bot] 4999ca5c82 Bump husky from 4.3.0 to 4.3.6 (#2575)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2020-12-14 05:29:16 +02:00
dependabot[bot] 174638889d Bump typescript from 4.0.5 to 4.1.3 (#2569)
Bumps [typescript](https://github.com/Microsoft/TypeScript) from 4.0.5 to 4.1.3.
- [Release notes](https://github.com/Microsoft/TypeScript/releases)
- [Commits](https://github.com/Microsoft/TypeScript/commits)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2020-12-14 05:28:55 +02:00
dependabot[bot] 480998582e Bump eslint-plugin-prettier from 3.1.4 to 3.3.0 (#2573)
Bumps [eslint-plugin-prettier](https://github.com/prettier/eslint-plugin-prettier) from 3.1.4 to 3.3.0.
- [Release notes](https://github.com/prettier/eslint-plugin-prettier/releases)
- [Changelog](https://github.com/prettier/eslint-plugin-prettier/blob/master/CHANGELOG.md)
- [Commits](https://github.com/prettier/eslint-plugin-prettier/compare/v3.1.4...v3.3.0)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2020-12-14 05:28:40 +02:00
dependabot[bot] 94544e458c Bump browser-nativefs from 0.11.1 to 0.11.2 (#2577)
Bumps [browser-nativefs](https://github.com/GoogleChromeLabs/browser-nativefs) from 0.11.1 to 0.11.2.
- [Release notes](https://github.com/GoogleChromeLabs/browser-nativefs/releases)
- [Commits](https://github.com/GoogleChromeLabs/browser-nativefs/commits)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2020-12-14 05:28:16 +02:00
dependabot[bot] f664ba9e1e Bump @sentry/integrations from 5.28.0 to 5.29.0 (#2579)
Bumps [@sentry/integrations](https://github.com/getsentry/sentry-javascript) from 5.28.0 to 5.29.0.
- [Release notes](https://github.com/getsentry/sentry-javascript/releases)
- [Changelog](https://github.com/getsentry/sentry-javascript/blob/master/CHANGELOG.md)
- [Commits](https://github.com/getsentry/sentry-javascript/compare/5.28.0...5.29.0)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2020-12-14 05:27:54 +02:00
Panayiotis Lipiridis 015a60638e chore: Update user for packages 2020-12-14 03:49:43 +02:00
Lipis 7abb80530f chore: Add dependabot configuration (#2535) 2020-12-14 03:47:13 +02:00
Zen Tang 5abe9b93e8 feat: Add zoom to fit for selected elements (#2522) 2020-12-13 22:54:35 +02:00
Aakansha Doshi 59cff0f219 ci: Add github action to make sure changelog for @excalidraw/excalidraw is updated (#2518)
Add guidelines for changelog and group the commits
update the changelog with the latest commits since the last release
Co-authored-by: Lipis <lipiridis@gmail.com>
2020-12-13 18:53:14 +05:30
Lipis 81c17a56fb RTL support for the stats dialog (#2530) 2020-12-13 13:39:45 +01:00
Zen Tang 802b8c50d5 Insert Library items in the middle of the screen (#2527)
Co-authored-by: Zen Tang <zen@wayve.ai>
2020-12-13 13:46:42 +02:00
128 changed files with 7795 additions and 12785 deletions
+1 -1
View File
@@ -1,5 +1,5 @@
REACT_APP_BACKEND_V1_GET_URL=https://json.excalidraw.com/api/v1/ REACT_APP_BACKEND_V1_GET_URL=https://json.excalidraw.com/api/v1/
REACT_APP_BACKEND_V2_GET_URL=https://json.excalidraw.com/api/v2/ REACT_APP_BACKEND_V2_GET_URL=https://json.excalidraw.com/api/v2/
REACT_APP_BACKEND_V2_POST_URL=https://json.excalidraw.com/api/v2/post/ REACT_APP_BACKEND_V2_POST_URL=https://json.excalidraw.com/api/v2/post/
REACT_APP_SOCKET_SERVER_URL=https://excalidraw-socket.herokuapp.com REACT_APP_SOCKET_SERVER_URL=https://portal.excalidraw.com
REACT_APP_FIREBASE_CONFIG='{"apiKey":"AIzaSyAd15pYlMci_xIp9ko6wkEsDzAAA0Dn0RU","authDomain":"excalidraw-room-persistence.firebaseapp.com","databaseURL":"https://excalidraw-room-persistence.firebaseio.com","projectId":"excalidraw-room-persistence","storageBucket":"excalidraw-room-persistence.appspot.com","messagingSenderId":"654800341332","appId":"1:654800341332:web:4a692de832b55bd57ce0c1"}' REACT_APP_FIREBASE_CONFIG='{"apiKey":"AIzaSyAd15pYlMci_xIp9ko6wkEsDzAAA0Dn0RU","authDomain":"excalidraw-room-persistence.firebaseapp.com","databaseURL":"https://excalidraw-room-persistence.firebaseio.com","projectId":"excalidraw-room-persistence","storageBucket":"excalidraw-room-persistence.appspot.com","messagingSenderId":"654800341332","appId":"1:654800341332:web:4a692de832b55bd57ce0c1"}'
+37
View File
@@ -0,0 +1,37 @@
version: 2
updates:
- package-ecosystem: npm
directory: "/"
schedule:
interval: weekly
day: sunday
time: "01:00"
open-pull-requests-limit: 99
reviewers:
- lipis
assignees:
- lipis
- package-ecosystem: npm
directory: "/src/packages/excalidraw/"
schedule:
interval: weekly
day: sunday
time: "01:00"
open-pull-requests-limit: 99
reviewers:
- ad1992
assignees:
- ad1992
- package-ecosystem: npm
directory: "/src/packages/utils/"
schedule:
interval: weekly
day: sunday
time: "01:00"
open-pull-requests-limit: 99
reviewers:
- ad1992
assignees:
- ad1992
-1
View File
@@ -4,7 +4,6 @@ on:
push: push:
branches: branches:
- master - master
pull_request:
jobs: jobs:
build-docker: build-docker:
-33
View File
@@ -1,33 +0,0 @@
name: "CodeQL"
on:
push:
branches: [master]
pull_request:
branches: [master]
schedule:
- cron: "18 7 * * 0"
jobs:
analyze:
name: Analyze
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
language: ["typescript"]
steps:
- name: Checkout repository
uses: actions/checkout@v2
- name: Initialize CodeQL
uses: github/codeql-action/init@v1
with:
languages: ${{ matrix.language }}
- name: Autobuild
uses: github/codeql-action/autobuild@v1
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v1
+15
View File
@@ -30,3 +30,18 @@ jobs:
git commit -am "Auto commit: Calculate translation coverage" git commit -am "Auto commit: Calculate translation coverage"
git push git push
fi fi
- name: Construct comment body
id: getCommentBody
run: |
body=$(npm run locales-coverage:description | grep '^[^>]')
body="${body//'%'/'%25'}"
body="${body//$'\n'/'%0A'}"
body="${body//$'\r'/'%0D'}"
echo ::set-output name=body::$body
- name: Update description with coverage
uses: kt3k/update-pr-description@v1.0.1
with:
pr_body: ${{ steps.getCommentBody.outputs.body }}
pr_title: "chore: New Crowdin updates"
github_token: ${{ secrets.PUSH_TRANSLATIONS_COVERAGE_PAT }}
+16
View File
@@ -0,0 +1,16 @@
name: "Semantic PR title"
on:
pull_request_target:
types:
- opened
- edited
- synchronize
jobs:
main:
runs-on: ubuntu-latest
steps:
- uses: amannn/action-semantic-pull-request@v2.1.0
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+386 -589
View File
File diff suppressed because it is too large Load Diff
+15 -15
View File
@@ -19,18 +19,18 @@
] ]
}, },
"dependencies": { "dependencies": {
"@sentry/browser": "5.28.0", "@sentry/browser": "5.29.2",
"@sentry/integrations": "5.28.0", "@sentry/integrations": "5.29.2",
"@testing-library/jest-dom": "5.11.6", "@testing-library/jest-dom": "5.11.8",
"@testing-library/react": "11.2.2", "@testing-library/react": "11.2.2",
"@types/jest": "26.0.16", "@types/jest": "26.0.19",
"@types/nanoid": "2.1.0", "@types/nanoid": "2.1.0",
"@types/react": "17.0.0", "@types/react": "17.0.0",
"@types/react-dom": "17.0.0", "@types/react-dom": "17.0.0",
"@types/socket.io-client": "1.4.34", "@types/socket.io-client": "1.4.34",
"browser-nativefs": "0.11.1", "browser-nativefs": "0.12.0",
"clsx": "1.1.1", "clsx": "1.1.1",
"firebase": "8.1.2", "firebase": "8.2.1",
"i18next-browser-languagedetector": "6.0.1", "i18next-browser-languagedetector": "6.0.1",
"lodash.throttle": "4.1.1", "lodash.throttle": "4.1.1",
"nanoid": "2.1.11", "nanoid": "2.1.11",
@@ -47,19 +47,18 @@
"react-scripts": "4.0.1", "react-scripts": "4.0.1",
"roughjs": "4.3.1", "roughjs": "4.3.1",
"socket.io-client": "2.3.1", "socket.io-client": "2.3.1",
"typescript": "4.0.5" "typescript": "4.1.3"
}, },
"devDependencies": { "devDependencies": {
"@types/lodash.throttle": "4.1.6", "@types/lodash.throttle": "4.1.6",
"@types/pako": "1.0.1", "@types/pako": "1.0.1",
"asar": "3.0.3", "eslint-config-prettier": "7.1.0",
"eslint-config-prettier": "7.0.0", "eslint-plugin-prettier": "3.3.0",
"eslint-plugin-prettier": "3.1.4", "firebase-tools": "9.1.0",
"firebase-tools": "8.17.0", "husky": "4.3.6",
"husky": "4.3.0",
"jest-canvas-mock": "2.3.0", "jest-canvas-mock": "2.3.0",
"lint-staged": "10.5.3", "lint-staged": "10.5.3",
"pepjs": "0.5.2", "pepjs": "0.5.3",
"prettier": "2.2.1", "prettier": "2.2.1",
"rewire": "5.0.0" "rewire": "5.0.0"
}, },
@@ -84,13 +83,14 @@
"build-node": "node ./scripts/build-node.js", "build-node": "node ./scripts/build-node.js",
"build:app:docker": "REACT_APP_INCLUDE_GTAG=false REACT_APP_DISABLE_SENTRY=true react-scripts build", "build:app:docker": "REACT_APP_INCLUDE_GTAG=false REACT_APP_DISABLE_SENTRY=true react-scripts build",
"build:app": "REACT_APP_INCLUDE_GTAG=true REACT_APP_GIT_SHA=$NOW_GITHUB_COMMIT_SHA react-scripts build", "build:app": "REACT_APP_INCLUDE_GTAG=true REACT_APP_GIT_SHA=$NOW_GITHUB_COMMIT_SHA react-scripts build",
"build:zip": "node ./scripts/build-version.js", "build:version": "node ./scripts/build-version.js",
"build": "npm run build:app && npm run build:zip", "build": "npm run build:app && npm run build:version",
"eject": "react-scripts eject", "eject": "react-scripts eject",
"fix:code": "npm run test:code -- --fix", "fix:code": "npm run test:code -- --fix",
"fix:other": "npm run prettier -- --write", "fix:other": "npm run prettier -- --write",
"fix": "npm run fix:other && npm run fix:code", "fix": "npm run fix:other && npm run fix:code",
"locales-coverage": "node scripts/build-locales-coverage.js", "locales-coverage": "node scripts/build-locales-coverage.js",
"locales-coverage:description": "node scripts/locales-coverage-description.js",
"prettier": "prettier \"**/*.{css,scss,json,md,html,yml}\" --ignore-path=.eslintignore", "prettier": "prettier \"**/*.{css,scss,json,md,html,yml}\" --ignore-path=.eslintignore",
"start": "react-scripts start", "start": "react-scripts start",
"test:all": "npm run test:typecheck && npm run test:code && npm run test:other && npm run test:app -- --watchAll=false", "test:all": "npm run test:typecheck && npm run test:code && npm run test:other && npm run test:app -- --watchAll=false",
+2
View File
@@ -55,6 +55,8 @@
<meta name="twitter:image" content="https://excalidraw.com/og-image.png" /> <meta name="twitter:image" content="https://excalidraw.com/og-image.png" />
<link rel="shortcut icon" href="favicon.ico" type="image/x-icon" /> <link rel="shortcut icon" href="favicon.ico" type="image/x-icon" />
<!-- Excalidraw version -->
<meta name="version" content="{version}" />
<link <link
rel="preload" rel="preload"
href="FG_Virgil.woff2" href="FG_Virgil.woff2"
+2 -1
View File
@@ -25,5 +25,6 @@
"application/vnd.excalidraw+json": [".excalidraw"] "application/vnd.excalidraw+json": [".excalidraw"]
} }
} }
] ],
"capture_links": "new_client"
} }
+15 -8
View File
@@ -2,7 +2,8 @@
const fs = require("fs"); const fs = require("fs");
const path = require("path"); const path = require("path");
const asar = require("asar"); const versionFile = path.join("build", "version.json");
const indexFile = path.join("build", "index.html");
const zero = (digit) => `0${digit}`.slice(-2); const zero = (digit) => `0${digit}`.slice(-2);
@@ -20,18 +21,24 @@ const now = new Date();
const data = JSON.stringify( const data = JSON.stringify(
{ {
asar: "excalidraw.asar",
version: versionDate(now), version: versionDate(now),
}, },
undefined, undefined,
2, 2,
); );
fs.writeFileSync(path.join("build", "version.json"), data); fs.writeFileSync(versionFile, data);
(async () => { // https://stackoverflow.com/a/14181136/8418
const src = "build/"; fs.readFile(indexFile, "utf8", (error, data) => {
const dest = path.join("build", `excalidraw.asar`); if (error) {
return console.error(error);
}
const result = data.replace(/{version}/g, versionDate(now));
await asar.createPackage(src, dest); fs.writeFile(indexFile, result, "utf8", (error) => {
})(); if (error) {
return console.error(error);
}
});
});
+155
View File
@@ -0,0 +1,155 @@
const fs = require("fs");
const THRESSHOLD = 85;
const crowdinMap = {
"ar-SA": "en-ar",
"el-GR": "en-el",
"fi-FI": "en-fi",
"ja-JP": "en-ja",
"bg-BG": "en-bg",
"ca-ES": "en-ca",
"de-DE": "en-de",
"es-ES": "en-es",
"fa-IR": "en-fa",
"fr-FR": "en-fr",
"he-IL": "en-he",
"hi-IN": "en-hi",
"hu-HU": "en-hu",
"id-ID": "en-id",
"it-IT": "en-it",
"ko-KR": "en-ko",
"my-MM": "en-my",
"nb-NO": "en-nb",
"nl-NL": "en-nl",
"nn-NO": "en-nnno",
"pl-PL": "en-pl",
"pt-BR": "en-ptbr",
"pt-PT": "en-pt",
"ro-RO": "en-ro",
"ru-RU": "en-ru",
"sk-SK": "en-sk",
"sv-SE": "en-sv",
"tr-TR": "en-tr",
"uk-UA": "en-uk",
"zh-CN": "en-zhcn",
"zh-TW": "en-zhtw",
};
const flags = {
"ar-SA": "🇸🇦",
"bg-BG": "🇧🇬",
"ca-ES": "🇪🇸",
"de-DE": "🇩🇪",
"el-GR": "🇬🇷",
"es-ES": "🇪🇸",
"fa-IR": "🇮🇷",
"fi-FI": "🇫🇮",
"fr-FR": "🇫🇷",
"he-IL": "🇮🇱",
"hi-IN": "🇮🇳",
"hu-HU": "🇭🇺",
"id-ID": "🇮🇩",
"it-IT": "🇮🇹",
"ja-JP": "🇯🇵",
"ko-KR": "🇰🇷",
"my-MM": "🇲🇲",
"nb-NO": "🇳🇴",
"nl-NL": "🇳🇱",
"nn-NO": "🇳🇴",
"pl-PL": "🇵🇱",
"pt-BR": "🇧🇷",
"pt-PT": "🇵🇹",
"ro-RO": "🇷🇴",
"ru-RU": "🇷🇺",
"sk-SK": "🇸🇰",
"sv-SE": "🇸🇪",
"tr-TR": "🇹🇷",
"uk-UA": "🇺🇦",
"zh-CN": "🇨🇳",
"zh-TW": "🇹🇼",
};
const languages = {
"ar-SA": "العربية",
"bg-BG": "Български",
"ca-ES": "Catalan",
"de-DE": "Deutsch",
"el-GR": "Ελληνικά",
"es-ES": "Español",
"fa-IR": "فارسی",
"fi-FI": "Suomi",
"fr-FR": "Français",
"he-IL": "עברית",
"hi-IN": "हिन्दी",
"hu-HU": "Magyar",
"id-ID": "Bahasa Indonesia",
"it-IT": "Italiano",
"ja-JP": "日本語",
"ko-KR": "한국어",
"my-MM": "Burmese",
"nb-NO": "Norsk bokmål",
"nl-NL": "Nederlands",
"nn-NO": "Norsk nynorsk",
"pl-PL": "Polski",
"pt-BR": "Português Brasileiro",
"pt-PT": "Português",
"ro-RO": "Română",
"ru-RU": "Русский",
"sk-SK": "Slovenčina",
"sv-SE": "Svenska",
"tr-TR": "Türkçe",
"uk-UA": "Українська",
"zh-CN": "简体中文",
"zh-TW": "繁體中文",
};
const percentages = fs.readFileSync(
`${__dirname}/../src/locales/percentages.json`,
);
const rowData = JSON.parse(percentages);
const coverages = Object.entries(rowData)
.sort(([, a], [, b]) => b - a)
.reduce((r, [k, v]) => ({ ...r, [k]: v }), {});
const boldIf = (text, condition) => (condition ? `**${text}**` : text);
const printHeader = () => {
let result = "| | Flag | Locale | % |\n";
result += "| :--: | :--: | -- | :--: |";
return result;
};
const printRow = (id, locale, coverage) => {
const isOver = coverage >= THRESSHOLD;
let result = `| ${isOver ? id : "..."} | `;
result += `${locale in flags ? flags[locale] : ""} | `;
const language = locale in languages ? languages[locale] : locale;
if (locale in crowdinMap && crowdinMap[locale]) {
result += `[${boldIf(
language,
isOver,
)}](https://crowdin.com/translate/excalidraw/10/${crowdinMap[locale]}) | `;
} else {
result += `${boldIf(language, isOver)} | `;
}
result += `${coverage === 100 ? "✅" : boldIf(coverage, isOver)} |`;
return result;
};
console.info(
`Each language must be at least **${THRESSHOLD}%** translated in order to appear on Excalidraw. Join us on [Crowdin](https://crowdin.com/project/excalidraw) and help us translate your own language. **Can't find yours yet?** Open an [issue](https://github.com/excalidraw/excalidraw/issues/new) and we'll add it to the list.`,
);
console.info("\n\r");
console.info(printHeader());
let index = 1;
for (const coverage in coverages) {
if (coverage === "en") {
continue;
}
console.info(printRow(index, coverage, coverages[coverage]));
index++;
}
console.info("\n\r");
console.info("\\* Languages in **bold** are going to appear on production.");
+84 -49
View File
@@ -1,21 +1,22 @@
import React from "react"; import React from "react";
import { ColorPicker } from "../components/ColorPicker";
import { getDefaultAppState } from "../appState";
import { trash, zoomIn, zoomOut, resetZoom } from "../components/icons";
import { ToolButton } from "../components/ToolButton";
import { t } from "../i18n";
import { getNormalizedZoom } from "../scene";
import { CODES, KEYS } from "../keys";
import { getShortcutKey } from "../utils";
import useIsMobile from "../is-mobile";
import { register } from "./register";
import { newElementWith } from "../element/mutateElement";
import { AppState, NormalizedZoomValue } from "../types";
import { getCommonBounds } from "../element";
import { getNewZoom } from "../scene/zoom";
import { centerScrollOn } from "../scene/scroll";
import { EVENT_ACTION, EVENT_CHANGE, trackEvent } from "../analytics"; import { EVENT_ACTION, EVENT_CHANGE, trackEvent } from "../analytics";
import { getDefaultAppState } from "../appState";
import colors from "../colors"; import colors from "../colors";
import { ColorPicker } from "../components/ColorPicker";
import { resetZoom, trash, zoomIn, zoomOut } from "../components/icons";
import { ToolButton } from "../components/ToolButton";
import { getCommonBounds, getNonDeletedElements } from "../element";
import { newElementWith } from "../element/mutateElement";
import { ExcalidrawElement } from "../element/types";
import { t } from "../i18n";
import useIsMobile from "../is-mobile";
import { CODES, KEYS } from "../keys";
import { getNormalizedZoom, getSelectedElements } from "../scene";
import { centerScrollOn } from "../scene/scroll";
import { getNewZoom } from "../scene/zoom";
import { AppState, NormalizedZoomValue } from "../types";
import { getNewSceneName, getShortcutKey } from "../utils";
import { register } from "./register";
export const actionChangeViewBackgroundColor = register({ export const actionChangeViewBackgroundColor = register({
name: "changeViewBackgroundColor", name: "changeViewBackgroundColor",
@@ -58,6 +59,7 @@ export const actionClearCanvas = register({
), ),
appState: { appState: {
...getDefaultAppState(), ...getDefaultAppState(),
name: getNewSceneName(),
appearance: appState.appearance, appearance: appState.appearance,
elementLocked: appState.elementLocked, elementLocked: appState.elementLocked,
exportBackground: appState.exportBackground, exportBackground: appState.exportBackground,
@@ -65,6 +67,7 @@ export const actionClearCanvas = register({
gridSize: appState.gridSize, gridSize: appState.gridSize,
shouldAddWatermark: appState.shouldAddWatermark, shouldAddWatermark: appState.shouldAddWatermark,
showStats: appState.showStats, showStats: appState.showStats,
pasteDialog: appState.pasteDialog,
}, },
commitToHistory: true, commitToHistory: true,
}; };
@@ -93,6 +96,7 @@ export const actionZoomIn = register({
const zoom = getNewZoom( const zoom = getNewZoom(
getNormalizedZoom(appState.zoom.value + ZOOM_STEP), getNormalizedZoom(appState.zoom.value + ZOOM_STEP),
appState.zoom, appState.zoom,
{ left: appState.offsetLeft, top: appState.offsetTop },
{ x: appState.width / 2, y: appState.height / 2 }, { x: appState.width / 2, y: appState.height / 2 },
); );
trackEvent(EVENT_ACTION, "zoom", "in", zoom.value * 100); trackEvent(EVENT_ACTION, "zoom", "in", zoom.value * 100);
@@ -126,6 +130,7 @@ export const actionZoomOut = register({
const zoom = getNewZoom( const zoom = getNewZoom(
getNormalizedZoom(appState.zoom.value - ZOOM_STEP), getNormalizedZoom(appState.zoom.value - ZOOM_STEP),
appState.zoom, appState.zoom,
{ left: appState.offsetLeft, top: appState.offsetTop },
{ x: appState.width / 2, y: appState.height / 2 }, { x: appState.width / 2, y: appState.height / 2 },
); );
@@ -161,10 +166,15 @@ export const actionResetZoom = register({
return { return {
appState: { appState: {
...appState, ...appState,
zoom: getNewZoom(1 as NormalizedZoomValue, appState.zoom, { zoom: getNewZoom(
x: appState.width / 2, 1 as NormalizedZoomValue,
y: appState.height / 2, appState.zoom,
}), { left: appState.offsetLeft, top: appState.offsetTop },
{
x: appState.width / 2,
y: appState.height / 2,
},
),
}, },
commitToHistory: false, commitToHistory: false,
}; };
@@ -204,38 +214,63 @@ const zoomValueToFitBoundsOnViewport = (
return clampedZoomValueToFitElements as NormalizedZoomValue; return clampedZoomValueToFitElements as NormalizedZoomValue;
}; };
const zoomToFitElements = (
elements: readonly ExcalidrawElement[],
appState: Readonly<AppState>,
zoomToSelection: boolean,
) => {
const nonDeletedElements = getNonDeletedElements(elements);
const selectedElements = getSelectedElements(nonDeletedElements, appState);
const commonBounds =
zoomToSelection && selectedElements.length > 0
? getCommonBounds(selectedElements)
: getCommonBounds(nonDeletedElements);
const zoomValue = zoomValueToFitBoundsOnViewport(commonBounds, {
width: appState.width,
height: appState.height,
});
const newZoom = getNewZoom(zoomValue, appState.zoom, {
left: appState.offsetLeft,
top: appState.offsetTop,
});
const action = zoomToSelection ? "selection" : "fit";
const [x1, y1, x2, y2] = commonBounds;
const centerX = (x1 + x2) / 2;
const centerY = (y1 + y2) / 2;
trackEvent(EVENT_ACTION, "zoom", action, newZoom.value * 100);
return {
appState: {
...appState,
...centerScrollOn({
scenePoint: { x: centerX, y: centerY },
viewportDimensions: {
width: appState.width,
height: appState.height,
},
zoom: newZoom,
}),
zoom: newZoom,
},
commitToHistory: false,
};
};
export const actionZoomToSelected = register({
name: "zoomToSelection",
perform: (elements, appState) => zoomToFitElements(elements, appState, true),
keyTest: (event) =>
event.code === CODES.TWO &&
event.shiftKey &&
!event.altKey &&
!event[KEYS.CTRL_OR_CMD],
});
export const actionZoomToFit = register({ export const actionZoomToFit = register({
name: "zoomToFit", name: "zoomToFit",
perform: (elements, appState) => { perform: (elements, appState) => zoomToFitElements(elements, appState, false),
const nonDeletedElements = elements.filter((element) => !element.isDeleted);
const commonBounds = getCommonBounds(nonDeletedElements);
const zoomValue = zoomValueToFitBoundsOnViewport(commonBounds, {
width: appState.width,
height: appState.height,
});
const newZoom = getNewZoom(zoomValue, appState.zoom);
const [x1, y1, x2, y2] = commonBounds;
const centerX = (x1 + x2) / 2;
const centerY = (y1 + y2) / 2;
trackEvent(EVENT_ACTION, "zoom", "fit", newZoom.value * 100);
return {
appState: {
...appState,
...centerScrollOn({
scenePoint: { x: centerX, y: centerY },
viewportDimensions: {
width: appState.width,
height: appState.height,
},
zoom: newZoom,
}),
zoom: newZoom,
},
commitToHistory: false,
};
},
keyTest: (event) => keyTest: (event) =>
event.code === CODES.ONE && event.code === CODES.ONE &&
event.shiftKey && event.shiftKey &&
+1 -1
View File
@@ -136,7 +136,7 @@ export const actionDeleteSelected = register({
}; };
}, },
contextItemLabel: "labels.delete", contextItemLabel: "labels.delete",
contextMenuOrder: 3, contextMenuOrder: 999999,
keyTest: (event) => event.key === KEYS.BACKSPACE || event.key === KEYS.DELETE, keyTest: (event) => event.key === KEYS.BACKSPACE || event.key === KEYS.DELETE,
PanelComponent: ({ elements, appState, updateData }) => ( PanelComponent: ({ elements, appState, updateData }) => (
<ToolButton <ToolButton
+13 -2
View File
@@ -3,12 +3,16 @@ import { EVENT_CHANGE, EVENT_IO, trackEvent } from "../analytics";
import { load, save, saveAs } from "../components/icons"; import { load, save, saveAs } from "../components/icons";
import { ProjectName } from "../components/ProjectName"; import { ProjectName } from "../components/ProjectName";
import { ToolButton } from "../components/ToolButton"; import { ToolButton } from "../components/ToolButton";
import { Tooltip } from "../components/Tooltip";
import { questionCircle } from "../components/icons";
import { loadFromJSON, saveAsJSON } from "../data"; import { loadFromJSON, saveAsJSON } from "../data";
import { t } from "../i18n"; import { t } from "../i18n";
import useIsMobile from "../is-mobile"; import useIsMobile from "../is-mobile";
import { KEYS } from "../keys"; import { KEYS } from "../keys";
import { muteFSAbortError } from "../utils"; import { muteFSAbortError } from "../utils";
import { register } from "./register"; import { register } from "./register";
import "../components/ToolIcon.scss";
import { SCENE_NAME_FALLBACK } from "../constants";
export const actionChangeProjectName = register({ export const actionChangeProjectName = register({
name: "changeProjectName", name: "changeProjectName",
@@ -19,7 +23,7 @@ export const actionChangeProjectName = register({
PanelComponent: ({ appState, updateData }) => ( PanelComponent: ({ appState, updateData }) => (
<ProjectName <ProjectName
label={t("labels.fileTitle")} label={t("labels.fileTitle")}
value={appState.name || "Unnamed"} value={appState.name || SCENE_NAME_FALLBACK}
onChange={(name: string) => updateData(name)} onChange={(name: string) => updateData(name)}
/> />
), ),
@@ -54,13 +58,20 @@ export const actionChangeExportEmbedScene = register({
}; };
}, },
PanelComponent: ({ appState, updateData }) => ( PanelComponent: ({ appState, updateData }) => (
<label title={t("labels.exportEmbedScene_details")}> <label style={{ display: "flex" }}>
<input <input
type="checkbox" type="checkbox"
checked={appState.exportEmbedScene} checked={appState.exportEmbedScene}
onChange={(event) => updateData(event.target.checked)} onChange={(event) => updateData(event.target.checked)}
/>{" "} />{" "}
{t("labels.exportEmbedScene")} {t("labels.exportEmbedScene")}
<Tooltip
label={t("labels.exportEmbedScene_details")}
position="above"
long={true}
>
<div className="TooltipIcon">{questionCircle}</div>
</Tooltip>
</label> </label>
), ),
}); });
+28 -128
View File
@@ -1,110 +1,28 @@
import { import {
isTextElement, isTextElement,
isExcalidrawElement,
redrawTextBoundingBox, redrawTextBoundingBox,
getNonDeletedElements,
} from "../element"; } from "../element";
import { CODES, KEYS } from "../keys"; import { CODES, KEYS } from "../keys";
import { register } from "./register"; import { register } from "./register";
import { newElementWith } from "../element/mutateElement"; import { mutateElement, newElementWith } from "../element/mutateElement";
import { import {
ExcalidrawElement, DEFAULT_FONT_SIZE,
ExcalidrawElementPossibleProps, DEFAULT_FONT_FAMILY,
} from "../element/types"; DEFAULT_TEXT_ALIGN,
import { AppState } from "../types"; } from "../constants";
import {
canChangeSharpness,
getSelectedElements,
hasBackground,
hasStroke,
hasText,
} from "../scene";
import { isLinearElement, isLinearElementType } from "../element/typeChecks";
type AppStateStyles = {
[K in AssertSubset<
keyof AppState,
typeof copyableStyles[number][0]
>]: AppState[K];
};
type ElementStyles = {
[K in AssertSubset<
keyof ExcalidrawElementPossibleProps,
typeof copyableStyles[number][1]
>]: ExcalidrawElementPossibleProps[K];
};
type ElemelementStylesByType = Record<ExcalidrawElement["type"], ElementStyles>;
// `copiedStyles` is exported only for tests. // `copiedStyles` is exported only for tests.
let COPIED_STYLES: { export let copiedStyles: string = "{}";
appStateStyles: Partial<AppStateStyles>;
elementStyles: Partial<ElementStyles>;
elementStylesByType: Partial<ElemelementStylesByType>;
} | null = null;
/* [AppState prop, ExcalidrawElement prop, predicate] */
const copyableStyles = [
["currentItemOpacity", "opacity", () => true],
["currentItemStrokeColor", "strokeColor", () => true],
["currentItemStrokeStyle", "strokeStyle", hasStroke],
["currentItemStrokeWidth", "strokeWidth", hasStroke],
["currentItemRoughness", "roughness", hasStroke],
["currentItemBackgroundColor", "backgroundColor", hasBackground],
["currentItemFillStyle", "fillStyle", hasBackground],
["currentItemStrokeSharpness", "strokeSharpness", canChangeSharpness],
["currentItemLinearStrokeSharpness", "strokeSharpness", isLinearElementType],
["currentItemStartArrowhead", "startArrowhead", isLinearElementType],
["currentItemEndArrowhead", "endArrowhead", isLinearElementType],
["currentItemFontFamily", "fontFamily", hasText],
["currentItemFontSize", "fontSize", hasText],
["currentItemTextAlign", "textAlign", hasText],
] as const;
const getCommonStyleProps = (
elements: readonly ExcalidrawElement[],
): Exclude<typeof COPIED_STYLES, null> => {
const appStateStyles = {} as AppStateStyles;
const elementStyles = {} as ElementStyles;
const elementStylesByType = elements.reduce((acc, element) => {
// only use the first element of given type
if (!acc[element.type]) {
acc[element.type] = {} as ElementStyles;
copyableStyles.forEach(([appStateProp, prop, predicate]) => {
const value = (element as any)[prop];
if (value !== undefined && predicate(element.type)) {
if (appStateStyles[appStateProp] === undefined) {
(appStateStyles as any)[appStateProp] = value;
}
if (elementStyles[prop] === undefined) {
(elementStyles as any)[prop] = value;
}
(acc as any)[element.type][prop] = value;
}
});
}
return acc;
}, {} as ElemelementStylesByType);
// clone in case we ever make some of the props into non-primitives
return JSON.parse(
JSON.stringify({ appStateStyles, elementStyles, elementStylesByType }),
);
};
export const actionCopyStyles = register({ export const actionCopyStyles = register({
name: "copyStyles", name: "copyStyles",
perform: (elements, appState) => { perform: (elements, appState) => {
COPIED_STYLES = getCommonStyleProps( const element = elements.find((el) => appState.selectedElementIds[el.id]);
getSelectedElements(getNonDeletedElements(elements), appState), if (element) {
); copiedStyles = JSON.stringify(element);
}
return { return {
appState: {
...appState,
...COPIED_STYLES.appStateStyles,
},
commitToHistory: false, commitToHistory: false,
}; };
}, },
@@ -117,49 +35,31 @@ export const actionCopyStyles = register({
export const actionPasteStyles = register({ export const actionPasteStyles = register({
name: "pasteStyles", name: "pasteStyles",
perform: (elements, appState) => { perform: (elements, appState) => {
if (!COPIED_STYLES) { const pastedElement = JSON.parse(copiedStyles);
if (!isExcalidrawElement(pastedElement)) {
return { elements, commitToHistory: false }; return { elements, commitToHistory: false };
} }
const getStyle = <T extends ExcalidrawElement, K extends keyof T>(
element: T,
prop: K,
) => {
return (COPIED_STYLES?.elementStylesByType[element.type]?.[
prop as keyof ElementStyles
] ??
COPIED_STYLES?.elementStyles[prop as keyof ElementStyles] ??
element[prop]) as T[K];
};
return { return {
elements: elements.map((element) => { elements: elements.map((element) => {
if (appState.selectedElementIds[element.id]) { if (appState.selectedElementIds[element.id]) {
const commonProps = { const newElement = newElementWith(element, {
backgroundColor: getStyle(element, "backgroundColor"), backgroundColor: pastedElement?.backgroundColor,
strokeWidth: getStyle(element, "strokeWidth"), strokeWidth: pastedElement?.strokeWidth,
strokeColor: getStyle(element, "strokeColor"), strokeColor: pastedElement?.strokeColor,
strokeStyle: getStyle(element, "strokeStyle"), strokeStyle: pastedElement?.strokeStyle,
fillStyle: getStyle(element, "fillStyle"), fillStyle: pastedElement?.fillStyle,
opacity: getStyle(element, "opacity"), opacity: pastedElement?.opacity,
roughness: getStyle(element, "roughness"), roughness: pastedElement?.roughness,
strokeSharpness: getStyle(element, "strokeSharpness"), });
}; if (isTextElement(newElement)) {
if (isTextElement(element)) { mutateElement(newElement, {
const newElement = newElementWith(element, { fontSize: pastedElement?.fontSize || DEFAULT_FONT_SIZE,
...commonProps, fontFamily: pastedElement?.fontFamily || DEFAULT_FONT_FAMILY,
fontSize: getStyle(element, "fontSize"), textAlign: pastedElement?.textAlign || DEFAULT_TEXT_ALIGN,
fontFamily: getStyle(element, "fontFamily"),
textAlign: getStyle(element, "textAlign"),
}); });
redrawTextBoundingBox(newElement); redrawTextBoundingBox(newElement);
return newElement;
} else if (isLinearElement(element)) {
return newElementWith(element, {
...commonProps,
startArrowhead: getStyle(element, "startArrowhead"),
endArrowhead: getStyle(element, "endArrowhead"),
});
} }
return newElementWith(element, commonProps); return newElement;
} }
return element; return element;
}), }),
+4 -4
View File
@@ -19,8 +19,8 @@ export type ShortcutName =
| "copyAsSvg" | "copyAsSvg"
| "group" | "group"
| "ungroup" | "ungroup"
| "toggleGridMode" | "gridMode"
| "toggleStats" | "stats"
| "addToLibrary"; | "addToLibrary";
const shortcutMap: Record<ShortcutName, string[]> = { const shortcutMap: Record<ShortcutName, string[]> = {
@@ -51,8 +51,8 @@ const shortcutMap: Record<ShortcutName, string[]> = {
copyAsSvg: [], copyAsSvg: [],
group: [getShortcutKey("CtrlOrCmd+G")], group: [getShortcutKey("CtrlOrCmd+G")],
ungroup: [getShortcutKey("CtrlOrCmd+Shift+G")], ungroup: [getShortcutKey("CtrlOrCmd+Shift+G")],
toggleGridMode: [getShortcutKey("CtrlOrCmd+'")], gridMode: [getShortcutKey("CtrlOrCmd+'")],
toggleStats: [], stats: [],
addToLibrary: [], addToLibrary: [],
}; };
+1
View File
@@ -58,6 +58,7 @@ export type ActionName =
| "zoomOut" | "zoomOut"
| "resetZoom" | "resetZoom"
| "zoomToFit" | "zoomToFit"
| "zoomToSelection"
| "changeFontFamily" | "changeFontFamily"
| "changeTextAlign" | "changeTextAlign"
| "toggleFullScreen" | "toggleFullScreen"
+14 -11
View File
@@ -11,14 +11,17 @@ export const EVENT_SHAPE = "shape";
export const EVENT_SHARE = "share"; export const EVENT_SHARE = "share";
export const EVENT_MAGIC = "magic"; export const EVENT_MAGIC = "magic";
export const trackEvent = window.gtag export const trackEvent =
? (category: string, name: string, label?: string, value?: number) => { typeof window !== "undefined" && window.gtag
window.gtag("event", name, { ? (category: string, name: string, label?: string, value?: number) => {
event_category: category, window.gtag("event", name, {
event_label: label, event_category: category,
value, event_label: label,
}); value,
} });
: (category: string, name: string, label?: string, value?: number) => { }
console.info("Track Event", category, name, label, value); : typeof process !== "undefined" && process?.env?.JEST_WORKER_ID
}; ? (category: string, name: string, label?: string, value?: number) => {}
: (category: string, name: string, label?: string, value?: number) => {
console.info("Track Event", category, name, label, value);
};
+71 -71
View File
@@ -1,79 +1,84 @@
import oc from "open-color"; import oc from "open-color";
import { AppState, FlooredNumber, NormalizedZoomValue } from "./types";
import { getDateTime } from "./utils";
import { t } from "./i18n";
import { import {
DEFAULT_FONT_SIZE,
DEFAULT_FONT_FAMILY, DEFAULT_FONT_FAMILY,
DEFAULT_FONT_SIZE,
SCENE_NAME_FALLBACK,
DEFAULT_TEXT_ALIGN, DEFAULT_TEXT_ALIGN,
} from "./constants"; } from "./constants";
import { AppState, FlooredNumber, NormalizedZoomValue } from "./types";
export const getDefaultAppState = (): Omit< type DefaultAppState = Omit<AppState, "offsetTop" | "offsetLeft" | "name"> & {
AppState, /**
"offsetTop" | "offsetLeft" * You should override this with current appState.name, or whatever is
> => { * applicable at a given place where you get default appState.
*/
name: undefined;
};
export const getDefaultAppState = (): DefaultAppState => {
return { return {
appearance: "light", appearance: "light",
isLoading: false, collaborators: new Map(),
errorMessage: null, currentChartType: "bar",
currentItemBackgroundColor: "transparent",
currentItemEndArrowhead: "arrow",
currentItemFillStyle: "hachure",
currentItemFontFamily: DEFAULT_FONT_FAMILY,
currentItemFontSize: DEFAULT_FONT_SIZE,
currentItemLinearStrokeSharpness: "round",
currentItemOpacity: 100,
currentItemRoughness: 1,
currentItemStartArrowhead: null,
currentItemStrokeColor: oc.black,
currentItemStrokeSharpness: "sharp",
currentItemStrokeStyle: "solid",
currentItemStrokeWidth: 1,
currentItemTextAlign: DEFAULT_TEXT_ALIGN,
cursorButton: "up",
draggingElement: null, draggingElement: null,
resizingElement: null,
multiElement: null,
editingElement: null, editingElement: null,
startBoundElement: null, editingGroupId: null,
editingLinearElement: null, editingLinearElement: null,
elementType: "selection",
elementLocked: false, elementLocked: false,
elementType: "selection",
errorMessage: null,
exportBackground: true, exportBackground: true,
exportEmbedScene: false, exportEmbedScene: false,
shouldAddWatermark: false, fileHandle: null,
currentItemStrokeColor: oc.black, gridSize: null,
currentItemBackgroundColor: "transparent", height: window.innerHeight,
currentItemFillStyle: "hachure",
currentItemStrokeWidth: 1,
currentItemStrokeStyle: "solid",
currentItemRoughness: 1,
currentItemOpacity: 100,
currentItemFontSize: DEFAULT_FONT_SIZE,
currentItemFontFamily: DEFAULT_FONT_FAMILY,
currentItemTextAlign: DEFAULT_TEXT_ALIGN,
currentItemStrokeSharpness: "sharp",
currentItemLinearStrokeSharpness: "round",
currentItemStartArrowhead: null,
currentItemEndArrowhead: "arrow",
viewBackgroundColor: oc.white,
scrollX: 0 as FlooredNumber,
scrollY: 0 as FlooredNumber,
cursorX: 0,
cursorY: 0,
cursorButton: "up",
scrolledOutside: false,
name: `${t("labels.untitled")}-${getDateTime()}`,
isBindingEnabled: true, isBindingEnabled: true,
isLibraryOpen: false,
isLoading: false,
isResizing: false, isResizing: false,
isRotating: false, isRotating: false,
selectionElement: null,
zoom: {
value: 1 as NormalizedZoomValue,
translation: { x: 0, y: 0 },
},
openMenu: null,
lastPointerDownWith: "mouse", lastPointerDownWith: "mouse",
selectedElementIds: {}, multiElement: null,
// for safety (because TS mostly doesn't distinguish optional types and
// undefined values), we set `name` to the fallback name, but we cast it to
// `undefined` so that TS forces us to explicitly specify it wherever
// possible
name: (SCENE_NAME_FALLBACK as unknown) as undefined,
openMenu: null,
pasteDialog: { shown: false, data: null },
previousSelectedElementIds: {}, previousSelectedElementIds: {},
resizingElement: null,
scrolledOutside: false,
scrollX: 0 as FlooredNumber,
scrollY: 0 as FlooredNumber,
selectedElementIds: {},
selectedGroupIds: {},
selectionElement: null,
shouldAddWatermark: false,
shouldCacheIgnoreZoom: false, shouldCacheIgnoreZoom: false,
showShortcutsDialog: false, showShortcutsDialog: false,
suggestedBindings: [],
zenModeEnabled: false,
gridSize: null,
editingGroupId: null,
selectedGroupIds: {},
width: window.innerWidth,
height: window.innerHeight,
isLibraryOpen: false,
fileHandle: null,
collaborators: new Map(),
showStats: false, showStats: false,
startBoundElement: null,
suggestedBindings: [],
viewBackgroundColor: oc.white,
width: window.innerWidth,
zenModeEnabled: false,
zoom: { value: 1 as NormalizedZoomValue, translation: { x: 0, y: 0 } },
}; };
}; };
@@ -93,26 +98,25 @@ const APP_STATE_STORAGE_CONF = (<
config: { [K in keyof T]: K extends keyof AppState ? T[K] : never }, config: { [K in keyof T]: K extends keyof AppState ? T[K] : never },
) => config)({ ) => config)({
appearance: { browser: true, export: false }, appearance: { browser: true, export: false },
collaborators: { browser: false, export: false },
currentChartType: { browser: true, export: false },
currentItemBackgroundColor: { browser: true, export: false }, currentItemBackgroundColor: { browser: true, export: false },
currentItemEndArrowhead: { browser: true, export: false },
currentItemFillStyle: { browser: true, export: false }, currentItemFillStyle: { browser: true, export: false },
currentItemFontFamily: { browser: true, export: false }, currentItemFontFamily: { browser: true, export: false },
currentItemFontSize: { browser: true, export: false }, currentItemFontSize: { browser: true, export: false },
currentItemLinearStrokeSharpness: { browser: true, export: false },
currentItemOpacity: { browser: true, export: false }, currentItemOpacity: { browser: true, export: false },
currentItemRoughness: { browser: true, export: false }, currentItemRoughness: { browser: true, export: false },
currentItemStartArrowhead: { browser: true, export: false },
currentItemStrokeColor: { browser: true, export: false }, currentItemStrokeColor: { browser: true, export: false },
currentItemStrokeSharpness: { browser: true, export: false },
currentItemStrokeStyle: { browser: true, export: false }, currentItemStrokeStyle: { browser: true, export: false },
currentItemStrokeWidth: { browser: true, export: false }, currentItemStrokeWidth: { browser: true, export: false },
currentItemTextAlign: { browser: true, export: false }, currentItemTextAlign: { browser: true, export: false },
currentItemStrokeSharpness: { browser: true, export: false },
currentItemLinearStrokeSharpness: { browser: true, export: false },
currentItemStartArrowhead: { browser: true, export: false },
currentItemEndArrowhead: { browser: true, export: false },
cursorButton: { browser: true, export: false }, cursorButton: { browser: true, export: false },
cursorX: { browser: true, export: false },
cursorY: { browser: true, export: false },
draggingElement: { browser: false, export: false }, draggingElement: { browser: false, export: false },
editingElement: { browser: false, export: false }, editingElement: { browser: false, export: false },
startBoundElement: { browser: false, export: false },
editingGroupId: { browser: true, export: false }, editingGroupId: { browser: true, export: false },
editingLinearElement: { browser: false, export: false }, editingLinearElement: { browser: false, export: false },
elementLocked: { browser: true, export: false }, elementLocked: { browser: true, export: false },
@@ -120,6 +124,7 @@ const APP_STATE_STORAGE_CONF = (<
errorMessage: { browser: false, export: false }, errorMessage: { browser: false, export: false },
exportBackground: { browser: true, export: false }, exportBackground: { browser: true, export: false },
exportEmbedScene: { browser: true, export: false }, exportEmbedScene: { browser: true, export: false },
fileHandle: { browser: false, export: false },
gridSize: { browser: true, export: true }, gridSize: { browser: true, export: true },
height: { browser: false, export: false }, height: { browser: false, export: false },
isBindingEnabled: { browser: false, export: false }, isBindingEnabled: { browser: false, export: false },
@@ -130,7 +135,10 @@ const APP_STATE_STORAGE_CONF = (<
lastPointerDownWith: { browser: true, export: false }, lastPointerDownWith: { browser: true, export: false },
multiElement: { browser: false, export: false }, multiElement: { browser: false, export: false },
name: { browser: true, export: false }, name: { browser: true, export: false },
offsetLeft: { browser: false, export: false },
offsetTop: { browser: false, export: false },
openMenu: { browser: true, export: false }, openMenu: { browser: true, export: false },
pasteDialog: { browser: false, export: false },
previousSelectedElementIds: { browser: true, export: false }, previousSelectedElementIds: { browser: true, export: false },
resizingElement: { browser: false, export: false }, resizingElement: { browser: false, export: false },
scrolledOutside: { browser: true, export: false }, scrolledOutside: { browser: true, export: false },
@@ -142,16 +150,13 @@ const APP_STATE_STORAGE_CONF = (<
shouldAddWatermark: { browser: true, export: false }, shouldAddWatermark: { browser: true, export: false },
shouldCacheIgnoreZoom: { browser: true, export: false }, shouldCacheIgnoreZoom: { browser: true, export: false },
showShortcutsDialog: { browser: false, export: false }, showShortcutsDialog: { browser: false, export: false },
showStats: { browser: true, export: false },
startBoundElement: { browser: false, export: false },
suggestedBindings: { browser: false, export: false }, suggestedBindings: { browser: false, export: false },
viewBackgroundColor: { browser: true, export: true }, viewBackgroundColor: { browser: true, export: true },
width: { browser: false, export: false }, width: { browser: false, export: false },
zenModeEnabled: { browser: true, export: false }, zenModeEnabled: { browser: true, export: false },
zoom: { browser: true, export: false }, zoom: { browser: true, export: false },
offsetTop: { browser: false, export: false },
offsetLeft: { browser: false, export: false },
fileHandle: { browser: false, export: false },
collaborators: { browser: false, export: false },
showStats: { browser: true, export: false },
}); });
const _clearAppStateForStorage = <ExportType extends "export" | "browser">( const _clearAppStateForStorage = <ExportType extends "export" | "browser">(
@@ -166,11 +171,6 @@ const _clearAppStateForStorage = <ExportType extends "export" | "browser">(
const stateForExport = {} as { [K in ExportableKeys]?: typeof appState[K] }; const stateForExport = {} as { [K in ExportableKeys]?: typeof appState[K] };
for (const key of Object.keys(appState) as (keyof typeof appState)[]) { for (const key of Object.keys(appState) as (keyof typeof appState)[]) {
const propConfig = APP_STATE_STORAGE_CONF[key]; const propConfig = APP_STATE_STORAGE_CONF[key];
if (!propConfig) {
console.error(
`_clearAppStateForStorage: appState key "${key}" config doesn't exist for "${exportType}" export type`,
);
}
if (propConfig?.[exportType]) { if (propConfig?.[exportType]) {
// @ts-ignore see https://github.com/microsoft/TypeScript/issues/31445 // @ts-ignore see https://github.com/microsoft/TypeScript/issues/31445
stateForExport[key] = appState[key]; stateForExport[key] = appState[key];
+326 -124
View File
@@ -1,13 +1,16 @@
import { EVENT_MAGIC, trackEvent } from "./analytics"; import { EVENT_MAGIC, trackEvent } from "./analytics";
import colors from "./colors"; import colors from "./colors";
import { DEFAULT_FONT_FAMILY, DEFAULT_FONT_SIZE } from "./constants"; import { DEFAULT_FONT_FAMILY, DEFAULT_FONT_SIZE, ENV } from "./constants";
import { newElement, newTextElement, newLinearElement } from "./element"; import { newElement, newLinearElement, newTextElement } from "./element";
import { ExcalidrawElement } from "./element/types"; import { NonDeletedExcalidrawElement } from "./element/types";
import { randomId } from "./random"; import { randomId } from "./random";
export type ChartElements = readonly NonDeletedExcalidrawElement[];
const BAR_WIDTH = 32; const BAR_WIDTH = 32;
const BAR_GAP = 12; const BAR_GAP = 12;
const BAR_HEIGHT = 256; const BAR_HEIGHT = 256;
const GRID_OPACITY = 50;
export interface Spreadsheet { export interface Spreadsheet {
title: string | null; title: string | null;
@@ -19,15 +22,15 @@ export const NOT_SPREADSHEET = "NOT_SPREADSHEET";
export const VALID_SPREADSHEET = "VALID_SPREADSHEET"; export const VALID_SPREADSHEET = "VALID_SPREADSHEET";
type ParseSpreadsheetResult = type ParseSpreadsheetResult =
| { type: typeof NOT_SPREADSHEET } | { type: typeof NOT_SPREADSHEET; reason: string }
| { type: typeof VALID_SPREADSHEET; spreadsheet: Spreadsheet }; | { type: typeof VALID_SPREADSHEET; spreadsheet: Spreadsheet };
const tryParseNumber = (s: string): number | null => { const tryParseNumber = (s: string): number | null => {
const match = /^[$€£¥₩]?([0-9]+(\.[0-9]+)?)$/.exec(s); const match = /^[$€£¥₩]?([0-9,]+(\.[0-9]+)?)$/.exec(s);
if (!match) { if (!match) {
return null; return null;
} }
return parseFloat(match[1]); return parseFloat(match[1].replace(/,/g, ""));
}; };
const isNumericColumn = (lines: string[][], columnIndex: number) => const isNumericColumn = (lines: string[][], columnIndex: number) =>
@@ -37,12 +40,12 @@ const tryParseCells = (cells: string[][]): ParseSpreadsheetResult => {
const numCols = cells[0].length; const numCols = cells[0].length;
if (numCols > 2) { if (numCols > 2) {
return { type: NOT_SPREADSHEET }; return { type: NOT_SPREADSHEET, reason: "More than 2 columns" };
} }
if (numCols === 1) { if (numCols === 1) {
if (!isNumericColumn(cells, 0)) { if (!isNumericColumn(cells, 0)) {
return { type: NOT_SPREADSHEET }; return { type: NOT_SPREADSHEET, reason: "Value is not numeric" };
} }
const hasHeader = tryParseNumber(cells[0][0]) === null; const hasHeader = tryParseNumber(cells[0][0]) === null;
@@ -51,7 +54,7 @@ const tryParseCells = (cells: string[][]): ParseSpreadsheetResult => {
); );
if (values.length < 2) { if (values.length < 2) {
return { type: NOT_SPREADSHEET }; return { type: NOT_SPREADSHEET, reason: "Less than two rows" };
} }
return { return {
@@ -67,7 +70,7 @@ const tryParseCells = (cells: string[][]): ParseSpreadsheetResult => {
const valueColumnIndex = isNumericColumn(cells, 0) ? 0 : 1; const valueColumnIndex = isNumericColumn(cells, 0) ? 0 : 1;
if (!isNumericColumn(cells, valueColumnIndex)) { if (!isNumericColumn(cells, valueColumnIndex)) {
return { type: NOT_SPREADSHEET }; return { type: NOT_SPREADSHEET, reason: "Value is not numeric" };
} }
const labelColumnIndex = (valueColumnIndex + 1) % 2; const labelColumnIndex = (valueColumnIndex + 1) % 2;
@@ -75,7 +78,7 @@ const tryParseCells = (cells: string[][]): ParseSpreadsheetResult => {
const rows = hasHeader ? cells.slice(1) : cells; const rows = hasHeader ? cells.slice(1) : cells;
if (rows.length < 2) { if (rows.length < 2) {
return { type: NOT_SPREADSHEET }; return { type: NOT_SPREADSHEET, reason: "Less than 2 rows" };
} }
return { return {
@@ -104,13 +107,13 @@ export const tryParseSpreadsheet = (text: string): ParseSpreadsheetResult => {
// Copy/paste from excel, spreadhseets, tsv, csv. // Copy/paste from excel, spreadhseets, tsv, csv.
// For now we only accept 2 columns with an optional header // For now we only accept 2 columns with an optional header
// Check for tab separeted values // Check for tab separated values
let lines = text let lines = text
.trim() .trim()
.split("\n") .split("\n")
.map((line) => line.trim().split("\t")); .map((line) => line.trim().split("\t"));
// Check for comma separeted files // Check for comma separated files
if (lines.length && lines[0].length !== 2) { if (lines.length && lines[0].length !== 2) {
lines = text lines = text
.trim() .trim()
@@ -119,14 +122,17 @@ export const tryParseSpreadsheet = (text: string): ParseSpreadsheetResult => {
} }
if (lines.length === 0) { if (lines.length === 0) {
return { type: NOT_SPREADSHEET }; return { type: NOT_SPREADSHEET, reason: "No values" };
} }
const numColsFirstLine = lines[0].length; const numColsFirstLine = lines[0].length;
const isSpreadsheet = lines.every((line) => line.length === numColsFirstLine); const isSpreadsheet = lines.every((line) => line.length === numColsFirstLine);
if (!isSpreadsheet) { if (!isSpreadsheet) {
return { type: NOT_SPREADSHEET }; return {
type: NOT_SPREADSHEET,
reason: "All rows don't have same number of columns",
};
} }
const result = tryParseCells(lines); const result = tryParseCells(lines);
@@ -136,111 +142,48 @@ export const tryParseSpreadsheet = (text: string): ParseSpreadsheetResult => {
return transposedResults; return transposedResults;
} }
} }
return result; return result;
}; };
// For the maths behind it https://excalidraw.com/#json=6320864370884608,O_5xfD-Agh32tytHpRJx1g const bgColors = colors.elementBackground.slice(
export const renderSpreadsheet = ( 2,
colors.elementBackground.length,
);
// 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],
strokeSharpness: "sharp",
strokeStyle: "solid",
strokeWidth: 1,
verticalAlign: "middle",
} as const;
const getChartDimentions = (spreadsheet: Spreadsheet) => {
const chartWidth =
(BAR_WIDTH + BAR_GAP) * spreadsheet.values.length + BAR_GAP;
const chartHeight = BAR_HEIGHT + BAR_GAP * 2;
return { chartWidth, chartHeight };
};
const chartXLabels = (
spreadsheet: Spreadsheet, spreadsheet: Spreadsheet,
x: number, x: number,
y: number, y: number,
): ExcalidrawElement[] => { groupId: string,
const values = spreadsheet.values; backgroundColor: string,
const max = Math.max(...values); ): ChartElements => {
const chartHeight = BAR_HEIGHT + BAR_GAP * 2; return (
const chartWidth = (BAR_WIDTH + BAR_GAP) * values.length + BAR_GAP;
const maxColors = colors.elementBackground.length;
const bgColors = colors.elementBackground.slice(2, maxColors);
// Put all the common properties here so when the whole chart is selected
// the properties dialog shows the correct selected values
const commonProps = {
backgroundColor: bgColors[Math.floor(Math.random() * bgColors.length)],
fillStyle: "hachure",
fontFamily: DEFAULT_FONT_FAMILY,
fontSize: DEFAULT_FONT_SIZE,
groupIds: [randomId()],
opacity: 100,
roughness: 1,
strokeColor: colors.elementStroke[0],
strokeSharpness: "sharp",
strokeStyle: "solid",
strokeWidth: 1,
verticalAlign: "middle",
} as const;
const minYLabel = newTextElement({
...commonProps,
x: x - BAR_GAP,
y: y - BAR_GAP,
text: "0",
textAlign: "right",
});
const maxYLabel = newTextElement({
...commonProps,
x: x - BAR_GAP,
y: y - BAR_HEIGHT - minYLabel.height / 2,
text: max.toLocaleString(),
textAlign: "right",
});
const xAxisLine = newLinearElement({
type: "line",
x,
y,
startArrowhead: null,
endArrowhead: null,
points: [
[0, 0],
[chartWidth, 0],
],
...commonProps,
});
const yAxisLine = newLinearElement({
type: "line",
x,
y,
startArrowhead: null,
endArrowhead: null,
points: [
[0, 0],
[0, -chartHeight],
],
...commonProps,
});
const maxValueLine = newLinearElement({
type: "line",
x,
y: y - BAR_HEIGHT - BAR_GAP,
startArrowhead: null,
endArrowhead: null,
...commonProps,
strokeStyle: "dotted",
points: [
[0, 0],
[chartWidth, 0],
],
});
const bars = values.map((value, index) => {
const barHeight = (value / max) * BAR_HEIGHT;
return newElement({
...commonProps,
type: "rectangle",
x: x + index * (BAR_WIDTH + BAR_GAP) + BAR_GAP,
y: y - barHeight - BAR_GAP,
width: BAR_WIDTH,
height: barHeight,
});
});
const xLabels =
spreadsheet.labels?.map((label, index) => { spreadsheet.labels?.map((label, index) => {
return newTextElement({ return newTextElement({
groupIds: [groupId],
backgroundColor,
...commonProps, ...commonProps,
text: label.length > 8 ? `${label.slice(0, 5)}...` : label, text: label.length > 8 ? `${label.slice(0, 5)}...` : label,
x: x + index * (BAR_WIDTH + BAR_GAP) + BAR_GAP * 2, x: x + index * (BAR_WIDTH + BAR_GAP) + BAR_GAP * 2,
@@ -251,29 +194,288 @@ export const renderSpreadsheet = (
textAlign: "center", textAlign: "center",
verticalAlign: "top", verticalAlign: "top",
}); });
}) || []; }) || []
);
};
const chartYLabels = (
spreadsheet: Spreadsheet,
x: number,
y: number,
groupId: string,
backgroundColor: string,
): ChartElements => {
const minYLabel = newTextElement({
groupIds: [groupId],
backgroundColor,
...commonProps,
x: x - BAR_GAP,
y: y - BAR_GAP,
text: "0",
textAlign: "right",
});
const maxYLabel = newTextElement({
groupIds: [groupId],
backgroundColor,
...commonProps,
x: x - BAR_GAP,
y: y - BAR_HEIGHT - minYLabel.height / 2,
text: Math.max(...spreadsheet.values).toLocaleString(),
textAlign: "right",
});
return [minYLabel, maxYLabel];
};
const chartLines = (
spreadsheet: Spreadsheet,
x: number,
y: number,
groupId: string,
backgroundColor: string,
): ChartElements => {
const { chartWidth, chartHeight } = getChartDimentions(spreadsheet);
const xLine = newLinearElement({
backgroundColor,
groupIds: [groupId],
...commonProps,
type: "line",
x,
y,
startArrowhead: null,
endArrowhead: null,
width: chartWidth,
points: [
[0, 0],
[chartWidth, 0],
],
});
const yLine = newLinearElement({
backgroundColor,
groupIds: [groupId],
...commonProps,
type: "line",
x,
y,
startArrowhead: null,
endArrowhead: null,
height: chartHeight,
points: [
[0, 0],
[0, -chartHeight],
],
});
const maxLine = newLinearElement({
backgroundColor,
groupIds: [groupId],
...commonProps,
type: "line",
x,
y: y - BAR_HEIGHT - BAR_GAP,
startArrowhead: null,
endArrowhead: null,
strokeStyle: "dotted",
width: chartWidth,
opacity: GRID_OPACITY,
points: [
[0, 0],
[chartWidth, 0],
],
});
return [xLine, yLine, maxLine];
};
// For the maths behind it https://excalidraw.com/#json=6320864370884608,O_5xfD-Agh32tytHpRJx1g
const chartBaseElements = (
spreadsheet: Spreadsheet,
x: number,
y: number,
groupId: string,
backgroundColor: string,
debug?: boolean,
): ChartElements => {
const { chartWidth, chartHeight } = getChartDimentions(spreadsheet);
const title = spreadsheet.title const title = spreadsheet.title
? newTextElement({ ? newTextElement({
backgroundColor,
groupIds: [groupId],
...commonProps, ...commonProps,
text: spreadsheet.title, text: spreadsheet.title,
x: x + chartWidth / 2, x: x + chartWidth / 2,
y: y - BAR_HEIGHT - BAR_GAP * 2 - maxYLabel.height, y: y - BAR_HEIGHT - BAR_GAP * 2 - DEFAULT_FONT_SIZE,
strokeSharpness: "sharp", strokeSharpness: "sharp",
strokeStyle: "solid", strokeStyle: "solid",
textAlign: "center", textAlign: "center",
}) })
: null; : null;
trackEvent(EVENT_MAGIC, "chart", "bars", bars.length); const debugRect = debug
? newElement({
backgroundColor,
groupIds: [groupId],
...commonProps,
type: "rectangle",
x,
y: y - chartHeight,
width: chartWidth,
height: chartHeight,
strokeColor: colors.elementStroke[0],
fillStyle: "solid",
opacity: 6,
})
: null;
return [ return [
title, ...(debugRect ? [debugRect] : []),
...bars, ...(title ? [title] : []),
...xLabels, ...chartXLabels(spreadsheet, x, y, groupId, backgroundColor),
xAxisLine, ...chartYLabels(spreadsheet, x, y, groupId, backgroundColor),
yAxisLine, ...chartLines(spreadsheet, x, y, groupId, backgroundColor),
maxValueLine, ];
minYLabel, };
maxYLabel,
].filter((element) => element !== null) as ExcalidrawElement[]; const chartTypeBar = (
spreadsheet: Spreadsheet,
x: number,
y: number,
): ChartElements => {
const max = Math.max(...spreadsheet.values);
const groupId = randomId();
const backgroundColor = bgColors[Math.floor(Math.random() * bgColors.length)];
const bars = spreadsheet.values.map((value, index) => {
const barHeight = (value / max) * BAR_HEIGHT;
return newElement({
backgroundColor,
groupIds: [groupId],
...commonProps,
type: "rectangle",
x: x + index * (BAR_WIDTH + BAR_GAP) + BAR_GAP,
y: y - barHeight - BAR_GAP,
width: BAR_WIDTH,
height: barHeight,
});
});
return [
...bars,
...chartBaseElements(
spreadsheet,
x,
y,
groupId,
backgroundColor,
process.env.NODE_ENV === ENV.DEVELOPMENT,
),
];
};
const chartTypeLine = (
spreadsheet: Spreadsheet,
x: number,
y: number,
): ChartElements => {
const max = Math.max(...spreadsheet.values);
const groupId = randomId();
const backgroundColor = bgColors[Math.floor(Math.random() * bgColors.length)];
let index = 0;
const points = [];
for (const value of spreadsheet.values) {
const cx = index * (BAR_WIDTH + BAR_GAP);
const cy = -(value / max) * BAR_HEIGHT;
points.push([cx, cy]);
index++;
}
const maxX = Math.max(...points.map((element) => element[0]));
const maxY = Math.max(...points.map((element) => element[1]));
const minX = Math.min(...points.map((element) => element[0]));
const minY = Math.min(...points.map((element) => element[1]));
const line = newLinearElement({
backgroundColor,
groupIds: [groupId],
...commonProps,
type: "line",
x: x + BAR_GAP + BAR_WIDTH / 2,
y: y - BAR_GAP,
startArrowhead: null,
endArrowhead: null,
height: maxY - minY,
width: maxX - minX,
strokeWidth: 2,
points: points as any,
});
const dots = spreadsheet.values.map((value, index) => {
const cx = index * (BAR_WIDTH + BAR_GAP) + BAR_GAP / 2;
const cy = -(value / max) * BAR_HEIGHT + BAR_GAP / 2;
return newElement({
backgroundColor,
groupIds: [groupId],
...commonProps,
fillStyle: "solid",
strokeWidth: 2,
type: "ellipse",
x: x + cx + BAR_WIDTH / 2,
y: y + cy - BAR_GAP * 2,
width: BAR_GAP,
height: BAR_GAP,
});
});
const lines = spreadsheet.values.map((value, index) => {
const cx = index * (BAR_WIDTH + BAR_GAP) + BAR_GAP / 2;
const cy = (value / max) * BAR_HEIGHT + BAR_GAP / 2 + BAR_GAP;
return newLinearElement({
backgroundColor,
groupIds: [groupId],
...commonProps,
type: "line",
x: x + cx + BAR_WIDTH / 2 + BAR_GAP / 2,
y: y - cy,
startArrowhead: null,
endArrowhead: null,
height: cy,
strokeStyle: "dotted",
opacity: GRID_OPACITY,
points: [
[0, 0],
[0, cy],
],
});
});
return [
...chartBaseElements(
spreadsheet,
x,
y,
groupId,
backgroundColor,
process.env.NODE_ENV === ENV.DEVELOPMENT,
),
line,
...lines,
...dots,
];
};
export const renderSpreadsheet = (
chartType: string,
spreadsheet: Spreadsheet,
x: number,
y: number,
): ChartElements => {
trackEvent(EVENT_MAGIC, "chart", chartType, spreadsheet.values.length);
if (chartType === "line") {
return chartTypeLine(spreadsheet, x, y);
}
return chartTypeBar(spreadsheet, x, y);
}; };
+254 -203
View File
@@ -1,180 +1,168 @@
import { Point, simplify } from "points-on-curve";
import React from "react"; import React from "react";
import rough from "roughjs/bin/rough";
import { RoughCanvas } from "roughjs/bin/canvas"; import { RoughCanvas } from "roughjs/bin/canvas";
import { simplify, Point } from "points-on-curve"; import rough from "roughjs/bin/rough";
import {
newElement,
newTextElement,
duplicateElement,
isInvisiblySmallElement,
isTextElement,
textWysiwyg,
getCommonBounds,
getCursorForResizingElement,
getPerfectElementSize,
getNormalizedDimensions,
newLinearElement,
transformElements,
getElementWithTransformHandleType,
getResizeOffsetXY,
getResizeArrowDirection,
getTransformHandleTypeFromCoords,
isNonDeletedElement,
updateTextElement,
dragSelectedElements,
getDragOffsetXY,
dragNewElement,
hitTest,
isHittingElementBoundingBoxWithoutHittingElement,
getNonDeletedElements,
} from "../element";
import {
getElementsWithinSelection,
isOverScrollBars,
getElementsAtPosition,
getElementContainingPosition,
getNormalizedZoom,
getSelectedElements,
isSomeElementSelected,
calculateScrollCenter,
} from "../scene";
import { loadFromBlob, exportCanvas } from "../data";
import { renderScene } from "../renderer";
import {
AppState,
GestureEvent,
Gesture,
ExcalidrawProps,
SceneData,
} from "../types";
import {
ExcalidrawElement,
ExcalidrawTextElement,
NonDeleted,
ExcalidrawGenericElement,
ExcalidrawLinearElement,
ExcalidrawBindableElement,
} from "../element/types";
import { distance2d, isPathALoop, getGridPoint } from "../math";
import {
isWritableElement,
isInputLike,
isToolIcon,
debounce,
distance,
resetCursor,
viewportCoordsToSceneCoords,
sceneCoordsToViewportCoords,
setCursorForShape,
tupleToCoors,
ResolvablePromise,
resolvablePromise,
withBatchedUpdates,
} from "../utils";
import {
KEYS,
isArrowKey,
getResizeCenterPointKey,
getResizeWithSidesSameLengthKey,
getRotateWithDiscreteAngleKey,
CODES,
} from "../keys";
import { findShapeByKey } from "../shapes";
import { createHistory, SceneHistory } from "../history";
import ContextMenu from "./ContextMenu";
import { ActionManager } from "../actions/manager";
import "../actions"; import "../actions";
import { actionDeleteSelected, actionFinalize } from "../actions";
import { createRedoAction, createUndoAction } from "../actions/actionHistory";
import { ActionManager } from "../actions/manager";
import { actions } from "../actions/register"; import { actions } from "../actions/register";
import { ActionResult } from "../actions/types"; import { ActionResult } from "../actions/types";
import { getDefaultAppState } from "../appState";
import { t, getLanguage } from "../i18n";
import {
copyToClipboard,
parseClipboard,
probablySupportsClipboardBlob,
probablySupportsClipboardWriteText,
} from "../clipboard";
import { normalizeScroll } from "../scene";
import { getCenter, getDistance } from "../gesture";
import { createUndoAction, createRedoAction } from "../actions/actionHistory";
import {
CURSOR_TYPE,
ELEMENT_SHIFT_TRANSLATE_AMOUNT,
ELEMENT_TRANSLATE_AMOUNT,
POINTER_BUTTON,
DRAGGING_THRESHOLD,
TEXT_TO_CENTER_SNAP_THRESHOLD,
LINE_CONFIRM_THRESHOLD,
EVENT,
ENV,
CANVAS_ONLY_ACTIONS,
DEFAULT_VERTICAL_ALIGN,
GRID_SIZE,
MIME_TYPES,
TAP_TWICE_TIMEOUT,
TOUCH_CTX_MENU_TIMEOUT,
} from "../constants";
import LayerUI from "./LayerUI";
import { ScrollBars, SceneState } from "../scene/types";
import { mutateElement } from "../element/mutateElement";
import { invalidateShapeForElement } from "../renderer/renderElement";
import {
isLinearElement,
isLinearElementType,
isBindingElement,
isBindingElementType,
} from "../element/typeChecks";
import { actionFinalize, actionDeleteSelected } from "../actions";
import { LinearElementEditor } from "../element/linearElementEditor";
import {
getSelectedGroupIds,
isSelectedViaGroup,
selectGroupsForSelectedElements,
isElementInGroup,
getSelectedGroupIdForElement,
getElementsInGroup,
editGroupForSelectedElement,
} from "../groups";
import { Library } from "../data/library";
import Scene from "../scene/Scene";
import {
getHoveredElementForBinding,
maybeBindLinearElement,
getEligibleElementsForBinding,
bindOrUnbindSelectedElements,
unbindLinearElements,
fixBindingsAfterDuplication,
fixBindingsAfterDeletion,
isLinearElementSimpleAndAlreadyBound,
isBindingEnabled,
updateBoundElements,
shouldEnableBindingForPointerEvent,
} from "../element/binding";
import { MaybeTransformHandleType } from "../element/transformHandles";
import { deepCopyElement } from "../element/newElement";
import { renderSpreadsheet } from "../charts";
import { isValidLibrary } from "../data/json";
import { getNewZoom } from "../scene/zoom";
import { restore } from "../data/restore";
import { import {
EVENT_DIALOG, EVENT_DIALOG,
EVENT_LIBRARY, EVENT_LIBRARY,
EVENT_SHAPE, EVENT_SHAPE,
trackEvent, trackEvent,
} from "../analytics"; } from "../analytics";
import { getDefaultAppState } from "../appState";
import {
copyToClipboard,
parseClipboard,
probablySupportsClipboardBlob,
probablySupportsClipboardWriteText,
} from "../clipboard";
import {
APP_NAME,
CANVAS_ONLY_ACTIONS,
CURSOR_TYPE,
DEFAULT_VERTICAL_ALIGN,
DRAGGING_THRESHOLD,
ELEMENT_SHIFT_TRANSLATE_AMOUNT,
ELEMENT_TRANSLATE_AMOUNT,
ENV,
EVENT,
GRID_SIZE,
LINE_CONFIRM_THRESHOLD,
MIME_TYPES,
POINTER_BUTTON,
TAP_TWICE_TIMEOUT,
TEXT_TO_CENTER_SNAP_THRESHOLD,
TOUCH_CTX_MENU_TIMEOUT,
} from "../constants";
import { exportCanvas, loadFromBlob } from "../data";
import { isValidLibrary } from "../data/json";
import { Library } from "../data/library";
import { restore } from "../data/restore";
import {
dragNewElement,
dragSelectedElements,
duplicateElement,
getCommonBounds,
getCursorForResizingElement,
getDragOffsetXY,
getElementWithTransformHandleType,
getNonDeletedElements,
getNormalizedDimensions,
getPerfectElementSize,
getResizeArrowDirection,
getResizeOffsetXY,
getTransformHandleTypeFromCoords,
hitTest,
isHittingElementBoundingBoxWithoutHittingElement,
isInvisiblySmallElement,
isNonDeletedElement,
isTextElement,
newElement,
newLinearElement,
newTextElement,
textWysiwyg,
transformElements,
updateTextElement,
} from "../element";
import {
bindOrUnbindSelectedElements,
fixBindingsAfterDeletion,
fixBindingsAfterDuplication,
getEligibleElementsForBinding,
getHoveredElementForBinding,
isBindingEnabled,
isLinearElementSimpleAndAlreadyBound,
maybeBindLinearElement,
shouldEnableBindingForPointerEvent,
unbindLinearElements,
updateBoundElements,
} from "../element/binding";
import { LinearElementEditor } from "../element/linearElementEditor";
import { mutateElement } from "../element/mutateElement";
import { deepCopyElement } from "../element/newElement";
import { MaybeTransformHandleType } from "../element/transformHandles";
import {
isBindingElement,
isBindingElementType,
isLinearElement,
isLinearElementType,
} from "../element/typeChecks";
import {
ExcalidrawBindableElement,
ExcalidrawElement,
ExcalidrawGenericElement,
ExcalidrawLinearElement,
ExcalidrawTextElement,
NonDeleted,
} from "../element/types";
import { getCenter, getDistance } from "../gesture";
import {
editGroupForSelectedElement,
getElementsInGroup,
getSelectedGroupIdForElement,
getSelectedGroupIds,
isElementInGroup,
isSelectedViaGroup,
selectGroupsForSelectedElements,
} from "../groups";
import { createHistory, SceneHistory } from "../history";
import { t, getLanguage, setLanguage, languages, defaultLang } from "../i18n";
import {
CODES,
getResizeCenterPointKey,
getResizeWithSidesSameLengthKey,
getRotateWithDiscreteAngleKey,
isArrowKey,
KEYS,
} from "../keys";
import { distance2d, getGridPoint, isPathALoop } from "../math";
import { renderScene } from "../renderer";
import { invalidateShapeForElement } from "../renderer/renderElement";
import {
calculateScrollCenter,
getElementContainingPosition,
getElementsAtPosition,
getElementsWithinSelection,
getNormalizedZoom,
getSelectedElements,
isOverScrollBars,
isSomeElementSelected,
normalizeScroll,
} from "../scene";
import Scene from "../scene/Scene";
import { SceneState, ScrollBars } from "../scene/types";
import { getNewZoom } from "../scene/zoom";
import { findShapeByKey } from "../shapes";
import {
AppState,
ExcalidrawProps,
Gesture,
GestureEvent,
SceneData,
} from "../types";
import {
debounce,
distance,
getNewSceneName,
isInputLike,
isToolIcon,
isWritableElement,
resetCursor,
ResolvablePromise,
resolvablePromise,
sceneCoordsToViewportCoords,
setCursorForShape,
tupleToCoors,
viewportCoordsToSceneCoords,
withBatchedUpdates,
} from "../utils";
import ContextMenu from "./ContextMenu";
import LayerUI from "./LayerUI";
import { Stats } from "./Stats"; import { Stats } from "./Stats";
const { history } = createHistory(); const { history } = createHistory();
@@ -294,6 +282,7 @@ class App extends React.Component<ExcalidrawProps, AppState> {
} = props; } = props;
this.state = { this.state = {
...defaultAppState, ...defaultAppState,
name: getNewSceneName(),
isLoading: true, isLoading: true,
width, width,
height, height,
@@ -345,12 +334,15 @@ class App extends React.Component<ExcalidrawProps, AppState> {
offsetLeft, offsetLeft,
} = this.state; } = this.state;
const { onCollabButtonClick } = this.props; const { onCollabButtonClick, onExportToBackend, renderFooter } = this.props;
const canvasScale = window.devicePixelRatio; const canvasScale = window.devicePixelRatio;
const canvasWidth = canvasDOMWidth * canvasScale; const canvasWidth = canvasDOMWidth * canvasScale;
const canvasHeight = canvasDOMHeight * canvasScale; const canvasHeight = canvasDOMHeight * canvasScale;
const DEFAULT_PASTE_X = canvasDOMWidth / 2;
const DEFAULT_PASTE_Y = canvasDOMHeight / 2;
return ( return (
<div <div
className="excalidraw" className="excalidraw"
@@ -370,13 +362,19 @@ class App extends React.Component<ExcalidrawProps, AppState> {
elements={this.scene.getElements()} elements={this.scene.getElements()}
onCollabButtonClick={onCollabButtonClick} onCollabButtonClick={onCollabButtonClick}
onLockToggle={this.toggleLock} onLockToggle={this.toggleLock}
onInsertShape={(elements) => onInsertElements={(elements) =>
this.addElementsFromPasteOrLibrary(elements) this.addElementsFromPasteOrLibrary(
elements,
DEFAULT_PASTE_X,
DEFAULT_PASTE_Y,
)
} }
zenModeEnabled={zenModeEnabled} zenModeEnabled={zenModeEnabled}
toggleZenMode={this.toggleZenMode} toggleZenMode={this.toggleZenMode}
lng={getLanguage().lng} langCode={getLanguage().code}
isCollaborating={this.props.isCollaborating || false} isCollaborating={this.props.isCollaborating || false}
onExportToBackend={onExportToBackend}
renderCustomFooter={renderFooter}
/> />
{this.state.showStats && ( {this.state.showStats && (
<Stats <Stats
@@ -494,7 +492,7 @@ class App extends React.Component<ExcalidrawProps, AppState> {
}; };
private importLibraryFromUrl = async (url: string) => { private importLibraryFromUrl = async (url: string) => {
window.history.replaceState({}, "Excalidraw", window.location.origin); window.history.replaceState({}, APP_NAME, window.location.origin);
try { try {
const request = await fetch(url); const request = await fetch(url);
const blob = await request.blob(); const blob = await request.blob();
@@ -532,6 +530,7 @@ class App extends React.Component<ExcalidrawProps, AppState> {
this.scene.replaceAllElements([]); this.scene.replaceAllElements([]);
this.setState((state) => ({ this.setState((state) => ({
...getDefaultAppState(), ...getDefaultAppState(),
name: getNewSceneName(),
isLoading: opts?.resetLoadingState ? false : state.isLoading, isLoading: opts?.resetLoadingState ? false : state.isLoading,
appearance: this.state.appearance, appearance: this.state.appearance,
})); }));
@@ -586,6 +585,8 @@ class App extends React.Component<ExcalidrawProps, AppState> {
scene.elements, scene.elements,
{ {
...scene.appState, ...scene.appState,
width: this.state.width,
height: this.state.height,
offsetTop: this.state.offsetTop, offsetTop: this.state.offsetTop,
offsetLeft: this.state.offsetLeft, offsetLeft: this.state.offsetLeft,
}, },
@@ -741,6 +742,10 @@ class App extends React.Component<ExcalidrawProps, AppState> {
} }
componentDidUpdate(prevProps: ExcalidrawProps, prevState: AppState) { componentDidUpdate(prevProps: ExcalidrawProps, prevState: AppState) {
if (prevProps.langCode !== this.props.langCode) {
this.updateLanguage();
}
if ( if (
prevProps.width !== this.props.width || prevProps.width !== this.props.width ||
prevProps.height !== this.props.height || prevProps.height !== this.props.height ||
@@ -864,7 +869,16 @@ class App extends React.Component<ExcalidrawProps, AppState> {
history.record(this.state, this.scene.getElementsIncludingDeleted()); history.record(this.state, this.scene.getElementsIncludingDeleted());
this.props.onChange?.(this.scene.getElementsIncludingDeleted(), this.state); // Do not notify consumers if we're still loading the scene. Among other
// potential issues, this fixes a case where the tab isn't focused during
// init, which would trigger onChange with empty elements, which would then
// override whatever is in localStorage currently.
if (!this.state.isLoading) {
this.props.onChange?.(
this.scene.getElementsIncludingDeleted(),
this.state,
);
}
} }
// Copy/paste // Copy/paste
@@ -993,9 +1007,12 @@ class App extends React.Component<ExcalidrawProps, AppState> {
if (data.errorMessage) { if (data.errorMessage) {
this.setState({ errorMessage: data.errorMessage }); this.setState({ errorMessage: data.errorMessage });
} else if (data.spreadsheet) { } else if (data.spreadsheet) {
this.addElementsFromPasteOrLibrary( this.setState({
renderSpreadsheet(data.spreadsheet, cursorX, cursorY), pasteDialog: {
); data: data.spreadsheet,
shown: true,
},
});
} else if (data.elements) { } else if (data.elements) {
this.addElementsFromPasteOrLibrary(data.elements); this.addElementsFromPasteOrLibrary(data.elements);
} else if (data.text) { } else if (data.text) {
@@ -1416,13 +1433,27 @@ class App extends React.Component<ExcalidrawProps, AppState> {
private onGestureChange = withBatchedUpdates((event: GestureEvent) => { private onGestureChange = withBatchedUpdates((event: GestureEvent) => {
event.preventDefault(); event.preventDefault();
this.setState(({ zoom }) => ({
zoom: getNewZoom( // onGestureChange only has zoom factor but not the center.
getNormalizedZoom(gesture.initialScale! * event.scale), // If we're on iPad or iPhone, then we recognize multi-touch and will
zoom, // zoom in at the right location on the touchMove handler already.
{ x: cursorX, y: cursorY }, // On Macbook, we don't have those events so will zoom in at the
), // current location instead.
})); if (gesture.pointers.size === 2) {
return;
}
const initialScale = gesture.initialScale;
if (initialScale) {
this.setState(({ zoom, offsetLeft, offsetTop }) => ({
zoom: getNewZoom(
getNormalizedZoom(initialScale * event.scale),
zoom,
{ left: offsetLeft, top: offsetTop },
{ x: cursorX, y: cursorY },
),
}));
}
}); });
private onGestureEnd = withBatchedUpdates((event: GestureEvent) => { private onGestureEnd = withBatchedUpdates((event: GestureEvent) => {
@@ -1734,21 +1765,28 @@ class App extends React.Component<ExcalidrawProps, AppState> {
}); });
} }
if (gesture.pointers.size === 2) { const initialScale = gesture.initialScale;
if (
gesture.pointers.size === 2 &&
gesture.lastCenter &&
initialScale &&
gesture.initialDistance
) {
const center = getCenter(gesture.pointers); const center = getCenter(gesture.pointers);
const deltaX = center.x - gesture.lastCenter!.x; const deltaX = center.x - gesture.lastCenter.x;
const deltaY = center.y - gesture.lastCenter!.y; const deltaY = center.y - gesture.lastCenter.y;
gesture.lastCenter = center; gesture.lastCenter = center;
const distance = getDistance(Array.from(gesture.pointers.values())); const distance = getDistance(Array.from(gesture.pointers.values()));
const scaleFactor = distance / gesture.initialDistance!; const scaleFactor = distance / gesture.initialDistance;
this.setState(({ zoom, scrollX, scrollY }) => ({ this.setState(({ zoom, scrollX, scrollY, offsetLeft, offsetTop }) => ({
scrollX: normalizeScroll(scrollX + deltaX / zoom.value), scrollX: normalizeScroll(scrollX + deltaX / zoom.value),
scrollY: normalizeScroll(scrollY + deltaY / zoom.value), scrollY: normalizeScroll(scrollY + deltaY / zoom.value),
zoom: getNewZoom( zoom: getNewZoom(
getNormalizedZoom(gesture.initialScale! * scaleFactor), getNormalizedZoom(initialScale * scaleFactor),
zoom, zoom,
{ left: offsetLeft, top: offsetTop },
center, center,
), ),
shouldCacheIgnoreZoom: true, shouldCacheIgnoreZoom: true,
@@ -2430,8 +2468,7 @@ class App extends React.Component<ExcalidrawProps, AppState> {
// otherwise, it will trigger selection based on current // otherwise, it will trigger selection based on current
// state of the box // state of the box
if (!this.state.selectedElementIds[hitElement.id]) { if (!this.state.selectedElementIds[hitElement.id]) {
// if we are currently editing a group, treat all selections outside of the group // if we are currently editing a group, exiting editing mode and deselect the group.
// as exiting editing mode.
if ( if (
this.state.editingGroupId && this.state.editingGroupId &&
!isElementInGroup(hitElement, this.state.editingGroupId) !isElementInGroup(hitElement, this.state.editingGroupId)
@@ -2441,7 +2478,6 @@ class App extends React.Component<ExcalidrawProps, AppState> {
selectedGroupIds: {}, selectedGroupIds: {},
editingGroupId: null, editingGroupId: null,
}); });
return true;
} }
// Add hit element to selection. At this point if we're not holding // Add hit element to selection. At this point if we're not holding
@@ -2579,9 +2615,9 @@ class App extends React.Component<ExcalidrawProps, AppState> {
); );
/* If arrow is pre-arrowheads, it will have undefined for both start and end arrowheads. /* If arrow is pre-arrowheads, it will have undefined for both start and end arrowheads.
If so, we want it to be null for start and "arrow" for end. If the linear item is not If so, we want it to be null for start and "arrow" for end. If the linear item is not
an arrow, we want it to be null for both. Otherwise, we want it to use the an arrow, we want it to be null for both. Otherwise, we want it to use the
values from appState. */ values from appState. */
const { currentItemStartArrowhead, currentItemEndArrowhead } = this.state; const { currentItemStartArrowhead, currentItemEndArrowhead } = this.state;
const [startArrowhead, endArrowhead] = const [startArrowhead, endArrowhead] =
@@ -3612,13 +3648,15 @@ class App extends React.Component<ExcalidrawProps, AppState> {
CANVAS_ONLY_ACTIONS.includes(action.name), CANVAS_ONLY_ACTIONS.includes(action.name),
), ),
{ {
shortcutName: "toggleGridMode", checked: this.state.gridSize !== null,
label: t("labels.toggleGridMode"), shortcutName: "gridMode",
label: t("labels.gridMode"),
action: this.toggleGridMode, action: this.toggleGridMode,
}, },
{ {
shortcutName: "toggleStats", checked: this.state.showStats,
label: t("labels.toggleStats"), shortcutName: "stats",
label: t("stats.title"),
action: this.toggleStats, action: this.toggleStats,
}, },
], ],
@@ -3695,11 +3733,16 @@ class App extends React.Component<ExcalidrawProps, AppState> {
}, 1000); }, 1000);
} }
this.setState(({ zoom }) => ({ this.setState(({ zoom, offsetLeft, offsetTop }) => ({
zoom: getNewZoom(getNormalizedZoom(zoom.value - delta / 100), zoom, { zoom: getNewZoom(
x: cursorX, getNormalizedZoom(zoom.value - delta / 100),
y: cursorY, zoom,
}), { left: offsetLeft, top: offsetTop },
{
x: cursorX,
y: cursorY,
},
),
selectedElementIds: {}, selectedElementIds: {},
previousSelectedElementIds: previousSelectedElementIds:
Object.keys(selectedElementIds).length !== 0 Object.keys(selectedElementIds).length !== 0
@@ -3814,6 +3857,14 @@ class App extends React.Component<ExcalidrawProps, AppState> {
offsetTop: typeof offsets?.offsetTop === "number" ? offsets.offsetTop : 0, offsetTop: typeof offsets?.offsetTop === "number" ? offsets.offsetTop : 0,
}; };
} }
private async updateLanguage() {
const currentLang =
languages.find((lang) => lang.code === this.props.langCode) ||
defaultLang;
await setLanguage(currentLang);
this.setAppState({});
}
} }
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
+1 -1
View File
@@ -218,7 +218,7 @@
left: 2px; left: 2px;
} }
@media #{$media-query} { @media #{$is-mobile-query} {
display: none; display: none;
} }
} }
+39 -3
View File
@@ -1,4 +1,4 @@
@import "open-color/open-color.scss"; @import "../css/_variables";
.excalidraw { .excalidraw {
.context-menu { .context-menu {
@@ -32,11 +32,26 @@
display: grid; display: grid;
grid-template-columns: 1fr 0.2fr; grid-template-columns: 1fr 0.2fr;
div:nth-child(1) { align-items: center;
&.checkmark::before {
position: absolute;
left: 6px;
margin-bottom: 1px;
content: "\2713";
}
&.dangerous {
.context-menu-option__label {
color: $oc-red-7;
}
}
.context-menu-option__label {
justify-self: start; justify-self: start;
margin-inline-end: 20px; margin-inline-end: 20px;
} }
div:nth-child(2) { .context-menu-option__shortcut {
justify-self: end; justify-self: end;
opacity: 0.6; opacity: 0.6;
font-size: 0.7rem; font-size: 0.7rem;
@@ -46,9 +61,30 @@
.context-menu-option:hover { .context-menu-option:hover {
color: var(--popup-background-color); color: var(--popup-background-color);
background-color: var(--select-highlight-color); background-color: var(--select-highlight-color);
&.dangerous {
.context-menu-option__label {
color: var(--popup-background-color);
}
background-color: $oc-red-6;
}
} }
.context-menu-option:focus { .context-menu-option:focus {
z-index: 1; z-index: 1;
} }
@media #{$is-mobile-query} {
.context-menu-option {
display: block;
.context-menu-option__label {
margin-inline-end: 0;
}
.context-menu-option__shortcut {
display: none;
}
}
}
} }
+10 -5
View File
@@ -10,6 +10,7 @@ import {
} from "../actions/shortcuts"; } from "../actions/shortcuts";
type ContextMenuOption = { type ContextMenuOption = {
checked?: boolean;
shortcutName: ShortcutName; shortcutName: ShortcutName;
label: string; label: string;
action(): void; action(): void;
@@ -26,7 +27,6 @@ const ContextMenu = ({ options, onCloseRequest, top, left }: Props) => {
const isDarkTheme = !!document const isDarkTheme = !!document
.querySelector(".excalidraw") .querySelector(".excalidraw")
?.classList.contains("Appearance_dark"); ?.classList.contains("Appearance_dark");
return ( return (
<div <div
className={clsx("excalidraw", { className={clsx("excalidraw", {
@@ -43,11 +43,16 @@ const ContextMenu = ({ options, onCloseRequest, top, left }: Props) => {
className="context-menu" className="context-menu"
onContextMenu={(event) => event.preventDefault()} onContextMenu={(event) => event.preventDefault()}
> >
{options.map(({ action, shortcutName, label }, idx) => ( {options.map(({ action, checked, shortcutName, label }, idx) => (
<li data-testid={shortcutName} key={idx} onClick={onCloseRequest}> <li data-testid={shortcutName} key={idx} onClick={onCloseRequest}>
<button className="context-menu-option" onClick={action}> <button
<div>{label}</div> className={`context-menu-option
<div> ${shortcutName === "delete" ? "dangerous" : ""}
${checked ? "checkmark" : ""}`}
onClick={action}
>
<div className="context-menu-option__label">{label}</div>
<div className="context-menu-option__shortcut">
{shortcutName {shortcutName
? getShortcutFromShortcutName(shortcutName) ? getShortcutFromShortcutName(shortcutName)
: ""} : ""}
+8 -2
View File
@@ -14,7 +14,9 @@ export const DarkModeToggle = (props: {
return ( return (
<label <label
className={`ToolIcon ToolIcon_type_floating ToolIcon_size_M`} className={`ToolIcon ToolIcon_type_floating ToolIcon_size_M`}
title={t("buttons.toggleDarkMode")} title={
props.value === "dark" ? t("buttons.lightMode") : t("buttons.darkMode")
}
> >
<input <input
className="ToolIcon_type_checkbox ToolIcon_toggle_opaque" className="ToolIcon_type_checkbox ToolIcon_toggle_opaque"
@@ -23,7 +25,11 @@ export const DarkModeToggle = (props: {
props.onChange(event.target.checked ? "dark" : "light") props.onChange(event.target.checked ? "dark" : "light")
} }
checked={props.value === "dark"} checked={props.value === "dark"}
aria-label={t("buttons.toggleDarkMode")} aria-label={
props.value === "dark"
? t("buttons.lightMode")
: t("buttons.darkMode")
}
/> />
<div className="ToolIcon__icon"> <div className="ToolIcon__icon">
{props.value === "light" ? ICONS.MOON : ICONS.SUN} {props.value === "light" ? ICONS.MOON : ICONS.SUN}
+9 -7
View File
@@ -7,6 +7,9 @@
margin-top: 0; margin-top: 0;
grid-template-columns: 1fr calc(var(--space-factor) * 7); grid-template-columns: 1fr calc(var(--space-factor) * 7);
grid-gap: var(--metric); grid-gap: var(--metric);
padding: calc(var(--space-factor) * 2);
text-align: center;
font-variant: small-caps;
} }
.Dialog__titleContent { .Dialog__titleContent {
@@ -18,7 +21,11 @@
margin: 0; margin: 0;
} }
@media #{$media-query} { .Dialog__content {
padding: 0 16px 16px;
}
@media #{$is-mobile-query} {
.Dialog { .Dialog {
--metric: calc(var(--space-factor) * 4); --metric: calc(var(--space-factor) * 4);
--inset-left: #{"max(var(--metric), var(--sal))"}; --inset-left: #{"max(var(--metric), var(--sal))"};
@@ -30,13 +37,8 @@
var(--space-factor) * 7 var(--space-factor) * 7
); );
position: sticky; position: sticky;
top: calc(-1 * var(--metric)); top: 0;
margin: calc(-1 * var(--inset-right));
margin-top: calc(-1 * var(--metric));
margin-bottom: var(--metric);
padding: calc(var(--space-factor) * 2); padding: calc(var(--space-factor) * 2);
padding-left: var(--inset-left);
padding-right: var(--inset-right);
background: var(--bg-color-island); background: var(--bg-color-island);
font-size: 1.25em; font-size: 1.25em;
+13 -13
View File
@@ -1,13 +1,12 @@
import React, { useCallback, useEffect, useState } from "react";
import clsx from "clsx"; import clsx from "clsx";
import { Modal } from "./Modal"; import React, { useCallback, useEffect, useState } from "react";
import { Island } from "./Island";
import { t } from "../i18n"; import { t } from "../i18n";
import useIsMobile from "../is-mobile"; import useIsMobile from "../is-mobile";
import { back, close } from "./icons";
import { KEYS } from "../keys"; import { KEYS } from "../keys";
import "./Dialog.scss"; import "./Dialog.scss";
import { back, close } from "./icons";
import { Island } from "./Island";
import { Modal } from "./Modal";
const useRefState = <T,>() => { const useRefState = <T,>() => {
const [refValue, setRefValue] = useState<T | null>(null); const [refValue, setRefValue] = useState<T | null>(null);
@@ -20,9 +19,10 @@ const useRefState = <T,>() => {
export const Dialog = (props: { export const Dialog = (props: {
children: React.ReactNode; children: React.ReactNode;
className?: string; className?: string;
maxWidth?: number; small?: boolean;
onCloseRequest(): void; onCloseRequest(): void;
title: React.ReactNode; title: React.ReactNode;
autofocus?: boolean;
}) => { }) => {
const [islandNode, setIslandNode] = useRefState<HTMLDivElement>(); const [islandNode, setIslandNode] = useRefState<HTMLDivElement>();
@@ -33,7 +33,7 @@ export const Dialog = (props: {
const focusableElements = queryFocusableElements(islandNode); const focusableElements = queryFocusableElements(islandNode);
if (focusableElements.length > 0) { if (focusableElements.length > 0 && props.autofocus !== false) {
// If there's an element other than close, focus it. // If there's an element other than close, focus it.
(focusableElements[1] || focusableElements[0]).focus(); (focusableElements[1] || focusableElements[0]).focus();
} }
@@ -62,7 +62,7 @@ export const Dialog = (props: {
islandNode.addEventListener("keydown", handleKeyDown); islandNode.addEventListener("keydown", handleKeyDown);
return () => islandNode.removeEventListener("keydown", handleKeyDown); return () => islandNode.removeEventListener("keydown", handleKeyDown);
}, [islandNode]); }, [islandNode, props.autofocus]);
const queryFocusableElements = (node: HTMLElement) => { const queryFocusableElements = (node: HTMLElement) => {
const focusableElements = node.querySelectorAll<HTMLElement>( const focusableElements = node.querySelectorAll<HTMLElement>(
@@ -76,11 +76,11 @@ export const Dialog = (props: {
<Modal <Modal
className={clsx("Dialog", props.className)} className={clsx("Dialog", props.className)}
labelledBy="dialog-title" labelledBy="dialog-title"
maxWidth={props.maxWidth} maxWidth={props.small ? 550 : 800}
onCloseRequest={props.onCloseRequest} onCloseRequest={props.onCloseRequest}
> >
<Island padding={4} ref={setIslandNode}> <Island ref={setIslandNode}>
<h2 id="dialog-title" className="Dialog__title"> <h3 id="dialog-title" className="Dialog__title">
<span className="Dialog__titleContent">{props.title}</span> <span className="Dialog__titleContent">{props.title}</span>
<button <button
className="Modal__close" className="Modal__close"
@@ -89,8 +89,8 @@ export const Dialog = (props: {
> >
{useIsMobile() ? back : close} {useIsMobile() ? back : close}
</button> </button>
</h2> </h3>
{props.children} <div className="Dialog__content">{props.children}</div>
</Island> </Island>
</Modal> </Modal>
); );
+9 -2
View File
@@ -24,11 +24,18 @@ export const ErrorDialog = ({
<> <>
{modalIsShown && ( {modalIsShown && (
<Dialog <Dialog
maxWidth={500} small
onCloseRequest={handleClose} onCloseRequest={handleClose}
title={t("errorDialog.title")} title={t("errorDialog.title")}
> >
<div>{message}</div> <div>
{message.split("\n").map((line) => (
<>
{line}
<br />
</>
))}
</div>
</Dialog> </Dialog>
)} )}
</> </>
+1 -3
View File
@@ -37,7 +37,7 @@
} }
} }
@media (max-width: 550px) { @media #{$is-mobile-query} {
.ExportDialog { .ExportDialog {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
@@ -51,9 +51,7 @@
.ExportDialog__actions > * { .ExportDialog__actions > * {
margin-bottom: calc(var(--space-factor) * 3); margin-bottom: calc(var(--space-factor) * 3);
} }
}
@media #{$media-query} {
.ExportDialog__preview canvas { .ExportDialog__preview canvas {
max-height: 30vh; max-height: 30vh;
} }
+12 -14
View File
@@ -67,7 +67,7 @@ const ExportModal = ({
onExportToPng: ExportCB; onExportToPng: ExportCB;
onExportToSvg: ExportCB; onExportToSvg: ExportCB;
onExportToClipboard: ExportCB; onExportToClipboard: ExportCB;
onExportToBackend: ExportCB; onExportToBackend?: ExportCB;
onCloseRequest: () => void; onCloseRequest: () => void;
}) => { }) => {
const someElementIsSelected = isSomeElementSelected(elements, appState); const someElementIsSelected = isSomeElementSelected(elements, appState);
@@ -155,13 +155,15 @@ const ExportModal = ({
onClick={() => onExportToClipboard(exportedElements, scale)} onClick={() => onExportToClipboard(exportedElements, scale)}
/> />
)} )}
<ToolButton {onExportToBackend && (
type="button" <ToolButton
icon={link} type="button"
title={t("buttons.getShareableLink")} icon={link}
aria-label={t("buttons.getShareableLink")} title={t("buttons.getShareableLink")}
onClick={() => onExportToBackend(exportedElements)} aria-label={t("buttons.getShareableLink")}
/> onClick={() => onExportToBackend(exportedElements)}
/>
)}
</Stack.Row> </Stack.Row>
<div className="ExportDialog__name"> <div className="ExportDialog__name">
{actionManager.renderAction("changeProjectName")} {actionManager.renderAction("changeProjectName")}
@@ -235,7 +237,7 @@ export const ExportDialog = ({
onExportToPng: ExportCB; onExportToPng: ExportCB;
onExportToSvg: ExportCB; onExportToSvg: ExportCB;
onExportToClipboard: ExportCB; onExportToClipboard: ExportCB;
onExportToBackend: ExportCB; onExportToBackend?: ExportCB;
}) => { }) => {
const [modalIsShown, setModalIsShown] = useState(false); const [modalIsShown, setModalIsShown] = useState(false);
const triggerButton = useRef<HTMLButtonElement>(null); const triggerButton = useRef<HTMLButtonElement>(null);
@@ -260,11 +262,7 @@ export const ExportDialog = ({
ref={triggerButton} ref={triggerButton}
/> />
{modalIsShown && ( {modalIsShown && (
<Dialog <Dialog onCloseRequest={handleClose} title={t("buttons.export")}>
maxWidth={800}
onCloseRequest={handleClose}
title={t("buttons.export")}
>
<ExportModal <ExportModal
elements={elements} elements={elements}
appState={appState} appState={appState}
+5 -5
View File
@@ -1,5 +1,8 @@
@import "../css/_variables"; @import "../css/_variables";
// this is loosely based on the longest hint text
$wide-viewport-width: 1000px;
.excalidraw { .excalidraw {
.HintViewer { .HintViewer {
pointer-events: none; pointer-events: none;
@@ -16,12 +19,9 @@
color: $oc-gray-6; color: $oc-gray-6;
font-size: 0.8rem; font-size: 0.8rem;
@media (min-width: 1200px) { @media #{$is-mobile-query} {
white-space: pre;
}
@media #{$media-query} {
position: static; position: static;
padding-right: 2em;
} }
> span { > span {
+2 -2
View File
@@ -38,8 +38,8 @@ const getHints = ({ appState, elements }: Hint) => {
selectedElements.length === 1 selectedElements.length === 1
) { ) {
const targetElement = selectedElements[0]; const targetElement = selectedElements[0];
if (isLinearElement(targetElement) && targetElement.points.length > 2) { if (isLinearElement(targetElement) && targetElement.points.length === 2) {
return null; return t("hints.lockAngle");
} }
return t("hints.resize"); return t("hints.resize");
} }
+4 -1
View File
@@ -93,6 +93,9 @@
grid-auto-flow: column; grid-auto-flow: column;
grid-gap: 0.5rem; grid-gap: 0.5rem;
border-radius: 4px; border-radius: 4px;
:root[dir="rtl"] & {
padding: 0.4rem;
}
} }
.picker-keybinding { .picker-keybinding {
@@ -107,7 +110,7 @@
:root[dir="rtl"] & { :root[dir="rtl"] & {
left: 2px; left: 2px;
} }
@media #{$media-query} { @media #{$is-mobile-query} {
display: none; display: none;
} }
} }
+17 -6
View File
@@ -1,18 +1,29 @@
import React from "react"; import React from "react";
import { LoadingMessage } from "./LoadingMessage"; import { LoadingMessage } from "./LoadingMessage";
import { setLanguageFirstTime } from "../i18n"; import {
defaultLang,
Language,
languages,
setLanguageFirstTime,
} from "../i18n";
export class InitializeApp extends React.Component< interface Props {
any, langCode: Language["code"];
{ isLoading: boolean } }
> { interface State {
isLoading: boolean;
}
export class InitializeApp extends React.Component<Props, State> {
public state: { isLoading: boolean } = { public state: { isLoading: boolean } = {
isLoading: true, isLoading: true,
}; };
async componentDidMount() { async componentDidMount() {
await setLanguageFirstTime(); const currentLang =
languages.find((lang) => lang.code === this.props.langCode) ||
defaultLang;
await setLanguageFirstTime(currentLang);
this.setState({ this.setState({
isLoading: false, isLoading: false,
}); });
+1 -1
View File
@@ -4,7 +4,7 @@
background-color: var(--bg-color-island); background-color: var(--bg-color-island);
backdrop-filter: saturate(100%) blur(10px); backdrop-filter: saturate(100%) blur(10px);
box-shadow: var(--shadow-island); box-shadow: var(--shadow-island);
border-radius: var(--border-radius-m); border-radius: 4px;
padding: calc(var(--padding) * var(--space-factor)); padding: calc(var(--padding) * var(--space-factor));
position: relative; position: relative;
transition: box-shadow 0.5s ease-in-out; transition: box-shadow 0.5s ease-in-out;
+17 -63
View File
@@ -7,11 +7,23 @@
align-items: center; align-items: center;
justify-content: center; justify-content: center;
.browse-libraries { .layer-ui__library-header {
position: absolute; display: flex;
right: 12px; align-items: center;
top: 16px; width: 100%;
white-space: nowrap; margin: 2px 0;
button {
// 2px from the left to account for focus border of left-most button
margin: 0 2px;
}
a {
margin-left: auto;
// 17px for scrollbar (needed for overlay scrollbars on Big Sur?) + 1px extra
padding-right: 18px;
white-space: nowrap;
}
} }
} }
@@ -39,64 +51,6 @@
width: 1.2rem; width: 1.2rem;
height: 1.2rem; height: 1.2rem;
} }
&.tooltip .tooltip-text {
visibility: hidden;
width: 20rem;
bottom: calc(50% + 0.8rem + 6px);
:root[dir="ltr"] & {
left: -5px;
}
:root[dir="rtl"] & {
right: -5px;
}
background-color: $oc-black;
color: $oc-white;
text-align: center;
border-radius: 6px;
padding: 5px;
position: absolute;
z-index: 10;
font-size: 13px;
line-height: 1.5;
white-space: pre-wrap;
&::after {
--size: 6px;
content: "";
border: var(--size) solid transparent;
border-top-color: $oc-black;
position: absolute;
bottom: calc(-2 * var(--size));
:root[dir="ltr"] & {
left: calc(5px + var(--size) / 2);
}
:root[dir="rtl"] & {
right: calc(5px + var(--size) / 2);
}
}
}
// the following 3 rules ensure that the tooltip doesn't show (nor affect
// the cursor) when you drag over when you draw on canvas, but at the same
// time it still works when clicking on the link/shield
body:active &.tooltip:not(:hover) {
pointer-events: none;
}
body:not(:active) &.tooltip:hover .tooltip-text {
visibility: visible;
}
.tooltip-text:hover {
visibility: visible;
}
} }
&__github-corner { &__github-corner {
+100 -103
View File
@@ -19,8 +19,7 @@ import { FixedSideContainer } from "./FixedSideContainer";
import { UserList } from "./UserList"; import { UserList } from "./UserList";
import { LockIcon } from "./LockIcon"; import { LockIcon } from "./LockIcon";
import { ExportDialog, ExportCB } from "./ExportDialog"; import { ExportDialog, ExportCB } from "./ExportDialog";
import { LanguageList } from "./LanguageList"; import { Language, t } from "../i18n";
import { t, languages, setLanguage } from "../i18n";
import { HintViewer } from "./HintViewer"; import { HintViewer } from "./HintViewer";
import useIsMobile from "../is-mobile"; import useIsMobile from "../is-mobile";
@@ -51,6 +50,7 @@ import {
EVENT_LIBRARY, EVENT_LIBRARY,
trackEvent, trackEvent,
} from "../analytics"; } from "../analytics";
import { PasteChartDialog } from "./PasteChartDialog";
interface LayerUIProps { interface LayerUIProps {
actionManager: ActionManager; actionManager: ActionManager;
@@ -60,11 +60,17 @@ interface LayerUIProps {
elements: readonly NonDeletedExcalidrawElement[]; elements: readonly NonDeletedExcalidrawElement[];
onCollabButtonClick?: () => void; onCollabButtonClick?: () => void;
onLockToggle: () => void; onLockToggle: () => void;
onInsertShape: (elements: LibraryItem) => void; onInsertElements: (elements: readonly NonDeletedExcalidrawElement[]) => void;
zenModeEnabled: boolean; zenModeEnabled: boolean;
toggleZenMode: () => void; toggleZenMode: () => void;
lng: string; langCode: Language["code"];
isCollaborating: boolean; isCollaborating: boolean;
onExportToBackend?: (
exportedElements: readonly NonDeletedExcalidrawElement[],
appState: AppState,
canvas: HTMLCanvasElement | null,
) => void;
renderCustomFooter?: (isMobile: boolean) => JSX.Element;
} }
const useOnClickOutside = ( const useOnClickOutside = (
@@ -118,9 +124,42 @@ const LibraryMenuItems = ({
let addedPendingElements = false; let addedPendingElements = false;
rows.push( rows.push(
<> <div className="layer-ui__library-header">
<ToolButton
key="import"
type="button"
title={t("buttons.load")}
aria-label={t("buttons.load")}
icon={load}
onClick={() => {
importLibraryFromJSON()
.then(() => {
// Maybe we should close and open the menu so that the items get updated.
// But for now we just close the menu.
setAppState({ isLibraryOpen: false });
})
.catch(muteFSAbortError)
.catch((error) => {
setAppState({ errorMessage: error.message });
});
}}
/>
<ToolButton
key="export"
type="button"
title={t("buttons.export")}
aria-label={t("buttons.export")}
icon={exportFile}
onClick={() => {
saveLibraryAsJSON()
.catch(muteFSAbortError)
.catch((error) => {
setAppState({ errorMessage: error.message });
});
}}
/>
<a <a
className="browse-libraries"
href="https://libraries.excalidraw.com" href="https://libraries.excalidraw.com"
target="_excalidraw_libraries" target="_excalidraw_libraries"
onClick={() => { onClick={() => {
@@ -129,48 +168,7 @@ const LibraryMenuItems = ({
> >
{t("labels.libraries")} {t("labels.libraries")}
</a> </a>
</div>,
<Stack.Row
align="center"
gap={1}
key={"actions"}
style={{ padding: "2px" }}
>
<ToolButton
key="import"
type="button"
title={t("buttons.load")}
aria-label={t("buttons.load")}
icon={load}
onClick={() => {
importLibraryFromJSON()
.then(() => {
// Maybe we should close and open the menu so that the items get updated.
// But for now we just close the menu.
setAppState({ isLibraryOpen: false });
})
.catch(muteFSAbortError)
.catch((error) => {
setAppState({ errorMessage: error.message });
});
}}
/>
<ToolButton
key="export"
type="button"
title={t("buttons.export")}
aria-label={t("buttons.export")}
icon={exportFile}
onClick={() => {
saveLibraryAsJSON()
.catch(muteFSAbortError)
.catch((error) => {
setAppState({ errorMessage: error.message });
});
}}
/>
</Stack.Row>
</>,
); );
for (let row = 0; row < numRows; row++) { for (let row = 0; row < numRows; row++) {
@@ -313,14 +311,15 @@ const LayerUI = ({
elements, elements,
onCollabButtonClick, onCollabButtonClick,
onLockToggle, onLockToggle,
onInsertShape, onInsertElements,
zenModeEnabled, zenModeEnabled,
toggleZenMode, toggleZenMode,
isCollaborating, isCollaborating,
onExportToBackend,
renderCustomFooter,
}: LayerUIProps) => { }: LayerUIProps) => {
const isMobile = useIsMobile(); const isMobile = useIsMobile();
// TODO: Extend tooltip component and use here.
const renderEncryptedIcon = () => ( const renderEncryptedIcon = () => (
<a <a
className={clsx("encrypted-icon tooltip zen-mode-visibility", { className={clsx("encrypted-icon tooltip zen-mode-visibility", {
@@ -333,10 +332,9 @@ const LayerUI = ({
trackEvent(EVENT_EXIT, "e2ee shield"); trackEvent(EVENT_EXIT, "e2ee shield");
}} }}
> >
<span className="tooltip-text" dir="auto"> <Tooltip label={t("encrypted.tooltip")} position="above" long={true}>
{t("encrypted.tooltip")} {shield}
</span> </Tooltip>
{shield}
</a> </a>
); );
@@ -360,6 +358,7 @@ const LayerUI = ({
}); });
} }
}; };
return ( return (
<ExportDialog <ExportDialog
elements={elements} elements={elements}
@@ -368,28 +367,14 @@ const LayerUI = ({
onExportToPng={createExporter("png")} onExportToPng={createExporter("png")}
onExportToSvg={createExporter("svg")} onExportToSvg={createExporter("svg")}
onExportToClipboard={createExporter("clipboard")} onExportToClipboard={createExporter("clipboard")}
onExportToBackend={async (exportedElements) => { onExportToBackend={
if (canvas) { onExportToBackend
try { ? (elements) => {
await exportCanvas( onExportToBackend &&
"backend", onExportToBackend(elements, appState, canvas);
exportedElements,
{
...appState,
selectedElementIds: {},
},
canvas,
appState,
);
} catch (error) {
if (error.name !== "AbortError") {
const { width, height } = canvas;
console.error(error, { width, height });
setAppState({ errorMessage: error.message });
} }
} : undefined
} }
}}
/> />
); );
}; };
@@ -465,7 +450,7 @@ const LayerUI = ({
<LibraryMenu <LibraryMenu
pendingElements={getSelectedElements(elements, appState)} pendingElements={getSelectedElements(elements, appState)}
onClickOutside={closeLibrary} onClickOutside={closeLibrary}
onInsertShape={onInsertShape} onInsertShape={onInsertElements}
onAddToLibrary={deselectItems} onAddToLibrary={deselectItems}
setAppState={setAppState} setAppState={setAppState}
/> />
@@ -567,14 +552,7 @@ const LayerUI = ({
"transition-right disable-pointerEvents": zenModeEnabled, "transition-right disable-pointerEvents": zenModeEnabled,
})} })}
> >
<LanguageList {renderCustomFooter?.(false)}
onChange={async (lng) => {
await setLanguage(lng);
setAppState({});
}}
languages={languages}
floating
/>
{actionManager.renderAction("toggleShortcuts")} {actionManager.renderAction("toggleShortcuts")}
</div> </div>
<button <button
@@ -601,21 +579,8 @@ const LayerUI = ({
</footer> </footer>
); );
return isMobile ? ( const dialogs = (
<MobileMenu <>
appState={appState}
elements={elements}
actionManager={actionManager}
libraryMenu={libraryMenu}
exportButton={renderExportDialog()}
setAppState={setAppState}
onCollabButtonClick={onCollabButtonClick}
onLockToggle={onLockToggle}
canvas={canvas}
isCollaborating={isCollaborating}
/>
) : (
<div className="layer-ui__wrapper">
{appState.isLoading && <LoadingMessage />} {appState.isLoading && <LoadingMessage />}
{appState.errorMessage && ( {appState.errorMessage && (
<ErrorDialog <ErrorDialog
@@ -628,6 +593,41 @@ const LayerUI = ({
onClose={() => setAppState({ showShortcutsDialog: false })} onClose={() => setAppState({ showShortcutsDialog: false })}
/> />
)} )}
{appState.pasteDialog.shown && (
<PasteChartDialog
setAppState={setAppState}
appState={appState}
onInsertChart={onInsertElements}
onClose={() =>
setAppState({
pasteDialog: { shown: false, data: null },
})
}
/>
)}
</>
);
return isMobile ? (
<>
{dialogs}
<MobileMenu
appState={appState}
elements={elements}
actionManager={actionManager}
libraryMenu={libraryMenu}
exportButton={renderExportDialog()}
setAppState={setAppState}
onCollabButtonClick={onCollabButtonClick}
onLockToggle={onLockToggle}
canvas={canvas}
isCollaborating={isCollaborating}
renderCustomFooter={renderCustomFooter}
/>
</>
) : (
<div className="layer-ui__wrapper">
{dialogs}
{renderFixedSideContainer()} {renderFixedSideContainer()}
{renderBottomAppMenu()} {renderBottomAppMenu()}
{ {
@@ -650,8 +650,6 @@ const LayerUI = ({
const areEqual = (prev: LayerUIProps, next: LayerUIProps) => { const areEqual = (prev: LayerUIProps, next: LayerUIProps) => {
const getNecessaryObj = (appState: AppState): Partial<AppState> => { const getNecessaryObj = (appState: AppState): Partial<AppState> => {
const { const {
cursorX,
cursorY,
suggestedBindings, suggestedBindings,
startBoundElement: boundElement, startBoundElement: boundElement,
...ret ...ret
@@ -662,9 +660,8 @@ const areEqual = (prev: LayerUIProps, next: LayerUIProps) => {
const nextAppState = getNecessaryObj(next.appState); const nextAppState = getNecessaryObj(next.appState);
const keys = Object.keys(prevAppState) as (keyof Partial<AppState>)[]; const keys = Object.keys(prevAppState) as (keyof Partial<AppState>)[];
return ( return (
prev.lng === next.lng && prev.langCode === next.langCode &&
prev.elements === next.elements && prev.elements === next.elements &&
keys.every((key) => prevAppState[key] === nextAppState[key]) keys.every((key) => prevAppState[key] === nextAppState[key])
); );
+6 -6
View File
@@ -1,13 +1,13 @@
import React, { useRef, useEffect, useState } from "react";
import clsx from "clsx"; import clsx from "clsx";
import { exportToSvg } from "../scene/export"; import oc from "open-color";
import React, { useEffect, useRef, useState } from "react";
import { close } from "../components/icons"; import { close } from "../components/icons";
import { MIME_TYPES } from "../constants";
import "./LibraryUnit.scss";
import { t } from "../i18n"; import { t } from "../i18n";
import useIsMobile from "../is-mobile"; import useIsMobile from "../is-mobile";
import { exportToSvg } from "../scene/export";
import { LibraryItem } from "../types"; import { LibraryItem } from "../types";
import { MIME_TYPES } from "../constants"; import "./LibraryUnit.scss";
// fa-plus // fa-plus
const PLUS_ICON = ( const PLUS_ICON = (
@@ -38,7 +38,7 @@ export const LibraryUnit = ({
} }
const svg = exportToSvg(elementsToRender, { const svg = exportToSvg(elementsToRender, {
exportBackground: false, exportBackground: false,
viewBackgroundColor: "#fff", viewBackgroundColor: oc.white,
shouldAddWatermark: false, shouldAddWatermark: false,
}); });
for (const child of ref.current!.children) { for (const child of ref.current!.children) {
+4 -13
View File
@@ -1,9 +1,8 @@
import React from "react"; import React from "react";
import { AppState } from "../types"; import { AppState } from "../types";
import { ActionManager } from "../actions/manager"; import { ActionManager } from "../actions/manager";
import { t, setLanguage } from "../i18n"; import { t } from "../i18n";
import Stack from "./Stack"; import Stack from "./Stack";
import { LanguageList } from "./LanguageList";
import { showSelectedShapeActions } from "../element"; import { showSelectedShapeActions } from "../element";
import { NonDeletedExcalidrawElement } from "../element/types"; import { NonDeletedExcalidrawElement } from "../element/types";
import { FixedSideContainer } from "./FixedSideContainer"; import { FixedSideContainer } from "./FixedSideContainer";
@@ -15,7 +14,6 @@ import { Section } from "./Section";
import CollabButton from "./CollabButton"; import CollabButton from "./CollabButton";
import { SCROLLBAR_WIDTH, SCROLLBAR_MARGIN } from "../scene/scrollbars"; import { SCROLLBAR_WIDTH, SCROLLBAR_MARGIN } from "../scene/scrollbars";
import { LockIcon } from "./LockIcon"; import { LockIcon } from "./LockIcon";
import { LoadingMessage } from "./LoadingMessage";
import { UserList } from "./UserList"; import { UserList } from "./UserList";
import { BackgroundPickerAndDarkModeToggle } from "./BackgroundPickerAndDarkModeToggle"; import { BackgroundPickerAndDarkModeToggle } from "./BackgroundPickerAndDarkModeToggle";
import { EVENT_ACTION, trackEvent } from "../analytics"; import { EVENT_ACTION, trackEvent } from "../analytics";
@@ -31,6 +29,7 @@ type MobileMenuProps = {
onLockToggle: () => void; onLockToggle: () => void;
canvas: HTMLCanvasElement | null; canvas: HTMLCanvasElement | null;
isCollaborating: boolean; isCollaborating: boolean;
renderCustomFooter?: (isMobile: boolean) => JSX.Element;
}; };
export const MobileMenu = ({ export const MobileMenu = ({
@@ -44,9 +43,9 @@ export const MobileMenu = ({
onLockToggle, onLockToggle,
canvas, canvas,
isCollaborating, isCollaborating,
renderCustomFooter,
}: MobileMenuProps) => ( }: MobileMenuProps) => (
<> <>
{appState.isLoading && <LoadingMessage />}
<FixedSideContainer side="top"> <FixedSideContainer side="top">
<Section heading="shapes"> <Section heading="shapes">
{(heading) => ( {(heading) => (
@@ -104,15 +103,7 @@ export const MobileMenu = ({
appState={appState} appState={appState}
setAppState={setAppState} setAppState={setAppState}
/> />
<fieldset> {renderCustomFooter?.(true)}
<legend>{t("labels.language")}</legend>
<LanguageList
onChange={async (lng) => {
await setLanguage(lng);
setAppState({});
}}
/>
</fieldset>
<fieldset> <fieldset>
<legend>{t("labels.collaborators")}</legend> <legend>{t("labels.collaborators")}</legend>
<UserList mobile> <UserList mobile>
+10 -8
View File
@@ -30,18 +30,26 @@
z-index: 2; z-index: 2;
width: 100%; width: 100%;
max-width: var(--max-width); max-width: var(--max-width);
max-height: 100%;
opacity: 0; opacity: 0;
transform: translateY(10px); transform: translateY(10px);
animation: Modal__content_fade-in 0.1s ease-out 0.05s forwards; animation: Modal__content_fade-in 0.1s ease-out 0.05s forwards;
position: relative; position: relative;
overflow-y: auto;
// for modals, reset blurry bg // for modals, reset blurry bg
background: var(--bg-color-island); background: var(--bg-color-island);
backdrop-filter: none; backdrop-filter: none;
@media #{$media-query} { border: 1px solid var(--dialog-border);
box-shadow: 0 2px 10px transparentize($oc-black, 0.75);
border-radius: 6px;
@media #{$is-mobile-query} {
max-width: 100%; max-width: 100%;
border: 0;
border-radius: 0;
} }
} }
@@ -68,13 +76,7 @@
} }
} }
.Modal__close--floating { @media #{$is-mobile-query} {
position: absolute;
right: calc(var(--space-factor) * 5);
top: calc(var(--space-factor) * 5);
}
@media #{$media-query} {
.Modal { .Modal {
padding: 0; padding: 0;
} }
+1 -5
View File
@@ -36,11 +36,7 @@ export const Modal = (props: {
<div className="Modal__background" onClick={props.onCloseRequest}></div> <div className="Modal__background" onClick={props.onCloseRequest}></div>
<div <div
className="Modal__content" className="Modal__content"
style={{ style={{ "--max-width": `${props.maxWidth}px` }}
"--max-width": `${props.maxWidth}px`,
maxHeight: "100%",
overflowY: "scroll",
}}
> >
{props.children} {props.children}
</div> </div>
+46
View File
@@ -0,0 +1,46 @@
@import "../css/_variables";
.excalidraw {
.PasteChartDialog {
@media #{$is-mobile-query} {
.Island {
display: flex;
flex-direction: column;
}
}
.container {
display: flex;
align-items: center;
justify-content: space-around;
flex-wrap: wrap;
@media #{$is-mobile-query} {
flex-direction: column;
justify-content: center;
}
}
.ChartPreview {
margin: 8px;
text-align: center;
width: 192px;
height: 128px;
border-radius: 2px;
padding: 1px;
border: 1px solid $oc-gray-4;
display: flex;
align-items: center;
justify-content: center;
background: transparent;
div {
display: inline-block;
}
svg {
max-height: 120px;
max-width: 186px;
}
&:hover {
padding: 0;
border: 2px solid $oc-blue-5;
}
}
}
}
+122
View File
@@ -0,0 +1,122 @@
import oc from "open-color";
import React, { useLayoutEffect, useRef, useState } from "react";
import { ChartElements, renderSpreadsheet, Spreadsheet } from "../charts";
import { ChartType } from "../element/types";
import { t } from "../i18n";
import { exportToSvg } from "../scene/export";
import { AppState, LibraryItem } from "../types";
import { Dialog } from "./Dialog";
import "./PasteChartDialog.scss";
type OnInsertChart = (chartType: ChartType, elements: ChartElements) => void;
const ChartPreviewBtn = (props: {
spreadsheet: Spreadsheet | null;
chartType: ChartType;
selected: boolean;
onClick: OnInsertChart;
}) => {
const previewRef = useRef<HTMLDivElement | null>(null);
const [chartElements, setChartElements] = useState<ChartElements | null>(
null,
);
useLayoutEffect(() => {
if (!props.spreadsheet) {
return;
}
const elements = renderSpreadsheet(
props.chartType,
props.spreadsheet,
0,
0,
);
setChartElements(elements);
const svg = exportToSvg(elements, {
exportBackground: false,
viewBackgroundColor: oc.white,
shouldAddWatermark: false,
});
const previewNode = previewRef.current!;
previewNode.appendChild(svg);
if (props.selected) {
(previewNode.parentNode as HTMLDivElement).focus();
}
return () => {
previewNode.removeChild(svg);
};
}, [props.spreadsheet, props.chartType, props.selected]);
return (
<button
className="ChartPreview"
onClick={() => {
if (chartElements) {
props.onClick(props.chartType, chartElements);
}
}}
>
<div ref={previewRef} />
</button>
);
};
export const PasteChartDialog = ({
setAppState,
appState,
onClose,
onInsertChart,
}: {
appState: AppState;
onClose: () => void;
setAppState: React.Component<any, AppState>["setState"];
onInsertChart: (elements: LibraryItem) => void;
}) => {
const handleClose = React.useCallback(() => {
if (onClose) {
onClose();
}
}, [onClose]);
const handleChartClick = (chartType: ChartType, elements: ChartElements) => {
onInsertChart(elements);
setAppState({
currentChartType: chartType,
pasteDialog: {
shown: false,
data: null,
},
});
};
return (
<Dialog
small
onCloseRequest={handleClose}
title={t("labels.pasteCharts")}
className={"PasteChartDialog"}
autofocus={false}
>
<div className={"container"}>
<ChartPreviewBtn
chartType="bar"
spreadsheet={appState.pasteDialog.data}
selected={appState.currentChartType === "bar"}
onClick={handleChartClick}
/>
<ChartPreviewBtn
chartType="line"
spreadsheet={appState.pasteDialog.data}
selected={appState.currentChartType === "line"}
onClick={handleChartClick}
/>
</div>
</Dialog>
);
};
+6 -9
View File
@@ -130,11 +130,7 @@ export const ShortcutsDialog = ({ onClose }: { onClose?: () => void }) => {
return ( return (
<> <>
<Dialog <Dialog onCloseRequest={handleClose} title={t("shortcutsDialog.title")}>
maxWidth={900}
onCloseRequest={handleClose}
title={t("shortcutsDialog.title")}
>
<Columns> <Columns>
<Column> <Column>
<ShortcutIsland caption={t("shortcutsDialog.shapes")}> <ShortcutIsland caption={t("shortcutsDialog.shapes")}>
@@ -207,15 +203,16 @@ export const ShortcutsDialog = ({ onClose }: { onClose?: () => void }) => {
shortcuts={["Shift+1"]} shortcuts={["Shift+1"]}
/> />
<Shortcut <Shortcut
label={t("buttons.toggleFullScreen")} label={t("shortcutsDialog.zoomToSelection")}
shortcuts={["F"]} shortcuts={["Shift+2"]}
/> />
<Shortcut label={t("buttons.fullScreen")} shortcuts={["F"]} />
<Shortcut <Shortcut
label={t("buttons.toggleZenMode")} label={t("buttons.zenMode")}
shortcuts={[getShortcutKey("Alt+Z")]} shortcuts={[getShortcutKey("Alt+Z")]}
/> />
<Shortcut <Shortcut
label={t("labels.toggleGridMode")} label={t("labels.gridMode")}
shortcuts={[getShortcutKey("CtrlOrCmd+'")]} shortcuts={[getShortcutKey("CtrlOrCmd+'")]}
/> />
</ShortcutIsland> </ShortcutIsland>
+15 -1
View File
@@ -6,8 +6,10 @@
right: 12px; right: 12px;
font-size: 12px; font-size: 12px;
z-index: 999; z-index: 999;
h3 { h3 {
margin: 0 24px 8px 0; margin: 0 24px 8px 0;
white-space: nowrap;
} }
.close { .close {
@@ -29,9 +31,21 @@
} }
tr { tr {
td:nth-child(2) { td:nth-child(2) {
min-width: 48px; min-width: 24px;
text-align: right; text-align: right;
} }
} }
} }
:root[dir="rtl"] & {
left: 12px;
right: initial;
h3 {
margin: 0 0 8px 24px;
}
.close {
float: left;
}
}
} }
-1
View File
@@ -85,7 +85,6 @@ export const Stats = (props: {
<td>{t("stats.total")}</td> <td>{t("stats.total")}</td>
<td>{nFormatter(storageSizes.total, 1)}</td> <td>{nFormatter(storageSizes.total, 1)}</td>
</tr> </tr>
{selectedElements.length === 1 && ( {selectedElements.length === 1 && (
<tr> <tr>
<th colSpan={2}>{t("stats.element")}</th> <th colSpan={2}>{t("stats.element")}</th>
+17
View File
@@ -1,4 +1,6 @@
@import "open-color/open-color.scss"; @import "open-color/open-color.scss";
@import "../css/variables";
.excalidraw { .excalidraw {
.ToolIcon { .ToolIcon {
display: inline-flex; display: inline-flex;
@@ -140,6 +142,7 @@
user-select: none; user-select: none;
} }
// shrink shape icons on small viewports to make them fit
@media (max-width: 425px) { @media (max-width: 425px) {
.Shape .ToolIcon__icon { .Shape .ToolIcon__icon {
width: 2rem; width: 2rem;
@@ -151,6 +154,8 @@
} }
} }
// move the lock button out of the way on small viewports
// it begins to collide with the GitHub icon before we switch to mobile mode
@media (max-width: 760px) { @media (max-width: 760px) {
.ToolIcon.ToolIcon__lock { .ToolIcon.ToolIcon__lock {
display: inline-block; display: inline-block;
@@ -160,6 +165,7 @@
margin-left: 0; margin-left: 0;
border-radius: 20px 0 0 20px; border-radius: 20px 0 0 20px;
z-index: 1;
background-color: var(--button-gray-1); background-color: var(--button-gray-1);
@@ -181,6 +187,17 @@
} }
} }
.TooltipIcon {
width: 0.9em;
height: 0.9em;
margin-left: 5px;
margin-top: 1px;
@media #{$is-mobile-query} {
display: none;
}
}
.unlocked-icon { .unlocked-icon {
:root[dir="ltr"] & { :root[dir="ltr"] & {
left: 2px; left: 2px;
+27 -10
View File
@@ -7,39 +7,56 @@
.Tooltip__label { .Tooltip__label {
--arrow-size: 4px; --arrow-size: 4px;
visibility: hidden; visibility: hidden;
width: 10ch;
background: $oc-black; background: $oc-black;
color: $oc-white; color: $oc-white;
text-align: center; text-align: center;
border-radius: 4px; border-radius: 6px;
padding: 4px; padding: 8px;
position: absolute; position: absolute;
z-index: 10; z-index: 10;
font-size: 0.7rem; font-size: 13px;
line-height: 1.5; line-height: 1.5;
top: calc(100% + var(--arrow-size) + 3px); font-weight: 500;
// extra pixel offset for unknown reasons // extra pixel offset for unknown reasons
left: calc(-50% + var(--arrow-size) / 2 - 1px); left: calc(50% + var(--arrow-size) / 2 - 1px);
transform: translateX(-50%);
word-wrap: break-word; word-wrap: break-word;
&::after { &::after {
content: ""; content: "";
border: var(--arrow-size) solid transparent; border: var(--arrow-size) solid transparent;
border-bottom-color: $oc-black;
position: absolute; position: absolute;
bottom: 100%;
left: calc(50% - var(--arrow-size)); left: calc(50% - var(--arrow-size));
} }
&--above {
bottom: calc(100% + var(--arrow-size) + 3px);
&::after {
border-top-color: $oc-black;
top: 100%;
}
}
&--below {
top: calc(100% + var(--arrow-size) + 3px);
&::after {
border-bottom-color: $oc-black;
bottom: 100%;
}
}
} }
// the following 3 rules ensure that the tooltip doesn't show (nor affect // the following 3 rules ensure that the tooltip doesn't show (nor affect
// the cursor) when you drag over when you draw on canvas, but at the same // the cursor) when you drag over when you draw on canvas, but at the same
// time it still works when clicking on the link/shield // time it still works when clicking on the link/shield
body:active .Tooltip:not(:hover) {
body:active & .Tooltip:not(:hover) {
pointer-events: none; pointer-events: none;
} }
body:not(:active) .Tooltip:hover .Tooltip__label { body:not(:active) & .Tooltip:hover .Tooltip__label {
visibility: visible; visibility: visible;
} }
+18 -2
View File
@@ -5,11 +5,27 @@ import React from "react";
type TooltipProps = { type TooltipProps = {
children: React.ReactNode; children: React.ReactNode;
label: string; label: string;
position?: "above" | "below";
long?: boolean;
}; };
export const Tooltip = ({ children, label }: TooltipProps) => ( export const Tooltip = ({
children,
label,
position = "below",
long = false,
}: TooltipProps) => (
<div className="Tooltip"> <div className="Tooltip">
<span className="Tooltip__label">{label}</span> <span
className={
position === "above"
? "Tooltip__label Tooltip__label--above"
: "Tooltip__label Tooltip__label--below"
}
style={{ width: long ? "50ch" : "10ch" }}
>
{label}
</span>
{children} {children}
</div> </div>
); );
+5
View File
@@ -108,6 +108,11 @@ export const redo = createIcon(
{ mirror: true }, { mirror: true },
); );
export const questionCircle = createIcon(
"M504 256c0 136.997-111.043 248-248 248S8 392.997 8 256C8 119.083 119.043 8 256 8s248 111.083 248 248zM262.655 90c-54.497 0-89.255 22.957-116.549 63.758-3.536 5.286-2.353 12.415 2.715 16.258l34.699 26.31c5.205 3.947 12.621 3.008 16.665-2.122 17.864-22.658 30.113-35.797 57.303-35.797 20.429 0 45.698 13.148 45.698 32.958 0 14.976-12.363 22.667-32.534 33.976C247.128 238.528 216 254.941 216 296v4c0 6.627 5.373 12 12 12h56c6.627 0 12-5.373 12-12v-1.333c0-28.462 83.186-29.647 83.186-106.667 0-58.002-60.165-102-116.531-102zM256 338c-25.365 0-46 20.635-46 46 0 25.364 20.635 46 46 46s46-20.636 46-46c0-25.365-20.635-46-46-46z",
{ mirror: true },
);
// Icon imported form Storybook // Icon imported form Storybook
// Storybook is licensed under MIT https://github.com/storybookjs/storybook/blob/next/LICENSE // Storybook is licensed under MIT https://github.com/storybookjs/storybook/blob/next/LICENSE
export const resetZoom = createIcon( export const resetZoom = createIcon(
+5
View File
@@ -1,5 +1,7 @@
import { FontFamily } from "./element/types"; import { FontFamily } from "./element/types";
export const APP_NAME = "Excalidraw";
export const DRAGGING_THRESHOLD = 10; // 10px export const DRAGGING_THRESHOLD = 10; // 10px
export const LINE_CONFIRM_THRESHOLD = 10; // 10px export const LINE_CONFIRM_THRESHOLD = 10; // 10px
export const ELEMENT_SHIFT_TRANSLATE_AMOUNT = 5; export const ELEMENT_SHIFT_TRANSLATE_AMOUNT = 5;
@@ -85,3 +87,6 @@ export const STORAGE_KEYS = {
// time in milliseconds // time in milliseconds
export const TAP_TWICE_TIMEOUT = 300; export const TAP_TWICE_TIMEOUT = 300;
export const TOUCH_CTX_MENU_TIMEOUT = 500; export const TOUCH_CTX_MENU_TIMEOUT = 500;
export const TITLE_TIMEOUT = 10000;
export const SCENE_NAME_FALLBACK = "Untitled";
+2 -1
View File
@@ -1,3 +1,4 @@
@import "open-color/open-color.scss"; @import "open-color/open-color.scss";
$media-query: "(max-width: 600px), (max-height: 500px) and (max-width: 1000px)"; // keep up to date with is-mobile.tsx
$is-mobile-query: "(max-width: 600px), (max-height: 500px) and (max-width: 1000px)";
+1 -1
View File
@@ -441,7 +441,7 @@
} }
} }
@media #{$media-query} { @media #{$is-mobile-query} {
aside { aside {
display: none; display: none;
} }
+2 -6
View File
@@ -3,7 +3,6 @@
:root { :root {
--bg-color-island: rgba(255, 255, 255, 0.9); --bg-color-island: rgba(255, 255, 255, 0.9);
--popup-background-color: #{$oc-white}; --popup-background-color: #{$oc-white};
--border-radius-m: 4px;
--space-factor: 0.25rem; --space-factor: 0.25rem;
--button-gray-1: #{$oc-gray-2}; --button-gray-1: #{$oc-gray-2};
--button-gray-2: #{$oc-gray-4}; --button-gray-2: #{$oc-gray-4};
@@ -15,7 +14,6 @@
--icon-fill-color: #{$oc-black}; --icon-fill-color: #{$oc-black};
--icon-green-fill-color: #{$oc-green-9}; --icon-green-fill-color: #{$oc-green-9};
--keybinding-color: #{$oc-gray-5}; --keybinding-color: #{$oc-gray-5};
--color-overlay-text-color: #ccc;
--sat: env(safe-area-inset-top); --sat: env(safe-area-inset-top);
--sab: env(safe-area-inset-bottom); --sab: env(safe-area-inset-bottom);
--sal: env(safe-area-inset-left); --sal: env(safe-area-inset-left);
@@ -23,8 +21,6 @@
--text-color-primary: #{$oc-gray-8}; --text-color-primary: #{$oc-gray-8};
--shadow-island: 0 1px 5px #{transparentize($oc-black, 0.85)}; --shadow-island: 0 1px 5px #{transparentize($oc-black, 0.85)};
--overlay-background-color: #{transparentize($oc-white, 0.12)}; --overlay-background-color: #{transparentize($oc-white, 0.12)};
--border-radius-m: 4px;
--space-factor: 0.25rem;
--dropdown-icon: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" width="292.4" height="292.4" viewBox="0 0 292 292"><path d="M287 197L159 69c-4-3-8-5-13-5s-9 2-13 5L5 197c-3 4-5 8-5 13s2 9 5 13c4 4 8 5 13 5h256c5 0 9-1 13-5s5-8 5-13-1-9-5-13z"/></svg>'); --dropdown-icon: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" width="292.4" height="292.4" viewBox="0 0 292 292"><path d="M287 197L159 69c-4-3-8-5-13-5s-9 2-13 5L5 197c-3 4-5 8-5 13s2 9 5 13c4 4 8 5 13 5h256c5 0 9-1 13-5s5-8 5-13-1-9-5-13z"/></svg>');
--focus-highlight-color: #{$oc-blue-2}; --focus-highlight-color: #{$oc-blue-2};
--select-highlight-color: #{$oc-blue-5}; --select-highlight-color: #{$oc-blue-5};
@@ -35,6 +31,7 @@
--popup-secondary-background-color: #{$oc-gray-1}; --popup-secondary-background-color: #{$oc-gray-1};
--popup-text-color: #{$oc-black}; --popup-text-color: #{$oc-black};
--popup-text-inverted-color: #{$oc-white}; --popup-text-inverted-color: #{$oc-white};
--dialog-border: #{$oc-gray-6};
} }
.excalidraw { .excalidraw {
@@ -60,10 +57,8 @@
--icon-fill-color: #{$oc-gray-4}; --icon-fill-color: #{$oc-gray-4};
--icon-green-fill-color: #{$oc-green-4}; --icon-green-fill-color: #{$oc-green-4};
--keybinding-color: #{$oc-gray-6}; --keybinding-color: #{$oc-gray-6};
--color-overlay-text-color: #bbb;
--shadow-island: 0 1px 5px #{transparentize($oc-black, 0.7)}; --shadow-island: 0 1px 5px #{transparentize($oc-black, 0.7)};
--overlay-background-color: rgba(30, 30, 30, 0.88); --overlay-background-color: rgba(30, 30, 30, 0.88);
// #{$oc-gray-4}; inlined
--dropdown-icon: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" width="292.4" height="292.4" viewBox="0 0 292 292"><path fill="%23ced4da" d="M287 197L159 69c-4-3-8-5-13-5s-9 2-13 5L5 197c-3 4-5 8-5 13s2 9 5 13c4 4 8 5 13 5h256c5 0 9-1 13-5s5-8 5-13-1-9-5-13z"/></svg>'); --dropdown-icon: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" width="292.4" height="292.4" viewBox="0 0 292 292"><path fill="%23ced4da" d="M287 197L159 69c-4-3-8-5-13-5s-9 2-13 5L5 197c-3 4-5 8-5 13s2 9 5 13c4 4 8 5 13 5h256c5 0 9-1 13-5s5-8 5-13-1-9-5-13z"/></svg>');
--focus-highlight-color: #{$oc-blue-6}; --focus-highlight-color: #{$oc-blue-6};
--select-highlight-color: #{$oc-blue-4}; --select-highlight-color: #{$oc-blue-4};
@@ -74,5 +69,6 @@
--popup-secondary-background-color: #222; --popup-secondary-background-color: #222;
--popup-text-color: #{$oc-gray-4}; --popup-text-color: #{$oc-gray-4};
--popup-text-inverted-color: #2c2c2c; --popup-text-inverted-color: #2c2c2c;
--dialog-border: #{$oc-gray-9};
} }
} }
+1 -1
View File
@@ -9,7 +9,7 @@ import { AppState } from "../types";
import { restore } from "./restore"; import { restore } from "./restore";
import { ImportedDataState, LibraryData } from "./types"; import { ImportedDataState, LibraryData } from "./types";
export const parseFileContents = async (blob: Blob | File) => { const parseFileContents = async (blob: Blob | File) => {
let contents: string; let contents: string;
if (blob.type === "image/png") { if (blob.type === "image/png") {
+1 -71
View File
@@ -1,14 +1,10 @@
import { fileSave } from "browser-nativefs"; import { fileSave } from "browser-nativefs";
import { EVENT_IO, trackEvent } from "../analytics"; import { EVENT_IO, trackEvent } from "../analytics";
import { getDefaultAppState } from "../appState";
import { import {
copyCanvasToClipboardAsPng, copyCanvasToClipboardAsPng,
copyTextToSystemClipboard, copyTextToSystemClipboard,
} from "../clipboard"; } from "../clipboard";
import { import { NonDeletedExcalidrawElement } from "../element/types";
ExcalidrawElement,
NonDeletedExcalidrawElement,
} from "../element/types";
import { t } from "../i18n"; import { t } from "../i18n";
import { exportToCanvas, exportToSvg } from "../scene/export"; import { exportToCanvas, exportToSvg } from "../scene/export";
import { ExportType } from "../scene/types"; import { ExportType } from "../scene/types";
@@ -19,65 +15,6 @@ import { serializeAsJSON } from "./json";
export { loadFromBlob } from "./blob"; export { loadFromBlob } from "./blob";
export { loadFromJSON, saveAsJSON } from "./json"; export { loadFromJSON, saveAsJSON } from "./json";
const BACKEND_V2_POST = process.env.REACT_APP_BACKEND_V2_POST_URL;
export const exportToBackend = async (
elements: readonly ExcalidrawElement[],
appState: AppState,
) => {
const json = serializeAsJSON(elements, appState);
const encoded = new TextEncoder().encode(json);
const key = await window.crypto.subtle.generateKey(
{
name: "AES-GCM",
length: 128,
},
true, // extractable
["encrypt", "decrypt"],
);
// The iv is set to 0. We are never going to reuse the same key so we don't
// need to have an iv. (I hope that's correct...)
const iv = new Uint8Array(12);
// We use symmetric encryption. AES-GCM is the recommended algorithm and
// includes checks that the ciphertext has not been modified by an attacker.
const encrypted = await window.crypto.subtle.encrypt(
{
name: "AES-GCM",
iv,
},
key,
encoded,
);
// We use jwk encoding to be able to extract just the base64 encoded key.
// We will hardcode the rest of the attributes when importing back the key.
const exportedKey = await window.crypto.subtle.exportKey("jwk", key);
try {
const response = await fetch(BACKEND_V2_POST, {
method: "POST",
body: encrypted,
});
const json = await response.json();
if (json.id) {
const url = new URL(window.location.href);
// We need to store the key (and less importantly the id) as hash instead
// of queryParam in order to never send it to the server
url.hash = `json=${json.id},${exportedKey.k!}`;
const urlString = url.toString();
window.prompt(`🔒${t("alerts.uploadedSecurly")}`, urlString);
trackEvent(EVENT_IO, "export", "backend");
} else if (json.error_class === "RequestTooLargeError") {
window.alert(t("alerts.couldNotCreateShareableLinkTooBig"));
} else {
window.alert(t("alerts.couldNotCreateShareableLink"));
}
} catch (error) {
console.error(error);
window.alert(t("alerts.couldNotCreateShareableLink"));
}
};
export const exportCanvas = async ( export const exportCanvas = async (
type: ExportType, type: ExportType,
elements: readonly NonDeletedExcalidrawElement[], elements: readonly NonDeletedExcalidrawElement[],
@@ -169,13 +106,6 @@ export const exportCanvas = async (
} }
throw new Error(t("alerts.couldNotCopyToClipboard")); throw new Error(t("alerts.couldNotCopyToClipboard"));
} }
} else if (type === "backend") {
exportToBackend(elements, {
...appState,
viewBackgroundColor: exportBackground
? appState.viewBackgroundColor
: getDefaultAppState().viewBackgroundColor,
});
} }
// clean up the DOM // clean up the DOM
+2
View File
@@ -15,6 +15,7 @@ import {
DEFAULT_VERTICAL_ALIGN, DEFAULT_VERTICAL_ALIGN,
} from "../constants"; } from "../constants";
import { getDefaultAppState } from "../appState"; import { getDefaultAppState } from "../appState";
import { getNewSceneName } from "../utils";
const getFontFamilyByName = (fontFamilyName: string): FontFamily => { const getFontFamilyByName = (fontFamilyName: string): FontFamily => {
for (const [id, fontFamilyString] of Object.entries(FONT_FAMILY)) { for (const [id, fontFamilyString] of Object.entries(FONT_FAMILY)) {
@@ -166,6 +167,7 @@ const restoreAppState = (
return { return {
...nextAppState, ...nextAppState,
name: appState.name ?? localAppState?.name ?? getNewSceneName(),
offsetLeft: appState.offsetLeft || 0, offsetLeft: appState.offsetLeft || 0,
offsetTop: appState.offsetTop || 0, offsetTop: appState.offsetTop || 0,
// Migrates from previous version where appState.zoom was a number // Migrates from previous version where appState.zoom was a number
+7 -10
View File
@@ -24,13 +24,13 @@ export const mutateElement = <TElement extends Mutable<ExcalidrawElement>>(
// (see https://github.com/microsoft/TypeScript/issues/21732) // (see https://github.com/microsoft/TypeScript/issues/21732)
const { points } = updates as any; const { points } = updates as any;
if (points !== undefined) { if (typeof points !== "undefined") {
updates = { ...getSizeFromPoints(points), ...updates }; updates = { ...getSizeFromPoints(points), ...updates };
} }
for (const key in updates) { for (const key in updates) {
const value = (updates as any)[key]; const value = (updates as any)[key];
if (value !== undefined) { if (typeof value !== "undefined") {
if ( if (
(element as any)[key] === value && (element as any)[key] === value &&
// if object, always update in case its deep prop was mutated // if object, always update in case its deep prop was mutated
@@ -72,9 +72,9 @@ export const mutateElement = <TElement extends Mutable<ExcalidrawElement>>(
} }
if ( if (
updates.height !== undefined || typeof updates.height !== "undefined" ||
updates.width !== undefined || typeof updates.width !== "undefined" ||
points !== undefined typeof points !== "undefined"
) { ) {
invalidateShapeForElement(element); invalidateShapeForElement(element);
} }
@@ -84,12 +84,9 @@ export const mutateElement = <TElement extends Mutable<ExcalidrawElement>>(
Scene.getScene(element)?.informMutation(); Scene.getScene(element)?.informMutation();
}; };
export const newElementWith = < export const newElementWith = <TElement extends ExcalidrawElement>(
TElement extends ExcalidrawElement,
K extends keyof Omit<TElement, "id" | "version" | "versionNonce">
>(
element: TElement, element: TElement,
updates: Pick<TElement, K>, updates: ElementUpdate<TElement>,
): TElement => ({ ): TElement => ({
...element, ...element,
...updates, ...updates,
+12 -14
View File
@@ -1,6 +1,7 @@
import { Point } from "../types"; import { Point } from "../types";
import { FONT_FAMILY } from "../constants"; import { FONT_FAMILY } from "../constants";
export type ChartType = "bar" | "line";
export type FillStyle = "hachure" | "cross-hatch" | "solid"; export type FillStyle = "hachure" | "cross-hatch" | "solid";
export type FontFamily = keyof typeof FONT_FAMILY; export type FontFamily = keyof typeof FONT_FAMILY;
export type FontString = string & { _brand: "fontString" }; export type FontString = string & { _brand: "fontString" };
@@ -21,16 +22,26 @@ type _ExcalidrawElementBase = Readonly<{
strokeWidth: number; strokeWidth: number;
strokeStyle: StrokeStyle; strokeStyle: StrokeStyle;
strokeSharpness: StrokeSharpness; strokeSharpness: StrokeSharpness;
roughness: 0 | 1 | 2; roughness: number;
opacity: number; opacity: number;
width: number; width: number;
height: number; height: number;
angle: number; angle: number;
/** Random integer used to seed shape generation so that the roughjs shape
doesn't differ across renders. */
seed: number; seed: number;
/** Integer that is sequentially incremented on each change. Used to reconcile
elements during collaboration or when saving to server. */
version: number; version: number;
/** Random integer that is regenerated on each change.
Used for deterministic reconciliation of updates during collaboration,
in case the versions (see above) are identical. */
versionNonce: number; versionNonce: number;
isDeleted: boolean; isDeleted: boolean;
/** List of groups the element belongs to.
Ordered from deepest to shallowest. */
groupIds: readonly GroupId[]; groupIds: readonly GroupId[];
/** Ids of (linear) elements that are bound to this element. */
boundElementIds: readonly ExcalidrawLinearElement["id"][] | null; boundElementIds: readonly ExcalidrawLinearElement["id"][] | null;
}>; }>;
@@ -110,16 +121,3 @@ export type ExcalidrawLinearElement = _ExcalidrawElementBase &
startArrowhead: Arrowhead | null; startArrowhead: Arrowhead | null;
endArrowhead: Arrowhead | null; endArrowhead: Arrowhead | null;
}>; }>;
export type ExcalidrawElementTypes = Pick<ExcalidrawElement, "type">["type"];
/** @private */
type __ExcalidrawElementPossibleProps_withoutType<T> = T extends any
? { [K in keyof Omit<T, "type">]: T[K] }
: never;
/** Do not use for anything unless you really need it for some abstract
API types */
export type ExcalidrawElementPossibleProps = UnionToIntersection<
__ExcalidrawElementPossibleProps_withoutType<ExcalidrawElement>
> & { type: ExcalidrawElementTypes };
+3 -7
View File
@@ -1,7 +1,7 @@
import React, { PureComponent } from "react"; import React, { PureComponent } from "react";
import throttle from "lodash.throttle"; import throttle from "lodash.throttle";
import { ENV, EVENT } from "../../constants"; import { APP_NAME, ENV, EVENT } from "../../constants";
import { import {
decryptAESGEM, decryptAESGEM,
@@ -157,11 +157,7 @@ class CollabWrapper extends PureComponent<Props, CollabState> {
}; };
openPortal = async () => { openPortal = async () => {
window.history.pushState( window.history.pushState({}, APP_NAME, await generateCollaborationLink());
{},
"Excalidraw",
await generateCollaborationLink(),
);
const elements = this.excalidrawRef.current!.getSceneElements(); const elements = this.excalidrawRef.current!.getSceneElements();
// remove deleted elements from elements array & history to ensure we don't // remove deleted elements from elements array & history to ensure we don't
// expose potentially sensitive user data in case user manually deletes // expose potentially sensitive user data in case user manually deletes
@@ -178,7 +174,7 @@ class CollabWrapper extends PureComponent<Props, CollabState> {
closePortal = () => { closePortal = () => {
this.saveCollabRoomToFirebase(); this.saveCollabRoomToFirebase();
window.history.pushState({}, "Excalidraw", window.location.origin); window.history.pushState({}, APP_NAME, window.location.origin);
this.destroySocketClient(); this.destroySocketClient();
trackEvent(EVENT_SHARE, "session end"); trackEvent(EVENT_SHARE, "session end");
}; };
+1 -5
View File
@@ -123,11 +123,7 @@ const RoomDialog = ({
); );
}; };
return ( return (
<Dialog <Dialog small onCloseRequest={handleClose} title={t("labels.createRoom")}>
maxWidth={800}
onCloseRequest={handleClose}
title={t("labels.createRoom")}
>
{renderRoomDialog()} {renderRoomDialog()}
</Dialog> </Dialog>
); );
@@ -1,16 +1,16 @@
import React from "react"; import React from "react";
import clsx from "clsx"; import clsx from "clsx";
import * as i18n from "../i18n"; import * as i18n from "../../i18n";
export const LanguageList = ({ export const LanguageList = ({
onChange, onChange,
languages = i18n.languages, languages = i18n.languages,
currentLanguage = i18n.getLanguage().lng, currentLangCode = i18n.getLanguage().code,
floating, floating,
}: { }: {
languages?: { lng: string; label: string }[]; languages?: { code: string; label: string }[];
onChange: (value: string) => void; onChange: (langCode: i18n.Language["code"]) => void;
currentLanguage?: string; currentLangCode?: i18n.Language["code"];
floating?: boolean; floating?: boolean;
}) => ( }) => (
<React.Fragment> <React.Fragment>
@@ -19,12 +19,12 @@ export const LanguageList = ({
"dropdown-select--floating": floating, "dropdown-select--floating": floating,
})} })}
onChange={({ target }) => onChange(target.value)} onChange={({ target }) => onChange(target.value)}
value={currentLanguage} value={currentLangCode}
aria-label={i18n.t("buttons.selectLanguage")} aria-label={i18n.t("buttons.selectLanguage")}
> >
{languages.map((language) => ( {languages.map((lang) => (
<option key={language.lng} value={language.lng}> <option key={lang.code} value={lang.code}>
{language.label} {lang.label}
</option> </option>
))} ))}
</select> </select>
+60 -1
View File
@@ -3,12 +3,14 @@ import { ExcalidrawElement } from "../../element/types";
import { AppState } from "../../types"; import { AppState } from "../../types";
import { ImportedDataState } from "../../data/types"; import { ImportedDataState } from "../../data/types";
import { restore } from "../../data/restore"; import { restore } from "../../data/restore";
import { EVENT_ACTION, trackEvent } from "../../analytics"; import { EVENT_ACTION, EVENT_IO, trackEvent } from "../../analytics";
import { serializeAsJSON } from "../../data/json";
const byteToHex = (byte: number): string => `0${byte.toString(16)}`.slice(-2); const byteToHex = (byte: number): string => `0${byte.toString(16)}`.slice(-2);
const BACKEND_GET = process.env.REACT_APP_BACKEND_V1_GET_URL; const BACKEND_GET = process.env.REACT_APP_BACKEND_V1_GET_URL;
const BACKEND_V2_GET = process.env.REACT_APP_BACKEND_V2_GET_URL; const BACKEND_V2_GET = process.env.REACT_APP_BACKEND_V2_GET_URL;
const BACKEND_V2_POST = process.env.REACT_APP_BACKEND_V2_POST_URL;
const generateRandomID = async () => { const generateRandomID = async () => {
const arr = new Uint8Array(10); const arr = new Uint8Array(10);
@@ -228,3 +230,60 @@ export const loadScene = async (
commitToHistory: false, commitToHistory: false,
}; };
}; };
export const exportToBackend = async (
elements: readonly ExcalidrawElement[],
appState: AppState,
) => {
const json = serializeAsJSON(elements, appState);
const encoded = new TextEncoder().encode(json);
const key = await window.crypto.subtle.generateKey(
{
name: "AES-GCM",
length: 128,
},
true, // extractable
["encrypt", "decrypt"],
);
// The iv is set to 0. We are never going to reuse the same key so we don't
// need to have an iv. (I hope that's correct...)
const iv = new Uint8Array(12);
// We use symmetric encryption. AES-GCM is the recommended algorithm and
// includes checks that the ciphertext has not been modified by an attacker.
const encrypted = await window.crypto.subtle.encrypt(
{
name: "AES-GCM",
iv,
},
key,
encoded,
);
// We use jwk encoding to be able to extract just the base64 encoded key.
// We will hardcode the rest of the attributes when importing back the key.
const exportedKey = await window.crypto.subtle.exportKey("jwk", key);
try {
const response = await fetch(BACKEND_V2_POST, {
method: "POST",
body: encrypted,
});
const json = await response.json();
if (json.id) {
const url = new URL(window.location.href);
// We need to store the key (and less importantly the id) as hash instead
// of queryParam in order to never send it to the server
url.hash = `json=${json.id},${exportedKey.k!}`;
const urlString = url.toString();
window.prompt(`🔒${t("alerts.uploadedSecurly")}`, urlString);
trackEvent(EVENT_IO, "export", "backend");
} else if (json.error_class === "RequestTooLargeError") {
window.alert(t("alerts.couldNotCreateShareableLinkTooBig"));
} else {
window.alert(t("alerts.couldNotCreateShareableLink"));
}
} catch (error) {
console.error(error);
window.alert(t("alerts.couldNotCreateShareableLink"));
}
};
+2 -1
View File
@@ -6,6 +6,7 @@ import {
} from "../../appState"; } from "../../appState";
import { clearElementsForLocalStorage } from "../../element"; import { clearElementsForLocalStorage } from "../../element";
import { STORAGE_KEYS as APP_STORAGE_KEYS } from "../../constants"; import { STORAGE_KEYS as APP_STORAGE_KEYS } from "../../constants";
import { ImportedDataState } from "../../data/types";
export const STORAGE_KEYS = { export const STORAGE_KEYS = {
LOCAL_STORAGE_ELEMENTS: "excalidraw", LOCAL_STORAGE_ELEMENTS: "excalidraw",
@@ -81,7 +82,7 @@ export const importFromLocalStorage = () => {
} }
} }
let appState = null; let appState: ImportedDataState["appState"] = null;
if (savedState) { if (savedState) {
try { try {
appState = { appState = {
+127 -33
View File
@@ -1,6 +1,16 @@
import React, { useState, useLayoutEffect, useEffect, useRef } from "react"; import React, {
useState,
useLayoutEffect,
useEffect,
useRef,
useCallback,
} from "react";
import LanguageDetector from "i18next-browser-languagedetector";
import Excalidraw from "../packages/excalidraw/index"; import Excalidraw, {
languages,
defaultLang,
} from "../packages/excalidraw/index";
import { import {
getTotalStorageSize, getTotalStorageSize,
@@ -12,17 +22,33 @@ import {
import { ImportedDataState } from "../data/types"; import { ImportedDataState } from "../data/types";
import CollabWrapper, { CollabAPI } from "./collab/CollabWrapper"; import CollabWrapper, { CollabAPI } from "./collab/CollabWrapper";
import { TopErrorBoundary } from "../components/TopErrorBoundary"; import { TopErrorBoundary } from "../components/TopErrorBoundary";
import { t } from "../i18n"; import { Language, t } from "../i18n";
import { loadScene } from "./data"; import { exportToBackend, loadScene } from "./data";
import { getCollaborationLinkData } from "./data"; import { getCollaborationLinkData } from "./data";
import { EVENT } from "../constants"; import { EVENT } from "../constants";
import { loadFromFirebase } from "./data/firebase"; import { loadFromFirebase } from "./data/firebase";
import { ExcalidrawImperativeAPI } from "../components/App"; import { ExcalidrawImperativeAPI } from "../components/App";
import { debounce, ResolvablePromise, resolvablePromise } from "../utils"; import { debounce, ResolvablePromise, resolvablePromise } from "../utils";
import { AppState, ExcalidrawAPIRefValue } from "../types"; import { AppState, ExcalidrawAPIRefValue } from "../types";
import { ExcalidrawElement } from "../element/types"; import {
ExcalidrawElement,
NonDeletedExcalidrawElement,
} from "../element/types";
import { SAVE_TO_LOCAL_STORAGE_TIMEOUT } from "./app_constants"; import { SAVE_TO_LOCAL_STORAGE_TIMEOUT } from "./app_constants";
import { EVENT_LOAD, EVENT_SHARE, trackEvent } from "../analytics"; import { EVENT_LOAD, EVENT_SHARE, trackEvent } from "../analytics";
import { ErrorDialog } from "../components/ErrorDialog";
import { getDefaultAppState } from "../appState";
import { APP_NAME, TITLE_TIMEOUT } from "../constants";
import { LanguageList } from "./components/LanguageList";
const languageDetector = new LanguageDetector();
languageDetector.init({
languageUtils: {
formatLanguageCode: (langCode: Language["code"]) => langCode,
isWhitelisted: () => true,
},
checkWhitelist: false,
});
const excalidrawRef: React.MutableRefObject< const excalidrawRef: React.MutableRefObject<
MarkRequired<ExcalidrawAPIRefValue, "ready" | "readyPromise"> MarkRequired<ExcalidrawAPIRefValue, "ready" | "readyPromise">
@@ -87,7 +113,6 @@ type Scene = ImportedDataState & { commitToHistory: boolean };
const initializeScene = async (opts: { const initializeScene = async (opts: {
resetScene: ExcalidrawImperativeAPI["resetScene"]; resetScene: ExcalidrawImperativeAPI["resetScene"];
initializeSocketClient: CollabAPI["initializeSocketClient"]; initializeSocketClient: CollabAPI["initializeSocketClient"];
onLateInitialization?: (scene: Scene) => void;
}): Promise<Scene | null> => { }): Promise<Scene | null> => {
const searchParams = new URLSearchParams(window.location.search); const searchParams = new URLSearchParams(window.location.search);
const id = searchParams.get("id"); const id = searchParams.get("id");
@@ -113,26 +138,24 @@ const initializeScene = async (opts: {
scene = await loadScene(jsonMatch[1], jsonMatch[2], initialData); scene = await loadScene(jsonMatch[1], jsonMatch[2], initialData);
} }
if (!isCollabScene) { if (!isCollabScene) {
window.history.replaceState({}, "Excalidraw", window.location.origin); window.history.replaceState({}, APP_NAME, window.location.origin);
} }
} else { } else {
// https://github.com/excalidraw/excalidraw/issues/1919 // https://github.com/excalidraw/excalidraw/issues/1919
if (document.hidden) { if (document.hidden) {
window.addEventListener( return new Promise((resolve, reject) => {
"focus", window.addEventListener(
() => "focus",
initializeScene(opts).then((_scene) => { () => initializeScene(opts).then(resolve).catch(reject),
opts?.onLateInitialization?.(_scene || scene); {
}), once: true,
{ },
once: true, );
}, });
);
return null;
} }
isCollabScene = false; isCollabScene = false;
window.history.replaceState({}, "Excalidraw", window.location.origin); window.history.replaceState({}, APP_NAME, window.location.origin);
} }
} }
if (isCollabScene) { if (isCollabScene) {
@@ -178,6 +201,9 @@ function ExcalidrawWrapper(props: { collab: CollabAPI }) {
width: window.innerWidth, width: window.innerWidth,
height: window.innerHeight, height: window.innerHeight,
}); });
const [errorMessage, setErrorMessage] = useState("");
const currentLangCode = languageDetector.detect() || defaultLang.code;
const [langCode, setLangCode] = useState(currentLangCode);
useLayoutEffect(() => { useLayoutEffect(() => {
const onResize = () => { const onResize = () => {
@@ -215,9 +241,6 @@ function ExcalidrawWrapper(props: { collab: CollabAPI }) {
initializeScene({ initializeScene({
resetScene: excalidrawApi.resetScene, resetScene: excalidrawApi.resetScene,
initializeSocketClient: collab.initializeSocketClient, initializeSocketClient: collab.initializeSocketClient,
onLateInitialization: (scene) => {
initialStatePromiseRef.current.promise.resolve(scene);
},
}).then((scene) => { }).then((scene) => {
initialStatePromiseRef.current.promise.resolve(scene); initialStatePromiseRef.current.promise.resolve(scene);
}); });
@@ -240,6 +263,10 @@ function ExcalidrawWrapper(props: { collab: CollabAPI }) {
} }
}; };
const titleTimeout = setTimeout(
() => (document.title = APP_NAME),
TITLE_TIMEOUT,
);
window.addEventListener(EVENT.HASHCHANGE, onHashChange, false); window.addEventListener(EVENT.HASHCHANGE, onHashChange, false);
window.addEventListener(EVENT.UNLOAD, onBlur, false); window.addEventListener(EVENT.UNLOAD, onBlur, false);
window.addEventListener(EVENT.BLUR, onBlur, false); window.addEventListener(EVENT.BLUR, onBlur, false);
@@ -247,9 +274,14 @@ function ExcalidrawWrapper(props: { collab: CollabAPI }) {
window.removeEventListener(EVENT.HASHCHANGE, onHashChange, false); window.removeEventListener(EVENT.HASHCHANGE, onHashChange, false);
window.removeEventListener(EVENT.UNLOAD, onBlur, false); window.removeEventListener(EVENT.UNLOAD, onBlur, false);
window.removeEventListener(EVENT.BLUR, onBlur, false); window.removeEventListener(EVENT.BLUR, onBlur, false);
clearTimeout(titleTimeout);
}; };
}, [collab.initializeSocketClient]); }, [collab.initializeSocketClient]);
useEffect(() => {
languageDetector.cacheUserLanguage(langCode);
}, [langCode]);
const onChange = ( const onChange = (
elements: readonly ExcalidrawElement[], elements: readonly ExcalidrawElement[],
appState: AppState, appState: AppState,
@@ -260,18 +292,80 @@ function ExcalidrawWrapper(props: { collab: CollabAPI }) {
} }
}; };
const onExportToBackend = async (
exportedElements: readonly NonDeletedExcalidrawElement[],
appState: AppState,
canvas: HTMLCanvasElement | null,
) => {
if (exportedElements.length === 0) {
return window.alert(t("alerts.cannotExportEmptyCanvas"));
}
if (canvas) {
try {
await exportToBackend(exportedElements, {
...appState,
viewBackgroundColor: appState.exportBackground
? appState.viewBackgroundColor
: getDefaultAppState().viewBackgroundColor,
});
} catch (error) {
if (error.name !== "AbortError") {
const { width, height } = canvas;
console.error(error, { width, height });
setErrorMessage(error.message);
}
}
}
};
const renderFooter = useCallback(
(isMobile: boolean) => {
const renderLanguageList = () => (
<LanguageList
onChange={(langCode) => {
setLangCode(langCode);
}}
languages={languages}
floating={!isMobile}
currentLangCode={langCode}
/>
);
if (isMobile) {
return (
<fieldset>
<legend>{t("labels.language")}</legend>
{renderLanguageList()}
</fieldset>
);
}
return renderLanguageList();
},
[langCode],
);
return ( return (
<Excalidraw <>
ref={excalidrawRef} <Excalidraw
onChange={onChange} ref={excalidrawRef}
width={dimensions.width} onChange={onChange}
height={dimensions.height} width={dimensions.width}
initialData={initialStatePromiseRef.current.promise} height={dimensions.height}
user={{ name: collab.username }} initialData={initialStatePromiseRef.current.promise}
onCollabButtonClick={collab.onCollabButtonClick} user={{ name: collab.username }}
isCollaborating={collab.isCollaborating} onCollabButtonClick={collab.onCollabButtonClick}
onPointerUpdate={collab.onPointerUpdate} isCollaborating={collab.isCollaborating}
/> onPointerUpdate={collab.onPointerUpdate}
onExportToBackend={onExportToBackend}
renderFooter={renderFooter}
langCode={langCode}
/>
{errorMessage && (
<ErrorDialog
message={errorMessage}
onClose={() => setErrorMessage("")}
/>
)}
</>
); );
} }
-9
View File
@@ -46,15 +46,6 @@ type MarkOptional<T, K extends keyof T> = Omit<T, K> & Partial<Pick<T, K>>;
type MarkRequired<T, RK extends keyof T> = Exclude<T, RK> & type MarkRequired<T, RK extends keyof T> = Exclude<T, RK> &
Required<Pick<T, RK>>; Required<Pick<T, RK>>;
type UnionToIntersection<T> = (T extends any ? (x: T) => any : never) extends (
x: infer R,
) => any
? R
: never;
/** Assert K is a subset of T, and returns K */
type AssertSubset<T, K extends T> = K;
// PNG encoding/decoding // PNG encoding/decoding
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
type TEXtChunk = { name: "tEXt"; data: Uint8Array }; type TEXtChunk = { name: "tEXt"; data: Uint8Array };
+56 -73
View File
@@ -1,93 +1,85 @@
import LanguageDetector from "i18next-browser-languagedetector";
import { EVENT_CHANGE, trackEvent } from "./analytics"; import { EVENT_CHANGE, trackEvent } from "./analytics";
import fallbackLanguageData from "./locales/en.json"; import fallbackLangData from "./locales/en.json";
import percentages from "./locales/percentages.json"; import percentages from "./locales/percentages.json";
const COMPLETION_THRESHOLD_TO_EXCEED = 85; const COMPLETION_THRESHOLD = 85;
interface Language { export interface Language {
lng: string; code: string;
label: string; label: string;
rtl?: boolean; rtl?: boolean;
} }
const allLanguages: Language[] = [ const allLanguages: Language[] = [
{ lng: "ar-SA", label: "العربية", rtl: true }, { code: "ar-SA", label: "العربية", rtl: true },
{ lng: "bg-BG", label: "Български" }, { code: "bg-BG", label: "Български" },
{ lng: "ca-ES", label: "Catalan" }, { code: "ca-ES", label: "Catalan" },
{ lng: "de-DE", label: "Deutsch" }, { code: "de-DE", label: "Deutsch" },
{ lng: "el-GR", label: "Ελληνικά" }, { code: "el-GR", label: "Ελληνικά" },
{ lng: "es-ES", label: "Español" }, { code: "es-ES", label: "Español" },
{ lng: "fa-IR", label: "فارسی", rtl: true }, { code: "fa-IR", label: "فارسی", rtl: true },
{ lng: "fi-FI", label: "Suomi" }, { code: "fi-FI", label: "Suomi" },
{ lng: "fr-FR", label: "Français" }, { code: "fr-FR", label: "Français" },
{ lng: "he-IL", label: "עברית", rtl: true }, { code: "he-IL", label: "עברית", rtl: true },
{ lng: "hi-IN", label: "हिन्दी" }, { code: "hi-IN", label: "हिन्दी" },
{ lng: "hu-HU", label: "Magyar" }, { code: "hu-HU", label: "Magyar" },
{ lng: "id-ID", label: "Bahasa Indonesia" }, { code: "id-ID", label: "Bahasa Indonesia" },
{ lng: "it-IT", label: "Italiano" }, { code: "it-IT", label: "Italiano" },
{ lng: "ja-JP", label: "日本語" }, { code: "ja-JP", label: "日本語" },
{ lng: "ko-KR", label: "한국어" }, { code: "ko-KR", label: "한국어" },
{ lng: "my-MM", label: "Burmese" }, { code: "my-MM", label: "Burmese" },
{ lng: "nb-NO", label: "Norsk bokmål" }, { code: "nb-NO", label: "Norsk bokmål" },
{ lng: "nl-NL", label: "Nederlands" }, { code: "nl-NL", label: "Nederlands" },
{ lng: "nn-NO", label: "Norsk nynorsk" }, { code: "nn-NO", label: "Norsk nynorsk" },
{ lng: "pl-PL", label: "Polski" }, { code: "pl-PL", label: "Polski" },
{ lng: "pt-PT", label: "Português" }, { code: "pt-BR", label: "Português Brasileiro" },
{ lng: "ro-RO", label: "Română" }, { code: "pt-PT", label: "Português" },
{ lng: "ru-RU", label: "Русский" }, { code: "ro-RO", label: "Română" },
{ lng: "sk-SK", label: "Slovenčina" }, { code: "ru-RU", label: "Русский" },
{ lng: "sv-SE", label: "Svenska" }, { code: "sk-SK", label: "Slovenčina" },
{ lng: "tr-TR", label: "Türkçe" }, { code: "sv-SE", label: "Svenska" },
{ lng: "uk-UA", label: "Українська" }, { code: "tr-TR", label: "Türkçe" },
{ lng: "zh-CN", label: "简体中文" }, { code: "uk-UA", label: "Українська" },
{ lng: "zh-TW", label: "繁體中文" }, { code: "zh-CN", label: "简体中文" },
{ code: "zh-TW", label: "繁體中文" },
]; ];
export const languages: Language[] = [{ lng: "en", label: "English" }] export const defaultLang = { code: "en", label: "English" };
export const languages: Language[] = [defaultLang]
.concat( .concat(
allLanguages.sort((left, right) => (left.label > right.label ? 1 : -1)), allLanguages.sort((left, right) => (left.label > right.label ? 1 : -1)),
) )
.filter( .filter(
(lang) => (lang) =>
(percentages as Record<string, number>)[lang.lng] > (percentages as Record<string, number>)[lang.code] >=
COMPLETION_THRESHOLD_TO_EXCEED, COMPLETION_THRESHOLD,
); );
let currentLanguage = languages[0]; let currentLang: Language = defaultLang;
let currentLanguageData = {}; let currentLangData = {};
const fallbackLanguage = languages[0];
export const setLanguage = async (newLng: string | undefined) => { export const setLanguage = async (lang: Language) => {
currentLanguage = currentLang = lang;
languages.find((language) => language.lng === newLng) || fallbackLanguage; document.documentElement.dir = currentLang.rtl ? "rtl" : "ltr";
document.documentElement.dir = currentLanguage.rtl ? "rtl" : "ltr"; currentLangData = await import(
/* webpackChunkName: "i18n-[request]" */ `./locales/${currentLang.code}.json`
currentLanguageData = await import(
/* webpackChunkName: "i18n-[request]" */ `./locales/${currentLanguage.lng}.json`
); );
languageDetector.cacheUserLanguage(currentLanguage.lng); trackEvent(EVENT_CHANGE, "language", currentLang.code);
trackEvent(EVENT_CHANGE, "language", currentLanguage.lng);
}; };
export const setLanguageFirstTime = async () => { export const setLanguageFirstTime = async (lang: Language) => {
const newLng: string | undefined = languageDetector.detect(); currentLang = lang;
document.documentElement.dir = currentLang.rtl ? "rtl" : "ltr";
currentLanguage = currentLangData = await import(
languages.find((language) => language.lng === newLng) || fallbackLanguage; /* webpackChunkName: "i18n-[request]" */ `./locales/${currentLang.code}.json`
document.documentElement.dir = currentLanguage.rtl ? "rtl" : "ltr";
currentLanguageData = await import(
/* webpackChunkName: "i18n-[request]" */ `./locales/${currentLanguage.lng}.json`
); );
languageDetector.cacheUserLanguage(currentLanguage.lng);
}; };
export const getLanguage = () => currentLanguage; export const getLanguage = () => currentLang;
const findPartsForData = (data: any, parts: string[]) => { const findPartsForData = (data: any, parts: string[]) => {
for (let index = 0; index < parts.length; ++index) { for (let index = 0; index < parts.length; ++index) {
@@ -106,8 +98,8 @@ const findPartsForData = (data: any, parts: string[]) => {
export const t = (path: string, replacement?: { [key: string]: string }) => { export const t = (path: string, replacement?: { [key: string]: string }) => {
const parts = path.split("."); const parts = path.split(".");
let translation = let translation =
findPartsForData(currentLanguageData, parts) || findPartsForData(currentLangData, parts) ||
findPartsForData(fallbackLanguageData, parts); findPartsForData(fallbackLangData, parts);
if (translation === undefined) { if (translation === undefined) {
throw new Error(`Can't find translation for ${path}`); throw new Error(`Can't find translation for ${path}`);
} }
@@ -119,12 +111,3 @@ export const t = (path: string, replacement?: { [key: string]: string }) => {
} }
return translation; return translation;
}; };
const languageDetector = new LanguageDetector();
languageDetector.init({
languageUtils: {
formatLanguageCode: (lng: string) => lng,
isWhitelisted: () => true,
},
checkWhitelist: false,
});
+2
View File
@@ -1,5 +1,6 @@
import { exportToCanvas } from "./scene/export"; import { exportToCanvas } from "./scene/export";
import { getDefaultAppState } from "./appState"; import { getDefaultAppState } from "./appState";
import { SCENE_NAME_FALLBACK } from "./constants";
const { registerFont, createCanvas } = require("canvas"); const { registerFont, createCanvas } = require("canvas");
@@ -61,6 +62,7 @@ const canvas = exportToCanvas(
elements as any, elements as any,
{ {
...getDefaultAppState(), ...getDefaultAppState(),
name: SCENE_NAME_FALLBACK,
offsetTop: 0, offsetTop: 0,
offsetLeft: 0, offsetLeft: 0,
}, },
+1
View File
@@ -11,6 +11,7 @@ export const IsMobileProvider = ({
if (!query.current) { if (!query.current) {
query.current = window.matchMedia query.current = window.matchMedia
? window.matchMedia( ? window.matchMedia(
// keep up to date with _variables.scss
"(max-width: 640px), (max-height: 500px) and (max-width: 1000px)", "(max-width: 640px), (max-height: 500px) and (max-width: 1000px)",
) )
: (({ : (({
+1
View File
@@ -9,6 +9,7 @@ export const CODES = {
BRACKET_RIGHT: "BracketRight", BRACKET_RIGHT: "BracketRight",
BRACKET_LEFT: "BracketLeft", BRACKET_LEFT: "BracketLeft",
ONE: "Digit1", ONE: "Digit1",
TWO: "Digit2",
NINE: "Digit9", NINE: "Digit9",
QUOTE: "Quote", QUOTE: "Quote",
ZERO: "Digit0", ZERO: "Digit0",
+27 -9
View File
@@ -4,6 +4,7 @@
"selectAll": "تحديد الكل", "selectAll": "تحديد الكل",
"multiSelect": "إضافة عنصر للتحديد", "multiSelect": "إضافة عنصر للتحديد",
"moveCanvas": "نقل لوح رسم", "moveCanvas": "نقل لوح رسم",
"cut": "",
"copy": "نسخ", "copy": "نسخ",
"copyAsPng": "نسخ إلى الحافظة بصيغة PNG", "copyAsPng": "نسخ إلى الحافظة بصيغة PNG",
"copyAsSvg": "نسخ بصيغة SVG", "copyAsSvg": "نسخ بصيغة SVG",
@@ -28,10 +29,15 @@
"edges": "الحواف", "edges": "الحواف",
"sharp": "حادة", "sharp": "حادة",
"round": "دائرية", "round": "دائرية",
"arrowheads": "",
"arrowhead_none": "",
"arrowhead_arrow": "",
"arrowhead_bar": "",
"arrowhead_dot": "",
"fontSize": "حجم الخط", "fontSize": "حجم الخط",
"fontFamily": "نوع الخط", "fontFamily": "نوع الخط",
"onlySelected": "المحدد فقط", "onlySelected": "المحدد فقط",
"withBackground": "مع الخلفية", "withBackground": "",
"exportEmbedScene": "تضمين المشهد في ملف التصدير", "exportEmbedScene": "تضمين المشهد في ملف التصدير",
"exportEmbedScene_details": "سيتم حفظ بيانات المشهد في ملف PNG/SVG المصدّر بحيث يمكن استعادة المشهد منه.\nسيزيد حجم الملف المصدر.", "exportEmbedScene_details": "سيتم حفظ بيانات المشهد في ملف PNG/SVG المصدّر بحيث يمكن استعادة المشهد منه.\nسيزيد حجم الملف المصدر.",
"addWatermark": "إضافة \"مصنوعة بواسطة Excalidraw\"", "addWatermark": "إضافة \"مصنوعة بواسطة Excalidraw\"",
@@ -70,10 +76,11 @@
"group": "تحديد مجموعة", "group": "تحديد مجموعة",
"ungroup": "إلغاء تحديد مجموعة", "ungroup": "إلغاء تحديد مجموعة",
"collaborators": "المتعاونون", "collaborators": "المتعاونون",
"toggleGridMode": "التبديل إلى وضع الشبكة", "gridMode": "",
"addToLibrary": "أضف إلى المكتبة", "addToLibrary": "أضف إلى المكتبة",
"removeFromLibrary": "حذف من المكتبة", "removeFromLibrary": "حذف من المكتبة",
"libraryLoadingMessage": "جارٍ تحميل المكتبة...", "libraryLoadingMessage": "جارٍ تحميل المكتبة...",
"libraries": "",
"loadingScene": "جاري تحميل المشهد...", "loadingScene": "جاري تحميل المشهد...",
"align": "محاذاة", "align": "محاذاة",
"alignTop": "محاذاة إلى اﻷعلى", "alignTop": "محاذاة إلى اﻷعلى",
@@ -110,9 +117,10 @@
"redo": "إعادة تنفيذ", "redo": "إعادة تنفيذ",
"roomDialog": "بدء المشاركة الحية", "roomDialog": "بدء المشاركة الحية",
"createNewRoom": "إنشاء غرفة جديدة", "createNewRoom": "إنشاء غرفة جديدة",
"toggleFullScreen": "التبديل لوضع ملء الشاشة", "fullScreen": "",
"toggleDarkMode": "تبديل الوضع الليلي", "darkMode": "",
"toggleZenMode": "تبديل الوضع الليلي", "lightMode": "",
"zenMode": "",
"exitZenMode": "إلغاء الوضع الليلى" "exitZenMode": "إلغاء الوضع الليلى"
}, },
"alerts": { "alerts": {
@@ -128,7 +136,7 @@
"loadSceneOverridePrompt": "تحميل الرسم الخارجي سيحل محل المحتوى الموجود لديك. هل ترغب في المتابعة؟", "loadSceneOverridePrompt": "تحميل الرسم الخارجي سيحل محل المحتوى الموجود لديك. هل ترغب في المتابعة؟",
"errorLoadingLibrary": "حصل خطأ أثناء تحميل مكتبة الطرف الثالث.", "errorLoadingLibrary": "حصل خطأ أثناء تحميل مكتبة الطرف الثالث.",
"confirmAddLibrary": "هذا سيضيف {{numShapes}} شكل إلى مكتبتك. هل أنت متأكد؟", "confirmAddLibrary": "هذا سيضيف {{numShapes}} شكل إلى مكتبتك. هل أنت متأكد؟",
"imageDoesNotContainScene": "لا يحتوي ملف الصورة على بيانات المشهد. هل قمت بتمكين هذا أثناء التصدير؟", "imageDoesNotContainScene": "",
"cannotRestoreFromImage": "تعذر استعادة المشهد من ملف الصورة" "cannotRestoreFromImage": "تعذر استعادة المشهد من ملف الصورة"
}, },
"toolBar": { "toolBar": {
@@ -153,6 +161,7 @@
"freeDraw": "انقر واسحب، افرج عند الانتهاء", "freeDraw": "انقر واسحب، افرج عند الانتهاء",
"text": "نصيحة: يمكنك أيضًا إضافة نص بالنقر المزدوج في أي مكان بأداة الاختيار", "text": "نصيحة: يمكنك أيضًا إضافة نص بالنقر المزدوج في أي مكان بأداة الاختيار",
"linearElementMulti": "انقر فوق النقطة الأخيرة أو اضغط على Esc أو Enter للإنهاء", "linearElementMulti": "انقر فوق النقطة الأخيرة أو اضغط على Esc أو Enter للإنهاء",
"lockAngle": "",
"resize": "يمكنك تقييد النسب بالضغط على SHIFT أثناء تغيير الحجم،\nاضغط على ALT لتغيير الحجم من المركز", "resize": "يمكنك تقييد النسب بالضغط على SHIFT أثناء تغيير الحجم،\nاضغط على ALT لتغيير الحجم من المركز",
"rotate": "يمكنك تقييد الزوايا من خلال الضغط على SHIFT أثناء الدوران", "rotate": "يمكنك تقييد الزوايا من خلال الضغط على SHIFT أثناء الدوران",
"lineEditor_info": "انقر نقراً مزدوجاً أو اضغط Enter لتعديل النقاط", "lineEditor_info": "انقر نقراً مزدوجاً أو اضغط Enter لتعديل النقاط",
@@ -205,13 +214,22 @@
"textNewLine": "إضافة سطر جديد (نص)", "textNewLine": "إضافة سطر جديد (نص)",
"textFinish": "الانتهاء من تحرير (النص)", "textFinish": "الانتهاء من تحرير (النص)",
"zoomToFit": "تكبير لتلائم جميع العناصر", "zoomToFit": "تكبير لتلائم جميع العناصر",
"zoomToSelection": "",
"preventBinding": "منع ربط السهم" "preventBinding": "منع ربط السهم"
}, },
"encrypted": { "encrypted": {
"tooltip": "رسوماتك مشفرة من النهاية إلى النهاية حتى أن خوادم Excalidraw لن تراها أبدا." "tooltip": "رسوماتك مشفرة من النهاية إلى النهاية حتى أن خوادم Excalidraw لن تراها أبدا."
}, },
"charts": { "stats": {
"noNumericColumn": "قمت بلصق جدول بيانات دون عمود رقمي.", "angle": "",
"tooManyColumns": "قمت بلصق جدول بيانات دون عمود رقمي." "element": "",
"elements": "",
"height": "",
"scene": "",
"selected": "",
"storage": "",
"title": "",
"total": "",
"width": ""
} }
} }
+26 -8
View File
@@ -4,6 +4,7 @@
"selectAll": "Маркирай всичко", "selectAll": "Маркирай всичко",
"multiSelect": "", "multiSelect": "",
"moveCanvas": "", "moveCanvas": "",
"cut": "",
"copy": "Копирай", "copy": "Копирай",
"copyAsPng": "Копиране в клипборда", "copyAsPng": "Копиране в клипборда",
"copyAsSvg": "Копиране в клипборда", "copyAsSvg": "Копиране в клипборда",
@@ -28,10 +29,15 @@
"edges": "", "edges": "",
"sharp": "", "sharp": "",
"round": "", "round": "",
"arrowheads": "",
"arrowhead_none": "",
"arrowhead_arrow": "Стрелка",
"arrowhead_bar": "",
"arrowhead_dot": "",
"fontSize": "Размер на шрифта", "fontSize": "Размер на шрифта",
"fontFamily": "Семейство шрифтове", "fontFamily": "Семейство шрифтове",
"onlySelected": "Само избраното", "onlySelected": "Само избраното",
"withBackground": "С фон", "withBackground": "",
"exportEmbedScene": "", "exportEmbedScene": "",
"exportEmbedScene_details": "", "exportEmbedScene_details": "",
"addWatermark": "", "addWatermark": "",
@@ -70,10 +76,11 @@
"group": "", "group": "",
"ungroup": "", "ungroup": "",
"collaborators": "", "collaborators": "",
"toggleGridMode": "", "gridMode": "",
"addToLibrary": "", "addToLibrary": "",
"removeFromLibrary": "", "removeFromLibrary": "",
"libraryLoadingMessage": "", "libraryLoadingMessage": "",
"libraries": "",
"loadingScene": "", "loadingScene": "",
"align": "", "align": "",
"alignTop": "", "alignTop": "",
@@ -110,9 +117,10 @@
"redo": "Повтори", "redo": "Повтори",
"roomDialog": "Започнете сътрудничество на живо", "roomDialog": "Започнете сътрудничество на живо",
"createNewRoom": "Създай нова стая", "createNewRoom": "Създай нова стая",
"toggleFullScreen": "Превключване на цял екран", "fullScreen": "",
"toggleDarkMode": "", "darkMode": "",
"toggleZenMode": "", "lightMode": "",
"zenMode": "",
"exitZenMode": "" "exitZenMode": ""
}, },
"alerts": { "alerts": {
@@ -153,6 +161,7 @@
"freeDraw": "", "freeDraw": "",
"text": "", "text": "",
"linearElementMulti": "Кликнете върху последната точка или натиснете Escape или Enter, за да завършите", "linearElementMulti": "Кликнете върху последната точка или натиснете Escape или Enter, за да завършите",
"lockAngle": "",
"resize": "", "resize": "",
"rotate": "Можете да ограничите ъглите, като държите SHIFT, докато се въртите", "rotate": "Можете да ограничите ъглите, като държите SHIFT, докато се въртите",
"lineEditor_info": "", "lineEditor_info": "",
@@ -205,13 +214,22 @@
"textNewLine": "Добавяне на нов ред (текст)", "textNewLine": "Добавяне на нов ред (текст)",
"textFinish": "Завършете редактиране (текст)", "textFinish": "Завършете редактиране (текст)",
"zoomToFit": "", "zoomToFit": "",
"zoomToSelection": "",
"preventBinding": "" "preventBinding": ""
}, },
"encrypted": { "encrypted": {
"tooltip": "" "tooltip": ""
}, },
"charts": { "stats": {
"noNumericColumn": "", "angle": "",
"tooManyColumns": "" "element": "",
"elements": "",
"height": "",
"scene": "",
"selected": "",
"storage": "",
"title": "",
"total": "",
"width": ""
} }
} }
+26 -8
View File
@@ -4,6 +4,7 @@
"selectAll": "Seleccionar tot", "selectAll": "Seleccionar tot",
"multiSelect": "Afegir element a la selecció", "multiSelect": "Afegir element a la selecció",
"moveCanvas": "Moure el llenç", "moveCanvas": "Moure el llenç",
"cut": "",
"copy": "Copiar", "copy": "Copiar",
"copyAsPng": "Copiar al porta-retalls com a PNG", "copyAsPng": "Copiar al porta-retalls com a PNG",
"copyAsSvg": "Copiar al porta-retalls com a SVG", "copyAsSvg": "Copiar al porta-retalls com a SVG",
@@ -28,10 +29,15 @@
"edges": "Vores", "edges": "Vores",
"sharp": "Agut", "sharp": "Agut",
"round": "Arrodonit", "round": "Arrodonit",
"arrowheads": "",
"arrowhead_none": "",
"arrowhead_arrow": "",
"arrowhead_bar": "",
"arrowhead_dot": "",
"fontSize": "Mida de lletra", "fontSize": "Mida de lletra",
"fontFamily": "Tipus de lletra", "fontFamily": "Tipus de lletra",
"onlySelected": "Només seleccionats", "onlySelected": "Només seleccionats",
"withBackground": "Amb fons", "withBackground": "",
"exportEmbedScene": "", "exportEmbedScene": "",
"exportEmbedScene_details": "", "exportEmbedScene_details": "",
"addWatermark": "Afegir \"Fet amb Excalidraw\"", "addWatermark": "Afegir \"Fet amb Excalidraw\"",
@@ -70,10 +76,11 @@
"group": "Agrupar la selecció", "group": "Agrupar la selecció",
"ungroup": "Desagrupar la selecció", "ungroup": "Desagrupar la selecció",
"collaborators": "Col·laboradors", "collaborators": "Col·laboradors",
"toggleGridMode": "Commutar línies de graella", "gridMode": "",
"addToLibrary": "Afegir a la biblioteca", "addToLibrary": "Afegir a la biblioteca",
"removeFromLibrary": "Eliminar de la biblioteca", "removeFromLibrary": "Eliminar de la biblioteca",
"libraryLoadingMessage": "Carregant la biblioteca...", "libraryLoadingMessage": "Carregant la biblioteca...",
"libraries": "",
"loadingScene": "Carregant escena ...", "loadingScene": "Carregant escena ...",
"align": "", "align": "",
"alignTop": "", "alignTop": "",
@@ -110,9 +117,10 @@
"redo": "Refer", "redo": "Refer",
"roomDialog": "Començar col·laboració en directe", "roomDialog": "Començar col·laboració en directe",
"createNewRoom": "Crear sala nova", "createNewRoom": "Crear sala nova",
"toggleFullScreen": "Commutar pantalla completa", "fullScreen": "",
"toggleDarkMode": "Commutar modo fosc", "darkMode": "",
"toggleZenMode": "Commutar modo zen", "lightMode": "",
"zenMode": "",
"exitZenMode": "Sortir de modo zen" "exitZenMode": "Sortir de modo zen"
}, },
"alerts": { "alerts": {
@@ -153,6 +161,7 @@
"freeDraw": "Fer clic i arrosegar, deixar anar al punt final", "freeDraw": "Fer clic i arrosegar, deixar anar al punt final",
"text": "Consell: també pots afegir text fent doble clic a qualsevol lloc amb l'eina de selecció", "text": "Consell: també pots afegir text fent doble clic a qualsevol lloc amb l'eina de selecció",
"linearElementMulti": "Fer clic a l'ultim punt, o polsar Escape o Enter per acabar", "linearElementMulti": "Fer clic a l'ultim punt, o polsar Escape o Enter per acabar",
"lockAngle": "",
"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", "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",
"rotate": "Per restringir els angles mentre gira, mantenir premut el majúscul (SHIFT)", "rotate": "Per restringir els angles mentre gira, mantenir premut el majúscul (SHIFT)",
"lineEditor_info": "Fes doble clic o premi Enter per editar punts", "lineEditor_info": "Fes doble clic o premi Enter per editar punts",
@@ -205,13 +214,22 @@
"textNewLine": "Afegir línea nova (text)", "textNewLine": "Afegir línea nova (text)",
"textFinish": "Acabar d'editar (text)", "textFinish": "Acabar d'editar (text)",
"zoomToFit": "Zoom per veure tots els elements", "zoomToFit": "Zoom per veure tots els elements",
"zoomToSelection": "",
"preventBinding": "Prevenir vinculació de la fletxa" "preventBinding": "Prevenir vinculació de la fletxa"
}, },
"encrypted": { "encrypted": {
"tooltip": "Els vostres dibuixos estan xifrats de punta a punta de manera que els servidors dExcalidraw no els veuran mai." "tooltip": "Els vostres dibuixos estan xifrats de punta a punta de manera que els servidors dExcalidraw no els veuran mai."
}, },
"charts": { "stats": {
"noNumericColumn": "Has enganxat un full de càlcul sense columna numèrica.", "angle": "",
"tooManyColumns": "Has enganxat un full de càlcul amb més de dues columnes." "element": "",
"elements": "",
"height": "",
"scene": "",
"selected": "",
"storage": "",
"title": "",
"total": "",
"width": ""
} }
} }
+26 -8
View File
@@ -4,6 +4,7 @@
"selectAll": "Alle auswählen", "selectAll": "Alle auswählen",
"multiSelect": "Element zur Auswahl hinzufügen", "multiSelect": "Element zur Auswahl hinzufügen",
"moveCanvas": "Leinwand verschieben", "moveCanvas": "Leinwand verschieben",
"cut": "Ausschneiden",
"copy": "Kopieren", "copy": "Kopieren",
"copyAsPng": "In Zwischenablage kopieren (PNG)", "copyAsPng": "In Zwischenablage kopieren (PNG)",
"copyAsSvg": "In Zwischenablage kopieren (SVG)", "copyAsSvg": "In Zwischenablage kopieren (SVG)",
@@ -28,6 +29,11 @@
"edges": "Kanten", "edges": "Kanten",
"sharp": "Eckig", "sharp": "Eckig",
"round": "Rund", "round": "Rund",
"arrowheads": "Pfeilspitzen",
"arrowhead_none": "Keine",
"arrowhead_arrow": "Pfeil",
"arrowhead_bar": "Balken",
"arrowhead_dot": "Punkt",
"fontSize": "Schriftgröße", "fontSize": "Schriftgröße",
"fontFamily": "Schriftfamilie", "fontFamily": "Schriftfamilie",
"onlySelected": "Nur ausgewählte", "onlySelected": "Nur ausgewählte",
@@ -70,10 +76,11 @@
"group": "Auswahl gruppieren", "group": "Auswahl gruppieren",
"ungroup": "Gruppierung aufheben", "ungroup": "Gruppierung aufheben",
"collaborators": "Mitarbeitende", "collaborators": "Mitarbeitende",
"toggleGridMode": "Gitterlinien ein-/ausschalten", "gridMode": "Rastermodus",
"addToLibrary": "Zur Bibliothek hinzufügen", "addToLibrary": "Zur Bibliothek hinzufügen",
"removeFromLibrary": "Aus Bibliothek entfernen", "removeFromLibrary": "Aus Bibliothek entfernen",
"libraryLoadingMessage": "Lade Bibliothek...", "libraryLoadingMessage": "Lade Bibliothek...",
"libraries": "Bibliotheken durchsuchen",
"loadingScene": "Lade Zeichnung...", "loadingScene": "Lade Zeichnung...",
"align": "Ausrichten", "align": "Ausrichten",
"alignTop": "Obere Kanten", "alignTop": "Obere Kanten",
@@ -110,9 +117,10 @@
"redo": "Wiederholen", "redo": "Wiederholen",
"roomDialog": "Live-Kollaborationssitzung starten", "roomDialog": "Live-Kollaborationssitzung starten",
"createNewRoom": "Neuen Raum erstellen", "createNewRoom": "Neuen Raum erstellen",
"toggleFullScreen": "Vollbild umschalten", "fullScreen": "Vollbildanzeige",
"toggleDarkMode": "Dunkles Design umschalten", "darkMode": "Dunkles Design",
"toggleZenMode": "Zen-Modus umschalten", "lightMode": "Helles Design",
"zenMode": "Zen-Modus",
"exitZenMode": "Zen-Modus verlassen" "exitZenMode": "Zen-Modus verlassen"
}, },
"alerts": { "alerts": {
@@ -128,7 +136,7 @@
"loadSceneOverridePrompt": "Das Laden der externen Zeichnung ersetzt den vorhandenen Inhalt. Möchtest Du fortfahren?", "loadSceneOverridePrompt": "Das Laden der externen Zeichnung ersetzt den vorhandenen Inhalt. Möchtest Du fortfahren?",
"errorLoadingLibrary": "Beim Laden der Drittanbieter-Bibliothek ist ein Fehler aufgetreten.", "errorLoadingLibrary": "Beim Laden der Drittanbieter-Bibliothek ist ein Fehler aufgetreten.",
"confirmAddLibrary": "Dieses fügt {{numShapes}} Form(en) zu deiner Bibliothek hinzu. Bist du sicher?", "confirmAddLibrary": "Dieses fügt {{numShapes}} Form(en) zu deiner Bibliothek hinzu. Bist du sicher?",
"imageDoesNotContainScene": "Bilddatei enthält keine Zeichnungsdaten. Hast du das Einbetten beim Export aktiviert?", "imageDoesNotContainScene": "Das Importieren von Bildern wird derzeit nicht unterstützt.\n\nMöchtest du eine Szene importieren? Dieses Bild scheint keine Zeichnungsdaten zu enthalten. Hast du dies beim Exportieren aktiviert?",
"cannotRestoreFromImage": "Die Zeichnung konnte aus dieser Bilddatei nicht wiederhergestellt werden" "cannotRestoreFromImage": "Die Zeichnung konnte aus dieser Bilddatei nicht wiederhergestellt werden"
}, },
"toolBar": { "toolBar": {
@@ -153,6 +161,7 @@
"freeDraw": "Klicke und ziehe. Lass los, wenn du fertig bist", "freeDraw": "Klicke und ziehe. Lass los, wenn du fertig bist",
"text": "Tipp: Du kannst auch Text hinzufügen indem Du mit dem Auswahlwerkzeug auf eine beliebige Stelle doppelklickst", "text": "Tipp: Du kannst auch Text hinzufügen indem Du mit dem Auswahlwerkzeug auf eine beliebige Stelle doppelklickst",
"linearElementMulti": "Zum Beenden auf den letzten Punkt klicken oder Escape oder Eingabe drücken", "linearElementMulti": "Zum Beenden auf den letzten Punkt klicken oder Escape oder Eingabe drücken",
"lockAngle": "Du kannst Winkel einschränken, indem du SHIFT gedrückt hältst",
"resize": "Du kannst die Proportionen einschränken, indem du SHIFT während der Größenänderung gedrückt hältst. Halte ALT gedrückt, um die Größe vom Zentrum aus zu ändern", "resize": "Du kannst die Proportionen einschränken, indem du SHIFT während der Größenänderung gedrückt hältst. Halte ALT gedrückt, um die Größe vom Zentrum aus zu ändern",
"rotate": "Du kannst Winkel einschränken, indem du SHIFT während der Drehung gedrückt hältst", "rotate": "Du kannst Winkel einschränken, indem du SHIFT während der Drehung gedrückt hältst",
"lineEditor_info": "Doppelklicken oder Eingabetaste drücken, um Punkte zu bearbeiten", "lineEditor_info": "Doppelklicken oder Eingabetaste drücken, um Punkte zu bearbeiten",
@@ -205,13 +214,22 @@
"textNewLine": "Neue Zeile hinzufügen (Text)", "textNewLine": "Neue Zeile hinzufügen (Text)",
"textFinish": "Bearbeiten beenden (Text)", "textFinish": "Bearbeiten beenden (Text)",
"zoomToFit": "Zoomen um alle Elemente einzupassen", "zoomToFit": "Zoomen um alle Elemente einzupassen",
"zoomToSelection": "Zoomauswahl",
"preventBinding": "Pfeil-Bindung verhindern" "preventBinding": "Pfeil-Bindung verhindern"
}, },
"encrypted": { "encrypted": {
"tooltip": "Da deine Zeichnungen Ende-zu-Ende verschlüsselt werden, sehen auch unsere Excalidraw-Server sie niemals." "tooltip": "Da deine Zeichnungen Ende-zu-Ende verschlüsselt werden, sehen auch unsere Excalidraw-Server sie niemals."
}, },
"charts": { "stats": {
"noNumericColumn": "Du hast eine Tabelle ohne numerische Spalte eingefügt.", "angle": "Winkel",
"tooManyColumns": "Du hast eine Tabelle mit mehr als zwei Spalten eingefügt." "element": "Element",
"elements": "Elemente",
"height": "Höhe",
"scene": "Zeichnung",
"selected": "Ausgewählt",
"storage": "Speicher",
"title": "Statistiken für Nerds",
"total": "Gesamt",
"width": "Breite"
} }
} }
+28 -10
View File
@@ -4,6 +4,7 @@
"selectAll": "Επιλογή όλων", "selectAll": "Επιλογή όλων",
"multiSelect": "Προσθέστε το στοιχείο στην επιλογή", "multiSelect": "Προσθέστε το στοιχείο στην επιλογή",
"moveCanvas": "Μετακίνηση καμβά", "moveCanvas": "Μετακίνηση καμβά",
"cut": "Αποκοπή",
"copy": "Αντιγραφή", "copy": "Αντιγραφή",
"copyAsPng": "Αντιγραφή στο πρόχειρο ως PNG", "copyAsPng": "Αντιγραφή στο πρόχειρο ως PNG",
"copyAsSvg": "Αντιγραφή στο πρόχειρο ως SVG", "copyAsSvg": "Αντιγραφή στο πρόχειρο ως SVG",
@@ -28,10 +29,15 @@
"edges": "Άκρες", "edges": "Άκρες",
"sharp": "Οξύ", "sharp": "Οξύ",
"round": "Στρογγυλό", "round": "Στρογγυλό",
"arrowheads": "Σύμβολα βελών",
"arrowhead_none": "Κανένα",
"arrowhead_arrow": "Βέλος",
"arrowhead_bar": "Μπάρα",
"arrowhead_dot": "Τελεία",
"fontSize": "Μέγεθος γραμματοσειράς", "fontSize": "Μέγεθος γραμματοσειράς",
"fontFamily": "Γραμματοσειρά", "fontFamily": "Γραμματοσειρά",
"onlySelected": "Μόνο τα Επιλεγμένα", "onlySelected": "Μόνο τα Επιλεγμένα",
"withBackground": "Με Φόντο", "withBackground": "Με φόντο",
"exportEmbedScene": "Ενσωμάτωση της σκηνής στο αρχείο προς εξαγωγή", "exportEmbedScene": "Ενσωμάτωση της σκηνής στο αρχείο προς εξαγωγή",
"exportEmbedScene_details": "Τα δεδομένα σκηνής θα αποθηκευτούν στο αρχείο PNG/SVG προς εξαγωγή ώστε η σκηνή να είναι δυνατό να αποκατασταθεί από αυτό.\nΘα αυξήσει το μέγεθος του αρχείου προς εξαγωγή.", "exportEmbedScene_details": "Τα δεδομένα σκηνής θα αποθηκευτούν στο αρχείο PNG/SVG προς εξαγωγή ώστε η σκηνή να είναι δυνατό να αποκατασταθεί από αυτό.\nΘα αυξήσει το μέγεθος του αρχείου προς εξαγωγή.",
"addWatermark": "Προσθήκη \"Φτιαγμένο με Excalidraw\"", "addWatermark": "Προσθήκη \"Φτιαγμένο με Excalidraw\"",
@@ -70,10 +76,11 @@
"group": "Δημιουργία ομάδας από επιλογή", "group": "Δημιουργία ομάδας από επιλογή",
"ungroup": "Κατάργηση ομάδας από επιλογή", "ungroup": "Κατάργηση ομάδας από επιλογή",
"collaborators": "Συνεργάτες", "collaborators": "Συνεργάτες",
"toggleGridMode": "Εναλλαγή λειτουργίας πλέγματος", "gridMode": "Εμφάνιση σε πλέγμα",
"addToLibrary": "Προσθήκη στη βιβλιοθήκη", "addToLibrary": "Προσθήκη στη βιβλιοθήκη",
"removeFromLibrary": "Αφαίρεση από τη βιβλιοθήκη", "removeFromLibrary": "Αφαίρεση από τη βιβλιοθήκη",
"libraryLoadingMessage": "Φόρτωση βιβλιοθήκης...", "libraryLoadingMessage": "Φόρτωση βιβλιοθήκης...",
"libraries": "Άλλες βιβλιοθήκες",
"loadingScene": "Φόρτωση σκηνής...", "loadingScene": "Φόρτωση σκηνής...",
"align": "Στοίχιση", "align": "Στοίχιση",
"alignTop": "Στοίχιση πάνω", "alignTop": "Στοίχιση πάνω",
@@ -110,9 +117,10 @@
"redo": "Επαναφορά", "redo": "Επαναφορά",
"roomDialog": "Έναρξη ζωντανής συνεργασίας", "roomDialog": "Έναρξη ζωντανής συνεργασίας",
"createNewRoom": "Δημιουργία νέου χώρου", "createNewRoom": "Δημιουργία νέου χώρου",
"toggleFullScreen": "Εναλλαγή πλήρους οθόνης", "fullScreen": "Πλήρης οθόνη",
"toggleDarkMode": "Εναλλαγή εμφάνισης σε dark", "darkMode": "Σκοτεινή λειτουργία",
"toggleZenMode": "Εναλλαγή λειτουργίας Zen", "lightMode": "Φωτεινή λειτουργία",
"zenMode": "Λειτουργία Zεν",
"exitZenMode": "Έξοδος απο την λειτουργία Zen" "exitZenMode": "Έξοδος απο την λειτουργία Zen"
}, },
"alerts": { "alerts": {
@@ -128,7 +136,7 @@
"loadSceneOverridePrompt": "Η φόρτωση εξωτερικού σχεδίου θα αντικαταστήσει το υπάρχον περιεχόμενο. Επιθυμείτε να συνεχίσετε;", "loadSceneOverridePrompt": "Η φόρτωση εξωτερικού σχεδίου θα αντικαταστήσει το υπάρχον περιεχόμενο. Επιθυμείτε να συνεχίσετε;",
"errorLoadingLibrary": "Υπήρξε ένα σφάλμα κατά τη φόρτωση της βιβλιοθήκης τρίτου μέρους.", "errorLoadingLibrary": "Υπήρξε ένα σφάλμα κατά τη φόρτωση της βιβλιοθήκης τρίτου μέρους.",
"confirmAddLibrary": "Αυτό θα προσθέσει {{numShapes}} σχήμα(τα) στη βιβιλιοθήκη σας. Είστε σίγουροι;", "confirmAddLibrary": "Αυτό θα προσθέσει {{numShapes}} σχήμα(τα) στη βιβιλιοθήκη σας. Είστε σίγουροι;",
"imageDoesNotContainScene": "Το αρχείο εικόνας δεν έχει δεδομένα σκηνής. Το είχατε ενεργοποιήσει αυτό κατά την εξαγωγή;", "imageDoesNotContainScene": "",
"cannotRestoreFromImage": "Η σκηνή δεν ήταν δυνατό να αποκατασταθεί από αυτό το αρχείο εικόνας" "cannotRestoreFromImage": "Η σκηνή δεν ήταν δυνατό να αποκατασταθεί από αυτό το αρχείο εικόνας"
}, },
"toolBar": { "toolBar": {
@@ -153,6 +161,7 @@
"freeDraw": "Κάντε κλικ και σύρατε, απελευθερώσατε όταν έχετε τελειώσει", "freeDraw": "Κάντε κλικ και σύρατε, απελευθερώσατε όταν έχετε τελειώσει",
"text": "Tip: μπορείτε επίσης να προσθέστε κείμενο με διπλό-κλικ οπουδήποτε με το εργαλείο επιλογών", "text": "Tip: μπορείτε επίσης να προσθέστε κείμενο με διπλό-κλικ οπουδήποτε με το εργαλείο επιλογών",
"linearElementMulti": "Κάνε κλικ στο τελευταίο σημείο ή πάτησε Escape ή Enter για να τελειώσεις", "linearElementMulti": "Κάνε κλικ στο τελευταίο σημείο ή πάτησε Escape ή Enter για να τελειώσεις",
"lockAngle": "",
"resize": "Μπορείς να περιορίσεις τις αναλογίες κρατώντας το SHIFT ενώ αλλάζεις μέγεθος,\nκράτησε πατημένο το ALT για αλλαγή μεγέθους από το κέντρο", "resize": "Μπορείς να περιορίσεις τις αναλογίες κρατώντας το SHIFT ενώ αλλάζεις μέγεθος,\nκράτησε πατημένο το ALT για αλλαγή μεγέθους από το κέντρο",
"rotate": "Μπορείς να περιορίσεις τις γωνίες κρατώντας πατημένο το πλήκτρο SHIFT κατά την περιστροφή", "rotate": "Μπορείς να περιορίσεις τις γωνίες κρατώντας πατημένο το πλήκτρο SHIFT κατά την περιστροφή",
"lineEditor_info": "Διπλό-κλικ ή πιέστε Enter για να επεξεργαστείτε τα σημεία", "lineEditor_info": "Διπλό-κλικ ή πιέστε Enter για να επεξεργαστείτε τα σημεία",
@@ -161,7 +170,7 @@
}, },
"canvasError": { "canvasError": {
"cannotShowPreview": "", "cannotShowPreview": "",
"canvasTooBig": "", "canvasTooBig": "Ο καμβάς μπορεί να είναι μεγάλος.",
"canvasTooBigTip": "" "canvasTooBigTip": ""
}, },
"errorSplash": { "errorSplash": {
@@ -205,13 +214,22 @@
"textNewLine": "Προσθήκη νέας γραμμής (κείμενο)", "textNewLine": "Προσθήκη νέας γραμμής (κείμενο)",
"textFinish": "Ολοκλήρωση επεξεργασίας (κείμενο)", "textFinish": "Ολοκλήρωση επεξεργασίας (κείμενο)",
"zoomToFit": "Zoom ώστε να χωρέσουν όλα τα στοιχεία", "zoomToFit": "Zoom ώστε να χωρέσουν όλα τα στοιχεία",
"zoomToSelection": "",
"preventBinding": "Αποτροπή δέσμευσης βέλων" "preventBinding": "Αποτροπή δέσμευσης βέλων"
}, },
"encrypted": { "encrypted": {
"tooltip": "Τα σχέδιά σου είναι κρυπτογραφημένα από άκρο σε άκρο, έτσι δεν θα έιναι ποτέ ορατά μέσα από τους διακομιστές του Excalidraw." "tooltip": "Τα σχέδιά σου είναι κρυπτογραφημένα από άκρο σε άκρο, έτσι δεν θα έιναι ποτέ ορατά μέσα από τους διακομιστές του Excalidraw."
}, },
"charts": { "stats": {
"noNumericColumn": "Επικόλλησες ένα υπολογιστικό φύλλο χωρίς αριθμητική στήλη.", "angle": "Γωνία",
"tooManyColumns": "Επικόλλησες ένα υπολογιστικό φύλλο με περισσότερες από δύο στήλες." "element": "Στοιχείο",
"elements": "Στοιχεία",
"height": "Ύψος",
"scene": "Σκηνή",
"selected": "Επιλεγμένα",
"storage": "Χώρος",
"title": "Στατιστικά για σπασίκλες",
"total": "Σύνολο ",
"width": "Πλάτος"
} }
} }
+10 -7
View File
@@ -1,6 +1,7 @@
{ {
"labels": { "labels": {
"paste": "Paste", "paste": "Paste",
"pasteCharts": "Paste charts",
"selectAll": "Select all", "selectAll": "Select all",
"multiSelect": "Add element to selection", "multiSelect": "Add element to selection",
"moveCanvas": "Move canvas", "moveCanvas": "Move canvas",
@@ -37,7 +38,7 @@
"fontSize": "Font size", "fontSize": "Font size",
"fontFamily": "Font family", "fontFamily": "Font family",
"onlySelected": "Only selected", "onlySelected": "Only selected",
"withBackground": "With Background", "withBackground": "With background",
"exportEmbedScene": "Embed scene into exported file", "exportEmbedScene": "Embed scene into exported file",
"exportEmbedScene_details": "Scene data will be saved into the exported PNG/SVG file so that the scene can be restored from it.\nWill increase exported file size.", "exportEmbedScene_details": "Scene data will be saved into the exported PNG/SVG file so that the scene can be restored from it.\nWill increase exported file size.",
"addWatermark": "Add \"Made with Excalidraw\"", "addWatermark": "Add \"Made with Excalidraw\"",
@@ -76,8 +77,7 @@
"group": "Group selection", "group": "Group selection",
"ungroup": "Ungroup selection", "ungroup": "Ungroup selection",
"collaborators": "Collaborators", "collaborators": "Collaborators",
"toggleGridMode": "Toggle grid mode", "gridMode": "Grid mode",
"toggleStats": "Toggle stats for nerds",
"addToLibrary": "Add to library", "addToLibrary": "Add to library",
"removeFromLibrary": "Remove from library", "removeFromLibrary": "Remove from library",
"libraryLoadingMessage": "Loading library...", "libraryLoadingMessage": "Loading library...",
@@ -118,9 +118,10 @@
"redo": "Redo", "redo": "Redo",
"roomDialog": "Start live collaboration", "roomDialog": "Start live collaboration",
"createNewRoom": "Create new room", "createNewRoom": "Create new room",
"toggleFullScreen": "Toggle full screen", "fullScreen": "Full screen",
"toggleDarkMode": "Toggle dark mode", "darkMode": "Dark mode",
"toggleZenMode": "Toggle zen mode", "lightMode": "Light mode",
"zenMode": "Zen mode",
"exitZenMode": "Exit zen mode" "exitZenMode": "Exit zen mode"
}, },
"alerts": { "alerts": {
@@ -136,7 +137,7 @@
"loadSceneOverridePrompt": "Loading external drawing will replace your existing content. Do you wish to continue?", "loadSceneOverridePrompt": "Loading external drawing will replace your existing content. Do you wish to continue?",
"errorLoadingLibrary": "There was an error loading the third party library.", "errorLoadingLibrary": "There was an error loading the third party library.",
"confirmAddLibrary": "This will add {{numShapes}} shape(s) to your library. Are you sure?", "confirmAddLibrary": "This will add {{numShapes}} shape(s) to your library. Are you sure?",
"imageDoesNotContainScene": "Image file doesn't contain scene data. Have you enabled this during export?", "imageDoesNotContainScene": "Importing images isn't supported at the moment.\n\nDid you want to import a scene? This image does not seem to contain any scene data. Have you enabled this during export?",
"cannotRestoreFromImage": "Scene couldn't be restored from this image file" "cannotRestoreFromImage": "Scene couldn't be restored from this image file"
}, },
"toolBar": { "toolBar": {
@@ -161,6 +162,7 @@
"freeDraw": "Click and drag, release when you're finished", "freeDraw": "Click and drag, release when you're finished",
"text": "Tip: you can also add text by double-clicking anywhere with the selection tool", "text": "Tip: you can also add text by double-clicking anywhere with the selection tool",
"linearElementMulti": "Click on last point or press Escape or Enter to finish", "linearElementMulti": "Click on last point or press Escape or Enter to finish",
"lockAngle": "You can constrain angle by holding SHIFT",
"resize": "You can constrain proportions by holding SHIFT while resizing,\nhold ALT to resize from the center", "resize": "You can constrain proportions by holding SHIFT while resizing,\nhold ALT to resize from the center",
"rotate": "You can constrain angles by holding SHIFT while rotating", "rotate": "You can constrain angles by holding SHIFT while rotating",
"lineEditor_info": "Double-click or press Enter to edit points", "lineEditor_info": "Double-click or press Enter to edit points",
@@ -213,6 +215,7 @@
"textNewLine": "Add new line (text)", "textNewLine": "Add new line (text)",
"textFinish": "Finish editing (text)", "textFinish": "Finish editing (text)",
"zoomToFit": "Zoom to fit all elements", "zoomToFit": "Zoom to fit all elements",
"zoomToSelection": "Zoom to selection",
"preventBinding": "Prevent arrow binding" "preventBinding": "Prevent arrow binding"
}, },
"encrypted": { "encrypted": {
+44 -26
View File
@@ -4,6 +4,7 @@
"selectAll": "Seleccionar todo", "selectAll": "Seleccionar todo",
"multiSelect": "Añadir elemento a la selección", "multiSelect": "Añadir elemento a la selección",
"moveCanvas": "Mover el lienzo", "moveCanvas": "Mover el lienzo",
"cut": "Cortar",
"copy": "Copiar", "copy": "Copiar",
"copyAsPng": "Copiar al portapapeles como PNG", "copyAsPng": "Copiar al portapapeles como PNG",
"copyAsSvg": "Copiar al portapapeles como SVG", "copyAsSvg": "Copiar al portapapeles como SVG",
@@ -28,12 +29,17 @@
"edges": "Bordes", "edges": "Bordes",
"sharp": "Afilado", "sharp": "Afilado",
"round": "Redondo", "round": "Redondo",
"arrowheads": "Puntas de flecha",
"arrowhead_none": "Vacía",
"arrowhead_arrow": "Flecha",
"arrowhead_bar": "Barra",
"arrowhead_dot": "Punto",
"fontSize": "Tamaño de la fuente", "fontSize": "Tamaño de la fuente",
"fontFamily": "Tipo de fuente", "fontFamily": "Tipo de fuente",
"onlySelected": "Sólo seleccionados", "onlySelected": "Sólo seleccionados",
"withBackground": "Con fondo", "withBackground": "Con fondo",
"exportEmbedScene": "", "exportEmbedScene": "Insertar escena en el archivo exportado",
"exportEmbedScene_details": "", "exportEmbedScene_details": "Los datos de escena se guardarán en el archivo PNG/SVG exportado para que la escena pueda ser restaurada de ella.\nIncrementará el tamaño del archivo exportado.",
"addWatermark": "Agregar \"Hecho con Excalidraw\"", "addWatermark": "Agregar \"Hecho con Excalidraw\"",
"handDrawn": "Dibujado a mano", "handDrawn": "Dibujado a mano",
"normal": "Normal", "normal": "Normal",
@@ -54,7 +60,7 @@
"architect": "Arquitecto", "architect": "Arquitecto",
"artist": "Artista", "artist": "Artista",
"cartoonist": "Caricatura", "cartoonist": "Caricatura",
"fileTitle": "", "fileTitle": "Título del archivo",
"colorPicker": "Selector de color", "colorPicker": "Selector de color",
"canvasBackground": "Fondo del lienzo", "canvasBackground": "Fondo del lienzo",
"drawingCanvas": "Lienzo de dibujo", "drawingCanvas": "Lienzo de dibujo",
@@ -63,27 +69,28 @@
"language": "Idioma", "language": "Idioma",
"createRoom": "Compartir una sesión de colaboración en vivo", "createRoom": "Compartir una sesión de colaboración en vivo",
"duplicateSelection": "Duplicar", "duplicateSelection": "Duplicar",
"untitled": "", "untitled": "Sin título",
"name": "Nombre", "name": "Nombre",
"yourName": "Tu nombre", "yourName": "Tu nombre",
"madeWithExcalidraw": "Hecho con Excalidraw", "madeWithExcalidraw": "Hecho con Excalidraw",
"group": "Selección de grupo", "group": "Selección de grupo",
"ungroup": "Desagrupar", "ungroup": "Desagrupar",
"collaborators": "Colaboradores", "collaborators": "Colaboradores",
"toggleGridMode": "Alternar modo cuadrícula", "gridMode": "Modo cuadrícula",
"addToLibrary": "Añadir a la biblioteca", "addToLibrary": "Añadir a la biblioteca",
"removeFromLibrary": "Eliminar de la biblioteca", "removeFromLibrary": "Eliminar de la biblioteca",
"libraryLoadingMessage": "Cargando biblioteca...", "libraryLoadingMessage": "Cargando biblioteca...",
"libraries": "Explorar librerías",
"loadingScene": "Cargando escena...", "loadingScene": "Cargando escena...",
"align": "", "align": "Alinear",
"alignTop": "", "alignTop": "Alinear arriba",
"alignBottom": "", "alignBottom": "Alinear abajo",
"alignLeft": "", "alignLeft": "Alinear a la izquierda",
"alignRight": "", "alignRight": "Alinear a la derecha",
"centerVertically": "", "centerVertically": "Centrar verticalmente",
"centerHorizontally": "", "centerHorizontally": "Centrar horizontalmente",
"distributeHorizontally": "", "distributeHorizontally": "Distribuir horizontalmente",
"distributeVertically": "" "distributeVertically": "Distribuir verticalmente"
}, },
"buttons": { "buttons": {
"clearReset": "Limpiar lienzo y reiniciar el color de fondo", "clearReset": "Limpiar lienzo y reiniciar el color de fondo",
@@ -110,15 +117,16 @@
"redo": "Rehacer", "redo": "Rehacer",
"roomDialog": "Iniciar colaboración en vivo", "roomDialog": "Iniciar colaboración en vivo",
"createNewRoom": "Crear nueva sala", "createNewRoom": "Crear nueva sala",
"toggleFullScreen": "Alternar pantalla completa", "fullScreen": "Pantalla completa",
"toggleDarkMode": "Cambiar a modo oscuro", "darkMode": "Modo oscuro",
"toggleZenMode": "Alternar modo zen", "lightMode": "Modo claro",
"zenMode": "Modo Zen",
"exitZenMode": "Salir del modo Zen" "exitZenMode": "Salir del modo Zen"
}, },
"alerts": { "alerts": {
"clearReset": "Esto limpiará todo el lienzo. Estás seguro?", "clearReset": "Esto limpiará todo el lienzo. Estás seguro?",
"couldNotCreateShareableLink": "No se pudo crear un enlace para compartir.", "couldNotCreateShareableLink": "No se pudo crear un enlace para compartir.",
"couldNotCreateShareableLinkTooBig": "", "couldNotCreateShareableLinkTooBig": "No se pudo crear el enlace para compartir: la escena es demasiado grande",
"couldNotLoadInvalidFile": "No se pudo cargar el archivo inválido", "couldNotLoadInvalidFile": "No se pudo cargar el archivo inválido",
"importBackendFailed": "La importación falló.", "importBackendFailed": "La importación falló.",
"cannotExportEmptyCanvas": "No se puede exportar un lienzo vació", "cannotExportEmptyCanvas": "No se puede exportar un lienzo vació",
@@ -128,8 +136,8 @@
"loadSceneOverridePrompt": "Si carga este dibujo externo, reemplazará el que tiene. ¿Desea continuar?", "loadSceneOverridePrompt": "Si carga este dibujo externo, reemplazará el que tiene. ¿Desea continuar?",
"errorLoadingLibrary": "Se ha producido un error al cargar la biblioteca de terceros.", "errorLoadingLibrary": "Se ha producido un error al cargar la biblioteca de terceros.",
"confirmAddLibrary": "Esto añadirá {{numShapes}} forma(s) a tu biblioteca. ¿Estás seguro?", "confirmAddLibrary": "Esto añadirá {{numShapes}} forma(s) a tu biblioteca. ¿Estás seguro?",
"imageDoesNotContainScene": "", "imageDoesNotContainScene": "La importación de imágenes no está soportada en este momento.\n\n¿Querías importar una escena? Esta imagen no parece contener ningún dato de escena. ¿Lo ha activado durante la exportación?",
"cannotRestoreFromImage": "" "cannotRestoreFromImage": "No se pudo restaurar la escena desde este archivo de imagen"
}, },
"toolBar": { "toolBar": {
"selection": "Selección", "selection": "Selección",
@@ -153,6 +161,7 @@
"freeDraw": "Haz clic y arrastra, suelta al terminar", "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": "Consejo: también puedes añadir texto haciendo doble clic en cualquier lugar con la herramienta de selección",
"linearElementMulti": "Haga clic en el último punto o pulse Escape o Enter para finalizar", "linearElementMulti": "Haga clic en el último punto o pulse Escape o Enter para finalizar",
"lockAngle": "",
"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", "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",
"rotate": "Puede restringir los ángulos manteniendo presionado SHIFT mientras gira", "rotate": "Puede restringir los ángulos manteniendo presionado SHIFT mientras gira",
"lineEditor_info": "haga doble clic o pulse Enter para editar puntos", "lineEditor_info": "haga doble clic o pulse Enter para editar puntos",
@@ -160,9 +169,9 @@
"lineEditor_nothingSelected": "Seleccione un punto para mover o eliminar, o mantenga pulsado Alt y haga clic para añadir nuevos puntos" "lineEditor_nothingSelected": "Seleccione un punto para mover o eliminar, o mantenga pulsado Alt y haga clic para añadir nuevos puntos"
}, },
"canvasError": { "canvasError": {
"cannotShowPreview": "", "cannotShowPreview": "No se puede mostrar la vista previa",
"canvasTooBig": "", "canvasTooBig": "El lienzo podría ser demasiado grande.",
"canvasTooBigTip": "" "canvasTooBigTip": "Sugerencia: intenta acercar un poco los elementos más lejanos."
}, },
"errorSplash": { "errorSplash": {
"headingMain_pre": "Se encontró un error. Intente ", "headingMain_pre": "Se encontró un error. Intente ",
@@ -205,13 +214,22 @@
"textNewLine": "Añadir nueva línea (texto)", "textNewLine": "Añadir nueva línea (texto)",
"textFinish": "Finalizar edición (texto)", "textFinish": "Finalizar edición (texto)",
"zoomToFit": "Ajustar para mostrar todos los elementos", "zoomToFit": "Ajustar para mostrar todos los elementos",
"zoomToSelection": "Hacer zoom a la selección",
"preventBinding": "Evitar enlace de flecha" "preventBinding": "Evitar enlace de flecha"
}, },
"encrypted": { "encrypted": {
"tooltip": "Tus dibujos están cifrados de punto a punto, por lo que los servidores de Excalidraw nunca los verán." "tooltip": "Tus dibujos están cifrados de punto a punto, por lo que los servidores de Excalidraw nunca los verán."
}, },
"charts": { "stats": {
"noNumericColumn": "Pegaste una hoja de cálculo sin una columna numérica.", "angle": "Ángulo",
"tooManyColumns": "Pegaste una hoja de cálculo con más de dos columnas." "element": "Elemento",
"elements": "Elementos",
"height": "Alto",
"scene": "Escena",
"selected": "Seleccionado",
"storage": "Almacenamiento",
"title": "",
"total": "Total",
"width": "Ancho"
} }
} }
+27 -9
View File
@@ -4,6 +4,7 @@
"selectAll": "انتخاب همه", "selectAll": "انتخاب همه",
"multiSelect": "یک ایتم به انتخاب شده ها اضافه کنید.", "multiSelect": "یک ایتم به انتخاب شده ها اضافه کنید.",
"moveCanvas": "بوم را حرکت بدهید", "moveCanvas": "بوم را حرکت بدهید",
"cut": "",
"copy": "کپی", "copy": "کپی",
"copyAsPng": "کپی در حافطه موقت به صورت PNG", "copyAsPng": "کپی در حافطه موقت به صورت PNG",
"copyAsSvg": "کپی در حافطه موقت به صورت SVG", "copyAsSvg": "کپی در حافطه موقت به صورت SVG",
@@ -28,10 +29,15 @@
"edges": "لبه ها", "edges": "لبه ها",
"sharp": "تیز", "sharp": "تیز",
"round": "دور", "round": "دور",
"arrowheads": "",
"arrowhead_none": "",
"arrowhead_arrow": "",
"arrowhead_bar": "",
"arrowhead_dot": "",
"fontSize": "اندازه قلم", "fontSize": "اندازه قلم",
"fontFamily": "نوع قلم", "fontFamily": "نوع قلم",
"onlySelected": "فقط انتخاب شده ها", "onlySelected": "فقط انتخاب شده ها",
"withBackground": "با پس زمینه", "withBackground": "",
"exportEmbedScene": "قرار دادن صحنه در فایل خروجی", "exportEmbedScene": "قرار دادن صحنه در فایل خروجی",
"exportEmbedScene_details": "متحوای صحنه به فایل خروجی SVG/PNG اضافه خواهد شد برای بازیابی صحنه به آن اضافه خواهد شد.\nباعث افزایش حجم فایل خروجی میشود.", "exportEmbedScene_details": "متحوای صحنه به فایل خروجی SVG/PNG اضافه خواهد شد برای بازیابی صحنه به آن اضافه خواهد شد.\nباعث افزایش حجم فایل خروجی میشود.",
"addWatermark": "\"ساخته شده با Excalidraw\" را اضافه کن", "addWatermark": "\"ساخته شده با Excalidraw\" را اضافه کن",
@@ -70,10 +76,11 @@
"group": "گروهبندی انتخابها", "group": "گروهبندی انتخابها",
"ungroup": "حذف گروهبندی انتخابها", "ungroup": "حذف گروهبندی انتخابها",
"collaborators": "همکاران", "collaborators": "همکاران",
"toggleGridMode": "سويچ خطوط راهنما", "gridMode": "",
"addToLibrary": "افزودن به کتابخانه", "addToLibrary": "افزودن به کتابخانه",
"removeFromLibrary": "حذف از کتابخانه", "removeFromLibrary": "حذف از کتابخانه",
"libraryLoadingMessage": "بارگذاری کتابخانه...", "libraryLoadingMessage": "بارگذاری کتابخانه...",
"libraries": "",
"loadingScene": "باگذاری صحنه...", "loadingScene": "باگذاری صحنه...",
"align": "تراز", "align": "تراز",
"alignTop": "تراز به بالا", "alignTop": "تراز به بالا",
@@ -110,9 +117,10 @@
"redo": "از سر", "redo": "از سر",
"roomDialog": "همکاری آنلاین را شروع کنید", "roomDialog": "همکاری آنلاین را شروع کنید",
"createNewRoom": "ایجاد یک اتاق جدید", "createNewRoom": "ایجاد یک اتاق جدید",
"toggleFullScreen": "تغییر به حالت تمام صفحه", "fullScreen": "",
"toggleDarkMode": "تغییر به حالت تاریک", "darkMode": "",
"toggleZenMode": "تغییر به حالت تمرکز", "lightMode": "",
"zenMode": "",
"exitZenMode": "خروج از حالت تمرکز" "exitZenMode": "خروج از حالت تمرکز"
}, },
"alerts": { "alerts": {
@@ -128,7 +136,7 @@
"loadSceneOverridePrompt": "بارگزاری یک طرح خارجی محتوای فعلی رو از بین میبرد. آیا میخواهید ادامه دهید؟", "loadSceneOverridePrompt": "بارگزاری یک طرح خارجی محتوای فعلی رو از بین میبرد. آیا میخواهید ادامه دهید؟",
"errorLoadingLibrary": "خطایی در بارگذاری کتابخانه ثالث وجود داشت.", "errorLoadingLibrary": "خطایی در بارگذاری کتابخانه ثالث وجود داشت.",
"confirmAddLibrary": "{{numShapes}} از اشکال به کتابخانه شما اضافه خواهد شد. مطمئن هستید؟", "confirmAddLibrary": "{{numShapes}} از اشکال به کتابخانه شما اضافه خواهد شد. مطمئن هستید؟",
"imageDoesNotContainScene": "فایل تصویر دارای محتوای صحنه نیست. آیا در هنگام خروجی گرفتن آن را فعال کرده‌اید؟", "imageDoesNotContainScene": "",
"cannotRestoreFromImage": "صحنه را نمی توان از این فایل تصویری بازیابی کرد" "cannotRestoreFromImage": "صحنه را نمی توان از این فایل تصویری بازیابی کرد"
}, },
"toolBar": { "toolBar": {
@@ -153,6 +161,7 @@
"freeDraw": "کلیک کنید و بکشید و وقتی کار تمام شد رها کنید", "freeDraw": "کلیک کنید و بکشید و وقتی کار تمام شد رها کنید",
"text": "نکته: با برنامه انتخاب شده شما میتوانید با دوبار کلیک کردن هرکجا میخواید متن اظاف کنید", "text": "نکته: با برنامه انتخاب شده شما میتوانید با دوبار کلیک کردن هرکجا میخواید متن اظاف کنید",
"linearElementMulti": "روی آخرین نقطه کلیک کنید یا کلید ESC را بزنید یا کلید Enter را بزنید برای اتمام کار", "linearElementMulti": "روی آخرین نقطه کلیک کنید یا کلید ESC را بزنید یا کلید Enter را بزنید برای اتمام کار",
"lockAngle": "",
"resize": "می توانید با نگه داشتن SHIFT در هنگام تغییر اندازه، نسبت ها را محدود کنید،ALT را برای تغییر اندازه از مرکز نگه دارید", "resize": "می توانید با نگه داشتن SHIFT در هنگام تغییر اندازه، نسبت ها را محدود کنید،ALT را برای تغییر اندازه از مرکز نگه دارید",
"rotate": "با نگه داشتن SHIFT هنگام چرخش می توانید زاویه ها را محدود کنید", "rotate": "با نگه داشتن SHIFT هنگام چرخش می توانید زاویه ها را محدود کنید",
"lineEditor_info": "دوبار کلیک کنید یا Enter را فشار دهید تا نقاط را ویرایش کنید", "lineEditor_info": "دوبار کلیک کنید یا Enter را فشار دهید تا نقاط را ویرایش کنید",
@@ -205,13 +214,22 @@
"textNewLine": "یک خط جدید اضافه کنید (متن)", "textNewLine": "یک خط جدید اضافه کنید (متن)",
"textFinish": "پایان ویرایش (متن)", "textFinish": "پایان ویرایش (متن)",
"zoomToFit": "بزرگنمایی برای دیدن تمام آیتم ها", "zoomToFit": "بزرگنمایی برای دیدن تمام آیتم ها",
"zoomToSelection": "",
"preventBinding": "مانع شدن از چسبیدن فلش ها" "preventBinding": "مانع شدن از چسبیدن فلش ها"
}, },
"encrypted": { "encrypted": {
"tooltip": "شما در یک محیط رمزگزاری شده دو طرفه در حال طراحی هستید پس Excalidraw هرگز طرح های شما را نمیبند." "tooltip": "شما در یک محیط رمزگزاری شده دو طرفه در حال طراحی هستید پس Excalidraw هرگز طرح های شما را نمیبند."
}, },
"charts": { "stats": {
"noNumericColumn": "شما یک صفحه گسترده را بدون ستون عددی کپی کرده اید.", "angle": "",
"tooManyColumns": "شما یک صفحه گسترده را با بیش از دو ستون کپی کرده اید." "element": "",
"elements": "",
"height": "",
"scene": "",
"selected": "",
"storage": "",
"title": "",
"total": "",
"width": ""
} }
} }
+26 -8
View File
@@ -4,6 +4,7 @@
"selectAll": "Valitse kaikki", "selectAll": "Valitse kaikki",
"multiSelect": "Lisää kohde valintaan", "multiSelect": "Lisää kohde valintaan",
"moveCanvas": "Siirrä piirtoaluetta", "moveCanvas": "Siirrä piirtoaluetta",
"cut": "Leikkaa",
"copy": "Kopioi", "copy": "Kopioi",
"copyAsPng": "Kopioi leikepöydälle PNG-tiedostona", "copyAsPng": "Kopioi leikepöydälle PNG-tiedostona",
"copyAsSvg": "Kopioi leikepöydälle SVG-tiedostona", "copyAsSvg": "Kopioi leikepöydälle SVG-tiedostona",
@@ -28,6 +29,11 @@
"edges": "Reunat", "edges": "Reunat",
"sharp": "Terävä", "sharp": "Terävä",
"round": "Pyöreä", "round": "Pyöreä",
"arrowheads": "Nuolenkärjet",
"arrowhead_none": "Ei mitään",
"arrowhead_arrow": "Nuoli",
"arrowhead_bar": "Tasapää",
"arrowhead_dot": "Piste",
"fontSize": "Kirjasinkoko", "fontSize": "Kirjasinkoko",
"fontFamily": "Kirjasintyyppi", "fontFamily": "Kirjasintyyppi",
"onlySelected": "Vain valitut", "onlySelected": "Vain valitut",
@@ -70,10 +76,11 @@
"group": "Ryhmitä valinta", "group": "Ryhmitä valinta",
"ungroup": "Pura valittu ryhmä", "ungroup": "Pura valittu ryhmä",
"collaborators": "Yhteistyökumppanit", "collaborators": "Yhteistyökumppanit",
"toggleGridMode": "Ruudukko päälle/pois", "gridMode": "Ruudukkotila",
"addToLibrary": "Lisää kirjastoon", "addToLibrary": "Lisää kirjastoon",
"removeFromLibrary": "Poista kirjastosta", "removeFromLibrary": "Poista kirjastosta",
"libraryLoadingMessage": "Ladataan kirjastoa...", "libraryLoadingMessage": "Ladataan kirjastoa...",
"libraries": "Selaa kirjastoja",
"loadingScene": "Ladataan työtä...", "loadingScene": "Ladataan työtä...",
"align": "Tasaa", "align": "Tasaa",
"alignTop": "Tasaa ylös", "alignTop": "Tasaa ylös",
@@ -110,9 +117,10 @@
"redo": "Tee uudelleen", "redo": "Tee uudelleen",
"roomDialog": "Aloita live-yhteistyö", "roomDialog": "Aloita live-yhteistyö",
"createNewRoom": "Luo huone", "createNewRoom": "Luo huone",
"toggleFullScreen": "Koko näytön tila päälle/pois", "fullScreen": "Koko näyttö",
"toggleDarkMode": "Pimeä tila päälle/pois", "darkMode": "Tumma tila",
"toggleZenMode": "Zen-tila päälle", "lightMode": "Vaalea tila",
"zenMode": "Zen-tila",
"exitZenMode": "Poistu zen-tilasta" "exitZenMode": "Poistu zen-tilasta"
}, },
"alerts": { "alerts": {
@@ -128,7 +136,7 @@
"loadSceneOverridePrompt": "Ulkopuolisen piirroksen lataaminen korvaa nykyisen sisältösi. Haluatko jatkaa?", "loadSceneOverridePrompt": "Ulkopuolisen piirroksen lataaminen korvaa nykyisen sisältösi. Haluatko jatkaa?",
"errorLoadingLibrary": "Kolmannen osapuolen kirjastoa ladattaessa tapahtui virhe.", "errorLoadingLibrary": "Kolmannen osapuolen kirjastoa ladattaessa tapahtui virhe.",
"confirmAddLibrary": "Tämä lisää {{numShapes}} muotoa kirjastoosi. Oletko varma?", "confirmAddLibrary": "Tämä lisää {{numShapes}} muotoa kirjastoosi. Oletko varma?",
"imageDoesNotContainScene": "Kuvatiedosto ei sisällä teostietoja. Valitsitko sisällyttää ne tallennusvaiheessa?", "imageDoesNotContainScene": "Kuvien lisääminen ei ole tällä hetkellä mahdollista.\n\nHaluatko tuoda piirroksen? Tämä kuva ei näytä sisältävän tarvittavia tietoja. Oletko ottanut piirrostietojen tallennuksen käyttöön viennin aikana?",
"cannotRestoreFromImage": "Teosta ei voitu palauttaa tästä kuvatiedostosta" "cannotRestoreFromImage": "Teosta ei voitu palauttaa tästä kuvatiedostosta"
}, },
"toolBar": { "toolBar": {
@@ -153,6 +161,7 @@
"freeDraw": "Paina ja raahaa, päästä irti kun olet valmis", "freeDraw": "Paina ja raahaa, päästä irti kun olet valmis",
"text": "Vinkki: voit myös lisätä tekstiä kaksoisnapsauttamalla mihin tahansa valintatyökalulla", "text": "Vinkki: voit myös lisätä tekstiä kaksoisnapsauttamalla mihin tahansa valintatyökalulla",
"linearElementMulti": "Klikkaa viimeistä pistettä, paina Escape tai paina Enter lopettaaksesi", "linearElementMulti": "Klikkaa viimeistä pistettä, paina Escape tai paina Enter lopettaaksesi",
"lockAngle": "Voit rajoittaa kulmaa pitämällä SHIFT pohjassa",
"resize": "Voit rajoittaa mittasuhteet pitämällä SHIFT pohjassa kun muutat kokoa, pidä ALT pohjassa muuttaaksesi kokoa keskipisteen suhteen", "resize": "Voit rajoittaa mittasuhteet pitämällä SHIFT pohjassa kun muutat kokoa, pidä ALT pohjassa muuttaaksesi kokoa keskipisteen suhteen",
"rotate": "Voit rajoittaa kulman pitämällä SHIFT pohjassa pyörittäessäsi", "rotate": "Voit rajoittaa kulman pitämällä SHIFT pohjassa pyörittäessäsi",
"lineEditor_info": "Kaksoisnapauta tai paina Enter muokataksesi pisteitä", "lineEditor_info": "Kaksoisnapauta tai paina Enter muokataksesi pisteitä",
@@ -205,13 +214,22 @@
"textNewLine": "Lisää uusi rivi (teksti)", "textNewLine": "Lisää uusi rivi (teksti)",
"textFinish": "Lopeta muokkaus (teksti)", "textFinish": "Lopeta muokkaus (teksti)",
"zoomToFit": "Zoomaa kaikki elementit näkyviin", "zoomToFit": "Zoomaa kaikki elementit näkyviin",
"zoomToSelection": "Zoomaa valintaan",
"preventBinding": "Estä nuolten sitominen" "preventBinding": "Estä nuolten sitominen"
}, },
"encrypted": { "encrypted": {
"tooltip": "Piirroksesi ovat päästä päähän salattuja, joten Excalidrawin palvelimet eivät koskaan näe niitä." "tooltip": "Piirroksesi ovat päästä päähän salattuja, joten Excalidrawin palvelimet eivät koskaan näe niitä."
}, },
"charts": { "stats": {
"noNumericColumn": "Liitit taulukon ilman lukuja sisältävää saraketta.", "angle": "Kulma",
"tooManyColumns": "Liitit taulukon, jossa on enemmän kuin kaksi saraketta." "element": "Elementti",
"elements": "Elementit",
"height": "Korkeus",
"scene": "Teos",
"selected": "Valitut",
"storage": "Tallennustila",
"title": "Nörttien tilastot",
"total": "Yhteensä",
"width": "Leveys"
} }
} }
+31 -13
View File
@@ -4,6 +4,7 @@
"selectAll": "Tout sélectionner", "selectAll": "Tout sélectionner",
"multiSelect": "Ajouter l'élément à la sélection", "multiSelect": "Ajouter l'élément à la sélection",
"moveCanvas": "Déplacer le canvas", "moveCanvas": "Déplacer le canvas",
"cut": "Couper",
"copy": "Copier", "copy": "Copier",
"copyAsPng": "Copier dans le presse-papier en PNG", "copyAsPng": "Copier dans le presse-papier en PNG",
"copyAsSvg": "Copier dans le presse-papier en SVG", "copyAsSvg": "Copier dans le presse-papier en SVG",
@@ -28,6 +29,11 @@
"edges": "Angles", "edges": "Angles",
"sharp": "Aigu", "sharp": "Aigu",
"round": "Rond", "round": "Rond",
"arrowheads": "Extrémités de ligne",
"arrowhead_none": "Aucun",
"arrowhead_arrow": "Flèche",
"arrowhead_bar": "Barre",
"arrowhead_dot": "Point",
"fontSize": "Taille de la police", "fontSize": "Taille de la police",
"fontFamily": "Police", "fontFamily": "Police",
"onlySelected": "Uniquement la sélection", "onlySelected": "Uniquement la sélection",
@@ -70,20 +76,21 @@
"group": "Grouper la sélection", "group": "Grouper la sélection",
"ungroup": "Dégrouper la sélection", "ungroup": "Dégrouper la sélection",
"collaborators": "Collaborateurs", "collaborators": "Collaborateurs",
"toggleGridMode": "Basculer le mode grille", "gridMode": "Mode grille",
"addToLibrary": "Ajouter à la bibliothèque", "addToLibrary": "Ajouter à la bibliothèque",
"removeFromLibrary": "Supprimer de la bibliothèque", "removeFromLibrary": "Supprimer de la bibliothèque",
"libraryLoadingMessage": "Chargement de la bibliothèque...", "libraryLoadingMessage": "Chargement de la bibliothèque...",
"libraries": "Explorer les bibliothèques",
"loadingScene": "Chargement de la scène...", "loadingScene": "Chargement de la scène...",
"align": "Aligner", "align": "Alignement",
"alignTop": "Aligner en haut", "alignTop": "Aligner en haut",
"alignBottom": "Aligner en bas", "alignBottom": "Aligner en bas",
"alignLeft": "Aligner à gauche", "alignLeft": "Aligner à gauche",
"alignRight": "Aligner à droite", "alignRight": "Aligner à droite",
"centerVertically": "Centrer verticalement", "centerVertically": "Centrer verticalement",
"centerHorizontally": "Centrer horizontalement", "centerHorizontally": "Centrer horizontalement",
"distributeHorizontally": "Répartir horizontalement", "distributeHorizontally": "Distribuer horizontalement",
"distributeVertically": "Répartir verticalement" "distributeVertically": "Distribuer verticalement"
}, },
"buttons": { "buttons": {
"clearReset": "Effacer le canvas & réinitialiser la couleur d'arrière-plan", "clearReset": "Effacer le canvas & réinitialiser la couleur d'arrière-plan",
@@ -110,9 +117,10 @@
"redo": "Rétablir", "redo": "Rétablir",
"roomDialog": "Démarrer le collaboration en temps réel", "roomDialog": "Démarrer le collaboration en temps réel",
"createNewRoom": "Créer un nouveau salon", "createNewRoom": "Créer un nouveau salon",
"toggleFullScreen": "Activer/désactiver le mode plein écran", "fullScreen": "Plein écran",
"toggleDarkMode": "Activer/désactiver le mode sombre", "darkMode": "Mode sombre",
"toggleZenMode": "Activer/désactiver le mode zen", "lightMode": "Mode Clair",
"zenMode": "Mode Zen",
"exitZenMode": "Quitter le mode zen" "exitZenMode": "Quitter le mode zen"
}, },
"alerts": { "alerts": {
@@ -128,7 +136,7 @@
"loadSceneOverridePrompt": "Le chargement d'un dessin externe remplacera votre contenu actuel. Souhaitez-vous continuer ?", "loadSceneOverridePrompt": "Le chargement d'un dessin externe remplacera votre contenu actuel. Souhaitez-vous continuer ?",
"errorLoadingLibrary": "Une erreur s'est produite lors du chargement de la bibliothèque tierce.", "errorLoadingLibrary": "Une erreur s'est produite lors du chargement de la bibliothèque tierce.",
"confirmAddLibrary": "Cela va ajouter {{numShapes}} forme(s) à votre bibliothèque. Êtes-vous sûr(e) ?", "confirmAddLibrary": "Cela va ajouter {{numShapes}} forme(s) à votre bibliothèque. Êtes-vous sûr(e) ?",
"imageDoesNotContainScene": "Le fichier image ne contient pas de données de scène. L'avez-vous activé lors de l'export ?", "imageDoesNotContainScene": "L'importation des images n'est pas prise en charge pour le moment.\n\nVoulez-vous importer une scène ? Cette image ne semble pas contenir de données de scène. Avez-vous activé cette option lors de l'exportation ?",
"cannotRestoreFromImage": "Impossible de restaurer la scène depuis ce fichier image" "cannotRestoreFromImage": "Impossible de restaurer la scène depuis ce fichier image"
}, },
"toolBar": { "toolBar": {
@@ -153,6 +161,7 @@
"freeDraw": "Cliquez et faites glissez, relâchez quand vous avez terminé", "freeDraw": "Cliquez et faites glissez, relâchez quand vous avez terminé",
"text": "Astuce : vous pouvez également ajouter du texte en double-cliquant n'importe où avec l'outil de sélection", "text": "Astuce : vous pouvez également ajouter du texte en double-cliquant n'importe où avec l'outil de sélection",
"linearElementMulti": "Cliquez sur le dernier point ou appuyez sur Échap ou Entrée pour terminer", "linearElementMulti": "Cliquez sur le dernier point ou appuyez sur Échap ou Entrée pour terminer",
"lockAngle": "Vous pouvez contraindre l'angle en maintenant SHIFT",
"resize": "Vous pouvez conserver les proportions en maintenant la touche SHIFT pendant le redimensionnement,\nen maintenant la touche ALT pour redimensionner par rapport au centre", "resize": "Vous pouvez conserver les proportions en maintenant la touche SHIFT pendant le redimensionnement,\nen maintenant la touche ALT pour redimensionner par rapport au centre",
"rotate": "Vous pouvez contraindre les angles en maintenant MAJ enfoncé pendant la rotation", "rotate": "Vous pouvez contraindre les angles en maintenant MAJ enfoncé pendant la rotation",
"lineEditor_info": "Double-cliquez ou appuyez sur Entrée pour éditer les points", "lineEditor_info": "Double-cliquez ou appuyez sur Entrée pour éditer les points",
@@ -161,8 +170,8 @@
}, },
"canvasError": { "canvasError": {
"cannotShowPreview": "Impossible dafficher laperçu", "cannotShowPreview": "Impossible dafficher laperçu",
"canvasTooBig": "Le tableau peut être trop grand.", "canvasTooBig": "Le dessin est peut-être trop grand.",
"canvasTooBigTip": "Astuce : essayez de rapprocher un peu les éléments les plus éloignés ensemble." "canvasTooBigTip": "Conseil : essayez de rapprocher un peu plus les éléments les plus éloignés."
}, },
"errorSplash": { "errorSplash": {
"headingMain_pre": "Une erreur est survenue. Essayez ", "headingMain_pre": "Une erreur est survenue. Essayez ",
@@ -205,13 +214,22 @@
"textNewLine": "Ajouter une nouvelle ligne (texte)", "textNewLine": "Ajouter une nouvelle ligne (texte)",
"textFinish": "Terminer l'édition (texte)", "textFinish": "Terminer l'édition (texte)",
"zoomToFit": "Zoomer pour visualiser tous les éléments", "zoomToFit": "Zoomer pour visualiser tous les éléments",
"zoomToSelection": "Zoom sur la sélection",
"preventBinding": "Empêcher la liaison de la flèche" "preventBinding": "Empêcher la liaison de la flèche"
}, },
"encrypted": { "encrypted": {
"tooltip": "Vos dessins sont chiffrés de bout en bout, les serveurs d'Excalidraw ne les verront jamais." "tooltip": "Vos dessins sont chiffrés de bout en bout, les serveurs d'Excalidraw ne les verront jamais."
}, },
"charts": { "stats": {
"noNumericColumn": "Vous avez collé une feuille de calcul sans données numérique.", "angle": "Angle",
"tooManyColumns": "Vous avez collé une feuille de calcul avec plus de deux colonnes." "element": "Élément",
"elements": "Éléments",
"height": "Hauteur",
"scene": "Scène",
"selected": "Sélectionné",
"storage": "Stockage",
"title": "Stats pour les nerds",
"total": "Total",
"width": "Largeur"
} }
} }
+64 -46
View File
@@ -2,8 +2,9 @@
"labels": { "labels": {
"paste": "הדבק", "paste": "הדבק",
"selectAll": "בחר הכל", "selectAll": "בחר הכל",
"multiSelect": "", "multiSelect": "הוסף אובייקט לבחירה",
"moveCanvas": "", "moveCanvas": "הזז את הקנבס",
"cut": "חתוך",
"copy": "העתק", "copy": "העתק",
"copyAsPng": "העתק ללוח כ PNG", "copyAsPng": "העתק ללוח כ PNG",
"copyAsSvg": "העתק ללוח כ SVG", "copyAsSvg": "העתק ללוח כ SVG",
@@ -25,15 +26,20 @@
"sloppiness": "סגנון", "sloppiness": "סגנון",
"opacity": "אטימות", "opacity": "אטימות",
"textAlign": "יישור טקסט", "textAlign": "יישור טקסט",
"edges": "", "edges": "קצוות",
"sharp": "", "sharp": "חד",
"round": "", "round": "עגול",
"arrowheads": "ראשי חצים",
"arrowhead_none": "ללא",
"arrowhead_arrow": "חץ",
"arrowhead_bar": "שורה",
"arrowhead_dot": "נקודה",
"fontSize": "גודל גופן", "fontSize": "גודל גופן",
"fontFamily": "סוג הגופן", "fontFamily": "סוג הגופן",
"onlySelected": "רק מה שנבחר", "onlySelected": "רק מה שנבחר",
"withBackground": "עם רקע", "withBackground": "עם רקע",
"exportEmbedScene": "", "exportEmbedScene": "שלב את התצוגה בקובץ המיוצא",
"exportEmbedScene_details": "", "exportEmbedScene_details": "מידע התצוגה יישמר לקובץ המיוצא מסוג PNG/SVG כך שיהיה ניתן לשחזרה ממנו.\nהפעולה תגדיל את גודל הקובץ המיוצא.",
"addWatermark": "הוסף \"נוצר באמצעות Excalidraw\"", "addWatermark": "הוסף \"נוצר באמצעות Excalidraw\"",
"handDrawn": "כתב יד", "handDrawn": "כתב יד",
"normal": "רגיל", "normal": "רגיל",
@@ -54,7 +60,7 @@
"architect": "ארכיטקט", "architect": "ארכיטקט",
"artist": "אמן", "artist": "אמן",
"cartoonist": "קריקטוריסט", "cartoonist": "קריקטוריסט",
"fileTitle": "", "fileTitle": "כותרת הקובץ",
"colorPicker": "בחירת צבע", "colorPicker": "בחירת צבע",
"canvasBackground": "רקע הלוח", "canvasBackground": "רקע הלוח",
"drawingCanvas": "לוח ציור", "drawingCanvas": "לוח ציור",
@@ -63,27 +69,28 @@
"language": "שפה", "language": "שפה",
"createRoom": "התחל שיתוף פעולה חי", "createRoom": "התחל שיתוף פעולה חי",
"duplicateSelection": "שכפל", "duplicateSelection": "שכפל",
"untitled": "", "untitled": "ללא כותרת",
"name": "שם", "name": "שם",
"yourName": "שם", "yourName": "שם",
"madeWithExcalidraw": "נוצר באמצעות Excalidraw", "madeWithExcalidraw": "נוצר באמצעות Excalidraw",
"group": "אחד לקבוצה", "group": "אחד לקבוצה",
"ungroup": "פרק קבוצה", "ungroup": "פרק קבוצה",
"collaborators": "", "collaborators": "שותפים",
"toggleGridMode": "", "gridMode": "",
"addToLibrary": "", "addToLibrary": "הוסף לספריה",
"removeFromLibrary": "", "removeFromLibrary": "הסר מספריה",
"libraryLoadingMessage": "", "libraryLoadingMessage": "טוען ספריה...",
"loadingScene": "", "libraries": "דפדף בספריות",
"align": "", "loadingScene": "טוען תצוגה...",
"alignTop": "", "align": "יישר",
"alignBottom": "", "alignTop": "יישר למעלה",
"alignLeft": "", "alignBottom": "יישר למטה",
"alignRight": "", "alignLeft": "יישר לשמאל",
"centerVertically": "", "alignRight": "יישר לימין",
"centerHorizontally": "", "centerVertically": "מרכז אנכית",
"distributeHorizontally": "", "centerHorizontally": "מרכז אופקית",
"distributeVertically": "" "distributeHorizontally": "חלוקה אופקית",
"distributeVertically": "חלוקה אנכית"
}, },
"buttons": { "buttons": {
"clearReset": "אפס את הלוח", "clearReset": "אפס את הלוח",
@@ -92,9 +99,9 @@
"exportToSvg": "יצא ל SVG", "exportToSvg": "יצא ל SVG",
"copyToClipboard": "העתק ללוח", "copyToClipboard": "העתק ללוח",
"copyPngToClipboard": "העתק PNG ללוח", "copyPngToClipboard": "העתק PNG ללוח",
"scale": "", "scale": "קנה מידה",
"save": "שמור", "save": "שמור",
"saveAs": "", "saveAs": "שמירה בשם",
"load": "טען", "load": "טען",
"getShareableLink": "קבל קישור לשיתוף", "getShareableLink": "קבל קישור לשיתוף",
"close": "סגור", "close": "סגור",
@@ -110,26 +117,27 @@
"redo": "בצע מחדש", "redo": "בצע מחדש",
"roomDialog": "התחל שיתוף חי", "roomDialog": "התחל שיתוף חי",
"createNewRoom": "צור חדר", "createNewRoom": "צור חדר",
"toggleFullScreen": "הפעל/הפסק מסך מלא", "fullScreen": "",
"toggleDarkMode": "", "darkMode": "",
"toggleZenMode": "התחל/הפסק מצב תפריט מרחף", "lightMode": "",
"zenMode": "",
"exitZenMode": "צא ממצב תפריט מרחף" "exitZenMode": "צא ממצב תפריט מרחף"
}, },
"alerts": { "alerts": {
"clearReset": "פעולה זו תנקה את כל הלוח. אתה בטוח?", "clearReset": "פעולה זו תנקה את כל הלוח. אתה בטוח?",
"couldNotCreateShareableLink": "לא ניתן לייצר לינק לשיתוף.", "couldNotCreateShareableLink": "לא ניתן לייצר לינק לשיתוף.",
"couldNotCreateShareableLinkTooBig": "", "couldNotCreateShareableLinkTooBig": "לא הצלחנו לייצר קישור לשיתוף: התצוגה גדולה מדי",
"couldNotLoadInvalidFile": "לא ניתן לטעון קובץ שאיננו תואם", "couldNotLoadInvalidFile": "לא ניתן לטעון קובץ שאיננו תואם",
"importBackendFailed": "ייבוא מהשרת נכשל.", "importBackendFailed": "ייבוא מהשרת נכשל.",
"cannotExportEmptyCanvas": "לא ניתן לייצא לוח ריק.", "cannotExportEmptyCanvas": "לא ניתן לייצא לוח ריק.",
"couldNotCopyToClipboard": "לא ניתן להעתיק ללוח. נסה להשתמש בדפדפן Chrome.", "couldNotCopyToClipboard": "לא ניתן להעתיק ללוח. נסה להשתמש בדפדפן Chrome.",
"decryptFailed": "לא ניתן לפענח מידע.", "decryptFailed": "לא ניתן לפענח מידע.",
"uploadedSecurly": "ההעלאה הוצפנה מקצה לקצה, ולכן שרת Excalidraw וצד שלישי לא יכולים לקרוא את התוכן.", "uploadedSecurly": "ההעלאה הוצפנה מקצה לקצה, ולכן שרת Excalidraw וצד שלישי לא יכולים לקרוא את התוכן.",
"loadSceneOverridePrompt": "", "loadSceneOverridePrompt": "טעינה של ציור חיצוני תחליף את התוכן הקיים שלך. האם תרצה להמשיך?",
"errorLoadingLibrary": "", "errorLoadingLibrary": "קרתה שגיאה בטעינת הספריה החיצונית.",
"confirmAddLibrary": "", "confirmAddLibrary": "הפעולה תוסיף {{numShapes}} צורה(ות) לספריה שלך. האם אתה בטוח?",
"imageDoesNotContainScene": "", "imageDoesNotContainScene": "",
"cannotRestoreFromImage": "" "cannotRestoreFromImage": "לא הצלחנו לשחזר את התצוגה מקובץ התמונה"
}, },
"toolBar": { "toolBar": {
"selection": "בחירה", "selection": "בחירה",
@@ -140,7 +148,7 @@
"arrow": "חץ", "arrow": "חץ",
"line": "קו", "line": "קו",
"text": "טקסט", "text": "טקסט",
"library": "", "library": "ספריה",
"lock": "השאר את הכלי הנבחר פעיל גם לאחר סיום הציור" "lock": "השאר את הכלי הנבחר פעיל גם לאחר סיום הציור"
}, },
"headings": { "headings": {
@@ -151,18 +159,19 @@
"hints": { "hints": {
"linearElement": "הקלק בשביל לבחור נקודות מרובות, גרור בשביל קו בודד", "linearElement": "הקלק בשביל לבחור נקודות מרובות, גרור בשביל קו בודד",
"freeDraw": "לחץ וגרור, שחרר כשסיימת", "freeDraw": "לחץ וגרור, שחרר כשסיימת",
"text": "", "text": "טיפ: אפשר להוסיף טקסט על ידי לחיצה כפולה בכל מקום עם כלי הבחירה",
"linearElementMulti": "הקלק על הנקודה האחרונה או הקש Escape או Enter לסיום", "linearElementMulti": "הקלק על הנקודה האחרונה או הקש Escape או Enter לסיום",
"lockAngle": "",
"resize": "ניתן להגביל פרופורציות על ידי לחיצה על SHIFT תוך כדי שינוי גודל,\nהחזק ALT בשביל לשנות גודל ביחס למרכז", "resize": "ניתן להגביל פרופורציות על ידי לחיצה על SHIFT תוך כדי שינוי גודל,\nהחזק ALT בשביל לשנות גודל ביחס למרכז",
"rotate": "ניתן להגביל זוויות על ידי לחיצה על SHIFT תוך כדי סיבוב", "rotate": "ניתן להגביל זוויות על ידי לחיצה על SHIFT תוך כדי סיבוב",
"lineEditor_info": "", "lineEditor_info": "לחץ לחיצה כפולה או אנטר לעריכת הנקודות",
"lineEditor_pointSelected": "", "lineEditor_pointSelected": "לחץ על Delete להסרת נקודה, CtrlOrCmd+D לשכפל, או גרור להזזה",
"lineEditor_nothingSelected": "" "lineEditor_nothingSelected": "בחר נקודה להזזה או הסרה, או החזק את כפתור Alt והקלק להוספת נקודות חדשות"
}, },
"canvasError": { "canvasError": {
"cannotShowPreview": "", "cannotShowPreview": "לא הצלחנו להציג את התצוגה המקדימה",
"canvasTooBig": "", "canvasTooBig": "הקנבס עלול להיות גדול מדי.",
"canvasTooBigTip": "" "canvasTooBigTip": "טיפ: נסה להזיז את האלמנטים הרחוקים ביותר מעט קרוב יותר יחד."
}, },
"errorSplash": { "errorSplash": {
"headingMain_pre": "אירעה שגיאה. נסה ", "headingMain_pre": "אירעה שגיאה. נסה ",
@@ -205,13 +214,22 @@
"textNewLine": "הוסף שורה חדשה (טקסט)", "textNewLine": "הוסף שורה חדשה (טקסט)",
"textFinish": "סיים עריכה (טקסט)", "textFinish": "סיים עריכה (טקסט)",
"zoomToFit": "זום להתאמת כל האלמנטים למסך", "zoomToFit": "זום להתאמת כל האלמנטים למסך",
"preventBinding": "" "zoomToSelection": "התמקד בבחירה",
"preventBinding": "מנע השתלבות חצים"
}, },
"encrypted": { "encrypted": {
"tooltip": "הרישומים שלך מוצפנים מקצה לקצה כך שהשרתים של Excalidraw לא יראו אותם לעולם." "tooltip": "הרישומים שלך מוצפנים מקצה לקצה כך שהשרתים של Excalidraw לא יראו אותם לעולם."
}, },
"charts": { "stats": {
"noNumericColumn": "", "angle": "זווית",
"tooManyColumns": "" "element": "אלמנט",
"elements": "אלמנטים",
"height": "גובה",
"scene": "תצוגה",
"selected": "נבחר/ים",
"storage": "אחסון",
"title": "סטטיסטיקות לחנונים",
"total": "סה״כ",
"width": "רוחב"
} }
} }
+26 -8
View File
@@ -4,6 +4,7 @@
"selectAll": "सभी चुनें", "selectAll": "सभी चुनें",
"multiSelect": "आकार को चयन में जोड़ें", "multiSelect": "आकार को चयन में जोड़ें",
"moveCanvas": "कैनवास को स्थानांतरित करें", "moveCanvas": "कैनवास को स्थानांतरित करें",
"cut": "काटें",
"copy": "प्रतिलिपि", "copy": "प्रतिलिपि",
"copyAsPng": "क्लिपबोर्ड पर कॉपी करें ,पीएनजी के रूप में", "copyAsPng": "क्लिपबोर्ड पर कॉपी करें ,पीएनजी के रूप में",
"copyAsSvg": "क्लिपबोर्ड पर कॉपी करें,एसवीजी के रूप में", "copyAsSvg": "क्लिपबोर्ड पर कॉपी करें,एसवीजी के रूप में",
@@ -28,6 +29,11 @@
"edges": "किनारा", "edges": "किनारा",
"sharp": "नुकीला", "sharp": "नुकीला",
"round": "गोल", "round": "गोल",
"arrowheads": "तीर शीर्ष",
"arrowhead_none": "कोई भी नहीं",
"arrowhead_arrow": "तीर",
"arrowhead_bar": "बार",
"arrowhead_dot": "बिंदु",
"fontSize": "फ़ॉन्ट का आकार", "fontSize": "फ़ॉन्ट का आकार",
"fontFamily": "फ़ॉन्ट का परिवार", "fontFamily": "फ़ॉन्ट का परिवार",
"onlySelected": "केवल चयनित", "onlySelected": "केवल चयनित",
@@ -63,17 +69,18 @@
"language": "भाषा", "language": "भाषा",
"createRoom": "अधिवेशन", "createRoom": "अधिवेशन",
"duplicateSelection": "डुप्लिकेट", "duplicateSelection": "डुप्लिकेट",
"untitled": "", "untitled": "अशीर्षित",
"name": "नाम", "name": "नाम",
"yourName": "आपका नाम", "yourName": "आपका नाम",
"madeWithExcalidraw": "मेड विथ एक्सकैलिडराव", "madeWithExcalidraw": "मेड विथ एक्सकैलिडराव",
"group": "समूह चयन", "group": "समूह चयन",
"ungroup": "समूह चयन असमूहीकृत करें", "ungroup": "समूह चयन असमूहीकृत करें",
"collaborators": "सहयोगी", "collaborators": "सहयोगी",
"toggleGridMode": "टॉगल ग्रिड मोड", "gridMode": "",
"addToLibrary": "लाइब्रेरी से जोड़ें", "addToLibrary": "लाइब्रेरी से जोड़ें",
"removeFromLibrary": "लाइब्रेरी से निकालें", "removeFromLibrary": "लाइब्रेरी से निकालें",
"libraryLoadingMessage": "लाइब्रेरी खुल रही है", "libraryLoadingMessage": "लाइब्रेरी खुल रही है",
"libraries": "",
"loadingScene": "दृश्य खुल रहा है", "loadingScene": "दृश्य खुल रहा है",
"align": "", "align": "",
"alignTop": "", "alignTop": "",
@@ -110,9 +117,10 @@
"redo": "फिर से करें", "redo": "फिर से करें",
"roomDialog": "लाइव सहयोग शुरू करें", "roomDialog": "लाइव सहयोग शुरू करें",
"createNewRoom": "एक नया कमरा बनाएं", "createNewRoom": "एक नया कमरा बनाएं",
"toggleFullScreen": "पूर्णस्क्रीन चालू करें", "fullScreen": "",
"toggleDarkMode": "", "darkMode": "",
"toggleZenMode": "टॉगल ज़ेन मोड", "lightMode": "",
"zenMode": "",
"exitZenMode": "जेन मोड से बाहर निकलें" "exitZenMode": "जेन मोड से बाहर निकलें"
}, },
"alerts": { "alerts": {
@@ -153,6 +161,7 @@
"freeDraw": "क्लिक करें और खींचें। समाप्त करने के लिए, छोड़ो", "freeDraw": "क्लिक करें और खींचें। समाप्त करने के लिए, छोड़ो",
"text": "", "text": "",
"linearElementMulti": "अंतिम बिंदु पर क्लिक करें या समाप्त होने के लिए एस्केप या एंटर दबाएं", "linearElementMulti": "अंतिम बिंदु पर क्लिक करें या समाप्त होने के लिए एस्केप या एंटर दबाएं",
"lockAngle": "",
"resize": "आकार बदलते समय आप SHIFT को पकड़ कर अनुपात में कमी कर सकते हैं,\nकेंद्र से आकार बदलने के लिए ALT दबाए रखें", "resize": "आकार बदलते समय आप SHIFT को पकड़ कर अनुपात में कमी कर सकते हैं,\nकेंद्र से आकार बदलने के लिए ALT दबाए रखें",
"rotate": "आप घूर्णन करते समय SHIFT पकड़कर कोणों को विवश कर सकते हैं", "rotate": "आप घूर्णन करते समय SHIFT पकड़कर कोणों को विवश कर सकते हैं",
"lineEditor_info": "बिंदुओं को संपादित करने के लिए Enter पर डबल-क्लिक करें या दबाएँ", "lineEditor_info": "बिंदुओं को संपादित करने के लिए Enter पर डबल-क्लिक करें या दबाएँ",
@@ -205,13 +214,22 @@
"textNewLine": "नई पंक्ति (पाठ) जोड़ें", "textNewLine": "नई पंक्ति (पाठ) जोड़ें",
"textFinish": "संपादन समाप्त करें (पाठ)", "textFinish": "संपादन समाप्त करें (पाठ)",
"zoomToFit": "सभी तत्वों को फिट करने के लिए ज़ूम करें", "zoomToFit": "सभी तत्वों को फिट करने के लिए ज़ूम करें",
"zoomToSelection": "",
"preventBinding": "" "preventBinding": ""
}, },
"encrypted": { "encrypted": {
"tooltip": "आपके चित्र अंत-से-अंत एन्क्रिप्टेड हैं, इसलिए एक्सक्लूसिव्रॉव के सर्वर उन्हें कभी नहीं देखेंगे।" "tooltip": "आपके चित्र अंत-से-अंत एन्क्रिप्टेड हैं, इसलिए एक्सक्लूसिव्रॉव के सर्वर उन्हें कभी नहीं देखेंगे।"
}, },
"charts": { "stats": {
"noNumericColumn": "आपने एक संख्यात्मक कॉलम के बिना एक स्प्रेडशीट चिपकाई।", "angle": "कोण",
"tooManyColumns": "आपने दो से अधिक कॉलम के साथ एक स्प्रेडशीट चिपकाई।" "element": "",
"elements": "",
"height": "ऊंचाई",
"scene": "दृश्य",
"selected": "चयनित",
"storage": "संग्रह",
"title": "",
"total": "कुल",
"width": "चौड़ाई"
} }
} }
+110 -92
View File
@@ -2,88 +2,95 @@
"labels": { "labels": {
"paste": "Beillesztés", "paste": "Beillesztés",
"selectAll": "Összes kijelölése", "selectAll": "Összes kijelölése",
"multiSelect": "", "multiSelect": "Elem hozzáadása a kiválasztáshoz",
"moveCanvas": "", "moveCanvas": "Vászon mozgatása",
"cut": "Kivágás",
"copy": "Másolás", "copy": "Másolás",
"copyAsPng": "Vágólapra másolás mint PNG", "copyAsPng": "Vágólapra másolás mint PNG",
"copyAsSvg": "Vágólapra másolás mint SVG", "copyAsSvg": "Vágólapra másolás mint SVG",
"bringForward": "Előrébb hozás", "bringForward": "Előrébb hozás",
"sendToBack": "", "sendToBack": "Hátraküldés",
"bringToFront": "", "bringToFront": "Előrehozás",
"sendBackward": "", "sendBackward": "Hátrébb küldés",
"delete": "Törlés", "delete": "Törlés",
"copyStyles": "", "copyStyles": "Stílus másolása",
"pasteStyles": "", "pasteStyles": "Stílus beillesztése",
"stroke": "Körvonal", "stroke": "Körvonal",
"background": "Háttér", "background": "Háttér",
"fill": "Kitöltés", "fill": "Kitöltés",
"strokeWidth": "", "strokeWidth": "Körvonal vastagsága",
"strokeStyle": "", "strokeStyle": "Körvonal stílusa",
"strokeStyle_solid": "", "strokeStyle_solid": "Kitöltött",
"strokeStyle_dashed": "", "strokeStyle_dashed": "Szaggatott",
"strokeStyle_dotted": "", "strokeStyle_dotted": "Pontozott",
"sloppiness": "Stílus", "sloppiness": "Stílus",
"opacity": "Áttetszőség", "opacity": "Áttetszőség",
"textAlign": "", "textAlign": "Szöveg igazítása",
"edges": "", "edges": "Szélek",
"sharp": "", "sharp": "Éles",
"round": "", "round": "Kerek",
"fontSize": "", "arrowheads": "Nyílhegyek",
"fontFamily": "", "arrowhead_none": "Nincs",
"arrowhead_arrow": "Nyíl",
"arrowhead_bar": "Oszlop",
"arrowhead_dot": "Pont",
"fontSize": "Betűméret",
"fontFamily": "Betűkészlet család",
"onlySelected": "Csak a kiválasztott", "onlySelected": "Csak a kiválasztott",
"withBackground": "Háttérrel együtt", "withBackground": "",
"exportEmbedScene": "", "exportEmbedScene": "Jelenet beágyazása az exportált fájlba",
"exportEmbedScene_details": "", "exportEmbedScene_details": "",
"addWatermark": "", "addWatermark": "Add hozzá, hogy \"Excalidraw-val készült\"",
"handDrawn": "", "handDrawn": "Kézzel rajzolt",
"normal": "Normál", "normal": "Normál",
"code": "Code", "code": "Code",
"small": "Kicsi", "small": "Kicsi",
"medium": "Közepes", "medium": "Közepes",
"large": "Nagy", "large": "Nagy",
"veryLarge": "", "veryLarge": "Nagyon nagy",
"solid": "Kitöltött", "solid": "Kitöltött",
"hachure": "Vonalkázott", "hachure": "Vonalkázott",
"crossHatch": "", "crossHatch": "Keresztcsíkozott",
"thin": "Vékony", "thin": "Vékony",
"bold": "Félkövér", "bold": "Félkövér",
"left": "Bal", "left": "Bal",
"center": "", "center": "Közép",
"right": "", "right": "Jobb",
"extraBold": "", "extraBold": "Extra Félkövér",
"architect": "Tervezői", "architect": "Tervezői",
"artist": "Művészi", "artist": "Művészi",
"cartoonist": "Karikatúrás", "cartoonist": "Karikatúrás",
"fileTitle": "", "fileTitle": "Fájl címe",
"colorPicker": "Színválasztó", "colorPicker": "Színválasztó",
"canvasBackground": "Vászon háttérszíne", "canvasBackground": "Vászon háttérszíne",
"drawingCanvas": "", "drawingCanvas": "Rajzvászon",
"layers": "Rétegek", "layers": "Rétegek",
"actions": "Műveletek", "actions": "Műveletek",
"language": "Nyelv", "language": "Nyelv",
"createRoom": "Élő együttmüködés megosztása", "createRoom": "Élő együttmüködés megosztása",
"duplicateSelection": "", "duplicateSelection": "Duplikálás",
"untitled": "", "untitled": "Névtelen",
"name": "Név", "name": "Név",
"yourName": "", "yourName": "Neved",
"madeWithExcalidraw": "", "madeWithExcalidraw": "Excalidraw-val készült",
"group": "", "group": "Csoportosítás",
"ungroup": "", "ungroup": "Csoportbontás",
"collaborators": "", "collaborators": "Közreműködők",
"toggleGridMode": "", "gridMode": "",
"addToLibrary": "", "addToLibrary": "Hozzáadás a könyvtárhoz",
"removeFromLibrary": "", "removeFromLibrary": "Eltávólítás a könyvtárból",
"libraryLoadingMessage": "", "libraryLoadingMessage": "Könyvtár betöltése...",
"loadingScene": "", "libraries": "Könyvtárak böngészése",
"align": "", "loadingScene": "Jelenet betöltése...",
"alignTop": "", "align": "Igazítás",
"alignBottom": "", "alignTop": "Felülre igazítás",
"alignLeft": "", "alignBottom": "Alulra igazítás",
"alignRight": "", "alignLeft": "Balra igazítás",
"centerVertically": "", "alignRight": "Jobbra igazítás",
"centerHorizontally": "", "centerVertically": "Függőlegesen középre igazított",
"distributeHorizontally": "", "centerHorizontally": "Vízszintesen középre igazított",
"distributeVertically": "" "distributeHorizontally": "Vízszintes elosztás",
"distributeVertically": "Függőleges elosztás"
}, },
"buttons": { "buttons": {
"clearReset": "Vászon törlése", "clearReset": "Vászon törlése",
@@ -92,13 +99,13 @@
"exportToSvg": "Exportálás SVG-be", "exportToSvg": "Exportálás SVG-be",
"copyToClipboard": "Vágólapra másolás", "copyToClipboard": "Vágólapra másolás",
"copyPngToClipboard": "PNG másolása a vágólapra", "copyPngToClipboard": "PNG másolása a vágólapra",
"scale": "", "scale": "Nagyítás",
"save": "Mentés", "save": "Mentés",
"saveAs": "", "saveAs": "Mentés másként",
"load": "Betöltés", "load": "Betöltés",
"getShareableLink": "Megosztható link létrehozása", "getShareableLink": "Megosztható link létrehozása",
"close": "Bezárás", "close": "Bezárás",
"selectLanguage": "", "selectLanguage": "Nyelv kiválasztása",
"scrollBackToContent": "Visszagörgetés a tartalomhoz", "scrollBackToContent": "Visszagörgetés a tartalomhoz",
"zoomIn": "Nagyítás", "zoomIn": "Nagyítás",
"zoomOut": "Kicsinyítés", "zoomOut": "Kicsinyítés",
@@ -110,37 +117,38 @@
"redo": "Újra végrehajtás", "redo": "Újra végrehajtás",
"roomDialog": "Élő együttműködés indítása", "roomDialog": "Élő együttműködés indítása",
"createNewRoom": "Új szoba létrehozása", "createNewRoom": "Új szoba létrehozása",
"toggleFullScreen": "", "fullScreen": "",
"toggleDarkMode": "", "darkMode": "",
"toggleZenMode": "", "lightMode": "",
"exitZenMode": "" "zenMode": "",
"exitZenMode": "Zen mód elhagyása"
}, },
"alerts": { "alerts": {
"clearReset": "Ez a művelet törli a vászont. Biztos benne?", "clearReset": "Ez a művelet törli a vászont. Biztos benne?",
"couldNotCreateShareableLink": "Nem sikerült megosztható linket létrehozni.", "couldNotCreateShareableLink": "Nem sikerült megosztható linket létrehozni.",
"couldNotCreateShareableLinkTooBig": "", "couldNotCreateShareableLinkTooBig": "Nem sikerült megosztható linket látrehozni: túl nagy a jelenet",
"couldNotLoadInvalidFile": "", "couldNotLoadInvalidFile": "Nem sikerült betölteni a helytelen fájlt",
"importBackendFailed": "Nem sikerült betölteni a szerverről.", "importBackendFailed": "Nem sikerült betölteni a szerverről.",
"cannotExportEmptyCanvas": "Üres vászont nem lehet exportálni.", "cannotExportEmptyCanvas": "Üres vászont nem lehet exportálni.",
"couldNotCopyToClipboard": "Nem sikerült vágólapra menteni. Próbálja meg Chrome böngészővel.", "couldNotCopyToClipboard": "Nem sikerült vágólapra menteni. Próbálja meg Chrome böngészővel.",
"decryptFailed": "Nem sikerült dekódolni az adatot.", "decryptFailed": "Nem sikerült visszafejteni a titkosított adatot.",
"uploadedSecurly": "A feltöltést végpontok közötti titkosítással biztosítottuk, ami azt jelenti, hogy az Excalidraw szerver és harmadik felek nem tudják elolvasni a feltöltés tartalmát.", "uploadedSecurly": "A feltöltést végpontok közötti titkosítással biztosítottuk, ami azt jelenti, hogy az Excalidraw szerver és harmadik felek nem tudják elolvasni a feltöltés tartalmát.",
"loadSceneOverridePrompt": "", "loadSceneOverridePrompt": "A betöltött külső rajz felül fogja írnia meglévőt. Szeretnéd folytatni?",
"errorLoadingLibrary": "", "errorLoadingLibrary": "Hibába ütközött a harmarmadik féltől származó könyvtár betöltése.",
"confirmAddLibrary": "", "confirmAddLibrary": "Ez a művelet {{numShapes}} formát fog hozzáadni a könyvtáradhoz. Biztos vagy benne?",
"imageDoesNotContainScene": "", "imageDoesNotContainScene": "",
"cannotRestoreFromImage": "" "cannotRestoreFromImage": ""
}, },
"toolBar": { "toolBar": {
"selection": "Kiválasztás", "selection": "Kiválasztás",
"draw": "", "draw": "Szabadkézi rajz",
"rectangle": "Téglalap", "rectangle": "Téglalap",
"diamond": "Rombusz", "diamond": "Rombusz",
"ellipse": "Ellipszis", "ellipse": "Ellipszis",
"arrow": "Nyíl", "arrow": "Nyíl",
"line": "Vonal", "line": "Vonal",
"text": "Szöveg", "text": "Szöveg",
"library": "", "library": "Könyvtár",
"lock": "Rajzolás után az aktív eszközt tartsa kiválasztva" "lock": "Rajzolás után az aktív eszközt tartsa kiválasztva"
}, },
"headings": { "headings": {
@@ -150,19 +158,20 @@
}, },
"hints": { "hints": {
"linearElement": "Kattintson a több pont elindításához, húzza az egyenes vonalhoz", "linearElement": "Kattintson a több pont elindításához, húzza az egyenes vonalhoz",
"freeDraw": "", "freeDraw": "Kattints és húzd, majd engedd el, amikor végeztél",
"text": "", "text": "Tipp: A kiválasztó eszközzel bárhol létrehozhatsz szöveget dupla kattintással",
"linearElementMulti": "Kattintson az utolsó pontra, vagy nyomja meg az Escape vagy az Enter billentyűt a befejezéshez", "linearElementMulti": "Kattintson az utolsó pontra, vagy nyomja meg az Escape vagy az Enter billentyűt a befejezéshez",
"lockAngle": "",
"resize": "", "resize": "",
"rotate": "A SHIFT billentyű lenyomva tartásával korlátozhatja a szögek illesztését", "rotate": "A SHIFT billentyű lenyomva tartásával korlátozhatja a szögek illesztését",
"lineEditor_info": "", "lineEditor_info": "Kattints duplán, vagy nyomj entert a pontok szerkesztéséhez",
"lineEditor_pointSelected": "", "lineEditor_pointSelected": "Nyomd meg a delete gombot a pont eltávolításához, Ctrl vagy Cmd + D-t a duplikáláshoz, vagy húzva mozgasd",
"lineEditor_nothingSelected": "" "lineEditor_nothingSelected": "Válassz ki egy pontot a mozgatáshoz vagy törtléshez, vagy az Alt lenyomása mellett kattintva hozz létre új pontokat"
}, },
"canvasError": { "canvasError": {
"cannotShowPreview": "", "cannotShowPreview": "Előnézet nem jeleníthető meg",
"canvasTooBig": "", "canvasTooBig": "A vászon talán túl nagy.",
"canvasTooBigTip": "" "canvasTooBigTip": "Tipp: próbáld meg a legtávolabbi elemeket közelebb mozgazni egy máshoz."
}, },
"errorSplash": { "errorSplash": {
"headingMain_pre": "Hiba történt. Próbálja ", "headingMain_pre": "Hiba történt. Próbálja ",
@@ -184,34 +193,43 @@
"button_stopSession": "Munkamenet leállítása", "button_stopSession": "Munkamenet leállítása",
"desc_inProgressIntro": "Az élő együttműködési munkamenet folyamatban van.", "desc_inProgressIntro": "Az élő együttműködési munkamenet folyamatban van.",
"desc_shareLink": "Ossza meg ezt a linket bárkivel, akivel együtt szeretne működni:", "desc_shareLink": "Ossza meg ezt a linket bárkivel, akivel együtt szeretne működni:",
"desc_exitSession": "" "desc_exitSession": "Az munkamenet leállítása kilépteti önt a szobából, de folytathatja a munkát a saját gépén. Vegye figyelembe, hogy ez nem érinti más emberek munkáját és ők továbbra is együttműködhetnek a saját változatukon."
}, },
"errorDialog": { "errorDialog": {
"title": "" "title": "Hiba"
}, },
"shortcutsDialog": { "shortcutsDialog": {
"title": "", "title": "Gyorsbillentyűk",
"shapes": "", "shapes": "Formák",
"or": "", "or": "vagy",
"click": "", "click": "klikk",
"drag": "", "drag": "húzd",
"curvedArrow": "", "curvedArrow": "Ívelt nyíl",
"curvedLine": "", "curvedLine": "Ívelt vonal",
"editor": "", "editor": "Szerkesztő",
"view": "", "view": "Nézet",
"blog": "", "blog": "Olvasd a blogunkat",
"howto": "", "howto": "Kövesd az útmutatóinkat",
"github": "", "github": "Hibát találtál? Küld be",
"textNewLine": "", "textNewLine": "Új sor hozzáadása (szöveg)",
"textFinish": "", "textFinish": "Szerkesztés befejezése (szöveg)",
"zoomToFit": "", "zoomToFit": "",
"zoomToSelection": "Kijelölésre nagyítás",
"preventBinding": "" "preventBinding": ""
}, },
"encrypted": { "encrypted": {
"tooltip": "" "tooltip": ""
}, },
"charts": { "stats": {
"noNumericColumn": "", "angle": "Szög",
"tooManyColumns": "" "element": "Elem",
"elements": "Elemek",
"height": "Magasság",
"scene": "",
"selected": "Kiválasztott",
"storage": "Tárhely",
"title": "",
"total": "Összesen",
"width": "Szélesség"
} }
} }
+27 -9
View File
@@ -4,6 +4,7 @@
"selectAll": "Pilih semua", "selectAll": "Pilih semua",
"multiSelect": "Tambahkan elemen ke pilihan", "multiSelect": "Tambahkan elemen ke pilihan",
"moveCanvas": "Pindahkan kanvas", "moveCanvas": "Pindahkan kanvas",
"cut": "Potong",
"copy": "Salin", "copy": "Salin",
"copyAsPng": "Salin ke papan klip sebagai PNG", "copyAsPng": "Salin ke papan klip sebagai PNG",
"copyAsSvg": "Salin ke papan klip sebagai SVG", "copyAsSvg": "Salin ke papan klip sebagai SVG",
@@ -28,10 +29,15 @@
"edges": "Tepi", "edges": "Tepi",
"sharp": "Tajam", "sharp": "Tajam",
"round": "Bulat", "round": "Bulat",
"arrowheads": "Mata panah",
"arrowhead_none": "Tidak ada",
"arrowhead_arrow": "Panah",
"arrowhead_bar": "Batang",
"arrowhead_dot": "Titik",
"fontSize": "Ukuran font", "fontSize": "Ukuran font",
"fontFamily": "Jenis font", "fontFamily": "Jenis font",
"onlySelected": "Hanya yang Dipilih", "onlySelected": "Hanya yang Dipilih",
"withBackground": "Dengan Latar", "withBackground": "Dengan latar",
"exportEmbedScene": "Sematkan pemandangan ke dalam file yang diekspor", "exportEmbedScene": "Sematkan pemandangan ke dalam file yang diekspor",
"exportEmbedScene_details": "Data pemandangan akan disimpan dalam file PNG/SVG yang diekspor, sehingga pemandangan itu dapat dipulihkan darinya.\nAkan membesarkan ukuran file yang diekspor.", "exportEmbedScene_details": "Data pemandangan akan disimpan dalam file PNG/SVG yang diekspor, sehingga pemandangan itu dapat dipulihkan darinya.\nAkan membesarkan ukuran file yang diekspor.",
"addWatermark": "Tambahkan \"Dibuat dengan Excalidraw\"", "addWatermark": "Tambahkan \"Dibuat dengan Excalidraw\"",
@@ -70,10 +76,11 @@
"group": "Kelompokan pilihan", "group": "Kelompokan pilihan",
"ungroup": "Pisahkan pilihan", "ungroup": "Pisahkan pilihan",
"collaborators": "Kolaborator", "collaborators": "Kolaborator",
"toggleGridMode": "Aktifkan/Matikan mode kisi", "gridMode": "Mode grid",
"addToLibrary": "Tambahkan ke pustaka", "addToLibrary": "Tambahkan ke pustaka",
"removeFromLibrary": "Hapus dari pustaka", "removeFromLibrary": "Hapus dari pustaka",
"libraryLoadingMessage": "Memuat pustaka...", "libraryLoadingMessage": "Memuat pustaka...",
"libraries": "Telusur pustaka",
"loadingScene": "Memuat pemandangan...", "loadingScene": "Memuat pemandangan...",
"align": "Perataan", "align": "Perataan",
"alignTop": "Rata atas", "alignTop": "Rata atas",
@@ -110,9 +117,10 @@
"redo": "Ulangi", "redo": "Ulangi",
"roomDialog": "Mulai kolaborasi langsung", "roomDialog": "Mulai kolaborasi langsung",
"createNewRoom": "Buat ruang baru", "createNewRoom": "Buat ruang baru",
"toggleFullScreen": "Beralih ke layar penuh", "fullScreen": "Layar penuh",
"toggleDarkMode": "Aktifkan/Matikan mode gelap", "darkMode": "Mode gelap",
"toggleZenMode": "Aktifkan/Matikan mode zen", "lightMode": "Mode terang",
"zenMode": "Mode zen",
"exitZenMode": "Keluar dari mode zen" "exitZenMode": "Keluar dari mode zen"
}, },
"alerts": { "alerts": {
@@ -128,7 +136,7 @@
"loadSceneOverridePrompt": "Memuat gambar external akan mengganti konten Anda yang ada. Apakah Anda ingin melanjutkan?", "loadSceneOverridePrompt": "Memuat gambar external akan mengganti konten Anda yang ada. Apakah Anda ingin melanjutkan?",
"errorLoadingLibrary": "Terdapat kesalahan dalam memuat pustaka pihak ketiga.", "errorLoadingLibrary": "Terdapat kesalahan dalam memuat pustaka pihak ketiga.",
"confirmAddLibrary": "Ini akan menambahkan {{numShapes}} bentuk ke pustaka Anda. Anda yakin?", "confirmAddLibrary": "Ini akan menambahkan {{numShapes}} bentuk ke pustaka Anda. Anda yakin?",
"imageDoesNotContainScene": "File gambar tidak berisi data pemandangan. Apa Anda sudah aktifkan ini selama ekspor?", "imageDoesNotContainScene": "Mengimpor gambar tidak didukung saat ini.\n\nApakah Anda ingin impor pemandangan? Gambar ini tidak berisi data pemandangan. Sudah ka Anda aktifkan ini ketika ekspor?",
"cannotRestoreFromImage": "Pemandangan tidak dapat dipulihkan dari file gambar ini" "cannotRestoreFromImage": "Pemandangan tidak dapat dipulihkan dari file gambar ini"
}, },
"toolBar": { "toolBar": {
@@ -153,6 +161,7 @@
"freeDraw": "Klik dan seret, lepaskan jika Anda selesai", "freeDraw": "Klik dan seret, lepaskan jika Anda selesai",
"text": "Tip: Anda juga dapat menambahkan teks dengan klik ganda di mana saja dengan alat pemilihan", "text": "Tip: Anda juga dapat menambahkan teks dengan klik ganda di mana saja dengan alat pemilihan",
"linearElementMulti": "Klik pada titik akhir atau tekan Escape atau Enter untuk menyelesaikan", "linearElementMulti": "Klik pada titik akhir atau tekan Escape atau Enter untuk menyelesaikan",
"lockAngle": "Anda dapat menjaga sudut dengan menahan SHIFT",
"resize": "Anda dapat menjaga proposi dengan menekan SHIFT sambil mengubah ukuran,\ntekan AlT untuk mengubah ukuran dari tengah", "resize": "Anda dapat menjaga proposi dengan menekan SHIFT sambil mengubah ukuran,\ntekan AlT untuk mengubah ukuran dari tengah",
"rotate": "Anda dapat menjaga sudut dengan menahan SHIFT sambil memutar", "rotate": "Anda dapat menjaga sudut dengan menahan SHIFT sambil memutar",
"lineEditor_info": "Klik ganda atau tekan Enter untuk mengedit titik", "lineEditor_info": "Klik ganda atau tekan Enter untuk mengedit titik",
@@ -205,13 +214,22 @@
"textNewLine": "Tambahkan baris baru (teks)", "textNewLine": "Tambahkan baris baru (teks)",
"textFinish": "Selesai mengedit (teks)", "textFinish": "Selesai mengedit (teks)",
"zoomToFit": "Perbesar agar sesuai dengan semua elemen", "zoomToFit": "Perbesar agar sesuai dengan semua elemen",
"zoomToSelection": "Perbesar ke seleksi",
"preventBinding": "Cegah pengikatan panah" "preventBinding": "Cegah pengikatan panah"
}, },
"encrypted": { "encrypted": {
"tooltip": "Gambar anda terenkripsi end-to-end sehingga server Excalidraw tidak akan pernah dapat melihatnya." "tooltip": "Gambar anda terenkripsi end-to-end sehingga server Excalidraw tidak akan pernah dapat melihatnya."
}, },
"charts": { "stats": {
"noNumericColumn": "Anda menempelkan sebuah lembar bentang tanpa sebuah kolom numerik.", "angle": "Sudut",
"tooManyColumns": "Anda menempelkan sebuah lembar bentang dengan lebih dari dua kolom." "element": "Elemen",
"elements": "Elemen",
"height": "Tinggi",
"scene": "Pemandangan",
"selected": "Terpilih",
"storage": "Penyimpanan",
"title": "Statistik untuk nerd",
"total": "Total",
"width": "Lebar"
} }
} }
+28 -10
View File
@@ -4,6 +4,7 @@
"selectAll": "Seleziona tutto", "selectAll": "Seleziona tutto",
"multiSelect": "Aggiungi elemento alla selezione", "multiSelect": "Aggiungi elemento alla selezione",
"moveCanvas": "Sposta tela", "moveCanvas": "Sposta tela",
"cut": "Taglia",
"copy": "Copia", "copy": "Copia",
"copyAsPng": "Copia negli appunti come PNG", "copyAsPng": "Copia negli appunti come PNG",
"copyAsSvg": "Copia negli appunti come SVG", "copyAsSvg": "Copia negli appunti come SVG",
@@ -28,10 +29,15 @@
"edges": "Bordi", "edges": "Bordi",
"sharp": "Acuto", "sharp": "Acuto",
"round": "Rotondo", "round": "Rotondo",
"arrowheads": "Punta della freccia",
"arrowhead_none": "Nessuno",
"arrowhead_arrow": "Freccia",
"arrowhead_bar": "Barra",
"arrowhead_dot": "Punto",
"fontSize": "Dimensione carattere", "fontSize": "Dimensione carattere",
"fontFamily": "Carattere", "fontFamily": "Carattere",
"onlySelected": "Solo selezionati", "onlySelected": "Solo selezionati",
"withBackground": "Con Sfondo", "withBackground": "Con sfondo",
"exportEmbedScene": "Incorpora la scena nel file esportato", "exportEmbedScene": "Incorpora la scena nel file esportato",
"exportEmbedScene_details": "I dati della scena saranno salvati nel file PNG/SVG esportato in modo che la scena possa essere ripristinata da esso.\nQuesto aumenterà la dimensione del file esportato.", "exportEmbedScene_details": "I dati della scena saranno salvati nel file PNG/SVG esportato in modo che la scena possa essere ripristinata da esso.\nQuesto aumenterà la dimensione del file esportato.",
"addWatermark": "Aggiungi \"Creato con Excalidraw\"", "addWatermark": "Aggiungi \"Creato con Excalidraw\"",
@@ -70,10 +76,11 @@
"group": "Crea gruppo da selezione", "group": "Crea gruppo da selezione",
"ungroup": "Dividi gruppo da selezione", "ungroup": "Dividi gruppo da selezione",
"collaborators": "Collaboratori", "collaborators": "Collaboratori",
"toggleGridMode": "Attiva/disattiva modalità quadrícula", "gridMode": "Modalità griglia",
"addToLibrary": "Aggiungi alla biblioteca", "addToLibrary": "Aggiungi alla biblioteca",
"removeFromLibrary": "Rimuovi dalla biblioteca", "removeFromLibrary": "Rimuovi dalla biblioteca",
"libraryLoadingMessage": "Caricamento della biblioteca...", "libraryLoadingMessage": "Caricamento della biblioteca...",
"libraries": "Sfoglia librerie",
"loadingScene": "Caricamento della scena...", "loadingScene": "Caricamento della scena...",
"align": "Allinea", "align": "Allinea",
"alignTop": "Allinea in alto", "alignTop": "Allinea in alto",
@@ -110,13 +117,14 @@
"redo": "Ripeti", "redo": "Ripeti",
"roomDialog": "Inizia collaborazione in diretta", "roomDialog": "Inizia collaborazione in diretta",
"createNewRoom": "Crea nuova stanza", "createNewRoom": "Crea nuova stanza",
"toggleFullScreen": "Attiva/Disattiva schermo intero", "fullScreen": "Schermo intero",
"toggleDarkMode": "Attiva tema scuro", "darkMode": "Tema scuro",
"toggleZenMode": "Attiva/Disattiva modalità zen", "lightMode": "Tema chiaro",
"zenMode": "Modalità Zen",
"exitZenMode": "Uscire dalla modalità zen" "exitZenMode": "Uscire dalla modalità zen"
}, },
"alerts": { "alerts": {
"clearReset": "Questo cancellerà l'intera tela. Sei sicuro?", "clearReset": "Questa azione cancellerà l'intera tela. Sei sicuro?",
"couldNotCreateShareableLink": "Non riesco a creare un link condivisibile.", "couldNotCreateShareableLink": "Non riesco a creare un link condivisibile.",
"couldNotCreateShareableLinkTooBig": "Impossibile creare il link condivisibile: la scena è troppo grande", "couldNotCreateShareableLinkTooBig": "Impossibile creare il link condivisibile: la scena è troppo grande",
"couldNotLoadInvalidFile": "Impossibile caricare un file no valido", "couldNotLoadInvalidFile": "Impossibile caricare un file no valido",
@@ -128,7 +136,7 @@
"loadSceneOverridePrompt": "Se carichi questo disegno esterno, sostituirà quello che hai. Vuoi continuare?", "loadSceneOverridePrompt": "Se carichi questo disegno esterno, sostituirà quello che hai. Vuoi continuare?",
"errorLoadingLibrary": "Si è verificato un errore nel caricamento della libreria di terze parti.", "errorLoadingLibrary": "Si è verificato un errore nel caricamento della libreria di terze parti.",
"confirmAddLibrary": "Questo aggiungerà {{numShapes}} forma(e) alla tua biblioteca. Sei sicuro?", "confirmAddLibrary": "Questo aggiungerà {{numShapes}} forma(e) alla tua biblioteca. Sei sicuro?",
"imageDoesNotContainScene": "Il file immagine non contiene dati di scena. È stato abilitato durante l'esportazione?", "imageDoesNotContainScene": "L'importazione di immagini al momento non è supportata.\n\nVuoi importare una scena? Questa immagine non sembra contenere alcun dato di scena. Hai abilitato questa opzione durante l'esportazione?",
"cannotRestoreFromImage": "Impossibile ripristinare la scena da questo file immagine" "cannotRestoreFromImage": "Impossibile ripristinare la scena da questo file immagine"
}, },
"toolBar": { "toolBar": {
@@ -153,6 +161,7 @@
"freeDraw": "Clicca e trascina, rilascia quando avrai finito", "freeDraw": "Clicca e trascina, rilascia quando avrai finito",
"text": "Suggerimento: puoi anche aggiungere del testo facendo doppio clic ovunque con lo strumento di selezione", "text": "Suggerimento: puoi anche aggiungere del testo facendo doppio clic ovunque con lo strumento di selezione",
"linearElementMulti": "Clicca sull'ultimo punto o premi Esc o Invio per finire", "linearElementMulti": "Clicca sull'ultimo punto o premi Esc o Invio per finire",
"lockAngle": "Puoi limitare l'angolo tenendo premuto SHIFT",
"resize": "Per vincolare le proporzioni, tenir premuto MAIUSC durante il ridimensionamento;\nper ridimensionare dal centro, tenir premuto ALT", "resize": "Per vincolare le proporzioni, tenir premuto MAIUSC durante il ridimensionamento;\nper ridimensionare dal centro, tenir premuto ALT",
"rotate": "Puoi mantenere gli angoli tenendo premuto SHIFT durante la rotazione", "rotate": "Puoi mantenere gli angoli tenendo premuto SHIFT durante la rotazione",
"lineEditor_info": "Fai doppio click o premi invio per modificare i punti", "lineEditor_info": "Fai doppio click o premi invio per modificare i punti",
@@ -205,13 +214,22 @@
"textNewLine": "Aggiungi nuova riga (testo)", "textNewLine": "Aggiungi nuova riga (testo)",
"textFinish": "Completa la modifica (testo)", "textFinish": "Completa la modifica (testo)",
"zoomToFit": "Adatta zoom per mostrare tutti gli elementi", "zoomToFit": "Adatta zoom per mostrare tutti gli elementi",
"zoomToSelection": "Zoom alla selezione",
"preventBinding": "Prevenire l'associazione freccia" "preventBinding": "Prevenire l'associazione freccia"
}, },
"encrypted": { "encrypted": {
"tooltip": "I tuoi disegni sono crittografati end-to-end in modo che i server di Excalidraw non li possano mai vedere." "tooltip": "I tuoi disegni sono crittografati end-to-end in modo che i server di Excalidraw non li possano mai vedere."
}, },
"charts": { "stats": {
"noNumericColumn": "Hai incollato un foglio di calcolo senza una colonna numerica.", "angle": "Angolo",
"tooManyColumns": "Hai incollato un foglio di calcolo con più di due colonne." "element": "Elemento",
"elements": "Elementi",
"height": "Altezza",
"scene": "Scena",
"selected": "Selezionato",
"storage": "Memoria",
"title": "Statistiche per nerd",
"total": "Totale",
"width": "Larghezza"
} }
} }
+27 -9
View File
@@ -4,6 +4,7 @@
"selectAll": "すべて選択", "selectAll": "すべて選択",
"multiSelect": "複数選択", "multiSelect": "複数選択",
"moveCanvas": "キャンバスを移動", "moveCanvas": "キャンバスを移動",
"cut": "",
"copy": "コピー", "copy": "コピー",
"copyAsPng": "PNGとしてクリップボードへコピー", "copyAsPng": "PNGとしてクリップボードへコピー",
"copyAsSvg": "SVGとしてクリップボードへコピー", "copyAsSvg": "SVGとしてクリップボードへコピー",
@@ -28,10 +29,15 @@
"edges": "角", "edges": "角",
"sharp": "四角", "sharp": "四角",
"round": "丸", "round": "丸",
"arrowheads": "",
"arrowhead_none": "",
"arrowhead_arrow": "",
"arrowhead_bar": "",
"arrowhead_dot": "",
"fontSize": "フォントの大きさ", "fontSize": "フォントの大きさ",
"fontFamily": "フォントの種類", "fontFamily": "フォントの種類",
"onlySelected": "選択中のみ", "onlySelected": "選択中のみ",
"withBackground": "背景を含める", "withBackground": "",
"exportEmbedScene": "エクスポートされたファイルにシーンを埋め込みます", "exportEmbedScene": "エクスポートされたファイルにシーンを埋め込みます",
"exportEmbedScene_details": "シーンデータはエクスポートされたPNG/SVGファイルに保存され、シーンを復元することができます。\nエクスポートされたファイルのサイズは増加します。", "exportEmbedScene_details": "シーンデータはエクスポートされたPNG/SVGファイルに保存され、シーンを復元することができます。\nエクスポートされたファイルのサイズは増加します。",
"addWatermark": "\"Made with Excalidraw\"と表示", "addWatermark": "\"Made with Excalidraw\"と表示",
@@ -70,10 +76,11 @@
"group": "図形のグループ化", "group": "図形のグループ化",
"ungroup": "グループ化を解除", "ungroup": "グループ化を解除",
"collaborators": "共同編集者", "collaborators": "共同編集者",
"toggleGridMode": "グリッドモードに切り替える", "gridMode": "",
"addToLibrary": "ライブラリに追加", "addToLibrary": "ライブラリに追加",
"removeFromLibrary": "ライブラリから削除", "removeFromLibrary": "ライブラリから削除",
"libraryLoadingMessage": "ライブラリを読み込み中...", "libraryLoadingMessage": "ライブラリを読み込み中...",
"libraries": "",
"loadingScene": "シーンを読み込み中...", "loadingScene": "シーンを読み込み中...",
"align": "整列", "align": "整列",
"alignTop": "上揃え", "alignTop": "上揃え",
@@ -110,9 +117,10 @@
"redo": "やり直し", "redo": "やり直し",
"roomDialog": "共同編集を開始する", "roomDialog": "共同編集を開始する",
"createNewRoom": "新しい部屋を作成する", "createNewRoom": "新しい部屋を作成する",
"toggleFullScreen": "全画面表示に切り替える", "fullScreen": "",
"toggleDarkMode": "ダークモードに切り替える", "darkMode": "",
"toggleZenMode": "集中モードに切り替える", "lightMode": "",
"zenMode": "",
"exitZenMode": "集中モードをやめる" "exitZenMode": "集中モードをやめる"
}, },
"alerts": { "alerts": {
@@ -128,7 +136,7 @@
"loadSceneOverridePrompt": "外部図面を読み込むと、既存のコンテンツが置き換わります。続行しますか?", "loadSceneOverridePrompt": "外部図面を読み込むと、既存のコンテンツが置き換わります。続行しますか?",
"errorLoadingLibrary": "サードパーティライブラリの読み込み中にエラーが発生しました。", "errorLoadingLibrary": "サードパーティライブラリの読み込み中にエラーが発生しました。",
"confirmAddLibrary": "{{numShapes}} 個の図形をライブラリに追加します。よろしいですか?", "confirmAddLibrary": "{{numShapes}} 個の図形をライブラリに追加します。よろしいですか?",
"imageDoesNotContainScene": "画像ファイルにシーンデータが含まれていません。エクスポート中にこれを有効にしましたか?", "imageDoesNotContainScene": "",
"cannotRestoreFromImage": "このイメージファイルからシーンを復元できませんでした" "cannotRestoreFromImage": "このイメージファイルからシーンを復元できませんでした"
}, },
"toolBar": { "toolBar": {
@@ -153,6 +161,7 @@
"freeDraw": "クリックしてドラッグします。離すと終了します", "freeDraw": "クリックしてドラッグします。離すと終了します",
"text": "ヒント: 選択ツールを使用して任意の場所をダブルクリックしてテキストを追加することもできます", "text": "ヒント: 選択ツールを使用して任意の場所をダブルクリックしてテキストを追加することもできます",
"linearElementMulti": "最後のポイントをクリックするか、エスケープまたはEnterを押して終了します", "linearElementMulti": "最後のポイントをクリックするか、エスケープまたはEnterを押して終了します",
"lockAngle": "",
"resize": "サイズを変更中にSHIFTを押しすと比率を制御できます。Altを押すと中央からサイズを変更できます。", "resize": "サイズを変更中にSHIFTを押しすと比率を制御できます。Altを押すと中央からサイズを変更できます。",
"rotate": "回転中にSHIFT キーを押すと角度を制限することができます", "rotate": "回転中にSHIFT キーを押すと角度を制限することができます",
"lineEditor_info": "ポイントを編集するには、ダブルクリックまたはEnterキーを押します", "lineEditor_info": "ポイントを編集するには、ダブルクリックまたはEnterキーを押します",
@@ -205,13 +214,22 @@
"textNewLine": "テキストの改行", "textNewLine": "テキストの改行",
"textFinish": "テキストの編集を終える", "textFinish": "テキストの編集を終える",
"zoomToFit": "すべての図形が収まるよう拡大/縮小", "zoomToFit": "すべての図形が収まるよう拡大/縮小",
"zoomToSelection": "",
"preventBinding": "矢印を結合しない" "preventBinding": "矢印を結合しない"
}, },
"encrypted": { "encrypted": {
"tooltip": "描画内容はエンドツーエンド暗号化が施されており、Excalidrawサーバーが内容を見ることはできません。" "tooltip": "描画内容はエンドツーエンド暗号化が施されており、Excalidrawサーバーが内容を見ることはできません。"
}, },
"charts": { "stats": {
"noNumericColumn": "数値の列が存在しないスプレッドシートをペーストしました。", "angle": "",
"tooManyColumns": "2列以上のスプレッドシートを貼り付けました." "element": "",
"elements": "",
"height": "",
"scene": "",
"selected": "",
"storage": "",
"title": "",
"total": "",
"width": ""
} }
} }
+26 -8
View File
@@ -4,6 +4,7 @@
"selectAll": "전체 선택", "selectAll": "전체 선택",
"multiSelect": "선택 영역에 추가하기", "multiSelect": "선택 영역에 추가하기",
"moveCanvas": "캔버스 이동", "moveCanvas": "캔버스 이동",
"cut": "잘라내기",
"copy": "복사하기", "copy": "복사하기",
"copyAsPng": "클립보드로 PNG 이미지 복사", "copyAsPng": "클립보드로 PNG 이미지 복사",
"copyAsSvg": "클립보드로 SVG 이미지 복사", "copyAsSvg": "클립보드로 SVG 이미지 복사",
@@ -28,10 +29,15 @@
"edges": "가장자리", "edges": "가장자리",
"sharp": "선명하게", "sharp": "선명하게",
"round": "둥글게", "round": "둥글게",
"arrowheads": "화살표 모양",
"arrowhead_none": "없음",
"arrowhead_arrow": "화살표",
"arrowhead_bar": "",
"arrowhead_dot": "",
"fontSize": "폰트 크기", "fontSize": "폰트 크기",
"fontFamily": "폰트 스타일", "fontFamily": "폰트 스타일",
"onlySelected": "선택한 항목만", "onlySelected": "선택한 항목만",
"withBackground": "배경 포함", "withBackground": "",
"exportEmbedScene": "", "exportEmbedScene": "",
"exportEmbedScene_details": "", "exportEmbedScene_details": "",
"addWatermark": "\"Made with Excalidraw\" 추가", "addWatermark": "\"Made with Excalidraw\" 추가",
@@ -70,10 +76,11 @@
"group": "그룹 생성", "group": "그룹 생성",
"ungroup": "그룹 해제", "ungroup": "그룹 해제",
"collaborators": "공동 작업자", "collaborators": "공동 작업자",
"toggleGridMode": "격자 모드 켜기/끄기", "gridMode": "",
"addToLibrary": "라이브러리에 추가", "addToLibrary": "라이브러리에 추가",
"removeFromLibrary": "라이브러리에서 제거", "removeFromLibrary": "라이브러리에서 제거",
"libraryLoadingMessage": "라이브러리 불러오는 중...", "libraryLoadingMessage": "라이브러리 불러오는 중...",
"libraries": "",
"loadingScene": "화면 불러오는 중...", "loadingScene": "화면 불러오는 중...",
"align": "", "align": "",
"alignTop": "", "alignTop": "",
@@ -110,9 +117,10 @@
"redo": "다시 실행", "redo": "다시 실행",
"roomDialog": "실시간 협업 시작하기", "roomDialog": "실시간 협업 시작하기",
"createNewRoom": "방 만들기", "createNewRoom": "방 만들기",
"toggleFullScreen": "전체화면", "fullScreen": "",
"toggleDarkMode": "다크 모드 켜기/끄기", "darkMode": "",
"toggleZenMode": "젠 모드 켜기/끄기", "lightMode": "",
"zenMode": "",
"exitZenMode": "젠 모드 종료하기" "exitZenMode": "젠 모드 종료하기"
}, },
"alerts": { "alerts": {
@@ -153,6 +161,7 @@
"freeDraw": "클릭 후 드래그하세요. 완료되면 놓으세요.", "freeDraw": "클릭 후 드래그하세요. 완료되면 놓으세요.",
"text": "", "text": "",
"linearElementMulti": "마지막 지점을 클릭하거나 Esc 또는 Enter 키를 눌러 완료하세요.", "linearElementMulti": "마지막 지점을 클릭하거나 Esc 또는 Enter 키를 눌러 완료하세요.",
"lockAngle": "",
"resize": "", "resize": "",
"rotate": "SHIFT 키를 누르면서 회전하면 각도를 제한할 수 있습니다.", "rotate": "SHIFT 키를 누르면서 회전하면 각도를 제한할 수 있습니다.",
"lineEditor_info": "포인트를 수정하려면 두 번 클릭하거나 엔터 키를 누르세요.", "lineEditor_info": "포인트를 수정하려면 두 번 클릭하거나 엔터 키를 누르세요.",
@@ -205,13 +214,22 @@
"textNewLine": "줄바꾸기", "textNewLine": "줄바꾸기",
"textFinish": "편집 완료", "textFinish": "편집 완료",
"zoomToFit": "", "zoomToFit": "",
"zoomToSelection": "",
"preventBinding": "" "preventBinding": ""
}, },
"encrypted": { "encrypted": {
"tooltip": "" "tooltip": ""
}, },
"charts": { "stats": {
"noNumericColumn": "", "angle": "",
"tooManyColumns": "" "element": "",
"elements": "",
"height": "",
"scene": "",
"selected": "",
"storage": "",
"title": "",
"total": "",
"width": ""
} }
} }
+31 -13
View File
@@ -4,6 +4,7 @@
"selectAll": "အကုန်ရွေး", "selectAll": "အကုန်ရွေး",
"multiSelect": "ရွေးထားသည့်ထဲပုံထည့်", "multiSelect": "ရွေးထားသည့်ထဲပုံထည့်",
"moveCanvas": "ကားချပ်ရွှေ့", "moveCanvas": "ကားချပ်ရွှေ့",
"cut": "",
"copy": "ကူး", "copy": "ကူး",
"copyAsPng": "PNG အနေဖြင့်ကူး", "copyAsPng": "PNG အနေဖြင့်ကူး",
"copyAsSvg": "SVG အနေဖြင့်ကူး", "copyAsSvg": "SVG အနေဖြင့်ကူး",
@@ -28,10 +29,15 @@
"edges": "အစွန်း", "edges": "အစွန်း",
"sharp": "ထောင့်ချွန်", "sharp": "ထောင့်ချွန်",
"round": "ထောင့်ဝိုင်း", "round": "ထောင့်ဝိုင်း",
"arrowheads": "မြှားခေါင်း",
"arrowhead_none": "ဘာမျှမရှိ",
"arrowhead_arrow": "မြှား",
"arrowhead_bar": "",
"arrowhead_dot": "",
"fontSize": "စာလုံးအရွယ်", "fontSize": "စာလုံးအရွယ်",
"fontFamily": "စာလုံးပုံစံ", "fontFamily": "စာလုံးပုံစံ",
"onlySelected": "ရွေးထားသလောက်", "onlySelected": "ရွေးထားသလောက်",
"withBackground": "နောက်ခံပါထည့်", "withBackground": "",
"exportEmbedScene": "မြင်ကွင်းပါမြှုပ်နှံ၍ထုတ်ပါ", "exportEmbedScene": "မြင်ကွင်းပါမြှုပ်နှံ၍ထုတ်ပါ",
"exportEmbedScene_details": "ထုတ်ယူလိုက်သော PNG/SVG ထဲမြင်ကွင်းအချက်အလက်များပါဝင်သဖြင့် ပြန်လည်ရယူနိုင်သော်လည်း ဖိုင်အရွယ်အစားကြီးပါမည်။", "exportEmbedScene_details": "ထုတ်ယူလိုက်သော PNG/SVG ထဲမြင်ကွင်းအချက်အလက်များပါဝင်သဖြင့် ပြန်လည်ရယူနိုင်သော်လည်း ဖိုင်အရွယ်အစားကြီးပါမည်။",
"addWatermark": "\"Excalidraw ဖြင့်ဖန်တီးသည်။\" စာသားထည့်", "addWatermark": "\"Excalidraw ဖြင့်ဖန်တီးသည်။\" စာသားထည့်",
@@ -54,7 +60,7 @@
"architect": "ဗိသုကာ", "architect": "ဗိသုကာ",
"artist": "ပန်းချီ", "artist": "ပန်းချီ",
"cartoonist": "ကာတွန်း", "cartoonist": "ကာတွန်း",
"fileTitle": "", "fileTitle": "ခေါင်းစဉ်",
"colorPicker": "အရောင်ရွေး", "colorPicker": "အရောင်ရွေး",
"canvasBackground": "ကားချပ်နောက်ခံ", "canvasBackground": "ကားချပ်နောက်ခံ",
"drawingCanvas": "ပုံဆွဲကားချပ်", "drawingCanvas": "ပုံဆွဲကားချပ်",
@@ -63,17 +69,18 @@
"language": "ဘာသာစကား", "language": "ဘာသာစကား",
"createRoom": "တိုက်ရိုက်ပူးပေါင်းဆောင်ရွက်ရန်အဖွဲ့ဖွဲ့", "createRoom": "တိုက်ရိုက်ပူးပေါင်းဆောင်ရွက်ရန်အဖွဲ့ဖွဲ့",
"duplicateSelection": "ပွား", "duplicateSelection": "ပွား",
"untitled": "", "untitled": "အမည်မရှိ",
"name": "အမည်", "name": "အမည်",
"yourName": "သင့်အမည်", "yourName": "သင့်အမည်",
"madeWithExcalidraw": "Excalidraw ဖြင့်ဖန်တီးသည်။", "madeWithExcalidraw": "Excalidraw ဖြင့်ဖန်တီးသည်။",
"group": "အုပ်စုဖွဲ့", "group": "အုပ်စုဖွဲ့",
"ungroup": "အုပ်စုဖျက်သိမ်း", "ungroup": "အုပ်စုဖျက်သိမ်း",
"collaborators": "ပူးပေါင်းပါဝင်သူများ", "collaborators": "ပူးပေါင်းပါဝင်သူများ",
"toggleGridMode": "ဇယားကွက်ဖော်/ဖျောက်", "gridMode": "",
"addToLibrary": "မှတ်တမ်းတင်", "addToLibrary": "မှတ်တမ်းတင်",
"removeFromLibrary": "မှတ်တမ်းမှထုတ်", "removeFromLibrary": "မှတ်တမ်းမှထုတ်",
"libraryLoadingMessage": "မှတ်တမ်းအား တင်သွင်းနေသည်...", "libraryLoadingMessage": "မှတ်တမ်းအား တင်သွင်းနေသည်...",
"libraries": "စာကြည့်တိုက်တွင်ရှာဖွေပါ",
"loadingScene": "မြင်ကွင်းဖော်နေသည်...", "loadingScene": "မြင်ကွင်းဖော်နေသည်...",
"align": "ချိန်ညှိ", "align": "ချိန်ညှိ",
"alignTop": "ထိပ်ညှိ", "alignTop": "ထိပ်ညှိ",
@@ -82,8 +89,8 @@
"alignRight": "ညာညှိ", "alignRight": "ညာညှိ",
"centerVertically": "ဒေါင်လိုက်အလယ်ညှိ", "centerVertically": "ဒေါင်လိုက်အလယ်ညှိ",
"centerHorizontally": "အလျားလိုက်အလယ်ညှိ", "centerHorizontally": "အလျားလိုက်အလယ်ညှိ",
"distributeHorizontally": "", "distributeHorizontally": "အလျားလိုက်",
"distributeVertically": "" "distributeVertically": "ထောင်လိုက်"
}, },
"buttons": { "buttons": {
"clearReset": "ကားချပ်ရှင်းလင်း", "clearReset": "ကားချပ်ရှင်းလင်း",
@@ -110,9 +117,10 @@
"redo": "ထပ်လုပ်", "redo": "ထပ်လုပ်",
"roomDialog": "တိုက်ရိုက်ပူးပေါင်းမှုစတင်", "roomDialog": "တိုက်ရိုက်ပူးပေါင်းမှုစတင်",
"createNewRoom": "အခန်းသစ်ဖွဲ့", "createNewRoom": "အခန်းသစ်ဖွဲ့",
"toggleFullScreen": "မြင်ကွင်းကျယ်ဖွင့်/ပိတ်", "fullScreen": "",
"toggleDarkMode": "အလင်း/အမှောင်", "darkMode": "",
"toggleZenMode": "ဇင်မြင်ကွင်းဖွင့်/ပိတ်", "lightMode": "",
"zenMode": "",
"exitZenMode": "ဇင်မြင်ကွင်းမှထွက်" "exitZenMode": "ဇင်မြင်ကွင်းမှထွက်"
}, },
"alerts": { "alerts": {
@@ -128,7 +136,7 @@
"loadSceneOverridePrompt": "လက်ရှိရေးဆွဲထားသမျှအား ပြင်ပမှတင်သွင်းသောပုံနှင့်အစားထိုးပါမည်။ ဆက်လက်ဆောင်ရွက်လိုပါသလား။", "loadSceneOverridePrompt": "လက်ရှိရေးဆွဲထားသမျှအား ပြင်ပမှတင်သွင်းသောပုံနှင့်အစားထိုးပါမည်။ ဆက်လက်ဆောင်ရွက်လိုပါသလား။",
"errorLoadingLibrary": "ပြင်ပမှမှတ်တမ်းအားတင်သွင်းရာတွင်အမှားအယွင်းရှိနေသည်။", "errorLoadingLibrary": "ပြင်ပမှမှတ်တမ်းအားတင်သွင်းရာတွင်အမှားအယွင်းရှိနေသည်။",
"confirmAddLibrary": "{{numShapes}} ခုသောပုံသဏ္ဌာန်အားမှတ်တမ်းတင်ပါမည်။ အတည်ပြုပါ။", "confirmAddLibrary": "{{numShapes}} ခုသောပုံသဏ္ဌာန်အားမှတ်တမ်းတင်ပါမည်။ အတည်ပြုပါ။",
"imageDoesNotContainScene": "ပုံတွင် မြင်ကွင်းအချက်အလက်များမပါဝင်ပါ။ ပုံထုတ်ယူချိန်တွင်ထည့်သွင်းခဲ့ပါသလား။", "imageDoesNotContainScene": "",
"cannotRestoreFromImage": "ဤပုံဖြင့်မြင်ကွင်းပြန်လည်မရယူနိုင်ပါ။" "cannotRestoreFromImage": "ဤပုံဖြင့်မြင်ကွင်းပြန်လည်မရယူနိုင်ပါ။"
}, },
"toolBar": { "toolBar": {
@@ -153,6 +161,7 @@
"freeDraw": "ကလစ်နှိပ်၍ တရွတ်ဆွဲပါ၊ ပြီးလျှင်လွှတ်ပါ။", "freeDraw": "ကလစ်နှိပ်၍ တရွတ်ဆွဲပါ၊ ပြီးလျှင်လွှတ်ပါ။",
"text": "မှတ်ချက်။ ။မည်သည့်ကိရိယာရွေးထားသည်ဖြစ်စေ ကလစ်နှစ်ချက်နှိပ်၍စာသားထည့်နိုင်သည်", "text": "မှတ်ချက်။ ။မည်သည့်ကိရိယာရွေးထားသည်ဖြစ်စေ ကလစ်နှစ်ချက်နှိပ်၍စာသားထည့်နိုင်သည်",
"linearElementMulti": "နောက်ဆုံးအမှတ်ပေါ်တွင်ကလစ်နှိပ်ခြင်း၊ Escape (သို့) Enter နှိပ်ခြင်းတို့ဖြင့်အဆုံးသတ်နိုင်", "linearElementMulti": "နောက်ဆုံးအမှတ်ပေါ်တွင်ကလစ်နှိပ်ခြင်း၊ Escape (သို့) Enter နှိပ်ခြင်းတို့ဖြင့်အဆုံးသတ်နိုင်",
"lockAngle": "",
"resize": "အချိုးအစားကန့်သတ်ရန် Shift နှင့် ဗဟိုမှချိန်ညှိရန် Alt တို့ကိုနှိပ်ထားနိုင်သည်", "resize": "အချိုးအစားကန့်သတ်ရန် Shift နှင့် ဗဟိုမှချိန်ညှိရန် Alt တို့ကိုနှိပ်ထားနိုင်သည်",
"rotate": "Shift ကိုနှိပ်ထားခြင်းဖြင့် ထောင့်အလိုက်လှည့်နိုင်သည်", "rotate": "Shift ကိုနှိပ်ထားခြင်းဖြင့် ထောင့်အလိုက်လှည့်နိုင်သည်",
"lineEditor_info": "အမှတ်များပြင်ဆင်သတ်မှတ်ရင် ကလစ်နှစ်ချက် (သို့) Enter ကိုနှိပ်ပါ", "lineEditor_info": "အမှတ်များပြင်ဆင်သတ်မှတ်ရင် ကလစ်နှစ်ချက် (သို့) Enter ကိုနှိပ်ပါ",
@@ -205,13 +214,22 @@
"textNewLine": "စာသားဖြည့်သွင်း", "textNewLine": "စာသားဖြည့်သွင်း",
"textFinish": "စာသားဖြည့်သွင်းပြီး", "textFinish": "စာသားဖြည့်သွင်းပြီး",
"zoomToFit": "ကားချပ်အပြည့်ဖေါ်", "zoomToFit": "ကားချပ်အပြည့်ဖေါ်",
"zoomToSelection": "",
"preventBinding": "မြှားများမပေါင်းစေရန်" "preventBinding": "မြှားများမပေါင်းစေရန်"
}, },
"encrypted": { "encrypted": {
"tooltip": "ရေးဆွဲထားသောပုံများအား နှစ်ဘက်စွန်းတိုင်လျှို့ဝှက်ထားသဖြင့် Excalidraw ၏ဆာဗာများပင်လျှင်မြင်တွေ့ရမည်မဟုတ်ပါ။" "tooltip": "ရေးဆွဲထားသောပုံများအား နှစ်ဘက်စွန်းတိုင်လျှို့ဝှက်ထားသဖြင့် Excalidraw ၏ဆာဗာများပင်လျှင်မြင်တွေ့ရမည်မဟုတ်ပါ။"
}, },
"charts": { "stats": {
"noNumericColumn": "အမှတ်စဉ်ကော်လံမပါဝင်သောဇယားအားထည့်သွင်းလိုက်သည်။", "angle": "ထောင့်",
"tooManyColumns": "ကော်လံနှစ်ခုထက်ပိုပါနေသောဇယားအားထည့်သွင်းလိုက်သည်။" "element": "",
"elements": "",
"height": "အမြင့်",
"scene": "မြင်ကွင်း",
"selected": "ရွေးချယ်သည်",
"storage": "သိုလှောင်ခန်း",
"title": "အက္ခရာများအတွက်အချက်အလက်များ",
"total": "စုစုပေါင်း",
"width": "အကျယ်"
} }
} }
+27 -9
View File
@@ -4,6 +4,7 @@
"selectAll": "Velg alt", "selectAll": "Velg alt",
"multiSelect": "Legg til element i utvalg", "multiSelect": "Legg til element i utvalg",
"moveCanvas": "Flytt lerretet", "moveCanvas": "Flytt lerretet",
"cut": "Klipp ut",
"copy": "Kopier", "copy": "Kopier",
"copyAsPng": "Kopier til PNG", "copyAsPng": "Kopier til PNG",
"copyAsSvg": "Kopier til utklippstavlen som SVG", "copyAsSvg": "Kopier til utklippstavlen som SVG",
@@ -28,10 +29,15 @@
"edges": "Kanter", "edges": "Kanter",
"sharp": "Skarp", "sharp": "Skarp",
"round": "Rund", "round": "Rund",
"arrowheads": "Pilspisser",
"arrowhead_none": "Ingen",
"arrowhead_arrow": "Pil",
"arrowhead_bar": "Søyle",
"arrowhead_dot": "Prikk",
"fontSize": "Skriftstørrelse", "fontSize": "Skriftstørrelse",
"fontFamily": "Fontfamilie", "fontFamily": "Fontfamilie",
"onlySelected": "Kun valgte", "onlySelected": "Kun valgte",
"withBackground": "Inkluder bakgrunn", "withBackground": "Med bakgrunn",
"exportEmbedScene": "Bygg inn scenen i den eksporterte filen", "exportEmbedScene": "Bygg inn scenen i den eksporterte filen",
"exportEmbedScene_details": "Scenedata vil bli lagret i den eksporterte PNG/SVG-filen, slik at scenen kan gjenopprettes fra den.\nDet vil øke den eksporterte filstørrelsen.", "exportEmbedScene_details": "Scenedata vil bli lagret i den eksporterte PNG/SVG-filen, slik at scenen kan gjenopprettes fra den.\nDet vil øke den eksporterte filstørrelsen.",
"addWatermark": "Legg til \"Laget med Excalidraw\"", "addWatermark": "Legg til \"Laget med Excalidraw\"",
@@ -70,10 +76,11 @@
"group": "Gruppér utvalg", "group": "Gruppér utvalg",
"ungroup": "Avgruppér utvalg", "ungroup": "Avgruppér utvalg",
"collaborators": "Samarbeidspartnere", "collaborators": "Samarbeidspartnere",
"toggleGridMode": "Slå av/på rutenett", "gridMode": "Rutevisning",
"addToLibrary": "Legg til i bibliotek", "addToLibrary": "Legg til i bibliotek",
"removeFromLibrary": "Fjern fra bibliotek", "removeFromLibrary": "Fjern fra bibliotek",
"libraryLoadingMessage": "Laster bibliotek...", "libraryLoadingMessage": "Laster bibliotek...",
"libraries": "Bla gjennom biblioteker",
"loadingScene": "Laster inn scene...", "loadingScene": "Laster inn scene...",
"align": "Juster", "align": "Juster",
"alignTop": "Juster øverst", "alignTop": "Juster øverst",
@@ -110,9 +117,10 @@
"redo": "Gjør om", "redo": "Gjør om",
"roomDialog": "Start sanntids-samarbeid", "roomDialog": "Start sanntids-samarbeid",
"createNewRoom": "Opprett et nytt rom", "createNewRoom": "Opprett et nytt rom",
"toggleFullScreen": "Skru fullskjerm av/på", "fullScreen": "Fullskjerm",
"toggleDarkMode": "Skru mørk modus av/på", "darkMode": "Mørk modus",
"toggleZenMode": "Slå av/på zen-modus", "lightMode": "Lys modus",
"zenMode": "Zen-modus",
"exitZenMode": "Avslutt zen-modus" "exitZenMode": "Avslutt zen-modus"
}, },
"alerts": { "alerts": {
@@ -128,7 +136,7 @@
"loadSceneOverridePrompt": "Å laste inn ekstern tegning vil erstatte det eksisterende innholdet. Ønsker du å fortsette?", "loadSceneOverridePrompt": "Å laste inn ekstern tegning vil erstatte det eksisterende innholdet. Ønsker du å fortsette?",
"errorLoadingLibrary": "Det oppstod en feil under lasting av tredjepartsbiblioteket.", "errorLoadingLibrary": "Det oppstod en feil under lasting av tredjepartsbiblioteket.",
"confirmAddLibrary": "Dette vil legge til {{numShapes}} figur(er) i biblioteket ditt. Er du sikker?", "confirmAddLibrary": "Dette vil legge til {{numShapes}} figur(er) i biblioteket ditt. Er du sikker?",
"imageDoesNotContainScene": "Bildefilen inneholder ikke scenedata. Har du aktivert dette under eksport?", "imageDoesNotContainScene": "Importering av bilder støttes ikke for øyeblikket.\n\nVil du importere en scene? Dette bildet ser ikke ut til å inneholde noen scene-data. Har du aktivert dette under eksporten?",
"cannotRestoreFromImage": "Scenen kunne ikke gjenopprettes fra denne bildefilen" "cannotRestoreFromImage": "Scenen kunne ikke gjenopprettes fra denne bildefilen"
}, },
"toolBar": { "toolBar": {
@@ -153,6 +161,7 @@
"freeDraw": "Klikk og dra, slipp når du er ferdig", "freeDraw": "Klikk og dra, slipp når du er ferdig",
"text": "Tips: du kan også legge til tekst ved å dobbeltklikke hvor som helst med utvalgsverktøyet", "text": "Tips: du kan også legge til tekst ved å dobbeltklikke hvor som helst med utvalgsverktøyet",
"linearElementMulti": "Klikk på siste punkt eller trykk Escape eller Enter for å fullføre", "linearElementMulti": "Klikk på siste punkt eller trykk Escape eller Enter for å fullføre",
"lockAngle": "Du kan låse vinkelen ved å holde nede SHIFT",
"resize": "Du kan beholde forholdet ved å trykke SHIFT mens du endrer størrelse,\ntrykk ALT for å endre størrelsen fra midten", "resize": "Du kan beholde forholdet ved å trykke SHIFT mens du endrer størrelse,\ntrykk ALT for å endre størrelsen fra midten",
"rotate": "Du kan låse vinklene ved å holde SHIFT mens du roterer", "rotate": "Du kan låse vinklene ved å holde SHIFT mens du roterer",
"lineEditor_info": "Dobbeltklikk eller trykk Enter for å redigere punkter", "lineEditor_info": "Dobbeltklikk eller trykk Enter for å redigere punkter",
@@ -205,13 +214,22 @@
"textNewLine": "Legg til ny linje (tekst)", "textNewLine": "Legg til ny linje (tekst)",
"textFinish": "Fullfør redigering (tekst)", "textFinish": "Fullfør redigering (tekst)",
"zoomToFit": "Zoom for å passe alle elementene", "zoomToFit": "Zoom for å passe alle elementene",
"zoomToSelection": "Zoom til utvalg",
"preventBinding": "Forhindre pilbinding" "preventBinding": "Forhindre pilbinding"
}, },
"encrypted": { "encrypted": {
"tooltip": "Dine tegninger er ende-til-ende-krypterte slik at Excalidraw sine servere aldri vil se dem." "tooltip": "Dine tegninger er ende-til-ende-krypterte slik at Excalidraw sine servere aldri vil se dem."
}, },
"charts": { "stats": {
"noNumericColumn": "Du limte inn et regneark uten en numerisk kolonne.", "angle": "Vinkel",
"tooManyColumns": "Du limte inn et regneark med mer enn to kolonner." "element": "Element",
"elements": "Elementer",
"height": "Høyde",
"scene": "Scene",
"selected": "Valgt",
"storage": "Lagring",
"title": "Statistikk for nerder",
"total": "Totalt",
"width": "Bredde"
} }
} }
+48 -30
View File
@@ -4,11 +4,12 @@
"selectAll": "Alles selecteren", "selectAll": "Alles selecteren",
"multiSelect": "Voeg element toe aan selectie", "multiSelect": "Voeg element toe aan selectie",
"moveCanvas": "Canvas verplaatsen", "moveCanvas": "Canvas verplaatsen",
"cut": "Knip",
"copy": "Kopiëren", "copy": "Kopiëren",
"copyAsPng": "Kopieer als PNG", "copyAsPng": "Kopieer als PNG",
"copyAsSvg": "Kopieer als SVG", "copyAsSvg": "Kopieer naar klembord als SVG",
"bringForward": "Breng naar voren", "bringForward": "Breng naar voren",
"sendToBack": "Breng naar achtergrond", "sendToBack": "Stuur naar achtergrond",
"bringToFront": "Breng naar voorgrond", "bringToFront": "Breng naar voorgrond",
"sendBackward": "Breng naar achter", "sendBackward": "Breng naar achter",
"delete": "Verwijderen", "delete": "Verwijderen",
@@ -28,12 +29,17 @@
"edges": "Randen", "edges": "Randen",
"sharp": "Hoekig", "sharp": "Hoekig",
"round": "Rond", "round": "Rond",
"arrowheads": "Pijlpunten",
"arrowhead_none": "Geen",
"arrowhead_arrow": "Pijl",
"arrowhead_bar": "Balk",
"arrowhead_dot": "Punt",
"fontSize": "Tekstgrootte", "fontSize": "Tekstgrootte",
"fontFamily": "Lettertype", "fontFamily": "Lettertype",
"onlySelected": "Enkel geselecteerde", "onlySelected": "Enkel geselecteerde",
"withBackground": "Met achtergrond", "withBackground": "Met achtergrond",
"exportEmbedScene": "", "exportEmbedScene": "Scène in geëxporteerd bestand invoegen",
"exportEmbedScene_details": "", "exportEmbedScene_details": "Scènegegevens worden in het geëxporteerde PNG/SVG-bestand opgeslagen zodat de scène kan worden hersteld.\nDe grootte van de geëxporteerde bestanden zal toenemen.",
"addWatermark": "Voeg \"Gemaakt met Excalidraw\" toe", "addWatermark": "Voeg \"Gemaakt met Excalidraw\" toe",
"handDrawn": "Handgetekend", "handDrawn": "Handgetekend",
"normal": "Normaal", "normal": "Normaal",
@@ -54,7 +60,7 @@
"architect": "Architect", "architect": "Architect",
"artist": "Artiest", "artist": "Artiest",
"cartoonist": "Cartoonist", "cartoonist": "Cartoonist",
"fileTitle": "", "fileTitle": "Bestandsnaam",
"colorPicker": "Kleurenkiezer", "colorPicker": "Kleurenkiezer",
"canvasBackground": "Canvas achtergrond", "canvasBackground": "Canvas achtergrond",
"drawingCanvas": "Canvas", "drawingCanvas": "Canvas",
@@ -63,27 +69,28 @@
"language": "Taal", "language": "Taal",
"createRoom": "Deel een live-samenwerkingssessie", "createRoom": "Deel een live-samenwerkingssessie",
"duplicateSelection": "Dupliceer", "duplicateSelection": "Dupliceer",
"untitled": "", "untitled": "Naamloos",
"name": "Naam", "name": "Naam",
"yourName": "Jouw naam", "yourName": "Jouw naam",
"madeWithExcalidraw": "Gemaakt met Excalidraw", "madeWithExcalidraw": "Gemaakt met Excalidraw",
"group": "Groeperen", "group": "Groeperen",
"ungroup": "Groep opheffen", "ungroup": "Groep opheffen",
"collaborators": "Deelnemers", "collaborators": "Deelnemers",
"toggleGridMode": "Rasterlijnen in-/uitschakelen", "gridMode": "Rasterweergave",
"addToLibrary": "Voeg toe aan bibliotheek", "addToLibrary": "Voeg toe aan bibliotheek",
"removeFromLibrary": "Verwijder uit bibliotheek", "removeFromLibrary": "Verwijder uit bibliotheek",
"libraryLoadingMessage": "Bibliotheek laden...", "libraryLoadingMessage": "Bibliotheek laden...",
"libraries": "Blader door bibliotheken",
"loadingScene": "Scène laden...", "loadingScene": "Scène laden...",
"align": "", "align": "Uitlijnen",
"alignTop": "", "alignTop": "Boven uitlijnen",
"alignBottom": "", "alignBottom": "Onder uitlijnen",
"alignLeft": "", "alignLeft": "Links uitlijnen",
"alignRight": "", "alignRight": "Rechts uitlijnen",
"centerVertically": "", "centerVertically": "Verticaal Centreren",
"centerHorizontally": "", "centerHorizontally": "Horizontaal Centreren",
"distributeHorizontally": "", "distributeHorizontally": "Horizontaal verspreiden",
"distributeVertically": "" "distributeVertically": "Verticaal distribueren"
}, },
"buttons": { "buttons": {
"clearReset": "Canvas opnieuw instellen", "clearReset": "Canvas opnieuw instellen",
@@ -110,15 +117,16 @@
"redo": "Herstel ongedaan maken", "redo": "Herstel ongedaan maken",
"roomDialog": "Live-samenwerkingssessie starten", "roomDialog": "Live-samenwerkingssessie starten",
"createNewRoom": "Creëer live-samenwerkingssessie", "createNewRoom": "Creëer live-samenwerkingssessie",
"toggleFullScreen": "Volledig scherm in-/uitschakelen", "fullScreen": "Volledig scherm",
"toggleDarkMode": "Donkere modus in-/uitschakelen", "darkMode": "Donkere modus",
"toggleZenMode": "Zen modus in-/uitschakelen", "lightMode": "Lichte modus",
"zenMode": "Zen modus",
"exitZenMode": "Verlaat zen modus" "exitZenMode": "Verlaat zen modus"
}, },
"alerts": { "alerts": {
"clearReset": "Dit zal het hele canvas verwijderen. Weet je het zeker?", "clearReset": "Dit zal het hele canvas verwijderen. Weet je het zeker?",
"couldNotCreateShareableLink": "Kon geen deelbare link aanmaken.", "couldNotCreateShareableLink": "Kon geen deelbare link aanmaken.",
"couldNotCreateShareableLinkTooBig": "", "couldNotCreateShareableLinkTooBig": "Kan geen deelbare link aanmaken: de scène is te groot",
"couldNotLoadInvalidFile": "Kan ongeldig bestand niet laden", "couldNotLoadInvalidFile": "Kan ongeldig bestand niet laden",
"importBackendFailed": "Importeren vanuit backend mislukt.", "importBackendFailed": "Importeren vanuit backend mislukt.",
"cannotExportEmptyCanvas": "Kan geen leeg canvas exporteren.", "cannotExportEmptyCanvas": "Kan geen leeg canvas exporteren.",
@@ -128,8 +136,8 @@
"loadSceneOverridePrompt": "Het laden van externe tekening zal uw bestaande inhoud vervangen. Wil je doorgaan?", "loadSceneOverridePrompt": "Het laden van externe tekening zal uw bestaande inhoud vervangen. Wil je doorgaan?",
"errorLoadingLibrary": "Bij het laden van de externe bibliotheek is een fout opgetreden.", "errorLoadingLibrary": "Bij het laden van de externe bibliotheek is een fout opgetreden.",
"confirmAddLibrary": "Hiermee worden {{numShapes}} vorm(n) aan uw bibliotheek toegevoegd. Ben je het zeker?", "confirmAddLibrary": "Hiermee worden {{numShapes}} vorm(n) aan uw bibliotheek toegevoegd. Ben je het zeker?",
"imageDoesNotContainScene": "", "imageDoesNotContainScene": "Afbeeldingen importeren wordt op dit moment niet ondersteund.\n\nWil je een scène importeren? Deze afbeelding lijkt geen scène gegevens te bevatten. Heb je dit geactiveerd tijdens het exporteren?",
"cannotRestoreFromImage": "" "cannotRestoreFromImage": "Scène kan niet worden hersteld vanuit dit afbeeldingsbestand"
}, },
"toolBar": { "toolBar": {
"selection": "Selectie", "selection": "Selectie",
@@ -153,6 +161,7 @@
"freeDraw": "Klik en sleep, laat los als je klaar bent", "freeDraw": "Klik en sleep, laat los als je klaar bent",
"text": "Tip: je kunt tekst toevoegen door ergens dubbel te klikken met de selectietool", "text": "Tip: je kunt tekst toevoegen door ergens dubbel te klikken met de selectietool",
"linearElementMulti": "Klik op het laatste punt of druk op Escape of Enter om te stoppen", "linearElementMulti": "Klik op het laatste punt of druk op Escape of Enter om te stoppen",
"lockAngle": "Je kunt de hoek beperken door SHIFT ingedrukt te houden",
"resize": "Houd tijdens het vergroten SHIFT ingedrukt om verhoudingen te behouden,\ngebruik ALT om vanuit het midden te vergroten/verkleinen", "resize": "Houd tijdens het vergroten SHIFT ingedrukt om verhoudingen te behouden,\ngebruik ALT om vanuit het midden te vergroten/verkleinen",
"rotate": "Je kan hoeken beperken door SHIFT ingedrukt te houden wanneer je draait", "rotate": "Je kan hoeken beperken door SHIFT ingedrukt te houden wanneer je draait",
"lineEditor_info": "Dubbelklik of druk op Enter om punten te bewerken", "lineEditor_info": "Dubbelklik of druk op Enter om punten te bewerken",
@@ -160,9 +169,9 @@
"lineEditor_nothingSelected": "Selecteer een punt om te verplaatsen of te verwijderen, of houd Alt ingedrukt en klik om nieuwe punten toe te voegen" "lineEditor_nothingSelected": "Selecteer een punt om te verplaatsen of te verwijderen, of houd Alt ingedrukt en klik om nieuwe punten toe te voegen"
}, },
"canvasError": { "canvasError": {
"cannotShowPreview": "", "cannotShowPreview": "Kan voorbeeld niet tonen",
"canvasTooBig": "", "canvasTooBig": "Het canvas is mogelijk te groot.",
"canvasTooBigTip": "" "canvasTooBigTip": "Tip: beweeg de verste elementen iets dichter bij elkaar."
}, },
"errorSplash": { "errorSplash": {
"headingMain_pre": "Fout opgetreden. Probeer ", "headingMain_pre": "Fout opgetreden. Probeer ",
@@ -184,7 +193,7 @@
"button_stopSession": "Sessie afbreken", "button_stopSession": "Sessie afbreken",
"desc_inProgressIntro": "De live-samenwerkingssessie is nu gestart.", "desc_inProgressIntro": "De live-samenwerkingssessie is nu gestart.",
"desc_shareLink": "Deel deze link met iedereen waarmee je wil samenwerken:", "desc_shareLink": "Deel deze link met iedereen waarmee je wil samenwerken:",
"desc_exitSession": "" "desc_exitSession": "Het stoppen van de sessie zal je loskoppelen van de kamer, maar je kunt lokaal doorwerken met de scène.\nPas op: dit heeft geen invloed op andere mensen en dat zij nog steeds in staat zullen zijn om samen te werken aan hun versie."
}, },
"errorDialog": { "errorDialog": {
"title": "Fout" "title": "Fout"
@@ -205,13 +214,22 @@
"textNewLine": "Nieuwe regel toevoegen (tekst)", "textNewLine": "Nieuwe regel toevoegen (tekst)",
"textFinish": "Voltooi bewerken (tekst)", "textFinish": "Voltooi bewerken (tekst)",
"zoomToFit": "Zoom in op alle elementen", "zoomToFit": "Zoom in op alle elementen",
"preventBinding": "" "zoomToSelection": "Inzoomen op selectie",
"preventBinding": "Pijlbinding voorkomen"
}, },
"encrypted": { "encrypted": {
"tooltip": "Je tekeningen zijn beveiligd met end-to-end encryptie, dus Excalidraw's servers zullen nooit zien wat je tekent." "tooltip": "Je tekeningen zijn beveiligd met end-to-end encryptie, dus Excalidraw's servers zullen nooit zien wat je tekent."
}, },
"charts": { "stats": {
"noNumericColumn": "Je hebt een werkblad geplakt zonder een numerieke kolom.", "angle": "Hoek",
"tooManyColumns": "Je hebt een werkblad geplakt met meer dan twee kolommen." "element": "Element",
"elements": "Elementen",
"height": "Hoogte",
"scene": "Scene",
"selected": "Geselecteerd",
"storage": "Opslag",
"title": "Statistieken voor nerds",
"total": "Totaal",
"width": "Breedte"
} }
} }
+45 -27
View File
@@ -4,6 +4,7 @@
"selectAll": "Vel alt", "selectAll": "Vel alt",
"multiSelect": "Legg til element i utval", "multiSelect": "Legg til element i utval",
"moveCanvas": "Flytt lerretet", "moveCanvas": "Flytt lerretet",
"cut": "Klipp ut",
"copy": "Kopier", "copy": "Kopier",
"copyAsPng": "Kopier til utklippstavla som PNG", "copyAsPng": "Kopier til utklippstavla som PNG",
"copyAsSvg": "Kopier til utklippstavla som SVG", "copyAsSvg": "Kopier til utklippstavla som SVG",
@@ -28,12 +29,17 @@
"edges": "Kanter", "edges": "Kanter",
"sharp": "Skarp", "sharp": "Skarp",
"round": "Rund", "round": "Rund",
"arrowheads": "Pilhovud",
"arrowhead_none": "Ingen",
"arrowhead_arrow": "Pil",
"arrowhead_bar": "Stolpe",
"arrowhead_dot": "Prikk",
"fontSize": "Skriftstorleik", "fontSize": "Skriftstorleik",
"fontFamily": "Skrifttype", "fontFamily": "Skrifttype",
"onlySelected": "Kun valde", "onlySelected": "Kun valde",
"withBackground": "Inkluder bakgrunn", "withBackground": "Med bakgrunn",
"exportEmbedScene": "", "exportEmbedScene": "Bygg scena inn i eksportert fil",
"exportEmbedScene_details": "", "exportEmbedScene_details": "Scenedataa vert lagra i den eksporterte PNG- eller SVG-fila slik at scena kan bli gjenopprettast frå den. Dette vil auke eksportert filstorleik.",
"addWatermark": "Legg til «Laga med Excalidraw»", "addWatermark": "Legg til «Laga med Excalidraw»",
"handDrawn": "Handteikna", "handDrawn": "Handteikna",
"normal": "Normal", "normal": "Normal",
@@ -54,7 +60,7 @@
"architect": "Arkitekt", "architect": "Arkitekt",
"artist": "Kunstnar", "artist": "Kunstnar",
"cartoonist": "Teiknar", "cartoonist": "Teiknar",
"fileTitle": "", "fileTitle": "Filnamn",
"colorPicker": "Fargeveljar", "colorPicker": "Fargeveljar",
"canvasBackground": "Lerretsbakgrunn", "canvasBackground": "Lerretsbakgrunn",
"drawingCanvas": "Lerret", "drawingCanvas": "Lerret",
@@ -63,27 +69,28 @@
"language": "Språk", "language": "Språk",
"createRoom": "Del ei sanntids-samarbeidsøkt", "createRoom": "Del ei sanntids-samarbeidsøkt",
"duplicateSelection": "Dupliser", "duplicateSelection": "Dupliser",
"untitled": "", "untitled": "Utan namn",
"name": "Namn", "name": "Namn",
"yourName": "Namnet ditt", "yourName": "Namnet ditt",
"madeWithExcalidraw": "Laga med Excalidraw", "madeWithExcalidraw": "Laga med Excalidraw",
"group": "Grupper utval", "group": "Grupper utval",
"ungroup": "Avgrupper utval", "ungroup": "Avgrupper utval",
"collaborators": "Samarbeidarar", "collaborators": "Samarbeidarar",
"toggleGridMode": "Sla på/av rutenett", "gridMode": "",
"addToLibrary": "Legg til i bibliotek", "addToLibrary": "Legg til i bibliotek",
"removeFromLibrary": "Fjern frå bibliotek", "removeFromLibrary": "Fjern frå bibliotek",
"libraryLoadingMessage": "Laster bibliotek...", "libraryLoadingMessage": "Laster bibliotek...",
"libraries": "Blad gjennom bibliotek",
"loadingScene": "Laster scene...", "loadingScene": "Laster scene...",
"align": "", "align": "Juster",
"alignTop": "", "alignTop": "Juster til topp",
"alignBottom": "", "alignBottom": "Juster til botn",
"alignLeft": "", "alignLeft": "Juster til venstre",
"alignRight": "", "alignRight": "Juster til høgre",
"centerVertically": "", "centerVertically": "Midtstill vertikalt",
"centerHorizontally": "", "centerHorizontally": "Midtstill horisontalt",
"distributeHorizontally": "", "distributeHorizontally": "Sprei horisontalt",
"distributeVertically": "" "distributeVertically": "Sprei vertikalt"
}, },
"buttons": { "buttons": {
"clearReset": "Tilbakestill lerretet", "clearReset": "Tilbakestill lerretet",
@@ -92,7 +99,7 @@
"exportToSvg": "Eksporter til SVG", "exportToSvg": "Eksporter til SVG",
"copyToClipboard": "Kopier til utklippstavla", "copyToClipboard": "Kopier til utklippstavla",
"copyPngToClipboard": "Kopier PNG til utklippstavla", "copyPngToClipboard": "Kopier PNG til utklippstavla",
"scale": "", "scale": "Skaler",
"save": "Lagre", "save": "Lagre",
"saveAs": "Lagre som", "saveAs": "Lagre som",
"load": "Opne", "load": "Opne",
@@ -110,15 +117,16 @@
"redo": "Gjer om", "redo": "Gjer om",
"roomDialog": "Start sanntids-samarbeid", "roomDialog": "Start sanntids-samarbeid",
"createNewRoom": "Lag nytt rom", "createNewRoom": "Lag nytt rom",
"toggleFullScreen": "Slå på/av fullskjerm", "fullScreen": "",
"toggleDarkMode": "Skru av/på skumringsmodus", "darkMode": "",
"toggleZenMode": "Slå på/av zen-modus", "lightMode": "",
"zenMode": "",
"exitZenMode": "Avslutt zen-modus" "exitZenMode": "Avslutt zen-modus"
}, },
"alerts": { "alerts": {
"clearReset": "Dette vil tømme lerretet. Er du sikker?", "clearReset": "Dette vil tømme lerretet. Er du sikker?",
"couldNotCreateShareableLink": "Kunne ikkje lage delingslenke.", "couldNotCreateShareableLink": "Kunne ikkje lage delingslenke.",
"couldNotCreateShareableLinkTooBig": "", "couldNotCreateShareableLinkTooBig": "Kunne ikkje opprette deleleg lenke: scena er for stor",
"couldNotLoadInvalidFile": "Kunne ikkje laste inn ugyldig fil", "couldNotLoadInvalidFile": "Kunne ikkje laste inn ugyldig fil",
"importBackendFailed": "Importering av backend feila.", "importBackendFailed": "Importering av backend feila.",
"cannotExportEmptyCanvas": "Kan ikkje eksportere eit tomt lerret.", "cannotExportEmptyCanvas": "Kan ikkje eksportere eit tomt lerret.",
@@ -129,7 +137,7 @@
"errorLoadingLibrary": "Det oppstod ein feil under lastinga av tredjepartsbibliotek.", "errorLoadingLibrary": "Det oppstod ein feil under lastinga av tredjepartsbibliotek.",
"confirmAddLibrary": "Dette vil legge til {{numShapes}} form(er) i biblioteket ditt. Er du sikker?", "confirmAddLibrary": "Dette vil legge til {{numShapes}} form(er) i biblioteket ditt. Er du sikker?",
"imageDoesNotContainScene": "", "imageDoesNotContainScene": "",
"cannotRestoreFromImage": "" "cannotRestoreFromImage": "Scena kunne ikkje gjenopprettast frå denne biletfila"
}, },
"toolBar": { "toolBar": {
"selection": "Vel", "selection": "Vel",
@@ -153,6 +161,7 @@
"freeDraw": "Klikk og drag, slepp når du er ferdig", "freeDraw": "Klikk og drag, slepp når du er ferdig",
"text": "Tips: du kan òg leggje til tekst ved å dobbeltklikke kor som helst med utvalgsverktyet", "text": "Tips: du kan òg leggje til tekst ved å dobbeltklikke kor som helst med utvalgsverktyet",
"linearElementMulti": "Klikk på siste punkt eller trykk Escape eller Enter for å fullføre", "linearElementMulti": "Klikk på siste punkt eller trykk Escape eller Enter for å fullføre",
"lockAngle": "",
"resize": "Du kan halde fram med forholdet ved å trykke SHIFT medan du endrar storleik,\ntrykk ALT for å endre storleiken frå midten", "resize": "Du kan halde fram med forholdet ved å trykke SHIFT medan du endrar storleik,\ntrykk ALT for å endre storleiken frå midten",
"rotate": "Du kan låse vinklane ved å halde SHIFT medan du roterer", "rotate": "Du kan låse vinklane ved å halde SHIFT medan du roterer",
"lineEditor_info": "Dobbeltklikk eller trykk Enter for å redigere punkt", "lineEditor_info": "Dobbeltklikk eller trykk Enter for å redigere punkt",
@@ -160,9 +169,9 @@
"lineEditor_nothingSelected": "Vel eit punkt å flytte eller fjerne, eller hald Alt og klikk for å legge til nye punkt" "lineEditor_nothingSelected": "Vel eit punkt å flytte eller fjerne, eller hald Alt og klikk for å legge til nye punkt"
}, },
"canvasError": { "canvasError": {
"cannotShowPreview": "", "cannotShowPreview": "Kan ikkje vise førehandsvising",
"canvasTooBig": "", "canvasTooBig": "Lerretet er mogleg for stort.",
"canvasTooBigTip": "" "canvasTooBigTip": "Tips: prøv å flytte elementa som er lengst frå kvarandre, litt nærare kvarandre."
}, },
"errorSplash": { "errorSplash": {
"headingMain_pre": "Ein feil oppstod. Prøv ", "headingMain_pre": "Ein feil oppstod. Prøv ",
@@ -205,13 +214,22 @@
"textNewLine": "Legg til ny linje (tekst)", "textNewLine": "Legg til ny linje (tekst)",
"textFinish": "Fullfør redigering (tekst)", "textFinish": "Fullfør redigering (tekst)",
"zoomToFit": "Zoom for å sjå alle elementa", "zoomToFit": "Zoom for å sjå alle elementa",
"zoomToSelection": "Zoom til utval",
"preventBinding": "Hindre pilkobling" "preventBinding": "Hindre pilkobling"
}, },
"encrypted": { "encrypted": {
"tooltip": "Teikningane dine er ende-til-ende-krypterte slik at Excalidraw sine serverar aldri får sjå dei." "tooltip": "Teikningane dine er ende-til-ende-krypterte slik at Excalidraw sine serverar aldri får sjå dei."
}, },
"charts": { "stats": {
"noNumericColumn": "Du limte inn eit rekneark utan ei numerisk kolonne.", "angle": "Vinkel",
"tooManyColumns": "Du limte inn eit rekneark med meir enn to kolonnar." "element": "Element",
"elements": "Element",
"height": "Høgde",
"scene": "Scene",
"selected": "Valde",
"storage": "Lagring",
"title": "Statistikk for nerdar",
"total": "Totalt",
"width": "Breidde"
} }
} }
+20 -19
View File
@@ -1,33 +1,34 @@
{ {
"ar-SA": 98, "ar-SA": 86,
"bg-BG": 67, "bg-BG": 60,
"ca-ES": 89, "ca-ES": 78,
"de-DE": 100, "de-DE": 100,
"el-GR": 96, "el-GR": 96,
"en": 100, "en": 100,
"es-ES": 89, "es-ES": 99,
"fa-IR": 98, "fa-IR": 86,
"fi-FI": 100, "fi-FI": 100,
"fr-FR": 100, "fr-FR": 100,
"he-IL": 76, "he-IL": 96,
"hi-IN": 85, "hi-IN": 82,
"hu-HU": 48, "hu-HU": 92,
"id-ID": 100, "id-ID": 100,
"it-IT": 100, "it-IT": 100,
"ja-JP": 98, "ja-JP": 85,
"ko-KR": 75, "ko-KR": 67,
"my-MM": 97, "my-MM": 93,
"nb-NO": 100, "nb-NO": 100,
"nl-NL": 88, "nl-NL": 100,
"nn-NO": 89, "nn-NO": 96,
"pl-PL": 88, "pl-PL": 95,
"pt-PT": 92, "pt-BR": 100,
"pt-PT": 100,
"ro-RO": 100, "ro-RO": 100,
"ru-RU": 85, "ru-RU": 97,
"sk-SK": 100, "sk-SK": 100,
"sv-SE": 100, "sv-SE": 100,
"tr-TR": 90, "tr-TR": 87,
"uk-UA": 100, "uk-UA": 99,
"zh-CN": 100, "zh-CN": 100,
"zh-TW": 100 "zh-TW": 99
} }
+46 -28
View File
@@ -4,6 +4,7 @@
"selectAll": "Zaznacz wszystko", "selectAll": "Zaznacz wszystko",
"multiSelect": "Dodaj element do zaznaczenia", "multiSelect": "Dodaj element do zaznaczenia",
"moveCanvas": "Przesuń obszar roboczy", "moveCanvas": "Przesuń obszar roboczy",
"cut": "Wytnij",
"copy": "Kopiuj", "copy": "Kopiuj",
"copyAsPng": "Skopiuj do schowka jako plik PNG", "copyAsPng": "Skopiuj do schowka jako plik PNG",
"copyAsSvg": "Skopiuj do schowka jako plik SVG", "copyAsSvg": "Skopiuj do schowka jako plik SVG",
@@ -28,12 +29,17 @@
"edges": "Krawędzie", "edges": "Krawędzie",
"sharp": "Ostry", "sharp": "Ostry",
"round": "Zaokrąglij", "round": "Zaokrąglij",
"arrowheads": "Groty",
"arrowhead_none": "Brak",
"arrowhead_arrow": "Strzałka",
"arrowhead_bar": "",
"arrowhead_dot": "Kropka",
"fontSize": "Rozmiar tekstu", "fontSize": "Rozmiar tekstu",
"fontFamily": "Krój pisma", "fontFamily": "Krój pisma",
"onlySelected": "Tylko wybrane", "onlySelected": "Tylko wybrane",
"withBackground": "Z tłem dokumentu", "withBackground": "",
"exportEmbedScene": "", "exportEmbedScene": "Osadź scenę w eksportowanym pliku",
"exportEmbedScene_details": "", "exportEmbedScene_details": "Dane sceny zostaną zapisane w eksportowanym pliku PNG/SVG tak, aby scena mogła zostać z niego przywrócona.\nZwiększy to rozmiar eksportowanego pliku.",
"addWatermark": "Dodaj \"Zrobione w Excalidraw\"", "addWatermark": "Dodaj \"Zrobione w Excalidraw\"",
"handDrawn": "Odręczny", "handDrawn": "Odręczny",
"normal": "Normalny", "normal": "Normalny",
@@ -54,7 +60,7 @@
"architect": "Dokładny", "architect": "Dokładny",
"artist": "Artystyczny", "artist": "Artystyczny",
"cartoonist": "Rysunkowy", "cartoonist": "Rysunkowy",
"fileTitle": "", "fileTitle": "Tytuł pliku",
"colorPicker": "Paleta kolorów", "colorPicker": "Paleta kolorów",
"canvasBackground": "Kolor dokumentu", "canvasBackground": "Kolor dokumentu",
"drawingCanvas": "Obszar roboczy", "drawingCanvas": "Obszar roboczy",
@@ -63,27 +69,28 @@
"language": "Język", "language": "Język",
"createRoom": "Udostępnij sesję współpracy na żywo", "createRoom": "Udostępnij sesję współpracy na żywo",
"duplicateSelection": "Powiel", "duplicateSelection": "Powiel",
"untitled": "", "untitled": "Bez tytułu",
"name": "Nazwa", "name": "Nazwa",
"yourName": "Twoje imię", "yourName": "Twoje imię",
"madeWithExcalidraw": "Zrobione w Excalidraw", "madeWithExcalidraw": "Zrobione w Excalidraw",
"group": "Zgrupuj wybrane", "group": "Zgrupuj wybrane",
"ungroup": "Rozgrupuj wybrane", "ungroup": "Rozgrupuj wybrane",
"collaborators": "Współtwórcy", "collaborators": "Współtwórcy",
"toggleGridMode": "Włącz siatkę", "gridMode": "Tryb siatki",
"addToLibrary": "Dodaj do biblioteki", "addToLibrary": "Dodaj do biblioteki",
"removeFromLibrary": "Usuń z biblioteki", "removeFromLibrary": "Usuń z biblioteki",
"libraryLoadingMessage": "Wczytywanie biblioteki...", "libraryLoadingMessage": "Wczytywanie biblioteki...",
"libraries": "Przeglądaj biblioteki",
"loadingScene": "Ładowanie sceny...", "loadingScene": "Ładowanie sceny...",
"align": "", "align": "Wyrównaj",
"alignTop": "", "alignTop": "Wyrównaj do góry",
"alignBottom": "", "alignBottom": "Wyrównaj do dołu",
"alignLeft": "", "alignLeft": "Wyrównaj do lewej",
"alignRight": "", "alignRight": "Wyrównaj do prawej",
"centerVertically": "", "centerVertically": "Wyśrodkuj w pionie",
"centerHorizontally": "", "centerHorizontally": "Wyśrodkuj w poziomie",
"distributeHorizontally": "", "distributeHorizontally": "Rozłóż poziomo",
"distributeVertically": "" "distributeVertically": "Rozłóż pionowo"
}, },
"buttons": { "buttons": {
"clearReset": "Wyczyść dokument i zresetuj kolor dokumentu", "clearReset": "Wyczyść dokument i zresetuj kolor dokumentu",
@@ -110,15 +117,16 @@
"redo": "Przywróć", "redo": "Przywróć",
"roomDialog": "Utwórz nową sesję współpracy na żywo", "roomDialog": "Utwórz nową sesję współpracy na żywo",
"createNewRoom": "Utwórz nowy pokój", "createNewRoom": "Utwórz nowy pokój",
"toggleFullScreen": "Włącz/wyłącz tryb pełnoekranowy", "fullScreen": "",
"toggleDarkMode": "Włącz tryb ciemny", "darkMode": "",
"toggleZenMode": "Włącz tryb Zen", "lightMode": "",
"zenMode": "",
"exitZenMode": "Wyjdź z trybu Zen" "exitZenMode": "Wyjdź z trybu Zen"
}, },
"alerts": { "alerts": {
"clearReset": "To spowoduje usunięcie wszystkiego z dokumentu. Czy chcesz kontynuować?", "clearReset": "To spowoduje usunięcie wszystkiego z dokumentu. Czy chcesz kontynuować?",
"couldNotCreateShareableLink": "Wystąpił błąd przy generowaniu linka do udostępniania.", "couldNotCreateShareableLink": "Wystąpił błąd przy generowaniu linka do udostępniania.",
"couldNotCreateShareableLinkTooBig": "", "couldNotCreateShareableLinkTooBig": "Nie można utworzyć udostępnialnego linku: scena jest za duża",
"couldNotLoadInvalidFile": "Nie udało się otworzyć pliku. Wybrany plik jest nieprawidłowy.", "couldNotLoadInvalidFile": "Nie udało się otworzyć pliku. Wybrany plik jest nieprawidłowy.",
"importBackendFailed": "Wystąpił błąd podczas importowania pliku.", "importBackendFailed": "Wystąpił błąd podczas importowania pliku.",
"cannotExportEmptyCanvas": "Najpierw musisz coś narysować, aby zapisać dokument.", "cannotExportEmptyCanvas": "Najpierw musisz coś narysować, aby zapisać dokument.",
@@ -126,10 +134,10 @@
"decryptFailed": "Nie udało się odszyfrować danych.", "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.", "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ć?", "loadSceneOverridePrompt": "Wczytanie zewnętrznego rysunku zastąpi istniejącą zawartość. Czy chcesz kontynuować?",
"errorLoadingLibrary": "", "errorLoadingLibrary": "Wystąpił błąd podczas ładowania biblioteki stron trzecich.",
"confirmAddLibrary": "", "confirmAddLibrary": "To doda {{numShapes}} kształtów do twojej biblioteki. Jesteś pewien?",
"imageDoesNotContainScene": "", "imageDoesNotContainScene": "",
"cannotRestoreFromImage": "" "cannotRestoreFromImage": "Scena nie mogła zostać przywrócona z pliku obrazu"
}, },
"toolBar": { "toolBar": {
"selection": "Zaznaczenie", "selection": "Zaznaczenie",
@@ -153,6 +161,7 @@
"freeDraw": "Naciśnij i przeciągnij by rysować, puść kiedy skończysz", "freeDraw": "Naciśnij i przeciągnij by rysować, puść kiedy skończysz",
"text": "Wskazówka: możesz również dodać tekst klikając dwukrotnie gdziekolwiek za pomocą narzędzia zaznaczania", "text": "Wskazówka: możesz również dodać tekst klikając dwukrotnie gdziekolwiek za pomocą narzędzia zaznaczania",
"linearElementMulti": "Aby zakończyć krzywą, ponownie kliknij w ostatni punkt, bądź naciśnij Esc albo Enter", "linearElementMulti": "Aby zakończyć krzywą, ponownie kliknij w ostatni punkt, bądź naciśnij Esc albo Enter",
"lockAngle": "",
"resize": "Możesz zachować proporcję trzymająć wcisnięty SHIFT, przytrzymaj ALT by zmienić rozmiar względem środka", "resize": "Możesz zachować proporcję trzymająć wcisnięty SHIFT, przytrzymaj ALT by zmienić rozmiar względem środka",
"rotate": "Możesz obracać element w równych odstępach trzymając wciśnięty SHIFT", "rotate": "Możesz obracać element w równych odstępach trzymając wciśnięty SHIFT",
"lineEditor_info": "Kliknij dwukrotnie lub naciśnij Enter, aby edytować punkty", "lineEditor_info": "Kliknij dwukrotnie lub naciśnij Enter, aby edytować punkty",
@@ -160,9 +169,9 @@
"lineEditor_nothingSelected": "Naciśnij w punkt by go edytować, przytrzymaj Alt i naciśnij by dodać nowy punkt" "lineEditor_nothingSelected": "Naciśnij w punkt by go edytować, przytrzymaj Alt i naciśnij by dodać nowy punkt"
}, },
"canvasError": { "canvasError": {
"cannotShowPreview": "", "cannotShowPreview": "Nie można pokazać podglądu",
"canvasTooBig": "", "canvasTooBig": "Płótno może być za duże.",
"canvasTooBigTip": "" "canvasTooBigTip": "Wskazówka: spróbuj nieco zbliżyć najdalej wysunięte elementy."
}, },
"errorSplash": { "errorSplash": {
"headingMain_pre": "Wystąpił błąd. Spróbuj ", "headingMain_pre": "Wystąpił błąd. Spróbuj ",
@@ -205,13 +214,22 @@
"textNewLine": "Dodaj nową linię (tekst)", "textNewLine": "Dodaj nową linię (tekst)",
"textFinish": "Zakończ edycję (tekst)", "textFinish": "Zakończ edycję (tekst)",
"zoomToFit": "Powiększ, aby wyświetlić wszystkie elementy", "zoomToFit": "Powiększ, aby wyświetlić wszystkie elementy",
"zoomToSelection": "Przybliż zaznaczenie",
"preventBinding": "Zablokuj przywiązanie strzałek do obiektu" "preventBinding": "Zablokuj przywiązanie strzałek do obiektu"
}, },
"encrypted": { "encrypted": {
"tooltip": "Twoje rysunki są zabezpieczone szyfrowaniem end-to-end, tak więc nawet w Excalidraw nie jesteśmy w stanie zobaczyć tego co tworzysz." "tooltip": "Twoje rysunki są zabezpieczone szyfrowaniem end-to-end, tak więc nawet w Excalidraw nie jesteśmy w stanie zobaczyć tego co tworzysz."
}, },
"charts": { "stats": {
"noNumericColumn": "Wklejono arkusz kalkulacyjny bez kolumny numerycznej.", "angle": "Kąt",
"tooManyColumns": "Wklejono arkusz kalkulacyjny z więcej niż dwoma kolumnami." "element": "Element",
"elements": "Elementy",
"height": "Wysokość",
"scene": "Scena",
"selected": "Zaznaczenie",
"storage": "Pamięć",
"title": "Statystyki dla nerdów",
"total": "Suma",
"width": "Szerokość"
} }
} }
+235
View File
@@ -0,0 +1,235 @@
{
"labels": {
"paste": "Colar",
"selectAll": "Selecionar tudo",
"multiSelect": "Adicionar elemento à seleção",
"moveCanvas": "Mover tela",
"cut": "Cortar",
"copy": "Copiar",
"copyAsPng": "Copiar para a área de transferência como PNG",
"copyAsSvg": "Copiar para a área de transferência como SVG",
"bringForward": "Trazer para a frente",
"sendToBack": "Enviar para o fundo",
"bringToFront": "Trazer para o primeiro plano",
"sendBackward": "Enviar para trás",
"delete": "Apagar",
"copyStyles": "Copiar os estilos",
"pasteStyles": "Colar os estilos",
"stroke": "Contorno",
"background": "Fundo",
"fill": "Preenchimento",
"strokeWidth": "Espessura do traço",
"strokeStyle": "Estilo de traço",
"strokeStyle_solid": "Sólido",
"strokeStyle_dashed": "Tracejado",
"strokeStyle_dotted": "Pontilhado",
"sloppiness": "Precisão do traço",
"opacity": "Opacidade",
"textAlign": "Alinhamento do texto",
"edges": "Arestas",
"sharp": "Pontudo",
"round": "Arredondado",
"arrowheads": "Pontas",
"arrowhead_none": "Nenhuma",
"arrowhead_arrow": "Flecha",
"arrowhead_bar": "Barra",
"arrowhead_dot": "Ponto",
"fontSize": "Tamanho da fonte",
"fontFamily": "Família da fonte",
"onlySelected": "Somente a seleção",
"withBackground": "Com fundo",
"exportEmbedScene": "Incorporar a cena no arquivo exportado",
"exportEmbedScene_details": "Os dados da cena serão salvos no arquivo PNG/SVG exportado para que a cena possa ser restaurada.\nIrá aumentar o tamanho do arquivo exportado.",
"addWatermark": "Adicionar \"Feito com Excalidraw\"",
"handDrawn": "Manuscrito",
"normal": "Normal",
"code": "Código",
"small": "Pequeno",
"medium": "Médio",
"large": "Grande",
"veryLarge": "Muito grande",
"solid": "Sólido",
"hachure": "Hachura",
"crossHatch": "Hachura cruzada",
"thin": "Fino",
"bold": "Espesso",
"left": "Esquerda",
"center": "Centralizar",
"right": "Direita",
"extraBold": "Muito espesso",
"architect": "Arquiteto",
"artist": "Artista",
"cartoonist": "Cartunista",
"fileTitle": "Título do arquivo",
"colorPicker": "Seletor de cores",
"canvasBackground": "Fundo da tela",
"drawingCanvas": "Tela de desenho",
"layers": "Camadas",
"actions": "Ações",
"language": "Idioma",
"createRoom": "Compartilhar uma sessão de colaboração ao vivo",
"duplicateSelection": "Duplicar",
"untitled": "Sem título",
"name": "Nome",
"yourName": "Seu nome",
"madeWithExcalidraw": "Feito com Excalidraw",
"group": "Agrupar seleção",
"ungroup": "Desagrupar seleção",
"collaborators": "Colaboradores",
"gridMode": "Modo grade",
"addToLibrary": "Adicionar à biblioteca",
"removeFromLibrary": "Remover da biblioteca",
"libraryLoadingMessage": "Carregando biblioteca...",
"libraries": "Procurar bibliotecas",
"loadingScene": "Carregando cena...",
"align": "Alinhamento",
"alignTop": "Alinhar ao topo",
"alignBottom": "Alinhar embaixo",
"alignLeft": "Alinhar à esquerda",
"alignRight": "Alinhar à direita",
"centerVertically": "Centralizar verticalmente",
"centerHorizontally": "Centralizar horizontalmente",
"distributeHorizontally": "Distribuir horizontalmente",
"distributeVertically": "Distribuir verticalmente"
},
"buttons": {
"clearReset": "Limpar o canvas e redefinir a cor de fundo",
"export": "Exportar",
"exportToPng": "Exportar em PNG",
"exportToSvg": "Exportar em SVG",
"copyToClipboard": "Copiar para o clipboard",
"copyPngToClipboard": "Copiar PNG para área de transferência",
"scale": "Escala",
"save": "Salvar",
"saveAs": "Salvar como",
"load": "Carregar",
"getShareableLink": "Obter um link de compartilhamento",
"close": "Fechar",
"selectLanguage": "Selecionar idioma",
"scrollBackToContent": "Voltar para o conteúdo",
"zoomIn": "Aumentar zoom",
"zoomOut": "Diminuir zoom",
"resetZoom": "Redefinir zoom",
"menu": "Menu",
"done": "Concluído",
"edit": "Editar",
"undo": "Desfazer",
"redo": "Refazer",
"roomDialog": "Iniciar colaboração ao vivo",
"createNewRoom": "Criar nova sala",
"fullScreen": "Tela cheia",
"darkMode": "Modo escuro",
"lightMode": "Modo claro",
"zenMode": "Modo Zen",
"exitZenMode": "Sair do modo zen"
},
"alerts": {
"clearReset": "Isto irá limpar toda a tela. Você tem certeza?",
"couldNotCreateShareableLink": "Não foi possível criar um link de compartilhamento.",
"couldNotCreateShareableLinkTooBig": "Não foi possível criar um link compartilhável: a cena é muito grande",
"couldNotLoadInvalidFile": "Não foi possível carregar o arquivo inválido",
"importBackendFailed": "A importação do servidor falhou.",
"cannotExportEmptyCanvas": "Não é possível exportar um canvas vazio.",
"couldNotCopyToClipboard": "Não foi possível copiar para a área de transferência. Experimente usando o navegador Chrome.",
"decryptFailed": "Não foi possível descriptografar os dados.",
"uploadedSecurly": "O upload foi protegido com criptografia de ponta a ponta, o que significa que o servidor do Excalidraw e terceiros não podem ler o conteúdo.",
"loadSceneOverridePrompt": "Carregar um desenho externo substituirá o seu conteúdo existente. Deseja continuar?",
"errorLoadingLibrary": "Houve um erro ao carregar a biblioteca de terceiros.",
"confirmAddLibrary": "Isso adicionará {{numShapes}} forma(s) à sua biblioteca. Tem certeza?",
"imageDoesNotContainScene": "A importação de imagens não é suportada no momento.\n\nVocê deseja importar uma cena? Esta imagem parece não conter dados de cena. Você ativou isto durante a exportação?",
"cannotRestoreFromImage": "Não foi possível restaurar a cena deste arquivo de imagem"
},
"toolBar": {
"selection": "Seleção",
"draw": "Desenho livre",
"rectangle": "Retângulo",
"diamond": "Losango",
"ellipse": "Elipse",
"arrow": "Flecha",
"line": "Linha",
"text": "Texto",
"library": "Biblioteca",
"lock": "Manter ativa a ferramenta selecionada após desenhar"
},
"headings": {
"canvasActions": "Ações da tela",
"selectedShapeActions": "Ações das formas selecionadas",
"shapes": "Formas"
},
"hints": {
"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",
"linearElementMulti": "Clique no último ponto ou pressione Escape ou Enter para terminar",
"lockAngle": "Você pode restringir o ângulo segurando o SHIFT",
"resize": "Você pode restringir proporções segurando SHIFT enquanto redimensiona,\nsegure ALT para redimensionar do centro",
"rotate": "Você pode restringir os ângulos segurando SHIFT enquanto gira",
"lineEditor_info": "Clique duas vezes ou pressione Enter para editar os pontos",
"lineEditor_pointSelected": "Pressione Deletar para remover o ponto, CtrlOuCmd+D para duplicar ou arraste para mover",
"lineEditor_nothingSelected": "Selecione um ponto para mover ou remover, ou segure Alt e clique para adicionar novos pontos"
},
"canvasError": {
"cannotShowPreview": "Não é possível mostrar pré-visualização",
"canvasTooBig": "A tela pode ser muito grande.",
"canvasTooBigTip": "Dica: tente aproximar um pouco os elementos mais distantes."
},
"errorSplash": {
"headingMain_pre": "Foi encontrado um erro. Tente ",
"headingMain_button": "recarregar a página.",
"clearCanvasMessage": "Se recarregar a página não funcionar, tente ",
"clearCanvasMessage_button": "limpando a tela.",
"clearCanvasCaveat": " Isso resultará em perda de trabalho ",
"trackedToSentry_pre": "O erro com o identificador ",
"trackedToSentry_post": " foi rastreado no nosso sistema.",
"openIssueMessage_pre": "Fomos muito cautelosos para não incluir suas informações de cena no erro. Se sua cena não for privada, por favor, considere seguir nosso ",
"openIssueMessage_button": "rastreador de bugs.",
"openIssueMessage_post": " Por favor, inclua informações abaixo, copiando e colando para a issue do GitHub.",
"sceneContent": "Conteúdo da cena:"
},
"roomDialog": {
"desc_intro": "Você pode convidar pessoas para sua cena atual para colaborar com você.",
"desc_privacy": "Não se preocupe, a sessão usa criptografia de ponta a ponta; portanto, o que você desenhar permanecerá privado. Nem mesmo nosso servidor poderá ver o que você cria.",
"button_startSession": "Iniciar sessão",
"button_stopSession": "Parar sessão",
"desc_inProgressIntro": "A sessão de colaboração ao vivo está agora em andamento.",
"desc_shareLink": "Compartilhe este link com qualquer pessoa com quem você queira colaborar:",
"desc_exitSession": "Interrompendo a sessão você irá se desconectar da sala, mas você poderá continuar trabalhando com a cena localmente. Observe que isso não afetará outras pessoas, e elas ainda poderão colaborar em suas versões."
},
"errorDialog": {
"title": "Erro"
},
"shortcutsDialog": {
"title": "Atalhos de teclado",
"shapes": "Formas",
"or": "ou",
"click": "clicar",
"drag": "arrastar",
"curvedArrow": "Seta curva",
"curvedLine": "Linha curva",
"editor": "Editor",
"view": "Visualizar",
"blog": "Leia o nosso blog",
"howto": "Siga os nossos guias",
"github": "Encontrou algum problema? Nos informe",
"textNewLine": "Adicionar nova linha (texto)",
"textFinish": "Finalizar edição (texto)",
"zoomToFit": "Ajustar para caber todos os elementos",
"zoomToSelection": "Ampliar a seleção",
"preventBinding": "Prevenir fixação de seta"
},
"encrypted": {
"tooltip": "Seus desenhos são criptografados de ponta a ponta, então os servidores do Excalidraw nunca os verão."
},
"stats": {
"angle": "Ângulo",
"element": "Elemento",
"elements": "Elementos",
"height": "Altura",
"scene": "Cena",
"selected": "Selecionado",
"storage": "Armazenamento",
"title": "Estatísticas para nerds",
"total": "Total",
"width": "Largura"
}
}
+40 -22
View File
@@ -4,6 +4,7 @@
"selectAll": "Selecionar tudo", "selectAll": "Selecionar tudo",
"multiSelect": "Adicionar elemento à seleção", "multiSelect": "Adicionar elemento à seleção",
"moveCanvas": "Mover tela", "moveCanvas": "Mover tela",
"cut": "Cortar",
"copy": "Copiar", "copy": "Copiar",
"copyAsPng": "Copiar para a área de transferência como PNG", "copyAsPng": "Copiar para a área de transferência como PNG",
"copyAsSvg": "Copiar para a área de transferência como SVG", "copyAsSvg": "Copiar para a área de transferência como SVG",
@@ -28,6 +29,11 @@
"edges": "Arestas", "edges": "Arestas",
"sharp": "Aguçado", "sharp": "Aguçado",
"round": "Redondo", "round": "Redondo",
"arrowheads": "Pontas",
"arrowhead_none": "Nenhuma",
"arrowhead_arrow": "Seta",
"arrowhead_bar": "Barra",
"arrowhead_dot": "Ponto",
"fontSize": "Tamanho da fonte", "fontSize": "Tamanho da fonte",
"fontFamily": "Família da fontes", "fontFamily": "Família da fontes",
"onlySelected": "Somente a seleção", "onlySelected": "Somente a seleção",
@@ -54,7 +60,7 @@
"architect": "Arquitecto", "architect": "Arquitecto",
"artist": "Artista", "artist": "Artista",
"cartoonist": "Caricaturista", "cartoonist": "Caricaturista",
"fileTitle": "", "fileTitle": "Título do ficheiro",
"colorPicker": "Seletor de cores", "colorPicker": "Seletor de cores",
"canvasBackground": "Fundo da tela", "canvasBackground": "Fundo da tela",
"drawingCanvas": "Tela de desenho", "drawingCanvas": "Tela de desenho",
@@ -63,27 +69,28 @@
"language": "Idioma", "language": "Idioma",
"createRoom": "Compartilhar uma sessão de colaboração ao vivo", "createRoom": "Compartilhar uma sessão de colaboração ao vivo",
"duplicateSelection": "Duplicar", "duplicateSelection": "Duplicar",
"untitled": "", "untitled": "Sem título",
"name": "Nome", "name": "Nome",
"yourName": "Seu nome", "yourName": "Seu nome",
"madeWithExcalidraw": "Feito com Excalidraw", "madeWithExcalidraw": "Feito com Excalidraw",
"group": "Agrupar seleção", "group": "Agrupar seleção",
"ungroup": "Desagrupar seleção", "ungroup": "Desagrupar seleção",
"collaborators": "Colaboradores", "collaborators": "Colaboradores",
"toggleGridMode": "Alternar modo de grade", "gridMode": "Modo grade",
"addToLibrary": "Adicionar à biblioteca", "addToLibrary": "Adicionar à biblioteca",
"removeFromLibrary": "Remover da biblioteca", "removeFromLibrary": "Remover da biblioteca",
"libraryLoadingMessage": "Carregando biblioteca...", "libraryLoadingMessage": "Carregando biblioteca...",
"libraries": "Procurar bibliotecas",
"loadingScene": "Carregando cena...", "loadingScene": "Carregando cena...",
"align": "", "align": "Alinhamento",
"alignTop": "", "alignTop": "Alinhar ao topo",
"alignBottom": "", "alignBottom": "Alinhar ao fundo",
"alignLeft": "", "alignLeft": "Alinhar à esquerda",
"alignRight": "", "alignRight": "Alinhar à direita",
"centerVertically": "", "centerVertically": "Centralizar verticalmente",
"centerHorizontally": "", "centerHorizontally": "Centralizar horizontalmente",
"distributeHorizontally": "", "distributeHorizontally": "Distribuir horizontalmente",
"distributeVertically": "" "distributeVertically": "Distribuir verticalmente"
}, },
"buttons": { "buttons": {
"clearReset": "Limpar o canvas e redefinir a cor de fundo", "clearReset": "Limpar o canvas e redefinir a cor de fundo",
@@ -110,9 +117,10 @@
"redo": "Refazer", "redo": "Refazer",
"roomDialog": "Iniciar colaboração ao vivo", "roomDialog": "Iniciar colaboração ao vivo",
"createNewRoom": "Criar nova sala", "createNewRoom": "Criar nova sala",
"toggleFullScreen": "Alternar tela cheia", "fullScreen": "Tela cheia",
"toggleDarkMode": "Alternar modo escuro", "darkMode": "Modo escuro",
"toggleZenMode": "Alternar modo zen", "lightMode": "Modo claro",
"zenMode": "Modo Zen",
"exitZenMode": "Sair do modo zen" "exitZenMode": "Sair do modo zen"
}, },
"alerts": { "alerts": {
@@ -128,7 +136,7 @@
"loadSceneOverridePrompt": "Carregar um desenho externo substituirá o seu conteúdo existente. Deseja continuar?", "loadSceneOverridePrompt": "Carregar um desenho externo substituirá o seu conteúdo existente. Deseja continuar?",
"errorLoadingLibrary": "Houve um erro ao carregar a biblioteca de terceiros.", "errorLoadingLibrary": "Houve um erro ao carregar a biblioteca de terceiros.",
"confirmAddLibrary": "Isso adicionará {{numShapes}} forma(s) à sua biblioteca. Tem certeza?", "confirmAddLibrary": "Isso adicionará {{numShapes}} forma(s) à sua biblioteca. Tem certeza?",
"imageDoesNotContainScene": "O arquivo de imagem não contém dados de cena. Você ativou durante a exportação?", "imageDoesNotContainScene": "A importação de imagens não é suportada no momento.\n\nVocê deseja importar uma cena? Esta imagem parece não conter dados de cena. Você ativou isto durante a exportação?",
"cannotRestoreFromImage": "Não foi possível restaurar a cena deste arquivo de imagem" "cannotRestoreFromImage": "Não foi possível restaurar a cena deste arquivo de imagem"
}, },
"toolBar": { "toolBar": {
@@ -153,6 +161,7 @@
"freeDraw": "Toque e arraste, solte quando terminar", "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", "text": "Dica: você também pode adicionar texto clicando duas vezes em qualquer lugar com a ferramenta de seleção",
"linearElementMulti": "Clique no último ponto ou pressione Escape ou Enter para terminar", "linearElementMulti": "Clique no último ponto ou pressione Escape ou Enter para terminar",
"lockAngle": "Você pode restringir o ângulo segurando SHIFT",
"resize": "Você pode restringir proporções segurando SHIFT enquanto redimensiona,\nsegure ALT para redimensionar do centro", "resize": "Você pode restringir proporções segurando SHIFT enquanto redimensiona,\nsegure ALT para redimensionar do centro",
"rotate": "Você pode restringir os ângulos segurando SHIFT enquanto gira", "rotate": "Você pode restringir os ângulos segurando SHIFT enquanto gira",
"lineEditor_info": "Clique duas vezes ou pressione Enter para editar os pontos", "lineEditor_info": "Clique duas vezes ou pressione Enter para editar os pontos",
@@ -160,9 +169,9 @@
"lineEditor_nothingSelected": "Selecione um ponto para mover ou remover, ou segure Alt e clique para adicionar novos pontos" "lineEditor_nothingSelected": "Selecione um ponto para mover ou remover, ou segure Alt e clique para adicionar novos pontos"
}, },
"canvasError": { "canvasError": {
"cannotShowPreview": "", "cannotShowPreview": "Não é possível mostrar pré-visualização",
"canvasTooBig": "", "canvasTooBig": "A tela pode ser muito grande.",
"canvasTooBigTip": "" "canvasTooBigTip": "Dica: tente aproximar um pouco os elementos mais distantes."
}, },
"errorSplash": { "errorSplash": {
"headingMain_pre": "Foi encontrado um erro. Tente ", "headingMain_pre": "Foi encontrado um erro. Tente ",
@@ -205,13 +214,22 @@
"textNewLine": "Adicionar nova linha (texto)", "textNewLine": "Adicionar nova linha (texto)",
"textFinish": "Finalizar edição (texto)", "textFinish": "Finalizar edição (texto)",
"zoomToFit": "Ajustar para caber todos os elementos", "zoomToFit": "Ajustar para caber todos os elementos",
"zoomToSelection": "Ampliar a seleção",
"preventBinding": "Prevenir fixação de seta" "preventBinding": "Prevenir fixação de seta"
}, },
"encrypted": { "encrypted": {
"tooltip": "Seus desenhos são criptografados de ponta a ponta, então os servidores do Excalidraw nunca os verão." "tooltip": "Seus desenhos são criptografados de ponta a ponta, então os servidores do Excalidraw nunca os verão."
}, },
"charts": { "stats": {
"noNumericColumn": "Você colou uma planilha sem uma coluna numérica.", "angle": "Ângulo",
"tooManyColumns": "Você colou uma planilha com mais de duas colunas." "element": "Elemento",
"elements": "Elementos",
"height": "Altura",
"scene": "Cena",
"selected": "Selecionado",
"storage": "Armazenamento",
"title": "Estatísticas para nerds",
"total": "Total",
"width": "Largura"
} }
} }
+26 -8
View File
@@ -4,6 +4,7 @@
"selectAll": "Selectare totală", "selectAll": "Selectare totală",
"multiSelect": "Adaugă element la selecție", "multiSelect": "Adaugă element la selecție",
"moveCanvas": "Mutare pânză", "moveCanvas": "Mutare pânză",
"cut": "Decupare",
"copy": "Copiere", "copy": "Copiere",
"copyAsPng": "Copiere în memoria temporară ca PNG", "copyAsPng": "Copiere în memoria temporară ca PNG",
"copyAsSvg": "Copiere în memoria temporară ca SVG", "copyAsSvg": "Copiere în memoria temporară ca SVG",
@@ -28,6 +29,11 @@
"edges": "Margini", "edges": "Margini",
"sharp": "Ascuțite", "sharp": "Ascuțite",
"round": "Rotunde", "round": "Rotunde",
"arrowheads": "Vârfuri de săgeată",
"arrowhead_none": "Niciunul",
"arrowhead_arrow": "Săgeată",
"arrowhead_bar": "Bară",
"arrowhead_dot": "Bulină",
"fontSize": "Dimensiune font", "fontSize": "Dimensiune font",
"fontFamily": "Familia de fonturi", "fontFamily": "Familia de fonturi",
"onlySelected": "Numai selecția", "onlySelected": "Numai selecția",
@@ -70,10 +76,11 @@
"group": "Grupare selecție", "group": "Grupare selecție",
"ungroup": "Degrupare selecție", "ungroup": "Degrupare selecție",
"collaborators": "Colaboratori", "collaborators": "Colaboratori",
"toggleGridMode": "Comută modul grilă", "gridMode": "Mod grilă",
"addToLibrary": "Adăugare la bibliotecă", "addToLibrary": "Adăugare la bibliotecă",
"removeFromLibrary": "Eliminare din bibliotecă", "removeFromLibrary": "Eliminare din bibliotecă",
"libraryLoadingMessage": "Se încarcă biblioteca...", "libraryLoadingMessage": "Se încarcă biblioteca...",
"libraries": "Răsfoiește bibliotecile",
"loadingScene": "Se încarcă scena...", "loadingScene": "Se încarcă scena...",
"align": "Aliniere", "align": "Aliniere",
"alignTop": "Aliniere sus", "alignTop": "Aliniere sus",
@@ -110,9 +117,10 @@
"redo": "Refacere", "redo": "Refacere",
"roomDialog": "Colaborare în direct", "roomDialog": "Colaborare în direct",
"createNewRoom": "Creare cameră nouă", "createNewRoom": "Creare cameră nouă",
"toggleFullScreen": "Comută modul ecran complet", "fullScreen": "Ecran complet",
"toggleDarkMode": "Comută modul întunecat", "darkMode": "Mod întunecat",
"toggleZenMode": "Comută modul zen", "lightMode": "Mod luminos",
"zenMode": "Mod zen",
"exitZenMode": "Ieșire din modul zen" "exitZenMode": "Ieșire din modul zen"
}, },
"alerts": { "alerts": {
@@ -128,7 +136,7 @@
"loadSceneOverridePrompt": "Încărcarea desenului extern va înlocui conținutul existent. Dorești să continui?", "loadSceneOverridePrompt": "Încărcarea desenului extern va înlocui conținutul existent. Dorești să continui?",
"errorLoadingLibrary": "A apărut o eroare la încărcarea bibliotecii terțe.", "errorLoadingLibrary": "A apărut o eroare la încărcarea bibliotecii terțe.",
"confirmAddLibrary": "Această acțiune va adăuga {{numShapes}} formă(e) la biblioteca ta. Confirmi?", "confirmAddLibrary": "Această acțiune va adăuga {{numShapes}} formă(e) la biblioteca ta. Confirmi?",
"imageDoesNotContainScene": "Fișierul de imagine nu conține date de scenă. Ai activat această opțiune pe durata exportării?", "imageDoesNotContainScene": "Importarea imaginilor nu este acceptată în acest moment.\n\nVoiai să imporți o scenă? Această imagine nu pare să conțină date de scenă. Ai activat această opțiune pe durata exportării?",
"cannotRestoreFromImage": "Scena nu a putut fi restaurată din acest fișier de imagine" "cannotRestoreFromImage": "Scena nu a putut fi restaurată din acest fișier de imagine"
}, },
"toolBar": { "toolBar": {
@@ -153,6 +161,7 @@
"freeDraw": "Dă clic pe pânză și glisează cursorul, apoi eliberează-l când ai terminat", "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", "text": "Sfat: poți adăuga text și dând dublu clic oriunde cu instrumentul de selecție",
"linearElementMulti": "Dă clic pe ultimul punct sau apasă tasta Escape sau tasta Enter pentru a termina", "linearElementMulti": "Dă clic pe ultimul punct sau apasă tasta Escape sau tasta Enter pentru a termina",
"lockAngle": "Poți constrânge unghiul prin ținerea apăsată a tastei SHIFT",
"resize": "Poți constrânge proporțiile, ținând apăsată tasta SHIFT în timp ce redimensionezi,\nține apăsată tasta ALT pentru a redimensiona de la centru", "resize": "Poți constrânge proporțiile, ținând apăsată tasta SHIFT în timp ce redimensionezi,\nține apăsată tasta ALT pentru a redimensiona de la centru",
"rotate": "Poți constrânge unghiurile, ținând apăsată tasta SHIFT în timp ce rotești", "rotate": "Poți constrânge unghiurile, ținând apăsată tasta SHIFT în timp ce rotești",
"lineEditor_info": "Dă dublu clic sau apasă tasta Enter pentru a edita punctele", "lineEditor_info": "Dă dublu clic sau apasă tasta Enter pentru a edita punctele",
@@ -205,13 +214,22 @@
"textNewLine": "Adaugă o linie nouă (text)", "textNewLine": "Adaugă o linie nouă (text)",
"textFinish": "Finalizează editarea (text)", "textFinish": "Finalizează editarea (text)",
"zoomToFit": "Apropiere/depărtare pentru a cuprinde totul", "zoomToFit": "Apropiere/depărtare pentru a cuprinde totul",
"zoomToSelection": "Panoramare la selecție",
"preventBinding": "Împiedică legarea săgeții" "preventBinding": "Împiedică legarea săgeții"
}, },
"encrypted": { "encrypted": {
"tooltip": "Desenele tale sunt criptate integral, astfel că serverele Excalidraw nu le vor vedea niciodată." "tooltip": "Desenele tale sunt criptate integral, astfel că serverele Excalidraw nu le vor vedea niciodată."
}, },
"charts": { "stats": {
"noNumericColumn": "Ai inserat o foaie de calcul fără o coloană numerică.", "angle": "Unghi",
"tooManyColumns": "Ai inserat o foaie de calcul cu mai mult de două coloane." "element": "Element",
"elements": "Elemente",
"height": "Înălțime",
"scene": "Scenă",
"selected": "Selectate",
"storage": "Stocare",
"title": "Statistici pentru pasionați",
"total": "Total",
"width": "Lățime"
} }
} }
+56 -38
View File
@@ -2,8 +2,9 @@
"labels": { "labels": {
"paste": "Вставить", "paste": "Вставить",
"selectAll": "Выбрать все", "selectAll": "Выбрать все",
"multiSelect": "Добавить элемент к выбору", "multiSelect": "Добавить элемент в выделенный фрагмент",
"moveCanvas": "Переместить холст", "moveCanvas": "Переместить холст",
"cut": "Вырезать",
"copy": "Копировать", "copy": "Копировать",
"copyAsPng": "Скопировать в буфер обмена как PNG", "copyAsPng": "Скопировать в буфер обмена как PNG",
"copyAsSvg": "Скопировать в буфер обмена как SVG", "copyAsSvg": "Скопировать в буфер обмена как SVG",
@@ -28,12 +29,17 @@
"edges": "Края", "edges": "Края",
"sharp": "Острые", "sharp": "Острые",
"round": "Скругленные", "round": "Скругленные",
"arrowheads": "Стрелка",
"arrowhead_none": "Без стрелки",
"arrowhead_arrow": "Cтрелка",
"arrowhead_bar": "Столбец",
"arrowhead_dot": "Точка",
"fontSize": "Размер шрифта", "fontSize": "Размер шрифта",
"fontFamily": "Семейство шрифтов", "fontFamily": "Семейство шрифтов",
"onlySelected": "Только выбранные", "onlySelected": "Только выбранные",
"withBackground": "С фоном", "withBackground": "С фоном",
"exportEmbedScene": "", "exportEmbedScene": "Встроить информацию о сцене в экспортируемый файл",
"exportEmbedScene_details": "", "exportEmbedScene_details": "Сцена будет сохранена в PNG/SVG файл так, чтобы всю сцену можно будет восстановить из этого файла. Это увеличит размер файла.",
"addWatermark": "Добавить \"Сделано с Excalidraw\"", "addWatermark": "Добавить \"Сделано с Excalidraw\"",
"handDrawn": "Нарисованный от руки", "handDrawn": "Нарисованный от руки",
"normal": "Обычный", "normal": "Обычный",
@@ -69,21 +75,22 @@
"madeWithExcalidraw": "Сделано в Excalidraw", "madeWithExcalidraw": "Сделано в Excalidraw",
"group": "Сгруппировать выделение", "group": "Сгруппировать выделение",
"ungroup": "Разделить выделение", "ungroup": "Разделить выделение",
"collaborators": "Сотрудники", "collaborators": "Участники",
"toggleGridMode": "Переключить режим сетки", "gridMode": "",
"addToLibrary": "Добавить в библиотеку", "addToLibrary": "Добавить в библиотеку",
"removeFromLibrary": "Удалить из библиотеки", "removeFromLibrary": "Удалить из библиотеки",
"libraryLoadingMessage": "Загрузка библиотеки...", "libraryLoadingMessage": "Загрузка библиотеки...",
"libraries": "Просмотреть библиотеки",
"loadingScene": "Загрузка сцены...", "loadingScene": "Загрузка сцены...",
"align": "", "align": "Выровнять",
"alignTop": "", "alignTop": "Выровнять по верхнему краю",
"alignBottom": "", "alignBottom": "Выровнять по нижнему краю",
"alignLeft": "", "alignLeft": "Выровнять по левому краю",
"alignRight": "", "alignRight": "Выровнять по правому краю",
"centerVertically": "", "centerVertically": "Центрировать по вертикали",
"centerHorizontally": "", "centerHorizontally": "Центрировать по горизонтали",
"distributeHorizontally": "", "distributeHorizontally": "Распределить по горизонтали",
"distributeVertically": "" "distributeVertically": "Распределить по вертикали"
}, },
"buttons": { "buttons": {
"clearReset": "Очистить холст и сбросить цвет фона", "clearReset": "Очистить холст и сбросить цвет фона",
@@ -92,7 +99,7 @@
"exportToSvg": "Экспорт в SVG", "exportToSvg": "Экспорт в SVG",
"copyToClipboard": "Скопировать в буфер обмена", "copyToClipboard": "Скопировать в буфер обмена",
"copyPngToClipboard": "Скопировать PNG в буфер обмена", "copyPngToClipboard": "Скопировать PNG в буфер обмена",
"scale": "Шкала", "scale": "Масштаб",
"save": "Сохранить", "save": "Сохранить",
"saveAs": "Сохранить как", "saveAs": "Сохранить как",
"load": "Загрузить", "load": "Загрузить",
@@ -110,26 +117,27 @@
"redo": "Шаг вперед", "redo": "Шаг вперед",
"roomDialog": "Начать совместную работу", "roomDialog": "Начать совместную работу",
"createNewRoom": "Создать новую комнату", "createNewRoom": "Создать новую комнату",
"toggleFullScreen": ереключить полноэкранный режим", "fullScreen": олный экран",
"toggleDarkMode": "Переключить тёмную тему", "darkMode": "",
"toggleZenMode": "Переключить режим концентрации внимания", "lightMode": "",
"zenMode": "",
"exitZenMode": "Выключить режим концентрации внимания" "exitZenMode": "Выключить режим концентрации внимания"
}, },
"alerts": { "alerts": {
"clearReset": "Это очистит весь холст. Вы уверены?", "clearReset": "Это очистит весь холст. Вы уверены?",
"couldNotCreateShareableLink": "Не удалось создать общедоступную ссылку.", "couldNotCreateShareableLink": "Не удалось создать общедоступную ссылку.",
"couldNotCreateShareableLinkTooBig": "", "couldNotCreateShareableLinkTooBig": "Нельзя создать ссылку, чтобы поделиться. Сцена слишком большая",
"couldNotLoadInvalidFile": "Не удалось загрузить недопустимый файл", "couldNotLoadInvalidFile": "Не удалось загрузить недопустимый файл",
"importBackendFailed": "Не удалось импортировать из бэкэнда.", "importBackendFailed": "Не удалось импортировать из бэкэнда.",
"cannotExportEmptyCanvas": "Не может экспортировать пустой холст.", "cannotExportEmptyCanvas": "Не может экспортировать пустой холст.",
"couldNotCopyToClipboard": "Не удалось скопировать в буфер обмена. Попробуйте использовать веб-браузер Chrome.", "couldNotCopyToClipboard": "Не удалось скопировать в буфер обмена. Попробуйте использовать веб-браузер Chrome.",
"decryptFailed": "Не удалось декодировать данные.", "decryptFailed": "Не удалось расшифровать данные.",
"uploadedSecurly": "Загружаемые данные защищена сквозным шифрованием, что означает, что сервер Excalidraw и третьи стороны не могут прочитать содержимое.", "uploadedSecurly": "Загружаемые данные защищена сквозным шифрованием, что означает, что сервер Excalidraw и третьи стороны не могут прочитать содержимое.",
"loadSceneOverridePrompt": "", "loadSceneOverridePrompt": "Загрузка рисунка приведёт к замене имеющегося содержимого. Вы хотите продолжить?",
"errorLoadingLibrary": "", "errorLoadingLibrary": "Произошла ошибка при загрузке сторонней библиотеки.",
"confirmAddLibrary": "", "confirmAddLibrary": "Будет добавлено {{numShapes}} фигур в вашу библиотеку. Продолжить?",
"imageDoesNotContainScene": "", "imageDoesNotContainScene": "",
"cannotRestoreFromImage": "" "cannotRestoreFromImage": "Сцена не может быть восстановлена из этого изображения"
}, },
"toolBar": { "toolBar": {
"selection": "Выделение области", "selection": "Выделение области",
@@ -151,18 +159,19 @@
"hints": { "hints": {
"linearElement": "Нажмите, чтобы начать несколько точек, перетащите для одной линии", "linearElement": "Нажмите, чтобы начать несколько точек, перетащите для одной линии",
"freeDraw": "Нажмите и перетаскивайте, отпустите по завершении", "freeDraw": "Нажмите и перетаскивайте, отпустите по завершении",
"text": "", "text": "Совет: при выбранном инструменте выделения дважды щёлкните в любом месте, чтобы добавить текст",
"linearElementMulti": "Кликните на последней точке или нажмите Escape или Enter чтобы закончить", "linearElementMulti": "Кликните на последней точке или нажмите Escape или Enter чтобы закончить",
"lockAngle": "",
"resize": "Вы можете ограничить пропорции, удерживая SHIFT во время изменения размеров,\nудерживайте ALT чтобы изменить размер из центра", "resize": "Вы можете ограничить пропорции, удерживая SHIFT во время изменения размеров,\nудерживайте ALT чтобы изменить размер из центра",
"rotate": "Вы можете ограничить углы, удерживая SHIFT во время вращения", "rotate": "Вы можете ограничить углы, удерживая SHIFT во время вращения",
"lineEditor_info": "Дважды кликните или нажмите Enter, чтобы редактировать точки", "lineEditor_info": "Дважды кликните или нажмите Enter, чтобы редактировать точки",
"lineEditor_pointSelected": "", "lineEditor_pointSelected": "Нажмите Delete для удаления точки, Ctrl или Cmd + D для дублирования, перетащите для перемещения",
"lineEditor_nothingSelected": "" "lineEditor_nothingSelected": "Выберите точку для перемещения или удаления. Alt + клик чтобы добавить новые точки"
}, },
"canvasError": { "canvasError": {
"cannotShowPreview": "", "cannotShowPreview": "Не удается отобразить предпросмотр",
"canvasTooBig": "", "canvasTooBig": "Сцена слишком большая.",
"canvasTooBigTip": "" "canvasTooBigTip": "Совет: попробуйте сблизить элементы рисунка."
}, },
"errorSplash": { "errorSplash": {
"headingMain_pre": "Возникла ошибка. Попробуйте ", "headingMain_pre": "Возникла ошибка. Попробуйте ",
@@ -180,11 +189,11 @@
"roomDialog": { "roomDialog": {
"desc_intro": "Вы можете пригласить людей в текущую сцену для совместной работы.", "desc_intro": "Вы можете пригласить людей в текущую сцену для совместной работы.",
"desc_privacy": "Не беспокойтесь, сессия использует сквозное шифрование, поэтому всё что вы нарисуете останется приватным. Ваша информация не будет доступна даже на наших серверах.", "desc_privacy": "Не беспокойтесь, сессия использует сквозное шифрование, поэтому всё что вы нарисуете останется приватным. Ваша информация не будет доступна даже на наших серверах.",
"button_startSession": "Начать сессию", "button_startSession": "Начать сеанс",
"button_stopSession": "Закончить сессию", "button_stopSession": "Завершить сеанс",
"desc_inProgressIntro": "Совместная сессия теперь активна.", "desc_inProgressIntro": "Совместная сессия теперь активна.",
"desc_shareLink": "Поделитесь этой ссылкой со всеми участниками:", "desc_shareLink": "Поделитесь этой ссылкой со всеми участниками:",
"desc_exitSession": "" "desc_exitSession": "Завершив сеанс, вы выйдете из комнаты, но сможете продолжить работать с документом локально. Это не повлияет на работу других пользователей — они смогут продолжить совместную работу с их версией документа."
}, },
"errorDialog": { "errorDialog": {
"title": "Ошибка" "title": "Ошибка"
@@ -204,14 +213,23 @@
"github": "Нашли проблему? Отправьте", "github": "Нашли проблему? Отправьте",
"textNewLine": "Добавить новую строку (текст)", "textNewLine": "Добавить новую строку (текст)",
"textFinish": "Закончить редактирование (текст)", "textFinish": "Закончить редактирование (текст)",
"zoomToFit": "", "zoomToFit": "Отмастштабировать, чтобы поместились все элементы",
"zoomToSelection": "Перейти к выделенному",
"preventBinding": "Предотвратить привязку стрелок" "preventBinding": "Предотвратить привязку стрелок"
}, },
"encrypted": { "encrypted": {
"tooltip": "" "tooltip": "Ваши данные защищены сквозным (End-to-end) шифрованием. Серверы Excalidraw никогда не получат доступ к ним."
}, },
"charts": { "stats": {
"noNumericColumn": "", "angle": "Угол",
"tooManyColumns": "Вы вставили таблицу с более чем двумя столбцами." "element": "Элемент",
"elements": "Элементы",
"height": "Высота",
"scene": "Сцены",
"selected": "Выбран",
"storage": "Хранилище",
"title": "Статистика для ботаников",
"total": "Всего",
"width": "Ширина"
} }
} }
+26 -8
View File
@@ -4,6 +4,7 @@
"selectAll": "Vybrať všetko", "selectAll": "Vybrať všetko",
"multiSelect": "Pridať prvok do výberu", "multiSelect": "Pridať prvok do výberu",
"moveCanvas": "Pohyb plátna", "moveCanvas": "Pohyb plátna",
"cut": "Vystrihnúť",
"copy": "Kopírovať", "copy": "Kopírovať",
"copyAsPng": "Kopírovať do schránky ako PNG", "copyAsPng": "Kopírovať do schránky ako PNG",
"copyAsSvg": "Kopírovať do schránky ako SVG", "copyAsSvg": "Kopírovať do schránky ako SVG",
@@ -28,6 +29,11 @@
"edges": "Okraje", "edges": "Okraje",
"sharp": "Ostré", "sharp": "Ostré",
"round": "Zaokrúhlené", "round": "Zaokrúhlené",
"arrowheads": "Zakončenie šípky",
"arrowhead_none": "Žiadne",
"arrowhead_arrow": "Šípka",
"arrowhead_bar": "Čiara",
"arrowhead_dot": "Bod",
"fontSize": "Veľkosť písma", "fontSize": "Veľkosť písma",
"fontFamily": "Písmo", "fontFamily": "Písmo",
"onlySelected": "Iba vybrané", "onlySelected": "Iba vybrané",
@@ -70,10 +76,11 @@
"group": "Zoskupiť", "group": "Zoskupiť",
"ungroup": "Zrušiť zoskupenie", "ungroup": "Zrušiť zoskupenie",
"collaborators": "Spolupracovníci", "collaborators": "Spolupracovníci",
"toggleGridMode": "Prepnúť mriežku", "gridMode": "Režim mriežky",
"addToLibrary": "Pridať do knižnice", "addToLibrary": "Pridať do knižnice",
"removeFromLibrary": "Odstrániť z knižnice", "removeFromLibrary": "Odstrániť z knižnice",
"libraryLoadingMessage": "Načítavanie knižnice...", "libraryLoadingMessage": "Načítavanie knižnice...",
"libraries": "Prehliadať knižnice",
"loadingScene": "Načítavanie scény...", "loadingScene": "Načítavanie scény...",
"align": "Zarovnanie", "align": "Zarovnanie",
"alignTop": "Zarovnať nahor", "alignTop": "Zarovnať nahor",
@@ -110,9 +117,10 @@
"redo": "Znova", "redo": "Znova",
"roomDialog": "Začať živú spoluprácu", "roomDialog": "Začať živú spoluprácu",
"createNewRoom": "Vytvoriť novú miestnosť", "createNewRoom": "Vytvoriť novú miestnosť",
"toggleFullScreen": "Prepnúť režim celej obrazovky", "fullScreen": "Celá obrazovka",
"toggleDarkMode": "Prepnúť tmavý režim", "darkMode": "Tmavý režim",
"toggleZenMode": "Prepnúť režim zen", "lightMode": "Svetlý režim",
"zenMode": "Režim zen",
"exitZenMode": "Zrušiť režim zen" "exitZenMode": "Zrušiť režim zen"
}, },
"alerts": { "alerts": {
@@ -128,7 +136,7 @@
"loadSceneOverridePrompt": "Nahratie externej kresby nahradí existujúci obsah. Prajete si pokračovať?", "loadSceneOverridePrompt": "Nahratie externej kresby nahradí existujúci obsah. Prajete si pokračovať?",
"errorLoadingLibrary": "Nepodarilo sa načítať externú knižnicu.", "errorLoadingLibrary": "Nepodarilo sa načítať externú knižnicu.",
"confirmAddLibrary": "Týmto sa pridá {{numShapes}} tvar(ov) do vašej knižnice. Ste si istí?", "confirmAddLibrary": "Týmto sa pridá {{numShapes}} tvar(ov) do vašej knižnice. Ste si istí?",
"imageDoesNotContainScene": "Obrázkový súbor neobsahuje údaje scény. Povolili ste ich pri exportovaní?", "imageDoesNotContainScene": "Importovanie obrázku v tomto momente nie je možné.\n\nChceli ste importovať scénu? Tento obrázok neobsahuje žiadne údaje scény. Povolili ste túto možnosť počas exportovania?",
"cannotRestoreFromImage": "Nepodarilo sa obnoviť scénu z tohto obrázkového súboru" "cannotRestoreFromImage": "Nepodarilo sa obnoviť scénu z tohto obrázkového súboru"
}, },
"toolBar": { "toolBar": {
@@ -153,6 +161,7 @@
"freeDraw": "Kliknite a ťahajte, pustite na ukončenie", "freeDraw": "Kliknite a ťahajte, pustite na ukončenie",
"text": "Tip: text môžete pridať aj dvojklikom kdekoľvek, ak je zvolený nástroj výber", "text": "Tip: text môžete pridať aj dvojklikom kdekoľvek, ak je zvolený nástroj výber",
"linearElementMulti": "Kliknite na počiatočný bod alebo stlačte Escape alebo Enter na ukončenie", "linearElementMulti": "Kliknite na počiatočný bod alebo stlačte Escape alebo Enter na ukončenie",
"lockAngle": "Počas rotácie obmedzíte uhol podržaním SHIFT",
"resize": "Počas zmeny veľkosti zachováte proporcie podržaním SHIFT,\\npodržaním ALT meníte veľkosť so zachovaním stredu", "resize": "Počas zmeny veľkosti zachováte proporcie podržaním SHIFT,\\npodržaním ALT meníte veľkosť so zachovaním stredu",
"rotate": "Počas rotácie obmedzíte uhol podržaním SHIFT", "rotate": "Počas rotácie obmedzíte uhol podržaním SHIFT",
"lineEditor_info": "Použite dvojklik alebo stlačte Enter na editáciu bodov", "lineEditor_info": "Použite dvojklik alebo stlačte Enter na editáciu bodov",
@@ -205,13 +214,22 @@
"textNewLine": "Vložiť nový riadok (text)", "textNewLine": "Vložiť nový riadok (text)",
"textFinish": "Ukončenie editovania (text)", "textFinish": "Ukončenie editovania (text)",
"zoomToFit": "Priblížiť aby boli zahrnuté všetky prvky", "zoomToFit": "Priblížiť aby boli zahrnuté všetky prvky",
"zoomToSelection": "Priblížiť na výber",
"preventBinding": "Zakázať pripájanie šípky" "preventBinding": "Zakázať pripájanie šípky"
}, },
"encrypted": { "encrypted": {
"tooltip": "Vaše kresby používajú end-to-end šifrovanie, takže ich Excalidraw server nedokáže prečítať." "tooltip": "Vaše kresby používajú end-to-end šifrovanie, takže ich Excalidraw server nedokáže prečítať."
}, },
"charts": { "stats": {
"noNumericColumn": "Prilepili ste tabuľku bez číselného stĺpca.", "angle": "Uhol",
"tooManyColumns": "Prilepili ste tabuľku s viac ako dvoma stĺpcami." "element": "Prvok",
"elements": "Prvky",
"height": "Výška",
"scene": "Scéna",
"selected": "Vybrané",
"storage": "Úložisko",
"title": "Štatistiky",
"total": "Celkom",
"width": "Šírka"
} }
} }
+26 -8
View File
@@ -4,6 +4,7 @@
"selectAll": "Markera alla", "selectAll": "Markera alla",
"multiSelect": "Lägg till element till markering", "multiSelect": "Lägg till element till markering",
"moveCanvas": "Flytta canvas", "moveCanvas": "Flytta canvas",
"cut": "Klipp ut",
"copy": "Kopiera", "copy": "Kopiera",
"copyAsPng": "Kopiera till urklipp som PNG", "copyAsPng": "Kopiera till urklipp som PNG",
"copyAsSvg": "Kopiera till urklipp som SVG", "copyAsSvg": "Kopiera till urklipp som SVG",
@@ -28,6 +29,11 @@
"edges": "Kanter", "edges": "Kanter",
"sharp": "Skarp", "sharp": "Skarp",
"round": "Rund", "round": "Rund",
"arrowheads": "Pilhuvuden",
"arrowhead_none": "Inga",
"arrowhead_arrow": "Pil",
"arrowhead_bar": "Stolpe",
"arrowhead_dot": "Punkt",
"fontSize": "Teckenstorlek", "fontSize": "Teckenstorlek",
"fontFamily": "Teckensnitt", "fontFamily": "Teckensnitt",
"onlySelected": "Endast markering", "onlySelected": "Endast markering",
@@ -70,10 +76,11 @@
"group": "Gruppera markering", "group": "Gruppera markering",
"ungroup": "Avgruppera markering", "ungroup": "Avgruppera markering",
"collaborators": "Medarbetare", "collaborators": "Medarbetare",
"toggleGridMode": "Växla rutnätsläge", "gridMode": "Rutnätsläge",
"addToLibrary": "Lägg till i biblioteket", "addToLibrary": "Lägg till i biblioteket",
"removeFromLibrary": "Ta bort från bibliotek", "removeFromLibrary": "Ta bort från bibliotek",
"libraryLoadingMessage": "Laddar bibliotek...", "libraryLoadingMessage": "Laddar bibliotek...",
"libraries": "Bläddra i bibliotek",
"loadingScene": "Laddar scen...", "loadingScene": "Laddar scen...",
"align": "Justera", "align": "Justera",
"alignTop": "Justera överkant", "alignTop": "Justera överkant",
@@ -110,9 +117,10 @@
"redo": "Gör om", "redo": "Gör om",
"roomDialog": "Starta live-samarbete", "roomDialog": "Starta live-samarbete",
"createNewRoom": "Skapa ett nytt rum", "createNewRoom": "Skapa ett nytt rum",
"toggleFullScreen": "Växla fullskärmsläge", "fullScreen": "Helskärm",
"toggleDarkMode": "Växla mörkt läge", "darkMode": "Mörkt läge",
"toggleZenMode": "Växla zen-läge", "lightMode": "Ljust läge",
"zenMode": "Zen-läge",
"exitZenMode": "Gå ur zen-läge" "exitZenMode": "Gå ur zen-läge"
}, },
"alerts": { "alerts": {
@@ -128,7 +136,7 @@
"loadSceneOverridePrompt": "Laddning av extern skiss kommer att ersätta ditt befintliga innehåll. Vill du fortsätta?", "loadSceneOverridePrompt": "Laddning av extern skiss kommer att ersätta ditt befintliga innehåll. Vill du fortsätta?",
"errorLoadingLibrary": "Fel vid inläsning av tredjeparts bibliotek.", "errorLoadingLibrary": "Fel vid inläsning av tredjeparts bibliotek.",
"confirmAddLibrary": "Detta kommer att lägga till {{numShapes}} form(er) till ditt bibliotek. Är du säker?", "confirmAddLibrary": "Detta kommer att lägga till {{numShapes}} form(er) till ditt bibliotek. Är du säker?",
"imageDoesNotContainScene": "Bildfilen innehåller inte skissdata. Har du aktiverat detta under export?", "imageDoesNotContainScene": "Importering av bilder stöds inte just nu.\n\nVill du importera en skiss? Den här bilden verkar inte innehålla någon skissdata. Har du aktiverat detta under export?",
"cannotRestoreFromImage": "Skiss kunde inte återställas från denna bildfil" "cannotRestoreFromImage": "Skiss kunde inte återställas från denna bildfil"
}, },
"toolBar": { "toolBar": {
@@ -153,6 +161,7 @@
"freeDraw": "Klicka och dra, släpp när du är klar", "freeDraw": "Klicka och dra, släpp när du är klar",
"text": "Tips: du kan också lägga till text genom att dubbelklicka var som helst med markeringsverktyget", "text": "Tips: du kan också lägga till text genom att dubbelklicka var som helst med markeringsverktyget",
"linearElementMulti": "Klicka på sista punkten eller tryck Escape eller Enter för att avsluta", "linearElementMulti": "Klicka på sista punkten eller tryck Escape eller Enter för att avsluta",
"lockAngle": "Du kan begränsa vinkeln genom att hålla SKIFT",
"resize": "Du kan behålla proportioner genom att hålla SHIFT medan du ändrar storlek,\nhåller du ALT ändras storlek relativt mitten", "resize": "Du kan behålla proportioner genom att hålla SHIFT medan du ändrar storlek,\nhåller du ALT ändras storlek relativt mitten",
"rotate": "Du kan begränsa vinklar genom att hålla SHIFT medan du roterar", "rotate": "Du kan begränsa vinklar genom att hålla SHIFT medan du roterar",
"lineEditor_info": "Dubbelklicka eller tryck på Enter för att redigera punkter", "lineEditor_info": "Dubbelklicka eller tryck på Enter för att redigera punkter",
@@ -205,13 +214,22 @@
"textNewLine": "Lägg till ny rad (text)", "textNewLine": "Lägg till ny rad (text)",
"textFinish": "Slutför redigering (text)", "textFinish": "Slutför redigering (text)",
"zoomToFit": "Zooma för att rymma alla element", "zoomToFit": "Zooma för att rymma alla element",
"zoomToSelection": "Zooma till markering",
"preventBinding": "Förhindra pilbindning" "preventBinding": "Förhindra pilbindning"
}, },
"encrypted": { "encrypted": {
"tooltip": "Dina skisser är krypterade från ände till ände så Excalidraws servrar kommer aldrig att se dem." "tooltip": "Dina skisser är krypterade från ände till ände så Excalidraws servrar kommer aldrig att se dem."
}, },
"charts": { "stats": {
"noNumericColumn": "Du klistrade in ett kalkylblad utan en numerisk kolumn.", "angle": "Vinkel",
"tooManyColumns": "Du klistrade in ett kalkylblad med mer än två kolumner." "element": "Element",
"elements": "Element",
"height": "Höjd",
"scene": "Skiss",
"selected": "Valda",
"storage": "Lagring",
"title": "Statistik för nördar",
"total": "Totalt",
"width": "Bredd"
} }
} }

Some files were not shown because too many files have changed in this diff Show More