Repository: SVG-Edit/svgedit (language: python)

Issue:
Pasting a line with markers produce invalid svg if original line was deleted
To reproduce:
- Create a line with markers
- Copy the line
- Delete the line
- Save the drawing
- Paste the line
  The marker defs is now missing from the svg.

My guess is that the defs are not copied along with the line, so when pasting the line, it refers to a defs that were removed when saving the drawing.

Possible solutions would be:
- copy the defs along with the element
- don't delete defs for elements on the clipboard

SVG-edit should use getIntersectionList
When drag-selecting ("multiselect" mode), the intersection checking should use the SVGElement.getIntersectionList() API method [1].  Blink/Webkit now support it.

[1] http://www.w3.org/TR/SVG/struct.html#__svg__SVGSVGElement__getIntersectionList
[2] https://bugs.webkit.org/show_bug.cgi?id=11274

SVG-edit should use getIntersectionList
When drag-selecting ("multiselect" mode), the intersection checking should use the SVGElement.getIntersectionList() API method [1].  Blink/Webkit now support it.

[1] http://www.w3.org/TR/SVG/struct.html#__svg__SVGSVGElement__getIntersectionList
[2] https://bugs.webkit.org/show_bug.cgi?id=11274

Switch imagelib to https urls, update project to be hosted over https
This pull request fixes issue https://github.com/SVG-Edit/svgedit/issues/31 so that svgedit can be served securely. There are side-channel attacks when using protocol relative urls (http://www.paulirish.com/2010/the-protocol-relative-url/) so this patch goes whole-hog on https.

Switch files to the one true line ending and indentation
Some files have old-style windows line endings such as ext-imagelib.js. We should clean these up.

Update README.md for 2.8.1 release

Hide overview extension in chrome
This works around issue #26 

Update README.md for 2.8.1 release

se connection line breaks when navigating away and back
Here's an example svg

```svg
<svg width="640" height="480" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg" xmlns:se="http://svg-edit.googlecode.com">
 <!-- Created with SVG-edit - http://svg-edit.googlecode.com/ -->
 <g>
  <title>Layer 1</title>
  <polyline se:connector="svg_6 svg_8" fill="none" stroke-width="5" stroke="#000000" points="182.6628875732422,166.5 180.1041259765625,203 177.5453643798828,239.5" id="svg_10"/>
  <polyline se:connector="svg_4 svg_5" fill="none" stroke-width="5" stroke="#000000" points="458.5787048339844,79.5 473.9290466308594,160 489.2793884277344,240.5" id="svg_9"/>
  <ellipse id="svg_1" ry="85.5" rx="95" cy="196.5" cx="327" stroke-width="5" stroke="#000000" fill="#FF0000"/>
  <ellipse id="svg_2" ry="65" rx="66.5" cy="330" cx="180.5" stroke-width="5" stroke="#000000" fill="#FF0000"/>
  <ellipse id="svg_3" ry="62" rx="53" cy="139" cx="107" stroke-width="5" stroke="#000000" fill="#FF0000"/>
  <ellipse id="svg_4" ry="32" rx="37" cy="45" cx="452" stroke-width="5" stroke="#000000" fill="#FF0000"/>
  <ellipse id="svg_5" ry="27.5" rx="33" cy="270.5" cx="495" stroke-width="5" stroke="#000000" fill="#FF0000"/>
  <rect id="svg_6" height="133" width="211" y="31" x="82" stroke-width="5" stroke="#000000" fill="#FF0000"/>
  <rect id="svg_7" height="76" width="180" y="302" x="308" stroke-width="5" stroke="#000000" fill="#FF0000"/>
  <rect id="svg_8" height="196" width="289" y="242" x="26" stroke-width="5" stroke="#000000" fill="#FF0000"/>
 </g>
</svg>
```

The poly line connectors work a treat until you navigate away and back,  When you do the line is broken and treated as a normal line but moving the attached objects stops updating it.

If however you take the raw svg as above and paste it into the source in svg edit it will start working again.

Fix for issue #48 - improper placement of rubber-band box for 'use' elements.
remove the commented 'if' to resolve #48

Safari clips the main canvas area
The main content area is clipped along compositing borders (see screenshot). I think this may be a bug in Safari.

![safari_clip_compositing_bug](https://cloud.githubusercontent.com/assets/569214/11551904/a06d1138-9932-11e5-9d41-dc979f122fc9.png)

Updating binary files for release 2.8.1

Extension structure
A modular system to build extensions so others could contribute more easily.

Hide overview extension in chrome
This works around issue #26 

update jquery (and jqueryui) -- 1.7.1 is 4 years old
SVG-Edit currently uses jQuery 1.7

version 1.8 or better 1.9 would be start towards jQuery 2.x, which is current.

E.g.  jQuery.andSelf  could be replaced with jQuery.addBack  

The rubberband box placement error
Issue: The rubberband box is not placed properly for imported SVG images in the editor
How to reproduce: Import an SVG image. click on the image. The rubber band box around the image is  not properly coming. its left and top is not coming properly.

The rubberband box placement error
Issue: The rubberband box is not placed properly for imported SVG images in the editor
How to reproduce: Import an SVG image. click on the image. The rubber band box around the image is  not properly coming. its left and top is not coming properly.

Usage of left control panel not obvious
It is not obvious that you have to drag the mouse to open context menus on the left vertical control panel. For the first time when you click on Shapes for example, a menu opens up. Next time you have to drag for the same menu to open. There should be a popup that explains this behaviour because it is not a usual GUI behaviour.

Fullscreen Mode
SVG Edit could use a fullscreen button. I have this working in my development copy of the app, complete with icons as needed. I copied the existing code formatting where possible and put the icon right after the Undo button in the top toolbar.

My question is this: is it ok to include [screenfull](https://github.com/sindresorhus/screenfull.js) in SVG Edit? It's covered under an MIT license.

If this is ok, I can submit a pull request later.

Fullscreen Mode
SVG Edit could use a fullscreen button. I have this working in my development copy of the app, complete with icons as needed. I copied the existing code formatting where possible and put the icon right after the Undo button in the top toolbar.

My question is this: is it ok to include [screenfull](https://github.com/sindresorhus/screenfull.js) in SVG Edit? It's covered under an MIT license.

If this is ok, I can submit a pull request later.

Safari clips the main canvas area
The main content area is clipped along compositing borders (see screenshot). I think this may be a bug in Safari.

![safari_clip_compositing_bug](https://cloud.githubusercontent.com/assets/569214/11551904/a06d1138-9932-11e5-9d41-dc979f122fc9.png)

Consider hosting the live repo as well as the latest release
https://rawgit.com is a free service by Ryan Grove for serving https requests directly from tip-of-tree. There's no SLA for this service and Ryan could always replace svg-edit with malware, but I think these concerns are pretty minimal for a project like svg-edit.

Here's a demo:
https://rawgit.com/SVG-Edit/svgedit/master/editor/svg-editor.html

Rawgit isn't behind a CDN (but there is a CDNified link with cache:forever set at https://cdn.rawgit.com/SVG-Edit/svgedit/master/editor/svg-editor.html) so we should keep that in mind.

I think it makes sense to add a tip-of-tree link in somewhat smaller text below the latest release. Wdyt?

Issue 94 - support for custom namespaces
This should fix issue #94 - support for custom namespaces.

Usage of left control panel not obvious
It is not obvious that you have to drag the mouse to open context menus on the left vertical control panel. For the first time when you click on Shapes for example, a menu opens up. Next time you have to drag for the same menu to open. There should be a popup that explains this behaviour because it is not a usual GUI behaviour.

Usage of left control panel not obvious
It is not obvious that you have to drag the mouse to open context menus on the left vertical control panel. For the first time when you click on Shapes for example, a menu opens up. Next time you have to drag for the same menu to open. There should be a popup that explains this behaviour because it is not a usual GUI behaviour.

Only disable overview panel in Chrome 48-, not all Chrome
Better fix for issue #26 now that Chrome has a fix scheduled for release.

Only disable overview panel in Chrome 48-, not all Chrome
Better fix for issue #26 now that Chrome has a fix scheduled for release.

Consider hosting the live repo as well as the latest release
https://rawgit.com is a free service by Ryan Grove for serving https requests directly from tip-of-tree. There's no SLA for this service and Ryan could always replace svg-edit with malware, but I think these concerns are pretty minimal for a project like svg-edit.

Here's a demo:
https://rawgit.com/SVG-Edit/svgedit/master/editor/svg-editor.html

Rawgit isn't behind a CDN (but there is a CDNified link with cache:forever set at https://cdn.rawgit.com/SVG-Edit/svgedit/master/editor/svg-editor.html) so we should keep that in mind.

I think it makes sense to add a tip-of-tree link in somewhat smaller text below the latest release. Wdyt?

Only disable overview panel in Chrome 48-, not all Chrome
Better fix for issue #26 now that Chrome has a fix scheduled for release.

Only disable overview panel in Chrome 48-, not all Chrome
Better fix for issue #26 now that Chrome has a fix scheduled for release.

Only disable overview panel in Chrome 48-, not all Chrome
Better fix for issue #26 now that Chrome has a fix scheduled for release.

Only disable overview panel in Chrome 48-, not all Chrome
Better fix for issue #26 now that Chrome has a fix scheduled for release.

Only disable overview panel in Chrome 48-, not all Chrome
Better fix for issue #26 now that Chrome has a fix scheduled for release.

Issue 94 - support for custom namespaces
This should fix issue #94 - support for custom namespaces.

Extension structure
A modular system to build extensions so others could contribute more easily.

jquery.contextMenu.js is version 1.01 - 4 years old  (js-hotkeys, jspdf, canvg/rgbcolor/stackblur-canvas, mathjax also have new versions and/or need updating from ours)
jquery.contextMenu.js is version 1.01 - 4 years old 
while https://github.com/swisnl/jQuery-contextMenu  
shows now version 2.0.1
It appears to have been continuously maintained...

does the new version work as a drop-in replacement ?

can anyone estimate how hard it would be to get it to work (if not)  ?

Bug: Useless and "null" attributes left in the SVG source when exporting
I've noticed that the output always has a border.

Here's an example of what I got :

```
<svg width="476" height="476" xmlns="http://www.w3.org/2000/svg">
 <!-- Created with SVG-edit - http://svg-edit.googlecode.com/ -->
 <g>
  <title>Layer 1</title>
  <ellipse ry="198.5" rx="198.5" id="svg_3" cy="238" cx="238" stroke-linecap="null" stroke-linejoin="null" stroke-width="0" stroke="#000000" fill="#f3f3f3"/>
 </g>
</svg>
```

When converting it to Android-file (using this tool: http://inloop.github.io/svg2android/ ) , I can see the border clearly 

Bug: Useless and "null" attributes left in the SVG source when exporting
I've noticed that the output always has a border.

Here's an example of what I got :

```
<svg width="476" height="476" xmlns="http://www.w3.org/2000/svg">
 <!-- Created with SVG-edit - http://svg-edit.googlecode.com/ -->
 <g>
  <title>Layer 1</title>
  <ellipse ry="198.5" rx="198.5" id="svg_3" cy="238" cx="238" stroke-linecap="null" stroke-linejoin="null" stroke-width="0" stroke="#000000" fill="#f3f3f3"/>
 </g>
</svg>
```

When converting it to Android-file (using this tool: http://inloop.github.io/svg2android/ ) , I can see the border clearly 

se connection line breaks when navigating away and back
Here's an example svg

```svg
<svg width="640" height="480" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg" xmlns:se="http://svg-edit.googlecode.com">
 <!-- Created with SVG-edit - http://svg-edit.googlecode.com/ -->
 <g>
  <title>Layer 1</title>
  <polyline se:connector="svg_6 svg_8" fill="none" stroke-width="5" stroke="#000000" points="182.6628875732422,166.5 180.1041259765625,203 177.5453643798828,239.5" id="svg_10"/>
  <polyline se:connector="svg_4 svg_5" fill="none" stroke-width="5" stroke="#000000" points="458.5787048339844,79.5 473.9290466308594,160 489.2793884277344,240.5" id="svg_9"/>
  <ellipse id="svg_1" ry="85.5" rx="95" cy="196.5" cx="327" stroke-width="5" stroke="#000000" fill="#FF0000"/>
  <ellipse id="svg_2" ry="65" rx="66.5" cy="330" cx="180.5" stroke-width="5" stroke="#000000" fill="#FF0000"/>
  <ellipse id="svg_3" ry="62" rx="53" cy="139" cx="107" stroke-width="5" stroke="#000000" fill="#FF0000"/>
  <ellipse id="svg_4" ry="32" rx="37" cy="45" cx="452" stroke-width="5" stroke="#000000" fill="#FF0000"/>
  <ellipse id="svg_5" ry="27.5" rx="33" cy="270.5" cx="495" stroke-width="5" stroke="#000000" fill="#FF0000"/>
  <rect id="svg_6" height="133" width="211" y="31" x="82" stroke-width="5" stroke="#000000" fill="#FF0000"/>
  <rect id="svg_7" height="76" width="180" y="302" x="308" stroke-width="5" stroke="#000000" fill="#FF0000"/>
  <rect id="svg_8" height="196" width="289" y="242" x="26" stroke-width="5" stroke="#000000" fill="#FF0000"/>
 </g>
</svg>
```

The poly line connectors work a treat until you navigate away and back,  When you do the line is broken and treated as a normal line but moving the attached objects stops updating it.

If however you take the raw svg as above and paste it into the source in svg edit it will start working again.

se connection line breaks when navigating away and back
Here's an example svg

```svg
<svg width="640" height="480" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg" xmlns:se="http://svg-edit.googlecode.com">
 <!-- Created with SVG-edit - http://svg-edit.googlecode.com/ -->
 <g>
  <title>Layer 1</title>
  <polyline se:connector="svg_6 svg_8" fill="none" stroke-width="5" stroke="#000000" points="182.6628875732422,166.5 180.1041259765625,203 177.5453643798828,239.5" id="svg_10"/>
  <polyline se:connector="svg_4 svg_5" fill="none" stroke-width="5" stroke="#000000" points="458.5787048339844,79.5 473.9290466308594,160 489.2793884277344,240.5" id="svg_9"/>
  <ellipse id="svg_1" ry="85.5" rx="95" cy="196.5" cx="327" stroke-width="5" stroke="#000000" fill="#FF0000"/>
  <ellipse id="svg_2" ry="65" rx="66.5" cy="330" cx="180.5" stroke-width="5" stroke="#000000" fill="#FF0000"/>
  <ellipse id="svg_3" ry="62" rx="53" cy="139" cx="107" stroke-width="5" stroke="#000000" fill="#FF0000"/>
  <ellipse id="svg_4" ry="32" rx="37" cy="45" cx="452" stroke-width="5" stroke="#000000" fill="#FF0000"/>
  <ellipse id="svg_5" ry="27.5" rx="33" cy="270.5" cx="495" stroke-width="5" stroke="#000000" fill="#FF0000"/>
  <rect id="svg_6" height="133" width="211" y="31" x="82" stroke-width="5" stroke="#000000" fill="#FF0000"/>
  <rect id="svg_7" height="76" width="180" y="302" x="308" stroke-width="5" stroke="#000000" fill="#FF0000"/>
  <rect id="svg_8" height="196" width="289" y="242" x="26" stroke-width="5" stroke="#000000" fill="#FF0000"/>
 </g>
</svg>
```

The poly line connectors work a treat until you navigate away and back,  When you do the line is broken and treated as a normal line but moving the attached objects stops updating it.

If however you take the raw svg as above and paste it into the source in svg edit it will start working again.

image URL dialog loads when images added by extensions
I'm writing an extension that will make use of an image.

When I add it at present the default dialog triggers to prompt for a URL.

I've got a change set where I have altered the editor to check the canvas mode to ensure it is in 'image' before it will prompt for the dialog.

Is that the appropriate way to do it?

image URL dialog loads when images added by extensions
I'm writing an extension that will make use of an image.

When I add it at present the default dialog triggers to prompt for a URL.

I've got a change set where I have altered the editor to check the canvas mode to ensure it is in 'image' before it will prompt for the dialog.

Is that the appropriate way to do it?

image URL dialog loads when images added by extensions
I'm writing an extension that will make use of an image.

When I add it at present the default dialog triggers to prompt for a URL.

I've got a change set where I have altered the editor to check the canvas mode to ensure it is in 'image' before it will prompt for the dialog.

Is that the appropriate way to do it?

image URL dialog loads when images added by extensions
I'm writing an extension that will make use of an image.

When I add it at present the default dialog triggers to prompt for a URL.

I've got a change set where I have altered the editor to check the canvas mode to ensure it is in 'image' before it will prompt for the dialog.

Is that the appropriate way to do it?

Pasting a line with markers produce invalid svg if original line was deleted
To reproduce:
- Create a line with markers
- Copy the line
- Delete the line
- Save the drawing
- Paste the line
  The marker defs is now missing from the svg.

My guess is that the defs are not copied along with the line, so when pasting the line, it refers to a defs that were removed when saving the drawing.

Possible solutions would be:
- copy the defs along with the element
- don't delete defs for elements on the clipboard

Pasting a line with markers produce invalid svg if original line was deleted
To reproduce:
- Create a line with markers
- Copy the line
- Delete the line
- Save the drawing
- Paste the line
  The marker defs is now missing from the svg.

My guess is that the defs are not copied along with the line, so when pasting the line, it refers to a defs that were removed when saving the drawing.

Possible solutions would be:
- copy the defs along with the element
- don't delete defs for elements on the clipboard

Font size is hidden
fix #62 

Corrected case of jquery.contextMenu.js in make file
Builds fail because they can't read jquery.contextMenu.js without this change.

Check for deleted defs after paste, fixes #82

Issue 94 - support for custom namespaces
This should fix issue #94 - support for custom namespaces.

Issue 94 - support for custom namespaces
This should fix issue #94 - support for custom namespaces.



Discussion / hints:



Looks like the link to the https version of the demo has been reinstated, but the code the editor is running off is the 2.8 release, rather than the latest master, so the problem of the image libraries not working is back.

I'm working on a 2.8.1 release right now, should be up shortly.

2.8 introduced a bug that broke all icons in Safari (https://github.com/SVG-Edit/svgedit/issues/29) so we needed to do a re-release anyway, and I figured this would be nice to have.

Sorry, didn't mean to hassle - just noticed and thought I should flag in case it had been overlooked.

Thanks for all your recent efforts on svgedit. I really should look back at some of the modifications I made to the version I am running and contribute these at some point - some things related to breaking apart imported svg <symbol> elements. Anyway getting OT now so will leave it there :)

Oh, no hassle at all :)

I think this is all ready now. Could you please give it a try and make sure it works for you too?

The open clipart library seems to be mostly fine although the links to the SVGs themselves are giving Mixed Content warning.

The IAN one doesn't actually load any SVGs at all - looks like it's an issue with getting jquery loaded as it's requesting that insecurely. I'll modify the links and see if I can get things working.

Ok, the IAN Library is fixed and working again now! 
Now I just need to put some time aside to improve the horribly dated interface :)

I'd also like to see us switch from tabs to 2-sp indenting... wdyt?

2-space indents would be great. There's quite a bit of inconsistent style; it would be nice to reformat everything using idiomatic js or the google js guide.

I think this should now be all covered in `master`...

This is great! Thanks for the quick fix.


Able to reproduce by refreshing page... Serialization for or deserialization from storage must be problematic...

Filed against webkit in https://bugs.webkit.org/show_bug.cgi?id=151791

Fixed! http://trac.webkit.org/changeset/193613

Sounds like this is no longer an issue, so closing--thanks for the report!

What do you see as not modular about the extensions system currently?
Closing due to lack of response. Feel free to comment further or reopen to provide more detail on your request. We have refactored the main code to use ES6 modules. We have some documentation to-dos, however, including for our new extension format. Feel free to check out the new release when it may be ready.
This is great! Thanks for the quick fix.

I agree with this. On that note, I've been using jQuery v1.11.3 in my local devleopment copy with no apparent problems resulting from using the newer version. I haven't gone through the unit tests though so there could be some complications present.

Note: I'm also using jQuery UI v1.11.4 with no apparent problems.

please let me know what are the files needs to change in order to reference latest jquery file. i tried but throwing many script errors

also please everywhere load the jquery using HTTPS!!!! #172
Though I fixed the HTTPS issue already, I'm working on a PR to avoid CDN use entirely (applies to imagelib and mathjax) (the latter URL wasn't working). This should help with local testing/offline apps as well as ensure future compatibility. Optional use of CDN could be added (PRs welcome).

Ideally any update to these older versions that I've copied will be added through npm instead, ensuring a smoother upgrade path.

There is a "jquery_release" variable in an HTML comment which was apparently intended to conditionally load the CDN version, but I'm not sure what build process uses that, as I couldn't find that elsewhere in the repo.

I've at least gotten rid of the even older version of jQuery used by the Imagelib extension (it had been using a 1.4.4 copy)...

One approach I've been considering (and I'd like your feedback) is to actually move away from jQuery in places if not entirely, using ES6, DOM enhancements (like ChildNode and ParentNode interfaces), and focused libraries, such as my project [Jamilih](https://github.com/brettz9/jamilih/)--which aims at a straightforward, light-weight approach to building the DOM--there is less need for jQuery.

And no doubt there are some light-weight, single-purpose substitute micro-libraries for traversal, etc. as well (https://github.com/WebReflection/query-result looks interesting to me despite its age). 

There are advantages to jQuery and frameworks, but with the magic can come less focused and breaking version changes, and some bloat, and, as we're experiencing now, difficulty to migrate.

What do people think?
I think this is a duplicate of: https://github.com/SVG-Edit/svgedit/issues/5 - it least it's the same as I describe in my comment. To reiterate here, this is a new since 2.6, although not sure whether it existed in 2.7.

This looks ok for me these days, I can remember the bug though previously. Is there a specific svg to test with (and which browser out of interest?).

Just checking, I'm wondering if its related to this pull request I did a while back.. https://github.com/SVG-Edit/svgedit/pull/7 I don't think it went through as needing some documentation updating, but I'm not so good with git (happy to take advice on what to do here), and can't see how I amend an existing pull request.

I tested by importing http://canvaschanger.project0884.com/sample/sample.svg to a new image canvas from https://svg-edit.github.io/svgedit/releases/svg-edit-2.8.1/svg-editor.html . It is the latest version, isn't it?

Can you try moving it more than once? thus the issue will happen.

Also I tried applying #7 to my local copy, but still my issue is not resolved.

I think this is a duplicate of: https://github.com/SVG-Edit/svgedit/issues/5 - it least it's the same as I describe in my comment. To reiterate here, this is a new since 2.6, although not sure whether it existed in 2.7.

This looks ok for me these days, I can remember the bug though previously. Is there a specific svg to test with (and which browser out of interest?).

Just checking, I'm wondering if its related to this pull request I did a while back.. https://github.com/SVG-Edit/svgedit/pull/7 I don't think it went through as needing some documentation updating, but I'm not so good with git (happy to take advice on what to do here), and can't see how I amend an existing pull request.

I tested by importing http://canvaschanger.project0884.com/sample/sample.svg to a new image canvas from https://svg-edit.github.io/svgedit/releases/svg-edit-2.8.1/svg-editor.html . It is the latest version, isn't it?

Can you try moving it more than once? thus the issue will happen.

Also I tried applying #7 to my local copy, but still my issue is not resolved.

Must admit, this did get me at first too, and still find it a bit unintuitive.

+1. I feel like the app would benefit from having more and more descriptive tooltips throughout the UI. jQuery Tooltip would be a good solution, although I know the bundled copy of jQuery is a little outdated.

On that note, I recently started using the newest version of jQuery with SVG Edit with no problems so far.

I'll be honest, I've forgotten the legalities of SVG-edit... Several of the third-party libraries we use are MIT licensed though, so I think this is ok.

Ok I will need to fork the repo and put in my edits from a clean copy of the base branch. The copy I have is too modified to upload changes directly.

@GoTeamScotch, have you any success with it?
@GoTeamScotch ?
I'll be honest, I've forgotten the legalities of SVG-edit... Several of the third-party libraries we use are MIT licensed though, so I think this is ok.

Ok I will need to fork the repo and put in my edits from a clean copy of the base branch. The copy I have is too modified to upload changes directly.

@GoTeamScotch, have you any success with it?
@GoTeamScotch ?
Filed against webkit in https://bugs.webkit.org/show_bug.cgi?id=151791

Fixed! http://trac.webkit.org/changeset/193613

Sounds like this is no longer an issue, so closing--thanks for the report!
I would expect [htmlpreview.github.io](https://htmlpreview.github.io/?https://raw.github.com/SVG-Edit/svgedit/master/editor/svg-editor.html) to work as well, but apparently there are some issues with the external resources. Just dropping the link here for reference.

Rawgit sounds good... Maybe we'll need to commit the config file after all (so that it'll work out of the box without modification) and just let the user configure it.
Sorry for the delay here - would you mind syncing with our trunk so I can review this?

I've now synced the branch.

Reg. case sensitivity: I think I noticed that the rest of the code was treating it as case insensitive and didn't question it. But I think you're right, in that it should be case sensitive. I've fixed this in the branch now.

Reg. ignoredNS vs. NS_IGNORE: It's currently implemented so namespaces to be ignored can be defined in config.js and this list is merged with that list. So it's not entirely a constant.

Well it was in response to @flintobrien, who pointed out that namespaces in xml and thus svg are case sensitive. It wasn't really part of issue #94, but I decided to fix it, while I was working on that part of it anyways. Adding a case insensitive conf setting, only to later change it, would also be bad.

But these values aren't constants. First of all constants don't actually exist in JS, but we could pretend and that does make sense, in order to "protect" their use, even if only enforced by code reviewers. But these are the keys in the object/hash/dictionary, which the 'constant' is made up of. These keys represent a lower case text, because most namespaces are referred to in lower case. If someone wanted to make a LaTeX namespace, they could and even use that particular casing, in order to avoid confusion with things with other casing. The actual name of the constant is NS and that part is upper case, after all.

Would anyone argue against this? The only argument I don't think I addressed, was that the pull request would be shorter without this change. But that's obviously not a good reason if it's an improvement and even kinda necessary, in order to not have a conf setting, which will be changed at some point.

---

Reg. the security issue of allowing to ignore a full custom namespace, defined in config.js. @distler: I can't see your example working in any way. I couldn't even get html:onload or svg:onload to work in Chrome in an svg, even if onload works. And even if they worked, they'd only work because they were namespaces that the browser recognized. The browser wouldn't know "se" as a namespace and thus wouldn't interpret se:onload and execute it. I'd love to see an example where this could be used in any way for something bad.

I could see an argument for not having "se" in the default ignored/whitelisted namespace list and specifying all allowed attributes. With that particular namespace, the reference implementation is here after all. But a site-defined (read: defined in config.js) namespace, should be possible to have without needing to keep your config.js in sync with the namespace. Particularly if this is a namespace the browser just ignores and leaves as regular ignored attributes (and tags, btw).

Part 1:  The NS.SVG "constants" are more like Java enums.  That's the
idea.  They are symbols.
Part 2:  http://codedread.com/testbed/circle.svg

On Thu, 4 Aug 2016 at 01:25 KB Jørgensen notifications@github.com wrote:

> Well it was in response to @flintobrien https://github.com/flintobrien,
> who pointed out that namespaces in xml and thus svg are case sensitive. It
> wasn't really part of issue #94
> https://github.com/SVG-Edit/svgedit/issues/94, but I decided to fix it,
> while I was working on that part of it anyways. Adding a case insensitive
> conf setting, only to later change it, would also be bad.
> 
> But these values aren't constants. First of all constants don't actually
> exist in JS, but we could pretend and that does make sense, in order to
> "protect" their use, even if only enforced by code reviewers. But these are
> the keys in the object/hash/dictionary, which the 'constant' is made up of.
> These keys represent a lower case text, because most namespaces are
> referred to in lower case. If someone wanted to make a LaTeX namespace,
> they could and even use that particular casing, in order to avoid confusion
> with things with other casing. The actual name of the constant is NS and
> that part is upper case, after all.
> 
> Would anyone argue against this? The only argument I don't think I
> addressed, was that the pull request would be shorter without this change.
> But that's obviously not a good reason if it's an improvement and even
> kinda necessary, in order to not have a conf setting, which will be changed
> 
> ## at some point.
> 
> Reg. the security issue of allowing to ignore a full custom namespace,
> defined in config.js. @distler https://github.com/distler: I can't see
> your example working in any way. I couldn't even get html:onload or
> svg:onload to work in Chrome in an svg, even if onload works. And even if
> they worked, they'd only work because they were namespaces that the browser
> recognized. The browser wouldn't know "se" as a namespace and thus wouldn't
> interpret se:onload and execute it. I'd love to see an example where this
> could be used in any way for something bad.
> 
> I could see an argument for not having "se" in the default
> ignored/whitelisted namespace list and specifying all allowed attributes.
> With that particular namespace, the reference implementation is here after
> all. But a site-defined (read: defined in config.js) namespace, should be
> possible to have without needing to keep your config.js in sync with the
> namespace. Particularly if this is a namespace the browser just ignores and
> leaves as regular ignored attributes (and tags, btw).
> 
> —
> You are receiving this because you commented.
> Reply to this email directly, view it on GitHub
> https://github.com/SVG-Edit/svgedit/pull/97#issuecomment-237486715, or mute
> the thread
> https://github.com/notifications/unsubscribe-auth/AAGIDO6nVVvF-tLefvkQW2LjTksb8aXgks5qcaHwgaJpZM4H3I7m
> .

FWIW, I don't care too strongly about the upper-case part and I didn't look
very closely at your patch.  I acknowledge that making them lowercase might
have made something easier that I don't quite understand.

On Thu, 4 Aug 2016 at 01:39 Jeff Schiller codedread@gmail.com wrote:

> Part 1:  The NS.SVG "constants" are more like Java enums.  That's the
> idea.  They are symbols.
> Part 2:  http://codedread.com/testbed/circle.svg
> 
> On Thu, 4 Aug 2016 at 01:25 KB Jørgensen notifications@github.com wrote:
> 
> > Well it was in response to @flintobrien https://github.com/flintobrien,
> > who pointed out that namespaces in xml and thus svg are case sensitive. It
> > wasn't really part of issue #94
> > https://github.com/SVG-Edit/svgedit/issues/94, but I decided to fix
> > it, while I was working on that part of it anyways. Adding a case
> > insensitive conf setting, only to later change it, would also be bad.
> > 
> > But these values aren't constants. First of all constants don't actually
> > exist in JS, but we could pretend and that does make sense, in order to
> > "protect" their use, even if only enforced by code reviewers. But these are
> > the keys in the object/hash/dictionary, which the 'constant' is made up of.
> > These keys represent a lower case text, because most namespaces are
> > referred to in lower case. If someone wanted to make a LaTeX namespace,
> > they could and even use that particular casing, in order to avoid confusion
> > with things with other casing. The actual name of the constant is NS and
> > that part is upper case, after all.
> > 
> > Would anyone argue against this? The only argument I don't think I
> > addressed, was that the pull request would be shorter without this change.
> > But that's obviously not a good reason if it's an improvement and even
> > kinda necessary, in order to not have a conf setting, which will be changed
> > 
> > ## at some point.
> > 
> > Reg. the security issue of allowing to ignore a full custom namespace,
> > defined in config.js. @distler https://github.com/distler: I can't see
> > your example working in any way. I couldn't even get html:onload or
> > svg:onload to work in Chrome in an svg, even if onload works. And even if
> > they worked, they'd only work because they were namespaces that the browser
> > recognized. The browser wouldn't know "se" as a namespace and thus wouldn't
> > interpret se:onload and execute it. I'd love to see an example where this
> > could be used in any way for something bad.
> > 
> > I could see an argument for not having "se" in the default
> > ignored/whitelisted namespace list and specifying all allowed attributes.
> > With that particular namespace, the reference implementation is here after
> > all. But a site-defined (read: defined in config.js) namespace, should be
> > possible to have without needing to keep your config.js in sync with the
> > namespace. Particularly if this is a namespace the browser just ignores and
> > leaves as regular ignored attributes (and tags, btw).
> > 
> > —
> > You are receiving this because you commented.
> > Reply to this email directly, view it on GitHub
> > https://github.com/SVG-Edit/svgedit/pull/97#issuecomment-237486715, or mute
> > the thread
> > https://github.com/notifications/unsubscribe-auth/AAGIDO6nVVvF-tLefvkQW2LjTksb8aXgks5qcaHwgaJpZM4H3I7m
> > .

@codedread: You're right. The url for the namespace is the part which identifies how it should be interpreted in the browser or if it should just be ignored. Because the url relates to the definition and html/svg/se are just names or aliases locally in the xml document. So as I said, the html and svg namespaces (defined by the URLs) can invoke scripting. Even if they're called "se" in the svg document. If you define xmlns:html="https://github.com/SVG-Edit/svgedit" in svg, the html:script tag won't work, but set it to xmlns:html="http://www.w3.org/1999/xhtml" and suddenly it works again.

So I did make a mistake. Thanks for guiding me there. I'm gonna change it to allow namespaces based on the url. This means we could allow them regardless of their local name/alias in the svg. Which also means, we could keep the name for them in the NS file as before and just have an array of site-defined namespace URLs to ignore, regardless of their namespace alias in the svg.

What do you say? Isn't that the right way? Should i revert the namespace names, so they're back to being in upper case? They'll be ignored in the actual code usage anyways and only used to refer to which is which.

I've now reverted the change to the variables, so the namespace prefix/alias defined for the built-in or whitelisted namespaces are all upper-case again. I've also moved the se namespace back to this list.

And now the ignored namespaces are detected by the URI of the namespaces (a.k.a. their definition) and not their prefix/alias. Which means you can ignore https://example.com/my-personal-extension regardless of what you call it. You could define it with xmlns:foo and then have foo:bar nodes/attributes. You can also define it with xmlns:html and have html:script, but if you've defined it to use a namespace that the browser doesn't recognize, it won't interpret it as script or anything else. So this isn't a security problem that way. And it's only allowed if it's set in the config.js file.

I think the built-in whitelist should be uri-based instead of alias-based as well. But I didn't include that here, to avoid scope creep in this pull request.

So are there any comments, issues, concerns or anything else about the pull request now? Did i miss something? I'm sorry it ended up being a messy bunch of commits. The end result is important though.

Thanks KB - I've been pretty busy to look at this.  If Jacques or others
have time, that'd be great - otherwise I'm likely to get to this by the
weekend...

On Mon, 8 Aug 2016 at 02:39 KB Jørgensen notifications@github.com wrote:

> I've now reverted the change to the variables, so the namespace
> prefix/alias defined for the built-in or whitelisted namespaces are all
> upper-case again. I've also moved the se namespace back to this list.
> 
> And now the ignored namespaces are detected by the URI of the namespaces
> (a.k.a. their definition) and not their prefix/alias. Which means you can
> ignore https://example.com/my-personal-extension regardless of what you
> call it. You could define it with xmlns:foo and then have foo:bar
> nodes/attributes. You can also define it with xmlns:html and have
> html:script, but if you've defined it to use a namespace that the browser
> doesn't recognize, it won't interpret it as script or anything else. So
> this isn't a security problem that way. And it's only allowed if it's set
> in the config.js file.
> 
> I think the built-in whitelist should be uri-based instead of alias-based
> as well. But I didn't include that here, to avoid scope creep in this pull
> request.
> 
> So are there any comments, issues, concerns or anything else about the
> pull request now? Did i miss something? I'm sorry it ended up being a messy
> bunch of commits. The end result is important though.
> 
> —
> You are receiving this because you were mentioned.
> 
> Reply to this email directly, view it on GitHub
> https://github.com/SVG-Edit/svgedit/pull/97#issuecomment-238188520, or mute
> the thread
> https://github.com/notifications/unsubscribe-auth/AAGIDC0Kz25Lho-bckAKdasys5-c3bddks5qdvlkgaJpZM4H3I7m
> .

Great. Thank you.

I've made a small change to support nodes with a custom namespace too. It will now treat any nodes with ignored namespaces as a group you can't ungroup and any svg elements in it will be shown and aren't editable.

I noticed an issue in resizing elements with custom namespaces. It should work better now.

I've also fixed an issue in ext-connector.js for this feature. This included a side-effect of improving/simplifying getBBoxWithTransform() quite a bit.

I tried to use the code from this tree and i see that there are different exceptions that are thrown.
svgutils.js:495 Uncaught TypeError: Cannot use 'in' operator to search for 'null' in undefined

if(elname === 'use' || elem.prefix in svgedit.ignoredNSUsedAlias) 

Is there some thing this is missed during checin process?
![image](https://cloud.githubusercontent.com/assets/19921843/20728142/65e2b0ca-b6a2-11e6-9415-bd6565c68724.png)

@evssraghavendra: This is now fixed in the latest commit. The svgedit.ignoredNSUsedAlias was only initialized in sanitizeSvg(), which is run when you load a drawing programmatically with setSvgString(), but not when opening a new drawing. It's now initialized when the drawing is initialized. This should cover the bases.
What is the mechanism to discuss about this tool other than logging an issue.
This is the pull request for this implementation. So if you want to discuss this implementation, it would be here. For the generic problem that this is trying to solve, the issue is issue https://github.com/SVG-Edit/svgedit/issues/94.
I found out that the custom namespaces weren't applied to nodes properly, which is why they were visible in the browser. I've now removed the faulty handling of these nodes, since if it's not visible, it's not clickable. So now both nodes and attributes from custom namespaces are safely ignored by svg-edit.

Then i've added a new se attribute called se:external, which you can set to "1" on a g node. If this is set, the group won't be ungroup-able and transforms won't be applied to children of the group. This is so that you can have groups that are treated as a regular element that you can set your own toolbar/etc. type controls for. And then you can use regular svg elements as its children, to make it display as you want in svg-edit.
Is there anything holding this pull request back? Are there any issues or concerns with it?
I'd need to test, but from my perspective, the thing holding it back is it unfortunately it needing rebasing so we can test again... That should greatly expedite the chances and speed by which I may be able to review and pull...
Hello, I've been recently added to the maintainer team. I see that this PR is quite old, had many commits but was never merged in the master branch. I think we can put some efforts into this PR but I would like to know if @iuyiuy is still interested and willing to help as he was the main (actually the only) contributor to this PR?
I don't recall whether the security issues raised were satisfactorily addressed. If you decide to resurrect this PR, that's something to check carefully.
Must admit, this did get me at first too, and still find it a bit unintuitive.

+1. I feel like the app would benefit from having more and more descriptive tooltips throughout the UI. jQuery Tooltip would be a good solution, although I know the bundled copy of jQuery is a little outdated.

On that note, I recently started using the newest version of jQuery with SVG Edit with no problems so far.

Must admit, this did get me at first too, and still find it a bit unintuitive.

+1. I feel like the app would benefit from having more and more descriptive tooltips throughout the UI. jQuery Tooltip would be a good solution, although I know the bundled copy of jQuery is a little outdated.

On that note, I recently started using the newest version of jQuery with SVG Edit with no problems so far.

Nice! Thanks for the quick fix

Nice! Thanks for the quick fix

I would expect [htmlpreview.github.io](https://htmlpreview.github.io/?https://raw.github.com/SVG-Edit/svgedit/master/editor/svg-editor.html) to work as well, but apparently there are some issues with the external resources. Just dropping the link here for reference.

Rawgit sounds good... Maybe we'll need to commit the config file after all (so that it'll work out of the box without modification) and just let the user configure it.
Nice! Thanks for the quick fix

Nice! Thanks for the quick fix

Nice! Thanks for the quick fix

Nice! Thanks for the quick fix

Nice! Thanks for the quick fix

Sorry for the delay here - would you mind syncing with our trunk so I can review this?

I've now synced the branch.

Reg. case sensitivity: I think I noticed that the rest of the code was treating it as case insensitive and didn't question it. But I think you're right, in that it should be case sensitive. I've fixed this in the branch now.

Reg. ignoredNS vs. NS_IGNORE: It's currently implemented so namespaces to be ignored can be defined in config.js and this list is merged with that list. So it's not entirely a constant.

Well it was in response to @flintobrien, who pointed out that namespaces in xml and thus svg are case sensitive. It wasn't really part of issue #94, but I decided to fix it, while I was working on that part of it anyways. Adding a case insensitive conf setting, only to later change it, would also be bad.

But these values aren't constants. First of all constants don't actually exist in JS, but we could pretend and that does make sense, in order to "protect" their use, even if only enforced by code reviewers. But these are the keys in the object/hash/dictionary, which the 'constant' is made up of. These keys represent a lower case text, because most namespaces are referred to in lower case. If someone wanted to make a LaTeX namespace, they could and even use that particular casing, in order to avoid confusion with things with other casing. The actual name of the constant is NS and that part is upper case, after all.

Would anyone argue against this? The only argument I don't think I addressed, was that the pull request would be shorter without this change. But that's obviously not a good reason if it's an improvement and even kinda necessary, in order to not have a conf setting, which will be changed at some point.

---

Reg. the security issue of allowing to ignore a full custom namespace, defined in config.js. @distler: I can't see your example working in any way. I couldn't even get html:onload or svg:onload to work in Chrome in an svg, even if onload works. And even if they worked, they'd only work because they were namespaces that the browser recognized. The browser wouldn't know "se" as a namespace and thus wouldn't interpret se:onload and execute it. I'd love to see an example where this could be used in any way for something bad.

I could see an argument for not having "se" in the default ignored/whitelisted namespace list and specifying all allowed attributes. With that particular namespace, the reference implementation is here after all. But a site-defined (read: defined in config.js) namespace, should be possible to have without needing to keep your config.js in sync with the namespace. Particularly if this is a namespace the browser just ignores and leaves as regular ignored attributes (and tags, btw).

Part 1:  The NS.SVG "constants" are more like Java enums.  That's the
idea.  They are symbols.
Part 2:  http://codedread.com/testbed/circle.svg

On Thu, 4 Aug 2016 at 01:25 KB Jørgensen notifications@github.com wrote:

> Well it was in response to @flintobrien https://github.com/flintobrien,
> who pointed out that namespaces in xml and thus svg are case sensitive. It
> wasn't really part of issue #94
> https://github.com/SVG-Edit/svgedit/issues/94, but I decided to fix it,
> while I was working on that part of it anyways. Adding a case insensitive
> conf setting, only to later change it, would also be bad.
> 
> But these values aren't constants. First of all constants don't actually
> exist in JS, but we could pretend and that does make sense, in order to
> "protect" their use, even if only enforced by code reviewers. But these are
> the keys in the object/hash/dictionary, which the 'constant' is made up of.
> These keys represent a lower case text, because most namespaces are
> referred to in lower case. If someone wanted to make a LaTeX namespace,
> they could and even use that particular casing, in order to avoid confusion
> with things with other casing. The actual name of the constant is NS and
> that part is upper case, after all.
> 
> Would anyone argue against this? The only argument I don't think I
> addressed, was that the pull request would be shorter without this change.
> But that's obviously not a good reason if it's an improvement and even
> kinda necessary, in order to not have a conf setting, which will be changed
> 
> ## at some point.
> 
> Reg. the security issue of allowing to ignore a full custom namespace,
> defined in config.js. @distler https://github.com/distler: I can't see
> your example working in any way. I couldn't even get html:onload or
> svg:onload to work in Chrome in an svg, even if onload works. And even if
> they worked, they'd only work because they were namespaces that the browser
> recognized. The browser wouldn't know "se" as a namespace and thus wouldn't
> interpret se:onload and execute it. I'd love to see an example where this
> could be used in any way for something bad.
> 
> I could see an argument for not having "se" in the default
> ignored/whitelisted namespace list and specifying all allowed attributes.
> With that particular namespace, the reference implementation is here after
> all. But a site-defined (read: defined in config.js) namespace, should be
> possible to have without needing to keep your config.js in sync with the
> namespace. Particularly if this is a namespace the browser just ignores and
> leaves as regular ignored attributes (and tags, btw).
> 
> —
> You are receiving this because you commented.
> Reply to this email directly, view it on GitHub
> https://github.com/SVG-Edit/svgedit/pull/97#issuecomment-237486715, or mute
> the thread
> https://github.com/notifications/unsubscribe-auth/AAGIDO6nVVvF-tLefvkQW2LjTksb8aXgks5qcaHwgaJpZM4H3I7m
> .

FWIW, I don't care too strongly about the upper-case part and I didn't look
very closely at your patch.  I acknowledge that making them lowercase might
have made something easier that I don't quite understand.

On Thu, 4 Aug 2016 at 01:39 Jeff Schiller codedread@gmail.com wrote:

> Part 1:  The NS.SVG "constants" are more like Java enums.  That's the
> idea.  They are symbols.
> Part 2:  http://codedread.com/testbed/circle.svg
> 
> On Thu, 4 Aug 2016 at 01:25 KB Jørgensen notifications@github.com wrote:
> 
> > Well it was in response to @flintobrien https://github.com/flintobrien,
> > who pointed out that namespaces in xml and thus svg are case sensitive. It
> > wasn't really part of issue #94
> > https://github.com/SVG-Edit/svgedit/issues/94, but I decided to fix
> > it, while I was working on that part of it anyways. Adding a case
> > insensitive conf setting, only to later change it, would also be bad.
> > 
> > But these values aren't constants. First of all constants don't actually
> > exist in JS, but we could pretend and that does make sense, in order to
> > "protect" their use, even if only enforced by code reviewers. But these are
> > the keys in the object/hash/dictionary, which the 'constant' is made up of.
> > These keys represent a lower case text, because most namespaces are
> > referred to in lower case. If someone wanted to make a LaTeX namespace,
> > they could and even use that particular casing, in order to avoid confusion
> > with things with other casing. The actual name of the constant is NS and
> > that part is upper case, after all.
> > 
> > Would anyone argue against this? The only argument I don't think I
> > addressed, was that the pull request would be shorter without this change.
> > But that's obviously not a good reason if it's an improvement and even
> > kinda necessary, in order to not have a conf setting, which will be changed
> > 
> > ## at some point.
> > 
> > Reg. the security issue of allowing to ignore a full custom namespace,
> > defined in config.js. @distler https://github.com/distler: I can't see
> > your example working in any way. I couldn't even get html:onload or
> > svg:onload to work in Chrome in an svg, even if onload works. And even if
> > they worked, they'd only work because they were namespaces that the browser
> > recognized. The browser wouldn't know "se" as a namespace and thus wouldn't
> > interpret se:onload and execute it. I'd love to see an example where this
> > could be used in any way for something bad.
> > 
> > I could see an argument for not having "se" in the default
> > ignored/whitelisted namespace list and specifying all allowed attributes.
> > With that particular namespace, the reference implementation is here after
> > all. But a site-defined (read: defined in config.js) namespace, should be
> > possible to have without needing to keep your config.js in sync with the
> > namespace. Particularly if this is a namespace the browser just ignores and
> > leaves as regular ignored attributes (and tags, btw).
> > 
> > —
> > You are receiving this because you commented.
> > Reply to this email directly, view it on GitHub
> > https://github.com/SVG-Edit/svgedit/pull/97#issuecomment-237486715, or mute
> > the thread
> > https://github.com/notifications/unsubscribe-auth/AAGIDO6nVVvF-tLefvkQW2LjTksb8aXgks5qcaHwgaJpZM4H3I7m
> > .

@codedread: You're right. The url for the namespace is the part which identifies how it should be interpreted in the browser or if it should just be ignored. Because the url relates to the definition and html/svg/se are just names or aliases locally in the xml document. So as I said, the html and svg namespaces (defined by the URLs) can invoke scripting. Even if they're called "se" in the svg document. If you define xmlns:html="https://github.com/SVG-Edit/svgedit" in svg, the html:script tag won't work, but set it to xmlns:html="http://www.w3.org/1999/xhtml" and suddenly it works again.

So I did make a mistake. Thanks for guiding me there. I'm gonna change it to allow namespaces based on the url. This means we could allow them regardless of their local name/alias in the svg. Which also means, we could keep the name for them in the NS file as before and just have an array of site-defined namespace URLs to ignore, regardless of their namespace alias in the svg.

What do you say? Isn't that the right way? Should i revert the namespace names, so they're back to being in upper case? They'll be ignored in the actual code usage anyways and only used to refer to which is which.

I've now reverted the change to the variables, so the namespace prefix/alias defined for the built-in or whitelisted namespaces are all upper-case again. I've also moved the se namespace back to this list.

And now the ignored namespaces are detected by the URI of the namespaces (a.k.a. their definition) and not their prefix/alias. Which means you can ignore https://example.com/my-personal-extension regardless of what you call it. You could define it with xmlns:foo and then have foo:bar nodes/attributes. You can also define it with xmlns:html and have html:script, but if you've defined it to use a namespace that the browser doesn't recognize, it won't interpret it as script or anything else. So this isn't a security problem that way. And it's only allowed if it's set in the config.js file.

I think the built-in whitelist should be uri-based instead of alias-based as well. But I didn't include that here, to avoid scope creep in this pull request.

So are there any comments, issues, concerns or anything else about the pull request now? Did i miss something? I'm sorry it ended up being a messy bunch of commits. The end result is important though.

Thanks KB - I've been pretty busy to look at this.  If Jacques or others
have time, that'd be great - otherwise I'm likely to get to this by the
weekend...

On Mon, 8 Aug 2016 at 02:39 KB Jørgensen notifications@github.com wrote:

> I've now reverted the change to the variables, so the namespace
> prefix/alias defined for the built-in or whitelisted namespaces are all
> upper-case again. I've also moved the se namespace back to this list.
> 
> And now the ignored namespaces are detected by the URI of the namespaces
> (a.k.a. their definition) and not their prefix/alias. Which means you can
> ignore https://example.com/my-personal-extension regardless of what you
> call it. You could define it with xmlns:foo and then have foo:bar
> nodes/attributes. You can also define it with xmlns:html and have
> html:script, but if you've defined it to use a namespace that the browser
> doesn't recognize, it won't interpret it as script or anything else. So
> this isn't a security problem that way. And it's only allowed if it's set
> in the config.js file.
> 
> I think the built-in whitelist should be uri-based instead of alias-based
> as well. But I didn't include that here, to avoid scope creep in this pull
> request.
> 
> So are there any comments, issues, concerns or anything else about the
> pull request now? Did i miss something? I'm sorry it ended up being a messy
> bunch of commits. The end result is important though.
> 
> —
> You are receiving this because you were mentioned.
> 
> Reply to this email directly, view it on GitHub
> https://github.com/SVG-Edit/svgedit/pull/97#issuecomment-238188520, or mute
> the thread
> https://github.com/notifications/unsubscribe-auth/AAGIDC0Kz25Lho-bckAKdasys5-c3bddks5qdvlkgaJpZM4H3I7m
> .

Great. Thank you.

I've made a small change to support nodes with a custom namespace too. It will now treat any nodes with ignored namespaces as a group you can't ungroup and any svg elements in it will be shown and aren't editable.

I noticed an issue in resizing elements with custom namespaces. It should work better now.

I've also fixed an issue in ext-connector.js for this feature. This included a side-effect of improving/simplifying getBBoxWithTransform() quite a bit.

I tried to use the code from this tree and i see that there are different exceptions that are thrown.
svgutils.js:495 Uncaught TypeError: Cannot use 'in' operator to search for 'null' in undefined

if(elname === 'use' || elem.prefix in svgedit.ignoredNSUsedAlias) 

Is there some thing this is missed during checin process?
![image](https://cloud.githubusercontent.com/assets/19921843/20728142/65e2b0ca-b6a2-11e6-9415-bd6565c68724.png)

@evssraghavendra: This is now fixed in the latest commit. The svgedit.ignoredNSUsedAlias was only initialized in sanitizeSvg(), which is run when you load a drawing programmatically with setSvgString(), but not when opening a new drawing. It's now initialized when the drawing is initialized. This should cover the bases.
What is the mechanism to discuss about this tool other than logging an issue.
This is the pull request for this implementation. So if you want to discuss this implementation, it would be here. For the generic problem that this is trying to solve, the issue is issue https://github.com/SVG-Edit/svgedit/issues/94.
I found out that the custom namespaces weren't applied to nodes properly, which is why they were visible in the browser. I've now removed the faulty handling of these nodes, since if it's not visible, it's not clickable. So now both nodes and attributes from custom namespaces are safely ignored by svg-edit.

Then i've added a new se attribute called se:external, which you can set to "1" on a g node. If this is set, the group won't be ungroup-able and transforms won't be applied to children of the group. This is so that you can have groups that are treated as a regular element that you can set your own toolbar/etc. type controls for. And then you can use regular svg elements as its children, to make it display as you want in svg-edit.
Is there anything holding this pull request back? Are there any issues or concerns with it?
I'd need to test, but from my perspective, the thing holding it back is it unfortunately it needing rebasing so we can test again... That should greatly expedite the chances and speed by which I may be able to review and pull...
Hello, I've been recently added to the maintainer team. I see that this PR is quite old, had many commits but was never merged in the master branch. I think we can put some efforts into this PR but I would like to know if @iuyiuy is still interested and willing to help as he was the main (actually the only) contributor to this PR?
I don't recall whether the security issues raised were satisfactorily addressed. If you decide to resurrect this PR, that's something to check carefully.
What do you see as not modular about the extensions system currently?
Closing due to lack of response. Feel free to comment further or reopen to provide more detail on your request. We have refactored the main code to use ES6 modules. We have some documentation to-dos, however, including for our new extension format. Feel free to check out the new release when it may be ready.
It's not a drop-in replacement. A few areas in SVG Edit would need to be modified. I just tried it and it seems to break the app because of javascript errors. Some research would need to be done to find the new function names and update the code to reflect those and other changes to contextMenu. That being said, it's probably more work than its worth. That old saying comes to mind: if it ain't broke, don't fix it.

Upgrading to the latest version would require an update to jQuery too as requested in issue #52 
Regarding jsPDF, per https://github.com/SVG-Edit/svgedit/issues/269#issuecomment-410118903 , we might take a look at https://github.com/alafr/SVG-to-PDFKit or per https://github.com/SVG-Edit/svgedit/issues/254#issuecomment-414483981 , see https://github.com/devongovett/svgkit or https://github.com/foliojs/pdfkit

Regarding hotkeys, though there is this [version](https://github.com/jeresig/jquery.hotkeys), we might look at this non-jQuery alternative: https://github.com/jaywcjlove/hotkeys
Are you saying that in SVG-edit you see the border?  Just tried your file on Chrome OSX and I don't see a border at all.

Could this be a border with the Android Drawables converter because we still have the stroke="#000000" attribute in there?

On the other hands, having those "null" values and stroke values that are not needed are not a great idea.  We should clean that up when doing the final sanitizing upon export...

by removing all stroke related things here, and then converting the file, I got the border removed. 
I think a color of "#000000"  is black. not transparent. 
And maybe the other values don't mean anything there. I'm not an expert in SVG and VectorDrawable (which is relatively very new).

I think that if the stroke-width is set to 0, it should remove any stroke related attribute.

Agreed - feel free to send along a patch :)

I know (almost) nothing about HTML5 and SVG. Sorry.
That's why I wrote about this issue here...

No worries and no pressure.  That's just the standard open source response for "acknowledged bug, but don't have time to work on it right now" :-)

Are you saying that in SVG-edit you see the border?  Just tried your file on Chrome OSX and I don't see a border at all.

Could this be a border with the Android Drawables converter because we still have the stroke="#000000" attribute in there?

On the other hands, having those "null" values and stroke values that are not needed are not a great idea.  We should clean that up when doing the final sanitizing upon export...

by removing all stroke related things here, and then converting the file, I got the border removed. 
I think a color of "#000000"  is black. not transparent. 
And maybe the other values don't mean anything there. I'm not an expert in SVG and VectorDrawable (which is relatively very new).

I think that if the stroke-width is set to 0, it should remove any stroke related attribute.

Agreed - feel free to send along a patch :)

I know (almost) nothing about HTML5 and SVG. Sorry.
That's why I wrote about this issue here...

No worries and no pressure.  That's just the standard open source response for "acknowledged bug, but don't have time to work on it right now" :-)

Able to reproduce by refreshing page... Serialization for or deserialization from storage must be problematic...
Able to reproduce by refreshing page... Serialization for or deserialization from storage must be problematic...








This pull request looks good but can you please add a better description? Something that describes why restoring the defs is the correct thing to do in this case.

When pasting an element, we must make sure any defs which are referenced by the element is actually present. In some cases the defs might have been deleted (as described in #82) so we call restoreRefElems to restore any deleted defs referenced by the element.

Thanks!

Sorry for the delay here - would you mind syncing with our trunk so I can review this?

I've now synced the branch.

Reg. case sensitivity: I think I noticed that the rest of the code was treating it as case insensitive and didn't question it. But I think you're right, in that it should be case sensitive. I've fixed this in the branch now.

Reg. ignoredNS vs. NS_IGNORE: It's currently implemented so namespaces to be ignored can be defined in config.js and this list is merged with that list. So it's not entirely a constant.

Well it was in response to @flintobrien, who pointed out that namespaces in xml and thus svg are case sensitive. It wasn't really part of issue #94, but I decided to fix it, while I was working on that part of it anyways. Adding a case insensitive conf setting, only to later change it, would also be bad.

But these values aren't constants. First of all constants don't actually exist in JS, but we could pretend and that does make sense, in order to "protect" their use, even if only enforced by code reviewers. But these are the keys in the object/hash/dictionary, which the 'constant' is made up of. These keys represent a lower case text, because most namespaces are referred to in lower case. If someone wanted to make a LaTeX namespace, they could and even use that particular casing, in order to avoid confusion with things with other casing. The actual name of the constant is NS and that part is upper case, after all.

Would anyone argue against this? The only argument I don't think I addressed, was that the pull request would be shorter without this change. But that's obviously not a good reason if it's an improvement and even kinda necessary, in order to not have a conf setting, which will be changed at some point.

---

Reg. the security issue of allowing to ignore a full custom namespace, defined in config.js. @distler: I can't see your example working in any way. I couldn't even get html:onload or svg:onload to work in Chrome in an svg, even if onload works. And even if they worked, they'd only work because they were namespaces that the browser recognized. The browser wouldn't know "se" as a namespace and thus wouldn't interpret se:onload and execute it. I'd love to see an example where this could be used in any way for something bad.

I could see an argument for not having "se" in the default ignored/whitelisted namespace list and specifying all allowed attributes. With that particular namespace, the reference implementation is here after all. But a site-defined (read: defined in config.js) namespace, should be possible to have without needing to keep your config.js in sync with the namespace. Particularly if this is a namespace the browser just ignores and leaves as regular ignored attributes (and tags, btw).

Part 1:  The NS.SVG "constants" are more like Java enums.  That's the
idea.  They are symbols.
Part 2:  http://codedread.com/testbed/circle.svg

On Thu, 4 Aug 2016 at 01:25 KB Jørgensen notifications@github.com wrote:

> Well it was in response to @flintobrien https://github.com/flintobrien,
> who pointed out that namespaces in xml and thus svg are case sensitive. It
> wasn't really part of issue #94
> https://github.com/SVG-Edit/svgedit/issues/94, but I decided to fix it,
> while I was working on that part of it anyways. Adding a case insensitive
> conf setting, only to later change it, would also be bad.
> 
> But these values aren't constants. First of all constants don't actually
> exist in JS, but we could pretend and that does make sense, in order to
> "protect" their use, even if only enforced by code reviewers. But these are
> the keys in the object/hash/dictionary, which the 'constant' is made up of.
> These keys represent a lower case text, because most namespaces are
> referred to in lower case. If someone wanted to make a LaTeX namespace,
> they could and even use that particular casing, in order to avoid confusion
> with things with other casing. The actual name of the constant is NS and
> that part is upper case, after all.
> 
> Would anyone argue against this? The only argument I don't think I
> addressed, was that the pull request would be shorter without this change.
> But that's obviously not a good reason if it's an improvement and even
> kinda necessary, in order to not have a conf setting, which will be changed
> 
> ## at some point.
> 
> Reg. the security issue of allowing to ignore a full custom namespace,
> defined in config.js. @distler https://github.com/distler: I can't see
> your example working in any way. I couldn't even get html:onload or
> svg:onload to work in Chrome in an svg, even if onload works. And even if
> they worked, they'd only work because they were namespaces that the browser
> recognized. The browser wouldn't know "se" as a namespace and thus wouldn't
> interpret se:onload and execute it. I'd love to see an example where this
> could be used in any way for something bad.
> 
> I could see an argument for not having "se" in the default
> ignored/whitelisted namespace list and specifying all allowed attributes.
> With that particular namespace, the reference implementation is here after
> all. But a site-defined (read: defined in config.js) namespace, should be
> possible to have without needing to keep your config.js in sync with the
> namespace. Particularly if this is a namespace the browser just ignores and
> leaves as regular ignored attributes (and tags, btw).
> 
> —
> You are receiving this because you commented.
> Reply to this email directly, view it on GitHub
> https://github.com/SVG-Edit/svgedit/pull/97#issuecomment-237486715, or mute
> the thread
> https://github.com/notifications/unsubscribe-auth/AAGIDO6nVVvF-tLefvkQW2LjTksb8aXgks5qcaHwgaJpZM4H3I7m
> .

FWIW, I don't care too strongly about the upper-case part and I didn't look
very closely at your patch.  I acknowledge that making them lowercase might
have made something easier that I don't quite understand.

On Thu, 4 Aug 2016 at 01:39 Jeff Schiller codedread@gmail.com wrote:

> Part 1:  The NS.SVG "constants" are more like Java enums.  That's the
> idea.  They are symbols.
> Part 2:  http://codedread.com/testbed/circle.svg
> 
> On Thu, 4 Aug 2016 at 01:25 KB Jørgensen notifications@github.com wrote:
> 
> > Well it was in response to @flintobrien https://github.com/flintobrien,
> > who pointed out that namespaces in xml and thus svg are case sensitive. It
> > wasn't really part of issue #94
> > https://github.com/SVG-Edit/svgedit/issues/94, but I decided to fix
> > it, while I was working on that part of it anyways. Adding a case
> > insensitive conf setting, only to later change it, would also be bad.
> > 
> > But these values aren't constants. First of all constants don't actually
> > exist in JS, but we could pretend and that does make sense, in order to
> > "protect" their use, even if only enforced by code reviewers. But these are
> > the keys in the object/hash/dictionary, which the 'constant' is made up of.
> > These keys represent a lower case text, because most namespaces are
> > referred to in lower case. If someone wanted to make a LaTeX namespace,
> > they could and even use that particular casing, in order to avoid confusion
> > with things with other casing. The actual name of the constant is NS and
> > that part is upper case, after all.
> > 
> > Would anyone argue against this? The only argument I don't think I
> > addressed, was that the pull request would be shorter without this change.
> > But that's obviously not a good reason if it's an improvement and even
> > kinda necessary, in order to not have a conf setting, which will be changed
> > 
> > ## at some point.
> > 
> > Reg. the security issue of allowing to ignore a full custom namespace,
> > defined in config.js. @distler https://github.com/distler: I can't see
> > your example working in any way. I couldn't even get html:onload or
> > svg:onload to work in Chrome in an svg, even if onload works. And even if
> > they worked, they'd only work because they were namespaces that the browser
> > recognized. The browser wouldn't know "se" as a namespace and thus wouldn't
> > interpret se:onload and execute it. I'd love to see an example where this
> > could be used in any way for something bad.
> > 
> > I could see an argument for not having "se" in the default
> > ignored/whitelisted namespace list and specifying all allowed attributes.
> > With that particular namespace, the reference implementation is here after
> > all. But a site-defined (read: defined in config.js) namespace, should be
> > possible to have without needing to keep your config.js in sync with the
> > namespace. Particularly if this is a namespace the browser just ignores and
> > leaves as regular ignored attributes (and tags, btw).
> > 
> > —
> > You are receiving this because you commented.
> > Reply to this email directly, view it on GitHub
> > https://github.com/SVG-Edit/svgedit/pull/97#issuecomment-237486715, or mute
> > the thread
> > https://github.com/notifications/unsubscribe-auth/AAGIDO6nVVvF-tLefvkQW2LjTksb8aXgks5qcaHwgaJpZM4H3I7m
> > .

@codedread: You're right. The url for the namespace is the part which identifies how it should be interpreted in the browser or if it should just be ignored. Because the url relates to the definition and html/svg/se are just names or aliases locally in the xml document. So as I said, the html and svg namespaces (defined by the URLs) can invoke scripting. Even if they're called "se" in the svg document. If you define xmlns:html="https://github.com/SVG-Edit/svgedit" in svg, the html:script tag won't work, but set it to xmlns:html="http://www.w3.org/1999/xhtml" and suddenly it works again.

So I did make a mistake. Thanks for guiding me there. I'm gonna change it to allow namespaces based on the url. This means we could allow them regardless of their local name/alias in the svg. Which also means, we could keep the name for them in the NS file as before and just have an array of site-defined namespace URLs to ignore, regardless of their namespace alias in the svg.

What do you say? Isn't that the right way? Should i revert the namespace names, so they're back to being in upper case? They'll be ignored in the actual code usage anyways and only used to refer to which is which.

I've now reverted the change to the variables, so the namespace prefix/alias defined for the built-in or whitelisted namespaces are all upper-case again. I've also moved the se namespace back to this list.

And now the ignored namespaces are detected by the URI of the namespaces (a.k.a. their definition) and not their prefix/alias. Which means you can ignore https://example.com/my-personal-extension regardless of what you call it. You could define it with xmlns:foo and then have foo:bar nodes/attributes. You can also define it with xmlns:html and have html:script, but if you've defined it to use a namespace that the browser doesn't recognize, it won't interpret it as script or anything else. So this isn't a security problem that way. And it's only allowed if it's set in the config.js file.

I think the built-in whitelist should be uri-based instead of alias-based as well. But I didn't include that here, to avoid scope creep in this pull request.

So are there any comments, issues, concerns or anything else about the pull request now? Did i miss something? I'm sorry it ended up being a messy bunch of commits. The end result is important though.

Thanks KB - I've been pretty busy to look at this.  If Jacques or others
have time, that'd be great - otherwise I'm likely to get to this by the
weekend...

On Mon, 8 Aug 2016 at 02:39 KB Jørgensen notifications@github.com wrote:

> I've now reverted the change to the variables, so the namespace
> prefix/alias defined for the built-in or whitelisted namespaces are all
> upper-case again. I've also moved the se namespace back to this list.
> 
> And now the ignored namespaces are detected by the URI of the namespaces
> (a.k.a. their definition) and not their prefix/alias. Which means you can
> ignore https://example.com/my-personal-extension regardless of what you
> call it. You could define it with xmlns:foo and then have foo:bar
> nodes/attributes. You can also define it with xmlns:html and have
> html:script, but if you've defined it to use a namespace that the browser
> doesn't recognize, it won't interpret it as script or anything else. So
> this isn't a security problem that way. And it's only allowed if it's set
> in the config.js file.
> 
> I think the built-in whitelist should be uri-based instead of alias-based
> as well. But I didn't include that here, to avoid scope creep in this pull
> request.
> 
> So are there any comments, issues, concerns or anything else about the
> pull request now? Did i miss something? I'm sorry it ended up being a messy
> bunch of commits. The end result is important though.
> 
> —
> You are receiving this because you were mentioned.
> 
> Reply to this email directly, view it on GitHub
> https://github.com/SVG-Edit/svgedit/pull/97#issuecomment-238188520, or mute
> the thread
> https://github.com/notifications/unsubscribe-auth/AAGIDC0Kz25Lho-bckAKdasys5-c3bddks5qdvlkgaJpZM4H3I7m
> .

Great. Thank you.

I've made a small change to support nodes with a custom namespace too. It will now treat any nodes with ignored namespaces as a group you can't ungroup and any svg elements in it will be shown and aren't editable.

I noticed an issue in resizing elements with custom namespaces. It should work better now.

I've also fixed an issue in ext-connector.js for this feature. This included a side-effect of improving/simplifying getBBoxWithTransform() quite a bit.

I tried to use the code from this tree and i see that there are different exceptions that are thrown.
svgutils.js:495 Uncaught TypeError: Cannot use 'in' operator to search for 'null' in undefined

if(elname === 'use' || elem.prefix in svgedit.ignoredNSUsedAlias) 

Is there some thing this is missed during checin process?
![image](https://cloud.githubusercontent.com/assets/19921843/20728142/65e2b0ca-b6a2-11e6-9415-bd6565c68724.png)

@evssraghavendra: This is now fixed in the latest commit. The svgedit.ignoredNSUsedAlias was only initialized in sanitizeSvg(), which is run when you load a drawing programmatically with setSvgString(), but not when opening a new drawing. It's now initialized when the drawing is initialized. This should cover the bases.
What is the mechanism to discuss about this tool other than logging an issue.
This is the pull request for this implementation. So if you want to discuss this implementation, it would be here. For the generic problem that this is trying to solve, the issue is issue https://github.com/SVG-Edit/svgedit/issues/94.
I found out that the custom namespaces weren't applied to nodes properly, which is why they were visible in the browser. I've now removed the faulty handling of these nodes, since if it's not visible, it's not clickable. So now both nodes and attributes from custom namespaces are safely ignored by svg-edit.

Then i've added a new se attribute called se:external, which you can set to "1" on a g node. If this is set, the group won't be ungroup-able and transforms won't be applied to children of the group. This is so that you can have groups that are treated as a regular element that you can set your own toolbar/etc. type controls for. And then you can use regular svg elements as its children, to make it display as you want in svg-edit.
Is there anything holding this pull request back? Are there any issues or concerns with it?
I'd need to test, but from my perspective, the thing holding it back is it unfortunately it needing rebasing so we can test again... That should greatly expedite the chances and speed by which I may be able to review and pull...
Hello, I've been recently added to the maintainer team. I see that this PR is quite old, had many commits but was never merged in the master branch. I think we can put some efforts into this PR but I would like to know if @iuyiuy is still interested and willing to help as he was the main (actually the only) contributor to this PR?
I don't recall whether the security issues raised were satisfactorily addressed. If you decide to resurrect this PR, that's something to check carefully.
Sorry for the delay here - would you mind syncing with our trunk so I can review this?

I've now synced the branch.

Reg. case sensitivity: I think I noticed that the rest of the code was treating it as case insensitive and didn't question it. But I think you're right, in that it should be case sensitive. I've fixed this in the branch now.

Reg. ignoredNS vs. NS_IGNORE: It's currently implemented so namespaces to be ignored can be defined in config.js and this list is merged with that list. So it's not entirely a constant.

Well it was in response to @flintobrien, who pointed out that namespaces in xml and thus svg are case sensitive. It wasn't really part of issue #94, but I decided to fix it, while I was working on that part of it anyways. Adding a case insensitive conf setting, only to later change it, would also be bad.

But these values aren't constants. First of all constants don't actually exist in JS, but we could pretend and that does make sense, in order to "protect" their use, even if only enforced by code reviewers. But these are the keys in the object/hash/dictionary, which the 'constant' is made up of. These keys represent a lower case text, because most namespaces are referred to in lower case. If someone wanted to make a LaTeX namespace, they could and even use that particular casing, in order to avoid confusion with things with other casing. The actual name of the constant is NS and that part is upper case, after all.

Would anyone argue against this? The only argument I don't think I addressed, was that the pull request would be shorter without this change. But that's obviously not a good reason if it's an improvement and even kinda necessary, in order to not have a conf setting, which will be changed at some point.

---

Reg. the security issue of allowing to ignore a full custom namespace, defined in config.js. @distler: I can't see your example working in any way. I couldn't even get html:onload or svg:onload to work in Chrome in an svg, even if onload works. And even if they worked, they'd only work because they were namespaces that the browser recognized. The browser wouldn't know "se" as a namespace and thus wouldn't interpret se:onload and execute it. I'd love to see an example where this could be used in any way for something bad.

I could see an argument for not having "se" in the default ignored/whitelisted namespace list and specifying all allowed attributes. With that particular namespace, the reference implementation is here after all. But a site-defined (read: defined in config.js) namespace, should be possible to have without needing to keep your config.js in sync with the namespace. Particularly if this is a namespace the browser just ignores and leaves as regular ignored attributes (and tags, btw).

Part 1:  The NS.SVG "constants" are more like Java enums.  That's the
idea.  They are symbols.
Part 2:  http://codedread.com/testbed/circle.svg

On Thu, 4 Aug 2016 at 01:25 KB Jørgensen notifications@github.com wrote:

> Well it was in response to @flintobrien https://github.com/flintobrien,
> who pointed out that namespaces in xml and thus svg are case sensitive. It
> wasn't really part of issue #94
> https://github.com/SVG-Edit/svgedit/issues/94, but I decided to fix it,
> while I was working on that part of it anyways. Adding a case insensitive
> conf setting, only to later change it, would also be bad.
> 
> But these values aren't constants. First of all constants don't actually
> exist in JS, but we could pretend and that does make sense, in order to
> "protect" their use, even if only enforced by code reviewers. But these are
> the keys in the object/hash/dictionary, which the 'constant' is made up of.
> These keys represent a lower case text, because most namespaces are
> referred to in lower case. If someone wanted to make a LaTeX namespace,
> they could and even use that particular casing, in order to avoid confusion
> with things with other casing. The actual name of the constant is NS and
> that part is upper case, after all.
> 
> Would anyone argue against this? The only argument I don't think I
> addressed, was that the pull request would be shorter without this change.
> But that's obviously not a good reason if it's an improvement and even
> kinda necessary, in order to not have a conf setting, which will be changed
> 
> ## at some point.
> 
> Reg. the security issue of allowing to ignore a full custom namespace,
> defined in config.js. @distler https://github.com/distler: I can't see
> your example working in any way. I couldn't even get html:onload or
> svg:onload to work in Chrome in an svg, even if onload works. And even if
> they worked, they'd only work because they were namespaces that the browser
> recognized. The browser wouldn't know "se" as a namespace and thus wouldn't
> interpret se:onload and execute it. I'd love to see an example where this
> could be used in any way for something bad.
> 
> I could see an argument for not having "se" in the default
> ignored/whitelisted namespace list and specifying all allowed attributes.
> With that particular namespace, the reference implementation is here after
> all. But a site-defined (read: defined in config.js) namespace, should be
> possible to have without needing to keep your config.js in sync with the
> namespace. Particularly if this is a namespace the browser just ignores and
> leaves as regular ignored attributes (and tags, btw).
> 
> —
> You are receiving this because you commented.
> Reply to this email directly, view it on GitHub
> https://github.com/SVG-Edit/svgedit/pull/97#issuecomment-237486715, or mute
> the thread
> https://github.com/notifications/unsubscribe-auth/AAGIDO6nVVvF-tLefvkQW2LjTksb8aXgks5qcaHwgaJpZM4H3I7m
> .

FWIW, I don't care too strongly about the upper-case part and I didn't look
very closely at your patch.  I acknowledge that making them lowercase might
have made something easier that I don't quite understand.

On Thu, 4 Aug 2016 at 01:39 Jeff Schiller codedread@gmail.com wrote:

> Part 1:  The NS.SVG "constants" are more like Java enums.  That's the
> idea.  They are symbols.
> Part 2:  http://codedread.com/testbed/circle.svg
> 
> On Thu, 4 Aug 2016 at 01:25 KB Jørgensen notifications@github.com wrote:
> 
> > Well it was in response to @flintobrien https://github.com/flintobrien,
> > who pointed out that namespaces in xml and thus svg are case sensitive. It
> > wasn't really part of issue #94
> > https://github.com/SVG-Edit/svgedit/issues/94, but I decided to fix
> > it, while I was working on that part of it anyways. Adding a case
> > insensitive conf setting, only to later change it, would also be bad.
> > 
> > But these values aren't constants. First of all constants don't actually
> > exist in JS, but we could pretend and that does make sense, in order to
> > "protect" their use, even if only enforced by code reviewers. But these are
> > the keys in the object/hash/dictionary, which the 'constant' is made up of.
> > These keys represent a lower case text, because most namespaces are
> > referred to in lower case. If someone wanted to make a LaTeX namespace,
> > they could and even use that particular casing, in order to avoid confusion
> > with things with other casing. The actual name of the constant is NS and
> > that part is upper case, after all.
> > 
> > Would anyone argue against this? The only argument I don't think I
> > addressed, was that the pull request would be shorter without this change.
> > But that's obviously not a good reason if it's an improvement and even
> > kinda necessary, in order to not have a conf setting, which will be changed
> > 
> > ## at some point.
> > 
> > Reg. the security issue of allowing to ignore a full custom namespace,
> > defined in config.js. @distler https://github.com/distler: I can't see
> > your example working in any way. I couldn't even get html:onload or
> > svg:onload to work in Chrome in an svg, even if onload works. And even if
> > they worked, they'd only work because they were namespaces that the browser
> > recognized. The browser wouldn't know "se" as a namespace and thus wouldn't
> > interpret se:onload and execute it. I'd love to see an example where this
> > could be used in any way for something bad.
> > 
> > I could see an argument for not having "se" in the default
> > ignored/whitelisted namespace list and specifying all allowed attributes.
> > With that particular namespace, the reference implementation is here after
> > all. But a site-defined (read: defined in config.js) namespace, should be
> > possible to have without needing to keep your config.js in sync with the
> > namespace. Particularly if this is a namespace the browser just ignores and
> > leaves as regular ignored attributes (and tags, btw).
> > 
> > —
> > You are receiving this because you commented.
> > Reply to this email directly, view it on GitHub
> > https://github.com/SVG-Edit/svgedit/pull/97#issuecomment-237486715, or mute
> > the thread
> > https://github.com/notifications/unsubscribe-auth/AAGIDO6nVVvF-tLefvkQW2LjTksb8aXgks5qcaHwgaJpZM4H3I7m
> > .

@codedread: You're right. The url for the namespace is the part which identifies how it should be interpreted in the browser or if it should just be ignored. Because the url relates to the definition and html/svg/se are just names or aliases locally in the xml document. So as I said, the html and svg namespaces (defined by the URLs) can invoke scripting. Even if they're called "se" in the svg document. If you define xmlns:html="https://github.com/SVG-Edit/svgedit" in svg, the html:script tag won't work, but set it to xmlns:html="http://www.w3.org/1999/xhtml" and suddenly it works again.

So I did make a mistake. Thanks for guiding me there. I'm gonna change it to allow namespaces based on the url. This means we could allow them regardless of their local name/alias in the svg. Which also means, we could keep the name for them in the NS file as before and just have an array of site-defined namespace URLs to ignore, regardless of their namespace alias in the svg.

What do you say? Isn't that the right way? Should i revert the namespace names, so they're back to being in upper case? They'll be ignored in the actual code usage anyways and only used to refer to which is which.

I've now reverted the change to the variables, so the namespace prefix/alias defined for the built-in or whitelisted namespaces are all upper-case again. I've also moved the se namespace back to this list.

And now the ignored namespaces are detected by the URI of the namespaces (a.k.a. their definition) and not their prefix/alias. Which means you can ignore https://example.com/my-personal-extension regardless of what you call it. You could define it with xmlns:foo and then have foo:bar nodes/attributes. You can also define it with xmlns:html and have html:script, but if you've defined it to use a namespace that the browser doesn't recognize, it won't interpret it as script or anything else. So this isn't a security problem that way. And it's only allowed if it's set in the config.js file.

I think the built-in whitelist should be uri-based instead of alias-based as well. But I didn't include that here, to avoid scope creep in this pull request.

So are there any comments, issues, concerns or anything else about the pull request now? Did i miss something? I'm sorry it ended up being a messy bunch of commits. The end result is important though.

Thanks KB - I've been pretty busy to look at this.  If Jacques or others
have time, that'd be great - otherwise I'm likely to get to this by the
weekend...

On Mon, 8 Aug 2016 at 02:39 KB Jørgensen notifications@github.com wrote:

> I've now reverted the change to the variables, so the namespace
> prefix/alias defined for the built-in or whitelisted namespaces are all
> upper-case again. I've also moved the se namespace back to this list.
> 
> And now the ignored namespaces are detected by the URI of the namespaces
> (a.k.a. their definition) and not their prefix/alias. Which means you can
> ignore https://example.com/my-personal-extension regardless of what you
> call it. You could define it with xmlns:foo and then have foo:bar
> nodes/attributes. You can also define it with xmlns:html and have
> html:script, but if you've defined it to use a namespace that the browser
> doesn't recognize, it won't interpret it as script or anything else. So
> this isn't a security problem that way. And it's only allowed if it's set
> in the config.js file.
> 
> I think the built-in whitelist should be uri-based instead of alias-based
> as well. But I didn't include that here, to avoid scope creep in this pull
> request.
> 
> So are there any comments, issues, concerns or anything else about the
> pull request now? Did i miss something? I'm sorry it ended up being a messy
> bunch of commits. The end result is important though.
> 
> —
> You are receiving this because you were mentioned.
> 
> Reply to this email directly, view it on GitHub
> https://github.com/SVG-Edit/svgedit/pull/97#issuecomment-238188520, or mute
> the thread
> https://github.com/notifications/unsubscribe-auth/AAGIDC0Kz25Lho-bckAKdasys5-c3bddks5qdvlkgaJpZM4H3I7m
> .

Great. Thank you.

I've made a small change to support nodes with a custom namespace too. It will now treat any nodes with ignored namespaces as a group you can't ungroup and any svg elements in it will be shown and aren't editable.

I noticed an issue in resizing elements with custom namespaces. It should work better now.

I've also fixed an issue in ext-connector.js for this feature. This included a side-effect of improving/simplifying getBBoxWithTransform() quite a bit.

I tried to use the code from this tree and i see that there are different exceptions that are thrown.
svgutils.js:495 Uncaught TypeError: Cannot use 'in' operator to search for 'null' in undefined

if(elname === 'use' || elem.prefix in svgedit.ignoredNSUsedAlias) 

Is there some thing this is missed during checin process?
![image](https://cloud.githubusercontent.com/assets/19921843/20728142/65e2b0ca-b6a2-11e6-9415-bd6565c68724.png)

@evssraghavendra: This is now fixed in the latest commit. The svgedit.ignoredNSUsedAlias was only initialized in sanitizeSvg(), which is run when you load a drawing programmatically with setSvgString(), but not when opening a new drawing. It's now initialized when the drawing is initialized. This should cover the bases.
What is the mechanism to discuss about this tool other than logging an issue.
This is the pull request for this implementation. So if you want to discuss this implementation, it would be here. For the generic problem that this is trying to solve, the issue is issue https://github.com/SVG-Edit/svgedit/issues/94.
I found out that the custom namespaces weren't applied to nodes properly, which is why they were visible in the browser. I've now removed the faulty handling of these nodes, since if it's not visible, it's not clickable. So now both nodes and attributes from custom namespaces are safely ignored by svg-edit.

Then i've added a new se attribute called se:external, which you can set to "1" on a g node. If this is set, the group won't be ungroup-able and transforms won't be applied to children of the group. This is so that you can have groups that are treated as a regular element that you can set your own toolbar/etc. type controls for. And then you can use regular svg elements as its children, to make it display as you want in svg-edit.
Is there anything holding this pull request back? Are there any issues or concerns with it?
I'd need to test, but from my perspective, the thing holding it back is it unfortunately it needing rebasing so we can test again... That should greatly expedite the chances and speed by which I may be able to review and pull...
Hello, I've been recently added to the maintainer team. I see that this PR is quite old, had many commits but was never merged in the master branch. I think we can put some efforts into this PR but I would like to know if @iuyiuy is still interested and willing to help as he was the main (actually the only) contributor to this PR?
I don't recall whether the security issues raised were satisfactorily addressed. If you decide to resurrect this PR, that's something to check carefully.

Fix patch:
diff --git a/package-lock.json b/package-lock.json
index d8023e05d..bca6d7db1 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -4253,6 +4253,7 @@
       "dependencies": {
         "anymatch": "~3.1.1",
         "braces": "~3.0.2",
+        "fsevents": "~2.3.1",
         "glob-parent": "~5.1.0",
         "is-binary-path": "~2.1.0",
         "is-glob": "~4.0.1",
@@ -7350,6 +7351,7 @@
         "anymatch": "^2.0.0",
         "async-each": "^1.0.1",
         "braces": "^2.3.2",
+        "fsevents": "^1.2.7",
         "glob-parent": "^3.1.0",
         "inherits": "^2.0.3",
         "is-binary-path": "^1.0.0",
@@ -9942,7 +9944,8 @@
         "esprima": "^4.0.1",
         "estraverse": "^4.2.0",
         "esutils": "^2.0.2",
-        "optionator": "^0.8.1"
+        "optionator": "^0.8.1",
+        "source-map": "~0.6.1"
       },
       "bin": {
         "escodegen": "bin/escodegen.js",
@@ -14726,7 +14729,8 @@
         "canvg": "^3.0.6",
         "core-js": "^3.6.0",
         "dompurify": "^2.2.0",
-        "fflate": "^0.4.8"
+        "fflate": "^0.4.8",
+        "html2canvas": "^1.0.0-rc.5"
       },
       "optionalDependencies": {
         "html2canvas": "^1.0.0-rc.5"
@@ -16285,6 +16289,7 @@
       "integrity": "sha512-akCrLDWfbdAWkMLBxJEeWTdNsjML+dt5YgOI4gJ53vuO0vrmYQkUPxa6j6V65s9CcePIr2SSWqjT2EcrNseryQ==",
       "dev": true,
       "dependencies": {
+        "encoding": "^0.1.12",
         "minipass": "^3.1.0",
         "minipass-sized": "^1.0.3",
         "minizlib": "^2.0.0"
@@ -16526,6 +16531,7 @@
       "dependencies": {
         "anymatch": "~3.1.1",
         "braces": "~3.0.2",
+        "fsevents": "~2.3.1",
         "glob-parent": "~5.1.0",
         "is-binary-path": "~2.1.0",
         "is-glob": "~4.0.1",
@@ -20278,6 +20284,9 @@
       "version": "2.47.0",
       "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.47.0.tgz",
       "integrity": "sha512-rqBjgq9hQfW0vRmz+0S062ORRNJXvwRpzxhFXORvar/maZqY6za3rgQ/p1Glg+j1hnc1GtYyQCPiAei95uTElg==",
+      "dependencies": {
+        "fsevents": "~2.3.1"
+      },
       "bin": {
         "rollup": "dist/bin/rollup"
       },
@@ -22853,6 +22862,7 @@
       "dependencies": {
         "anymatch": "~3.1.1",
         "braces": "~3.0.2",
+        "fsevents": "~2.1.2",
         "glob-parent": "~5.1.0",
         "is-binary-path": "~2.1.0",
         "is-glob": "~4.0.1",
diff --git a/src/editor/locale/lang.af.js b/src/editor/locale/lang.af.js
index 65aa4e52b..73831393d 100644
--- a/src/editor/locale/lang.af.js
+++ b/src/editor/locale/lang.af.js
@@ -25,7 +25,8 @@ export default {
     pathNodeTooltip: 'Drag node to move it. Double-click node to change segment type',
     pathCtrlPtTooltip: 'Drag control point to adjust curve properties',
     pick_stroke_paint_opacity: 'Pick a Stroke Paint and Opacity',
-    pick_fill_paint_opacity: 'Pick a Fill Paint and Opacity'
+    pick_fill_paint_opacity: 'Pick a Fill Paint and Opacity',
+    group_identify_label: 'Group identification label'
   },
   properties: {
     id: 'Identify the element',
@@ -73,7 +74,16 @@ export default {
     italic: 'Italic Text',
     text_anchor_start: 'Align the text in start',
     text_anchor_middle: 'Align the text in middle',
-    text_anchor_end: 'Align the text in end'
+    text_anchor_end: 'Align the text in end',
+    class: 'Element class',
+    serif: 'Serif',
+    sans_serif: 'Sans-serif',
+    cursive: 'Cursive',
+    fantasy: 'Fantasy',
+    monospace: 'Monospace',
+    courier: 'Courier',
+    helvetica: 'Helvetica',
+    times: 'times'
   },
   tools: {
     main_menu: 'Main Menu',
@@ -147,7 +157,10 @@ export default {
     move_front: 'Bring to Front',
     move_up: 'Bring Forward',
     move_down: 'Send Backward',
-    move_back: 'Send to Back'
+    move_back: 'Send to Back',
+    tool_unlink_use: 'Break link to reference element (make unique)',
+    ellipse_circle_tool: 'Ellipse/Circle Tool',
+    square_rect_tool: 'Square/Rect Tool'
   },
   layers: {
     layer: 'Layer',
diff --git a/src/editor/locale/lang.ar.js b/src/editor/locale/lang.ar.js
index 9fcd5f5e8..b9a290f89 100644
--- a/src/editor/locale/lang.ar.js
+++ b/src/editor/locale/lang.ar.js
@@ -25,7 +25,8 @@ export default {
     pathNodeTooltip: 'Drag node to move it. Double-click node to change segment type',
     pathCtrlPtTooltip: 'Drag control point to adjust curve properties',
     pick_stroke_paint_opacity: 'Pick a Stroke Paint and Opacity',
-    pick_fill_paint_opacity: 'Pick a Fill Paint and Opacity'
+    pick_fill_paint_opacity: 'Pick a Fill Paint and Opacity',
+    group_identify_label: 'Group identification label'
   },
   properties: {
     id: 'Identify the element',
@@ -73,7 +74,16 @@ export default {
     italic: 'مائل نص',
     text_anchor_start: 'Align the text in start',
     text_anchor_middle: 'Align the text in middle',
-    text_anchor_end: 'Align the text in end'
+    text_anchor_end: 'Align the text in end',
+    class: 'Element class',
+    serif: 'Serif',
+    sans_serif: 'Sans-serif',
+    cursive: 'Cursive',
+    fantasy: 'Fantasy',
+    monospace: 'Monospace',
+    courier: 'Courier',
+    helvetica: 'Helvetica',
+    times: 'times'
   },
   tools: {
     main_menu: 'Main Menu',
@@ -147,7 +157,10 @@ export default {
     move_front: 'Bring to Front',
     move_up: 'Bring Forward',
     move_down: 'Send Backward',
-    move_back: 'Send to Back'
+    move_back: 'Send to Back',
+    tool_unlink_use: 'Break link to reference element (make unique)',
+    ellipse_circle_tool: 'Ellipse/Circle Tool',
+    square_rect_tool: 'Square/Rect Tool'
   },
   layers: {
     layer: 'Layer',
diff --git a/src/editor/locale/lang.az.js b/src/editor/locale/lang.az.js
index 892f4effd..5fa4ca5bc 100644
--- a/src/editor/locale/lang.az.js
+++ b/src/editor/locale/lang.az.js
@@ -25,7 +25,8 @@ export default {
     pathNodeTooltip: 'Drag node to move it. Double-click node to change segment type',
     pathCtrlPtTooltip: 'Drag control point to adjust curve properties',
     pick_stroke_paint_opacity: 'Pick a Stroke Paint and Opacity',
-    pick_fill_paint_opacity: 'Pick a Fill Paint and Opacity'
+    pick_fill_paint_opacity: 'Pick a Fill Paint and Opacity',
+    group_identify_label: 'Group identification label'
   },
   properties: {
     id: 'Identify the element',
@@ -73,7 +74,16 @@ export default {
     italic: 'Italic Text',
     text_anchor_start: 'Align the text in start',
     text_anchor_middle: 'Align the text in middle',
-    text_anchor_end: 'Align the text in end'
+    text_anchor_end: 'Align the text in end',
+    class: 'Element class',
+    serif: 'Serif',
+    sans_serif: 'Sans-serif',
+    cursive: 'Cursive',
+    fantasy: 'Fantasy',
+    monospace: 'Monospace',
+    courier: 'Courier',
+    helvetica: 'Helvetica',
+    times: 'times'
   },
   tools: {
     main_menu: 'Main Menu',
@@ -147,7 +157,10 @@ export default {
     move_front: 'Bring to Front',
     move_up: 'Bring Forward',
     move_down: 'Send Backward',
-    move_back: 'Send to Back'
+    move_back: 'Send to Back',
+    tool_unlink_use: 'Break link to reference element (make unique)',
+    ellipse_circle_tool: 'Ellipse/Circle Tool',
+    square_rect_tool: 'Square/Rect Tool'
   },
   layers: {
     layer: 'Layer',
diff --git a/src/editor/locale/lang.be.js b/src/editor/locale/lang.be.js
index 26e568120..11813d9ca 100644
--- a/src/editor/locale/lang.be.js
+++ b/src/editor/locale/lang.be.js
@@ -25,7 +25,8 @@ export default {
     pathNodeTooltip: 'Drag node to move it. Double-click node to change segment type',
     pathCtrlPtTooltip: 'Drag control point to adjust curve properties',
     pick_stroke_paint_opacity: 'Pick a Stroke Paint and Opacity',
-    pick_fill_paint_opacity: 'Pick a Fill Paint and Opacity'
+    pick_fill_paint_opacity: 'Pick a Fill Paint and Opacity',
+    group_identify_label: 'Group identification label'
   },
   properties: {
     id: 'Identify the element',
@@ -73,7 +74,16 @@ export default {
     italic: 'Нахілены тэкст',
     text_anchor_start: 'Align the text in start',
     text_anchor_middle: 'Align the text in middle',
-    text_anchor_end: 'Align the text in end'
+    text_anchor_end: 'Align the text in end',
+    class: 'Element class',
+    serif: 'Serif',
+    sans_serif: 'Sans-serif',
+    cursive: 'Cursive',
+    fantasy: 'Fantasy',
+    monospace: 'Monospace',
+    courier: 'Courier',
+    helvetica: 'Helvetica',
+    times: 'times'
   },
   tools: {
     main_menu: 'Main Menu',
@@ -147,7 +157,10 @@ export default {
     move_front: 'Bring to Front',
     move_up: 'Bring Forward',
     move_down: 'Send Backward',
-    move_back: 'Send to Back'
+    move_back: 'Send to Back',
+    tool_unlink_use: 'Break link to reference element (make unique)',
+    ellipse_circle_tool: 'Ellipse/Circle Tool',
+    square_rect_tool: 'Square/Rect Tool'
   },
   layers: {
     layer: 'Layer',
diff --git a/src/editor/locale/lang.bg.js b/src/editor/locale/lang.bg.js
index 31a0c60ad..d476606b3 100644
--- a/src/editor/locale/lang.bg.js
+++ b/src/editor/locale/lang.bg.js
@@ -25,7 +25,8 @@ export default {
     pathNodeTooltip: 'Drag node to move it. Double-click node to change segment type',
     pathCtrlPtTooltip: 'Drag control point to adjust curve properties',
     pick_stroke_paint_opacity: 'Pick a Stroke Paint and Opacity',
-    pick_fill_paint_opacity: 'Pick a Fill Paint and Opacity'
+    pick_fill_paint_opacity: 'Pick a Fill Paint and Opacity',
+    group_identify_label: 'Group identification label'
   },
   properties: {
     id: 'Identify the element',
@@ -73,7 +74,16 @@ export default {
     italic: 'Курсив текст',
     text_anchor_start: 'Align the text in start',
     text_anchor_middle: 'Align the text in middle',
-    text_anchor_end: 'Align the text in end'
+    text_anchor_end: 'Align the text in end',
+    class: 'Element class',
+    serif: 'Serif',
+    sans_serif: 'Sans-serif',
+    cursive: 'Cursive',
+    fantasy: 'Fantasy',
+    monospace: 'Monospace',
+    courier: 'Courier',
+    helvetica: 'Helvetica',
+    times: 'times'
   },
   tools: {
     main_menu: 'Main Menu',
@@ -147,7 +157,10 @@ export default {
     move_front: 'Bring to Front',
     move_up: 'Bring Forward',
     move_down: 'Send Backward',
-    move_back: 'Send to Back'
+    move_back: 'Send to Back',
+    tool_unlink_use: 'Break link to reference element (make unique)',
+    ellipse_circle_tool: 'Ellipse/Circle Tool',
+    square_rect_tool: 'Square/Rect Tool'
   },
   layers: {
     layer: 'Layer',
diff --git a/src/editor/locale/lang.ca.js b/src/editor/locale/lang.ca.js
index c63a448f1..40c095562 100644
--- a/src/editor/locale/lang.ca.js
+++ b/src/editor/locale/lang.ca.js
@@ -25,7 +25,8 @@ export default {
     pathNodeTooltip: 'Drag node to move it. Double-click node to change segment type',
     pathCtrlPtTooltip: 'Drag control point to adjust curve properties',
     pick_stroke_paint_opacity: 'Pick a Stroke Paint and Opacity',
-    pick_fill_paint_opacity: 'Pick a Fill Paint and Opacity'
+    pick_fill_paint_opacity: 'Pick a Fill Paint and Opacity',
+    group_identify_label: 'Group identification label'
   },
   properties: {
     id: 'Identify the element',
@@ -73,7 +74,16 @@ export default {
     italic: 'Text en cursiva',
     text_anchor_start: 'Align the text in start',
     text_anchor_middle: 'Align the text in middle',
-    text_anchor_end: 'Align the text in end'
+    text_anchor_end: 'Align the text in end',
+    class: 'Element class',
+    serif: 'Serif',
+    sans_serif: 'Sans-serif',
+    cursive: 'Cursive',
+    fantasy: 'Fantasy',
+    monospace: 'Monospace',
+    courier: 'Courier',
+    helvetica: 'Helvetica',
+    times: 'times'
   },
   tools: {
     main_menu: 'Main Menu',
@@ -147,7 +157,10 @@ export default {
     move_front: 'Bring to Front',
     move_up: 'Bring Forward',
     move_down: 'Send Backward',
-    move_back: 'Send to Back'
+    move_back: 'Send to Back',
+    tool_unlink_use: 'Break link to reference element (make unique)',
+    ellipse_circle_tool: 'Ellipse/Circle Tool',
+    square_rect_tool: 'Square/Rect Tool'
   },
   layers: {
     layer: 'Layer',
diff --git a/src/editor/locale/lang.cs.js b/src/editor/locale/lang.cs.js
index 698b86bdc..5885bff41 100644
--- a/src/editor/locale/lang.cs.js
+++ b/src/editor/locale/lang.cs.js
@@ -25,7 +25,8 @@ export default {
     pathNodeTooltip: 'Drag node to move it. Double-click node to change segment type',
     pathCtrlPtTooltip: 'Drag control point to adjust curve properties',
     pick_stroke_paint_opacity: 'Pick a Stroke Paint and Opacity',
-    pick_fill_paint_opacity: 'Pick a Fill Paint and Opacity'
+    pick_fill_paint_opacity: 'Pick a Fill Paint and Opacity',
+    group_identify_label: 'Group identification label'
   },
   properties: {
     id: 'Změnit ID elementu',
@@ -73,7 +74,16 @@ export default {
     italic: 'Kurzíva',
     text_anchor_start: 'Align the text in start',
     text_anchor_middle: 'Align the text in middle',
-    text_anchor_end: 'Align the text in end'
+    text_anchor_end: 'Align the text in end',
+    class: 'Element class',
+    serif: 'Serif',
+    sans_serif: 'Sans-serif',
+    cursive: 'Cursive',
+    fantasy: 'Fantasy',
+    monospace: 'Monospace',
+    courier: 'Courier',
+    helvetica: 'Helvetica',
+    times: 'times'
   },
   tools: {
     main_menu: 'Hlavní menu',
@@ -147,7 +157,10 @@ export default {
     move_front: 'Bring to Front',
     move_up: 'Bring Forward',
     move_down: 'Send Backward',
-    move_back: 'Send to Back'
+    move_back: 'Send to Back',
+    tool_unlink_use: 'Break link to reference element (make unique)',
+    ellipse_circle_tool: 'Ellipse/Circle Tool',
+    square_rect_tool: 'Square/Rect Tool'
   },
   layers: {
     layer: 'Vrstva',
diff --git a/src/editor/locale/lang.cy.js b/src/editor/locale/lang.cy.js
index 3d1f77220..d00322fa0 100644
--- a/src/editor/locale/lang.cy.js
+++ b/src/editor/locale/lang.cy.js
@@ -25,7 +25,8 @@ export default {
     pathNodeTooltip: 'Drag node to move it. Double-click node to change segment type',
     pathCtrlPtTooltip: 'Drag control point to adjust curve properties',
     pick_stroke_paint_opacity: 'Pick a Stroke Paint and Opacity',
-    pick_fill_paint_opacity: 'Pick a Fill Paint and Opacity'
+    pick_fill_paint_opacity: 'Pick a Fill Paint and Opacity',
+    group_identify_label: 'Group identification label'
   },
   properties: {
     id: 'Identify the element',
@@ -73,7 +74,16 @@ export default {
     italic: 'Italig Testun',
     text_anchor_start: 'Align the text in start',
     text_anchor_middle: 'Align the text in middle',
-    text_anchor_end: 'Align the text in end'
+    text_anchor_end: 'Align the text in end',
+    class: 'Element class',
+    serif: 'Serif',
+    sans_serif: 'Sans-serif',
+    cursive: 'Cursive',
+    fantasy: 'Fantasy',
+    monospace: 'Monospace',
+    courier: 'Courier',
+    helvetica: 'Helvetica',
+    times: 'times'
   },
   tools: {
     main_menu: 'Main Menu',
@@ -147,7 +157,10 @@ export default {
     move_front: 'Bring to Front',
     move_up: 'Bring Forward',
     move_down: 'Send Backward',
-    move_back: 'Send to Back'
+    move_back: 'Send to Back',
+    tool_unlink_use: 'Break link to reference element (make unique)',
+    ellipse_circle_tool: 'Ellipse/Circle Tool',
+    square_rect_tool: 'Square/Rect Tool'
   },
   layers: {
     layer: 'Layer',
diff --git a/src/editor/locale/lang.da.js b/src/editor/locale/lang.da.js
index e915730ad..030424270 100644
--- a/src/editor/locale/lang.da.js
+++ b/src/editor/locale/lang.da.js
@@ -25,7 +25,8 @@ export default {
     pathNodeTooltip: 'Drag node to move it. Double-click node to change segment type',
     pathCtrlPtTooltip: 'Drag control point to adjust curve properties',
     pick_stroke_paint_opacity: 'Pick a Stroke Paint and Opacity',
-    pick_fill_paint_opacity: 'Pick a Fill Paint and Opacity'
+    pick_fill_paint_opacity: 'Pick a Fill Paint and Opacity',
+    group_identify_label: 'Group identification label'
   },
   properties: {
     id: 'Identify the element',
@@ -73,7 +74,16 @@ export default {
     italic: 'Italic Text',
     text_anchor_start: 'Align the text in start',
     text_anchor_middle: 'Align the text in middle',
-    text_anchor_end: 'Align the text in end'
+    text_anchor_end: 'Align the text in end',
+    class: 'Element class',
+    serif: 'Serif',
+    sans_serif: 'Sans-serif',
+    cursive: 'Cursive',
+    fantasy: 'Fantasy',
+    monospace: 'Monospace',
+    courier: 'Courier',
+    helvetica: 'Helvetica',
+    times: 'times'
   },
   tools: {
     main_menu: 'Main Menu',
@@ -161,7 +171,10 @@ export default {
     merge_down: 'Merge Down',
     merge_all: 'Merge All',
     move_elems_to: 'Move elements to:',
-    move_selected: 'Move selected elements to a different layer'
+    move_selected: 'Move selected elements to a different layer',
+    tool_unlink_use: 'Break link to reference element (make unique)',
+    ellipse_circle_tool: 'Ellipse/Circle Tool',
+    square_rect_tool: 'Square/Rect Tool'
   },
   config: {
     image_props: 'Image Properties',
diff --git a/src/editor/locale/lang.de.js b/src/editor/locale/lang.de.js
index e7925bd98..982016d2b 100644
--- a/src/editor/locale/lang.de.js
+++ b/src/editor/locale/lang.de.js
@@ -25,7 +25,8 @@ export default {
     pathNodeTooltip: 'Drag node to move it. Double-click node to change segment type',
     pathCtrlPtTooltip: 'Drag control point to adjust curve properties',
     pick_stroke_paint_opacity: 'Pick a Stroke Paint and Opacity',
-    pick_fill_paint_opacity: 'Pick a Fill Paint and Opacity'
+    pick_fill_paint_opacity: 'Pick a Fill Paint and Opacity',
+    group_identify_label: 'Group identification label'
   },
   properties: {
     id: 'Element identifizieren',
@@ -73,7 +74,16 @@ export default {
     italic: 'Kursiver Text',
     text_anchor_start: 'Den Text linksbündig ausrichten',
     text_anchor_middle: 'Den Text zentriert ausrichten',
-    text_anchor_end: 'Den Text rechtsbündig ausrichten'
+    text_anchor_end: 'Den Text rechtsbündig ausrichten',
+    class: 'Element class',
+    serif: 'Serif',
+    sans_serif: 'Sans-serif',
+    cursive: 'Cursive',
+    fantasy: 'Fantasy',
+    monospace: 'Monospace',
+    courier: 'Courier',
+    helvetica: 'Helvetica',
+    times: 'times'
   },
   tools: {
     main_menu: 'Hauptmenü',
@@ -147,7 +157,10 @@ export default {
     move_front: 'Nach ganz oben verschieben',
     move_up: 'Hochschieben',
     move_down: 'Herunterschieben',
-    move_back: 'Nach ganz unten verschieben'
+    move_back: 'Nach ganz unten verschieben',
+    tool_unlink_use: 'Break link to reference element (make unique)',
+    ellipse_circle_tool: 'Ellipse/Circle Tool',
+    square_rect_tool: 'Square/Rect Tool'
   },
   layers: {
     layer: 'Ebene',
diff --git a/src/editor/locale/lang.el.js b/src/editor/locale/lang.el.js
index f53f32aa2..390a81a73 100644
--- a/src/editor/locale/lang.el.js
+++ b/src/editor/locale/lang.el.js
@@ -25,7 +25,8 @@ export default {
     pathNodeTooltip: 'Drag node to move it. Double-click node to change segment type',
     pathCtrlPtTooltip: 'Drag control point to adjust curve properties',
     pick_stroke_paint_opacity: 'Pick a Stroke Paint and Opacity',
-    pick_fill_paint_opacity: 'Pick a Fill Paint and Opacity'
+    pick_fill_paint_opacity: 'Pick a Fill Paint and Opacity',
+    group_identify_label: 'Group identification label'
   },
   properties: {
     id: 'Identify the element',
@@ -73,7 +74,16 @@ export default {
     italic: 'Πλάγιους',
     text_anchor_start: 'Align the text in start',
     text_anchor_middle: 'Align the text in middle',
-    text_anchor_end: 'Align the text in end'
+    text_anchor_end: 'Align the text in end',
+    class: 'Element class',
+    serif: 'Serif',
+    sans_serif: 'Sans-serif',
+    cursive: 'Cursive',
+    fantasy: 'Fantasy',
+    monospace: 'Monospace',
+    courier: 'Courier',
+    helvetica: 'Helvetica',
+    times: 'times'
   },
   tools: {
     main_menu: 'Main Menu',
@@ -147,7 +157,10 @@ export default {
     move_front: 'Bring to Front',
     move_up: 'Bring Forward',
     move_down: 'Send Backward',
-    move_back: 'Send to Back'
+    move_back: 'Send to Back',
+    tool_unlink_use: 'Break link to reference element (make unique)',
+    ellipse_circle_tool: 'Ellipse/Circle Tool',
+    square_rect_tool: 'Square/Rect Tool'
   },
   layers: {
     layer: 'Layer',
diff --git a/src/editor/locale/lang.en.js b/src/editor/locale/lang.en.js
index 1b94fb2c3..a88a66039 100644
--- a/src/editor/locale/lang.en.js
+++ b/src/editor/locale/lang.en.js
@@ -25,7 +25,8 @@ export default {
     pathNodeTooltip: 'Drag node to move it. Double-click node to change segment type',
     pathCtrlPtTooltip: 'Drag control point to adjust curve properties',
     pick_stroke_paint_opacity: 'Pick a Stroke Paint and Opacity',
-    pick_fill_paint_opacity: 'Pick a Fill Paint and Opacity'
+    pick_fill_paint_opacity: 'Pick a Fill Paint and Opacity',
+    group_identify_label: 'Group identification label'
   },
   properties: {
     id: 'Identify the element',
@@ -73,7 +74,19 @@ export default {
     italic: 'Italic Text [I]',
     text_anchor_start: 'Align the text in start',
     text_anchor_middle: 'Align the text in middle',
-    text_anchor_end: 'Align the text in end'
+    text_anchor_end: 'Align the text in end',
+    class: 'Element class',
+    serif: 'Serif',
+    sans_serif: 'Sans-serif',
+    cursive: 'Cursive',
+    fantasy: 'Fantasy',
+    monospace: 'Monospace',
+    courier: 'Courier',
+    helvetica: 'Helvetica',
+    times: 'times',
+    prefs_and_content: 'Store preferences and SVG content locally',
+    prefs_only: 'Only store preferences locally',
+    no_prefs_or_content: 'Do not store my preferences or SVG content locally'
   },
   tools: {
     main_menu: 'Main Menu',
@@ -147,7 +160,10 @@ export default {
     move_front: 'Bring to Front',
     move_up: 'Bring Forward',
     move_down: 'Send Backward',
-    move_back: 'Send to Back'
+    move_back: 'Send to Back',
+    tool_unlink_use: 'Break link to reference element (make unique)',
+    ellipse_circle_tool: 'Ellipse/Circle Tool',
+    square_rect_tool: 'Square/Rect Tool'
   },
   layers: {
     layer: 'Layer',
@@ -216,6 +232,7 @@ export default {
     exportNoBlur: 'Blurred elements will appear as un-blurred',
     exportNoforeignObject: 'foreignObject elements will not appear',
     exportNoDashArray: 'Strokes will appear filled',
-    exportNoText: 'Text may not appear as expected'
+    exportNoText: 'Text may not appear as expected',
+    editorPreferencesMsg: 'By default and where supported, SVG-Edit can store your editor preferences and SVG content locally on your machine so you do not need to add these back each time you load SVG-Edit. If, for privacy reasons, you do not wish to store this information on your machine, you can change away from the default option below.'
   }
 };
diff --git a/src/editor/locale/lang.es.js b/src/editor/locale/lang.es.js
index 092f5d2a3..e73db3846 100644
--- a/src/editor/locale/lang.es.js
+++ b/src/editor/locale/lang.es.js
@@ -25,7 +25,8 @@ export default {
     pathNodeTooltip: 'Drag node to move it. Double-click node to change segment type',
     pathCtrlPtTooltip: 'Drag control point to adjust curve properties',
     pick_stroke_paint_opacity: 'Pick a Stroke Paint and Opacity',
-    pick_fill_paint_opacity: 'Pick a Fill Paint and Opacity'
+    pick_fill_paint_opacity: 'Pick a Fill Paint and Opacity',
+    group_identify_label: 'Group identification label'
   },
   properties: {
     id: 'Identify the element',
@@ -73,7 +74,16 @@ export default {
     italic: 'Texto en cursiva',
     text_anchor_start: 'Align the text in start',
     text_anchor_middle: 'Align the text in middle',
-    text_anchor_end: 'Align the text in end'
+    text_anchor_end: 'Align the text in end',
+    class: 'Element class',
+    serif: 'Serif',
+    sans_serif: 'Sans-serif',
+    cursive: 'Cursive',
+    fantasy: 'Fantasy',
+    monospace: 'Monospace',
+    courier: 'Courier',
+    helvetica: 'Helvetica',
+    times: 'times'
   },
   tools: {
     main_menu: 'Menú principal',
@@ -147,7 +157,10 @@ export default {
     move_front: 'Bring to Front',
     move_up: 'Bring Forward',
     move_down: 'Send Backward',
-    move_back: 'Send to Back'
+    move_back: 'Send to Back',
+    tool_unlink_use: 'Break link to reference element (make unique)',
+    ellipse_circle_tool: 'Ellipse/Circle Tool',
+    square_rect_tool: 'Square/Rect Tool'
   },
   layers: {
     layer: 'Capa',
diff --git a/src/editor/locale/lang.et.js b/src/editor/locale/lang.et.js
index a7180e04b..384647e6f 100644
--- a/src/editor/locale/lang.et.js
+++ b/src/editor/locale/lang.et.js
@@ -25,7 +25,8 @@ export default {
     pathNodeTooltip: 'Drag node to move it. Double-click node to change segment type',
     pathCtrlPtTooltip: 'Drag control point to adjust curve properties',
     pick_stroke_paint_opacity: 'Pick a Stroke Paint and Opacity',
-    pick_fill_paint_opacity: 'Pick a Fill Paint and Opacity'
+    pick_fill_paint_opacity: 'Pick a Fill Paint and Opacity',
+    group_identify_label: 'Group identification label'
   },
   properties: {
     id: 'Identify the element',
@@ -73,7 +74,16 @@ export default {
     italic: 'Kursiiv',
     text_anchor_start: 'Align the text in start',
     text_anchor_middle: 'Align the text in middle',
-    text_anchor_end: 'Align the text in end'
+    text_anchor_end: 'Align the text in end',
+    class: 'Element class',
+    serif: 'Serif',
+    sans_serif: 'Sans-serif',
+    cursive: 'Cursive',
+    fantasy: 'Fantasy',
+    monospace: 'Monospace',
+    courier: 'Courier',
+    helvetica: 'Helvetica',
+    times: 'times'
   },
   tools: {
     main_menu: 'Main Menu',
@@ -147,7 +157,10 @@ export default {
     move_front: 'Bring to Front',
     move_up: 'Bring Forward',
     move_down: 'Send Backward',
-    move_back: 'Send to Back'
+    move_back: 'Send to Back',
+    tool_unlink_use: 'Break link to reference element (make unique)',
+    ellipse_circle_tool: 'Ellipse/Circle Tool',
+    square_rect_tool: 'Square/Rect Tool'
   },
   layers: {
     layer: 'Layer',
diff --git a/src/editor/locale/lang.fa.js b/src/editor/locale/lang.fa.js
index 08f8539dc..f42a65d62 100644
--- a/src/editor/locale/lang.fa.js
+++ b/src/editor/locale/lang.fa.js
@@ -25,7 +25,8 @@ export default {
     pathNodeTooltip: 'Drag node to move it. Double-click node to change segment type',
     pathCtrlPtTooltip: 'Drag control point to adjust curve properties',
     pick_stroke_paint_opacity: 'Pick a Stroke Paint and Opacity',
-    pick_fill_paint_opacity: 'Pick a Fill Paint and Opacity'
+    pick_fill_paint_opacity: 'Pick a Fill Paint and Opacity',
+    group_identify_label: 'Group identification label'
   },
   properties: {
     id: 'Identify the element',
@@ -73,7 +74,16 @@ export default {
     italic: '‫متن کج ‬',
     text_anchor_start: 'Align the text in start',
     text_anchor_middle: 'Align the text in middle',
-    text_anchor_end: 'Align the text in end'
+    text_anchor_end: 'Align the text in end',
+    class: 'Element class',
+    serif: 'Serif',
+    sans_serif: 'Sans-serif',
+    cursive: 'Cursive',
+    fantasy: 'Fantasy',
+    monospace: 'Monospace',
+    courier: 'Courier',
+    helvetica: 'Helvetica',
+    times: 'times'
   },
   tools: {
     main_menu: 'Main Menu',
@@ -147,7 +157,10 @@ export default {
     move_front: 'Bring to Front',
     move_up: 'Bring Forward',
     move_down: 'Send Backward',
-    move_back: 'Send to Back'
+    move_back: 'Send to Back',
+    tool_unlink_use: 'Break link to reference element (make unique)',
+    ellipse_circle_tool: 'Ellipse/Circle Tool',
+    square_rect_tool: 'Square/Rect Tool'
   },
   layers: {
     layer: '‫لایه‬',
diff --git a/src/editor/locale/lang.fi.js b/src/editor/locale/lang.fi.js
index bd53255e5..a2f78237f 100644
--- a/src/editor/locale/lang.fi.js
+++ b/src/editor/locale/lang.fi.js
@@ -25,7 +25,8 @@ export default {
     pathNodeTooltip: 'Drag node to move it. Double-click node to change segment type',
     pathCtrlPtTooltip: 'Drag control point to adjust curve properties',
     pick_stroke_paint_opacity: 'Pick a Stroke Paint and Opacity',
-    pick_fill_paint_opacity: 'Pick a Fill Paint and Opacity'
+    pick_fill_paint_opacity: 'Pick a Fill Paint and Opacity',
+    group_identify_label: 'Group identification label'
   },
   properties: {
     id: 'Identify the element',
@@ -73,7 +74,16 @@ export default {
     italic: 'Kursivoitu',
     text_anchor_start: 'Align the text in start',
     text_anchor_middle: 'Align the text in middle',
-    text_anchor_end: 'Align the text in end'
+    text_anchor_end: 'Align the text in end',
+    class: 'Element class',
+    serif: 'Serif',
+    sans_serif: 'Sans-serif',
+    cursive: 'Cursive',
+    fantasy: 'Fantasy',
+    monospace: 'Monospace',
+    courier: 'Courier',
+    helvetica: 'Helvetica',
+    times: 'times'
   },
   tools: {
     main_menu: 'Main Menu',
@@ -147,7 +157,10 @@ export default {
     move_front: 'Bring to Front',
     move_up: 'Bring Forward',
     move_down: 'Send Backward',
-    move_back: 'Send to Back'
+    move_back: 'Send to Back',
+    tool_unlink_use: 'Break link to reference element (make unique)',
+    ellipse_circle_tool: 'Ellipse/Circle Tool',
+    square_rect_tool: 'Square/Rect Tool'
   },
   layers: {
     layer: 'Layer',
diff --git a/src/editor/locale/lang.fr.js b/src/editor/locale/lang.fr.js
index e1b0e9c35..0da882af7 100644
--- a/src/editor/locale/lang.fr.js
+++ b/src/editor/locale/lang.fr.js
@@ -25,7 +25,8 @@ export default {
     pathNodeTooltip: 'Drag node to move it. Double-click node to change segment type',
     pathCtrlPtTooltip: 'Drag control point to adjust curve properties',
     pick_stroke_paint_opacity: 'Pick a Stroke Paint and Opacity',
-    pick_fill_paint_opacity: 'Pick a Fill Paint and Opacity'
+    pick_fill_paint_opacity: 'Pick a Fill Paint and Opacity',
+    group_identify_label: 'Group identification label'
   },
   properties: {
     id: "Identifier l'élément",
@@ -73,7 +74,16 @@ export default {
     italic: 'Texte en italique',
     text_anchor_start: 'Align the text in start',
     text_anchor_middle: 'Align the text in middle',
-    text_anchor_end: 'Align the text in end'
+    text_anchor_end: 'Align the text in end',
+    class: 'Element class',
+    serif: 'Serif',
+    sans_serif: 'Sans-serif',
+    cursive: 'Cursive',
+    fantasy: 'Fantasy',
+    monospace: 'Monospace',
+    courier: 'Courier',
+    helvetica: 'Helvetica',
+    times: 'times'
   },
   tools: {
     main_menu: 'Menu principal',
@@ -147,7 +157,10 @@ export default {
     move_front: 'Placer au premier plan',
     move_up: "Avancer d'un plan",
     move_down: "Reculer d'un plan",
-    move_back: 'Placer au fond'
+    move_back: 'Placer au fond',
+    tool_unlink_use: 'Break link to reference element (make unique)',
+    ellipse_circle_tool: 'Ellipse/Circle Tool',
+    square_rect_tool: 'Square/Rect Tool'
   },
   layers: {
     layer: 'Calque',
diff --git a/src/editor/locale/lang.fy.js b/src/editor/locale/lang.fy.js
index aacfaafce..777ecaa43 100644
--- a/src/editor/locale/lang.fy.js
+++ b/src/editor/locale/lang.fy.js
@@ -25,7 +25,8 @@ export default {
     pathNodeTooltip: 'Drag node to move it. Double-click node to change segment type',
     pathCtrlPtTooltip: 'Drag control point to adjust curve properties',
     pick_stroke_paint_opacity: 'Pick a Stroke Paint and Opacity',
-    pick_fill_paint_opacity: 'Pick a Fill Paint and Opacity'
+    pick_fill_paint_opacity: 'Pick a Fill Paint and Opacity',
+    group_identify_label: 'Group identification label'
   },
   properties: {
     id: 'Identify the element',
@@ -73,7 +74,16 @@ export default {
     italic: 'Skean',
     text_anchor_start: 'Align the text in start',
     text_anchor_middle: 'Align the text in middle',
-    text_anchor_end: 'Align the text in end'
+    text_anchor_end: 'Align the text in end',
+    class: 'Element class',
+    serif: 'Serif',
+    sans_serif: 'Sans-serif',
+    cursive: 'Cursive',
+    fantasy: 'Fantasy',
+    monospace: 'Monospace',
+    courier: 'Courier',
+    helvetica: 'Helvetica',
+    times: 'times'
   },
   tools: {
     main_menu: 'Main Menu',
@@ -147,7 +157,10 @@ export default {
     move_front: 'Bring to Front',
     move_up: 'Bring Forward',
     move_down: 'Send Backward',
-    move_back: 'Send to Back'
+    move_back: 'Send to Back',
+    tool_unlink_use: 'Break link to reference element (make unique)',
+    ellipse_circle_tool: 'Ellipse/Circle Tool',
+    square_rect_tool: 'Square/Rect Tool'
   },
   layers: {
     layer: 'Laach',
diff --git a/src/editor/locale/lang.ga.js b/src/editor/locale/lang.ga.js
index 59d5e5d96..b7e5a57ca 100644
--- a/src/editor/locale/lang.ga.js
+++ b/src/editor/locale/lang.ga.js
@@ -25,7 +25,8 @@ export default {
     pathNodeTooltip: 'Drag node to move it. Double-click node to change segment type',
     pathCtrlPtTooltip: 'Drag control point to adjust curve properties',
     pick_stroke_paint_opacity: 'Pick a Stroke Paint and Opacity',
-    pick_fill_paint_opacity: 'Pick a Fill Paint and Opacity'
+    pick_fill_paint_opacity: 'Pick a Fill Paint and Opacity',
+    group_identify_label: 'Group identification label'
   },
   properties: {
     id: 'Identify the element',
@@ -73,7 +74,16 @@ export default {
     italic: 'Iodálach Téacs',
     text_anchor_start: 'Align the text in start',
     text_anchor_middle: 'Align the text in middle',
-    text_anchor_end: 'Align the text in end'
+    text_anchor_end: 'Align the text in end',
+    class: 'Element class',
+    serif: 'Serif',
+    sans_serif: 'Sans-serif',
+    cursive: 'Cursive',
+    fantasy: 'Fantasy',
+    monospace: 'Monospace',
+    courier: 'Courier',
+    helvetica: 'Helvetica',
+    times: 'times'
   },
   tools: {
     main_menu: 'Main Menu',
@@ -147,7 +157,10 @@ export default {
     move_front: 'Bring to Front',
     move_up: 'Bring Forward',
     move_down: 'Send Backward',
-    move_back: 'Send to Back'
+    move_back: 'Send to Back',
+    tool_unlink_use: 'Break link to reference element (make unique)',
+    ellipse_circle_tool: 'Ellipse/Circle Tool',
+    square_rect_tool: 'Square/Rect Tool'
   },
   layers: {
     layer: 'Layer',
diff --git a/src/editor/locale/lang.gl.js b/src/editor/locale/lang.gl.js
index 7fc2d6f61..0fe66e4f4 100644
--- a/src/editor/locale/lang.gl.js
+++ b/src/editor/locale/lang.gl.js
@@ -25,7 +25,8 @@ export default {
     pathNodeTooltip: 'Drag node to move it. Double-click node to change segment type',
     pathCtrlPtTooltip: 'Drag control point to adjust curve properties',
     pick_stroke_paint_opacity: 'Pick a Stroke Paint and Opacity',
-    pick_fill_paint_opacity: 'Pick a Fill Paint and Opacity'
+    pick_fill_paint_opacity: 'Pick a Fill Paint and Opacity',
+    group_identify_label: 'Group identification label'
   },
   properties: {
     id: 'Identify the element',
@@ -73,7 +74,16 @@ export default {
     italic: 'Texto en cursiva',
     text_anchor_start: 'Align the text in start',
     text_anchor_middle: 'Align the text in middle',
-    text_anchor_end: 'Align the text in end'
+    text_anchor_end: 'Align the text in end',
+    class: 'Element class',
+    serif: 'Serif',
+    sans_serif: 'Sans-serif',
+    cursive: 'Cursive',
+    fantasy: 'Fantasy',
+    monospace: 'Monospace',
+    courier: 'Courier',
+    helvetica: 'Helvetica',
+    times: 'times'
   },
   tools: {
     main_menu: 'Main Menu',
@@ -147,7 +157,10 @@ export default {
     move_front: 'Bring to Front',
     move_up: 'Bring Forward',
     move_down: 'Send Backward',
-    move_back: 'Send to Back'
+    move_back: 'Send to Back',
+    tool_unlink_use: 'Break link to reference element (make unique)',
+    ellipse_circle_tool: 'Ellipse/Circle Tool',
+    square_rect_tool: 'Square/Rect Tool'
   },
   layers: {
     layer: 'Layer',
diff --git a/src/editor/locale/lang.he.js b/src/editor/locale/lang.he.js
index f69a9d703..14bf96b8c 100755
--- a/src/editor/locale/lang.he.js
+++ b/src/editor/locale/lang.he.js
@@ -25,7 +25,8 @@ export default {
     pathNodeTooltip: 'Drag node to move it. Double-click node to change segment type',
     pathCtrlPtTooltip: 'Drag control point to adjust curve properties',
     pick_stroke_paint_opacity: 'Pick a Stroke Paint and Opacity',
-    pick_fill_paint_opacity: 'Pick a Fill Paint and Opacity'
+    pick_fill_paint_opacity: 'Pick a Fill Paint and Opacity',
+    group_identify_label: 'Group identification label'
   },
   properties: {
     id: 'Identify the element',
@@ -73,7 +74,16 @@ export default {
     italic: 'טקסט נטוי',
     text_anchor_start: 'Align the text in start',
     text_anchor_middle: 'Align the text in middle',
-    text_anchor_end: 'Align the text in end'
+    text_anchor_end: 'Align the text in end',
+    class: 'Element class',
+    serif: 'Serif',
+    sans_serif: 'Sans-serif',
+    cursive: 'Cursive',
+    fantasy: 'Fantasy',
+    monospace: 'Monospace',
+    courier: 'Courier',
+    helvetica: 'Helvetica',
+    times: 'times'
   },
   tools: {
     main_menu: 'Main Menu',
@@ -147,7 +157,10 @@ export default {
     move_front: 'Bring to Front',
     move_up: 'Bring Forward',
     move_down: 'Send Backward',
-    move_back: 'Send to Back'
+    move_back: 'Send to Back',
+    tool_unlink_use: 'Break link to reference element (make unique)',
+    ellipse_circle_tool: 'Ellipse/Circle Tool',
+    square_rect_tool: 'Square/Rect Tool'
   },
   layers: {
     layer: 'Layer',
diff --git a/src/editor/locale/lang.hi.js b/src/editor/locale/lang.hi.js
index 4c2813db7..52b63b1c9 100644
--- a/src/editor/locale/lang.hi.js
+++ b/src/editor/locale/lang.hi.js
@@ -25,7 +25,8 @@ export default {
     pathNodeTooltip: 'Drag node to move it. Double-click node to change segment type',
     pathCtrlPtTooltip: 'Drag control point to adjust curve properties',
     pick_stroke_paint_opacity: 'Pick a Stroke Paint and Opacity',
-    pick_fill_paint_opacity: 'Pick a Fill Paint and Opacity'
+    pick_fill_paint_opacity: 'Pick a Fill Paint and Opacity',
+    group_identify_label: 'Group identification label'
   },
   properties: {
     id: 'Identify the element',
@@ -73,7 +74,16 @@ export default {
     italic: 'इटैलिक पाठ',
     text_anchor_start: 'Align the text in start',
     text_anchor_middle: 'Align the text in middle',
-    text_anchor_end: 'Align the text in end'
+    text_anchor_end: 'Align the text in end',
+    class: 'Element class',
+    serif: 'Serif',
+    sans_serif: 'Sans-serif',
+    cursive: 'Cursive',
+    fantasy: 'Fantasy',
+    monospace: 'Monospace',
+    courier: 'Courier',
+    helvetica: 'Helvetica',
+    times: 'times'
   },
   tools: {
     main_menu: 'Main Menu',
@@ -147,7 +157,10 @@ export default {
     move_front: 'Bring to Front',
     move_up: 'Bring Forward',
     move_down: 'Send Backward',
-    move_back: 'Send to Back'
+    move_back: 'Send to Back',
+    tool_unlink_use: 'Break link to reference element (make unique)',
+    ellipse_circle_tool: 'Ellipse/Circle Tool',
+    square_rect_tool: 'Square/Rect Tool'
   },
   layers: {
     layer: 'परत',
diff --git a/src/editor/locale/lang.hr.js b/src/editor/locale/lang.hr.js
index b130a753e..afac52de5 100644
--- a/src/editor/locale/lang.hr.js
+++ b/src/editor/locale/lang.hr.js
@@ -25,7 +25,8 @@ export default {
     pathNodeTooltip: 'Drag node to move it. Double-click node to change segment type',
     pathCtrlPtTooltip: 'Drag control point to adjust curve properties',
     pick_stroke_paint_opacity: 'Pick a Stroke Paint and Opacity',
-    pick_fill_paint_opacity: 'Pick a Fill Paint and Opacity'
+    pick_fill_paint_opacity: 'Pick a Fill Paint and Opacity',
+    group_identify_label: 'Group identification label'
   },
   properties: {
     id: 'Identify the element',
@@ -73,7 +74,16 @@ export default {
     italic: 'Italic Text',
     text_anchor_start: 'Align the text in start',
     text_anchor_middle: 'Align the text in middle',
-    text_anchor_end: 'Align the text in end'
+    text_anchor_end: 'Align the text in end',
+    class: 'Element class',
+    serif: 'Serif',
+    sans_serif: 'Sans-serif',
+    cursive: 'Cursive',
+    fantasy: 'Fantasy',
+    monospace: 'Monospace',
+    courier: 'Courier',
+    helvetica: 'Helvetica',
+    times: 'times'
   },
   tools: {
     main_menu: 'Main Menu',
@@ -147,7 +157,10 @@ export default {
     move_front: 'Bring to Front',
     move_up: 'Bring Forward',
     move_down: 'Send Backward',
-    move_back: 'Send to Back'
+    move_back: 'Send to Back',
+    tool_unlink_use: 'Break link to reference element (make unique)',
+    ellipse_circle_tool: 'Ellipse/Circle Tool',
+    square_rect_tool: 'Square/Rect Tool'
   },
   layers: {
     layer: 'Layer',
diff --git a/src/editor/locale/lang.hu.js b/src/editor/locale/lang.hu.js
index 876d56c35..0f6fe4e6d 100644
--- a/src/editor/locale/lang.hu.js
+++ b/src/editor/locale/lang.hu.js
@@ -25,7 +25,8 @@ export default {
     pathNodeTooltip: 'Drag node to move it. Double-click node to change segment type',
     pathCtrlPtTooltip: 'Drag control point to adjust curve properties',
     pick_stroke_paint_opacity: 'Pick a Stroke Paint and Opacity',
-    pick_fill_paint_opacity: 'Pick a Fill Paint and Opacity'
+    pick_fill_paint_opacity: 'Pick a Fill Paint and Opacity',
+    group_identify_label: 'Group identification label'
   },
   properties: {
     id: 'Identify the element',
@@ -73,7 +74,16 @@ export default {
     italic: 'Dőlt szöveg',
     text_anchor_start: 'Align the text in start',
     text_anchor_middle: 'Align the text in middle',
-    text_anchor_end: 'Align the text in end'
+    text_anchor_end: 'Align the text in end',
+    class: 'Element class',
+    serif: 'Serif',
+    sans_serif: 'Sans-serif',
+    cursive: 'Cursive',
+    fantasy: 'Fantasy',
+    monospace: 'Monospace',
+    courier: 'Courier',
+    helvetica: 'Helvetica',
+    times: 'times'
   },
   tools: {
     main_menu: 'Main Menu',
@@ -147,7 +157,10 @@ export default {
     move_front: 'Bring to Front',
     move_up: 'Bring Forward',
     move_down: 'Send Backward',
-    move_back: 'Send to Back'
+    move_back: 'Send to Back',
+    tool_unlink_use: 'Break link to reference element (make unique)',
+    ellipse_circle_tool: 'Ellipse/Circle Tool',
+    square_rect_tool: 'Square/Rect Tool'
   },
   layers: {
     layer: 'Layer',
diff --git a/src/editor/locale/lang.hy.js b/src/editor/locale/lang.hy.js
index 10bc87f08..232b60821 100644
--- a/src/editor/locale/lang.hy.js
+++ b/src/editor/locale/lang.hy.js
@@ -25,7 +25,8 @@ export default {
     pathNodeTooltip: 'Drag node to move it. Double-click node to change segment type',
     pathCtrlPtTooltip: 'Drag control point to adjust curve properties',
     pick_stroke_paint_opacity: 'Pick a Stroke Paint and Opacity',
-    pick_fill_paint_opacity: 'Pick a Fill Paint and Opacity'
+    pick_fill_paint_opacity: 'Pick a Fill Paint and Opacity',
+    group_identify_label: 'Group identification label'
   },
   properties: {
     id: 'Identify the element',
@@ -73,7 +74,16 @@ export default {
     italic: 'Italic Text',
     text_anchor_start: 'Align the text in start',
     text_anchor_middle: 'Align the text in middle',
-    text_anchor_end: 'Align the text in end'
+    text_anchor_end: 'Align the text in end',
+    class: 'Element class',
+    serif: 'Serif',
+    sans_serif: 'Sans-serif',
+    cursive: 'Cursive',
+    fantasy: 'Fantasy',
+    monospace: 'Monospace',
+    courier: 'Courier',
+    helvetica: 'Helvetica',
+    times: 'times'
   },
   tools: {
     main_menu: 'Main Menu',
@@ -147,7 +157,10 @@ export default {
     move_front: 'Bring to Front',
     move_up: 'Bring Forward',
     move_down: 'Send Backward',
-    move_back: 'Send to Back'
+    move_back: 'Send to Back',
+    tool_unlink_use: 'Break link to reference element (make unique)',
+    ellipse_circle_tool: 'Ellipse/Circle Tool',
+    square_rect_tool: 'Square/Rect Tool'
   },
   layers: {
     layer: 'Layer',
diff --git a/src/editor/locale/lang.id.js b/src/editor/locale/lang.id.js
index 7300cfacf..6ec67e247 100644
--- a/src/editor/locale/lang.id.js
+++ b/src/editor/locale/lang.id.js
@@ -25,7 +25,8 @@ export default {
     pathNodeTooltip: 'Drag node to move it. Double-click node to change segment type',
     pathCtrlPtTooltip: 'Drag control point to adjust curve properties',
     pick_stroke_paint_opacity: 'Pick a Stroke Paint and Opacity',
-    pick_fill_paint_opacity: 'Pick a Fill Paint and Opacity'
+    pick_fill_paint_opacity: 'Pick a Fill Paint and Opacity',
+    group_identify_label: 'Group identification label'
   },
   properties: {
     id: 'Identify the element',
@@ -73,7 +74,16 @@ export default {
     italic: 'Italic Teks',
     text_anchor_start: 'Align the text in start',
     text_anchor_middle: 'Align the text in middle',
-    text_anchor_end: 'Align the text in end'
+    text_anchor_end: 'Align the text in end',
+    class: 'Element class',
+    serif: 'Serif',
+    sans_serif: 'Sans-serif',
+    cursive: 'Cursive',
+    fantasy: 'Fantasy',
+    monospace: 'Monospace',
+    courier: 'Courier',
+    helvetica: 'Helvetica',
+    times: 'times'
   },
   tools: {
     main_menu: 'Main Menu',
@@ -147,7 +157,10 @@ export default {
     move_front: 'Bring to Front',
     move_up: 'Bring Forward',
     move_down: 'Send Backward',
-    move_back: 'Send to Back'
+    move_back: 'Send to Back',
+    tool_unlink_use: 'Break link to reference element (make unique)',
+    ellipse_circle_tool: 'Ellipse/Circle Tool',
+    square_rect_tool: 'Square/Rect Tool'
   },
   layers: {
     layer: 'Layer',
diff --git a/src/editor/locale/lang.is.js b/src/editor/locale/lang.is.js
index 7f0816d8c..3969accc6 100644
--- a/src/editor/locale/lang.is.js
+++ b/src/editor/locale/lang.is.js
@@ -25,7 +25,8 @@ export default {
     pathNodeTooltip: 'Drag node to move it. Double-click node to change segment type',
     pathCtrlPtTooltip: 'Drag control point to adjust curve properties',
     pick_stroke_paint_opacity: 'Pick a Stroke Paint and Opacity',
-    pick_fill_paint_opacity: 'Pick a Fill Paint and Opacity'
+    pick_fill_paint_opacity: 'Pick a Fill Paint and Opacity',
+    group_identify_label: 'Group identification label'
   },
   properties: {
     id: 'Identify the element',
@@ -73,7 +74,16 @@ export default {
     italic: 'Italic Text',
     text_anchor_start: 'Align the text in start',
     text_anchor_middle: 'Align the text in middle',
-    text_anchor_end: 'Align the text in end'
+    text_anchor_end: 'Align the text in end',
+    class: 'Element class',
+    serif: 'Serif',
+    sans_serif: 'Sans-serif',
+    cursive: 'Cursive',
+    fantasy: 'Fantasy',
+    monospace: 'Monospace',
+    courier: 'Courier',
+    helvetica: 'Helvetica',
+    times: 'times'
   },
   tools: {
     main_menu: 'Main Menu',
@@ -147,7 +157,10 @@ export default {
     move_front: 'Bring to Front',
     move_up: 'Bring Forward',
     move_down: 'Send Backward',
-    move_back: 'Send to Back'
+    move_back: 'Send to Back',
+    tool_unlink_use: 'Break link to reference element (make unique)',
+    ellipse_circle_tool: 'Ellipse/Circle Tool',
+    square_rect_tool: 'Square/Rect Tool'
   },
   layers: {
     layer: 'Layer',
diff --git a/src/editor/locale/lang.it.js b/src/editor/locale/lang.it.js
index 69afc948e..3469b40fe 100644
--- a/src/editor/locale/lang.it.js
+++ b/src/editor/locale/lang.it.js
@@ -25,7 +25,8 @@ export default {
     pathNodeTooltip: 'Drag node to move it. Double-click node to change segment type',
     pathCtrlPtTooltip: 'Drag control point to adjust curve properties',
     pick_stroke_paint_opacity: 'Pick a Stroke Paint and Opacity',
-    pick_fill_paint_opacity: 'Pick a Fill Paint and Opacity'
+    pick_fill_paint_opacity: 'Pick a Fill Paint and Opacity',
+    group_identify_label: 'Group identification label'
   },
   properties: {
     id: "Identifica l'elemento",
@@ -73,7 +74,16 @@ export default {
     italic: 'Corsivo',
     text_anchor_start: 'Align the text in start',
     text_anchor_middle: 'Align the text in middle',
-    text_anchor_end: 'Align the text in end'
+    text_anchor_end: 'Align the text in end',
+    class: 'Element class',
+    serif: 'Serif',
+    sans_serif: 'Sans-serif',
+    cursive: 'Cursive',
+    fantasy: 'Fantasy',
+    monospace: 'Monospace',
+    courier: 'Courier',
+    helvetica: 'Helvetica',
+    times: 'times'
   },
   tools: {
     main_menu: 'Menù principale',
@@ -147,7 +157,10 @@ export default {
     move_front: 'Bring to Front',
     move_up: 'Bring Forward',
     move_down: 'Send Backward',
-    move_back: 'Send to Back'
+    move_back: 'Send to Back',
+    tool_unlink_use: 'Break link to reference element (make unique)',
+    ellipse_circle_tool: 'Ellipse/Circle Tool',
+    square_rect_tool: 'Square/Rect Tool'
   },
   layers: {
     layer: 'Livello',
diff --git a/src/editor/locale/lang.ja.js b/src/editor/locale/lang.ja.js
index 4d97e1dbb..c4ae55393 100644
--- a/src/editor/locale/lang.ja.js
+++ b/src/editor/locale/lang.ja.js
@@ -25,7 +25,8 @@ export default {
     pathNodeTooltip: 'Drag node to move it. Double-click node to change segment type',
     pathCtrlPtTooltip: 'Drag control point to adjust curve properties',
     pick_stroke_paint_opacity: 'Pick a Stroke Paint and Opacity',
-    pick_fill_paint_opacity: 'Pick a Fill Paint and Opacity'
+    pick_fill_paint_opacity: 'Pick a Fill Paint and Opacity',
+    group_identify_label: 'Group identification label'
   },
   properties: {
     id: 'Identify the element',
@@ -73,7 +74,16 @@ export default {
     italic: 'イタリック体',
     text_anchor_start: 'Align the text in start',
     text_anchor_middle: 'Align the text in middle',
-    text_anchor_end: 'Align the text in end'
+    text_anchor_end: 'Align the text in end',
+    class: 'Element class',
+    serif: 'Serif',
+    sans_serif: 'Sans-serif',
+    cursive: 'Cursive',
+    fantasy: 'Fantasy',
+    monospace: 'Monospace',
+    courier: 'Courier',
+    helvetica: 'Helvetica',
+    times: 'times'
   },
   tools: {
     main_menu: 'Main Menu',
@@ -147,7 +157,10 @@ export default {
     move_front: 'Bring to Front',
     move_up: 'Bring Forward',
     move_down: 'Send Backward',
-    move_back: 'Send to Back'
+    move_back: 'Send to Back',
+    tool_unlink_use: 'Break link to reference element (make unique)',
+    ellipse_circle_tool: 'Ellipse/Circle Tool',
+    square_rect_tool: 'Square/Rect Tool'
   },
   layers: {
     layer: 'レイヤ',
diff --git a/src/editor/locale/lang.ko.js b/src/editor/locale/lang.ko.js
index a6da45f9e..757009bac 100644
--- a/src/editor/locale/lang.ko.js
+++ b/src/editor/locale/lang.ko.js
@@ -25,7 +25,8 @@ export default {
     pathNodeTooltip: 'Drag node to move it. Double-click node to change segment type',
     pathCtrlPtTooltip: 'Drag control point to adjust curve properties',
     pick_stroke_paint_opacity: 'Pick a Stroke Paint and Opacity',
-    pick_fill_paint_opacity: 'Pick a Fill Paint and Opacity'
+    pick_fill_paint_opacity: 'Pick a Fill Paint and Opacity',
+    group_identify_label: 'Group identification label'
   },
   properties: {
     id: 'Identify the element',
@@ -73,7 +74,16 @@ export default {
     italic: '기울임꼴 텍스트',
     text_anchor_start: 'Align the text in start',
     text_anchor_middle: 'Align the text in middle',
-    text_anchor_end: 'Align the text in end'
+    text_anchor_end: 'Align the text in end',
+    class: 'Element class',
+    serif: 'Serif',
+    sans_serif: 'Sans-serif',
+    cursive: 'Cursive',
+    fantasy: 'Fantasy',
+    monospace: 'Monospace',
+    courier: 'Courier',
+    helvetica: 'Helvetica',
+    times: 'times'
   },
   tools: {
     main_menu: 'Main Menu',
@@ -147,7 +157,10 @@ export default {
     move_front: 'Bring to Front',
     move_up: 'Bring Forward',
     move_down: 'Send Backward',
-    move_back: 'Send to Back'
+    move_back: 'Send to Back',
+    tool_unlink_use: 'Break link to reference element (make unique)',
+    ellipse_circle_tool: 'Ellipse/Circle Tool',
+    square_rect_tool: 'Square/Rect Tool'
   },
   layers: {
     layer: 'Layer',
diff --git a/src/editor/locale/lang.lt.js b/src/editor/locale/lang.lt.js
index cc42c6e14..86a2d474c 100644
--- a/src/editor/locale/lang.lt.js
+++ b/src/editor/locale/lang.lt.js
@@ -25,7 +25,8 @@ export default {
     pathNodeTooltip: 'Drag node to move it. Double-click node to change segment type',
     pathCtrlPtTooltip: 'Drag control point to adjust curve properties',
     pick_stroke_paint_opacity: 'Pick a Stroke Paint and Opacity',
-    pick_fill_paint_opacity: 'Pick a Fill Paint and Opacity'
+    pick_fill_paint_opacity: 'Pick a Fill Paint and Opacity',
+    group_identify_label: 'Group identification label'
   },
   properties: {
     id: 'Identify the element',
@@ -73,7 +74,16 @@ export default {
     italic: 'Kursyvas',
     text_anchor_start: 'Align the text in start',
     text_anchor_middle: 'Align the text in middle',
-    text_anchor_end: 'Align the text in end'
+    text_anchor_end: 'Align the text in end',
+    class: 'Element class',
+    serif: 'Serif',
+    sans_serif: 'Sans-serif',
+    cursive: 'Cursive',
+    fantasy: 'Fantasy',
+    monospace: 'Monospace',
+    courier: 'Courier',
+    helvetica: 'Helvetica',
+    times: 'times'
   },
   tools: {
     main_menu: 'Main Menu',
@@ -147,7 +157,10 @@ export default {
     move_front: 'Bring to Front',
     move_up: 'Bring Forward',
     move_down: 'Send Backward',
-    move_back: 'Send to Back'
+    move_back: 'Send to Back',
+    tool_unlink_use: 'Break link to reference element (make unique)',
+    ellipse_circle_tool: 'Ellipse/Circle Tool',
+    square_rect_tool: 'Square/Rect Tool'
   },
   layers: {
     layer: 'Layer',
diff --git a/src/editor/locale/lang.lv.js b/src/editor/locale/lang.lv.js
index b93d6f1a5..4b8719df7 100644
--- a/src/editor/locale/lang.lv.js
+++ b/src/editor/locale/lang.lv.js
@@ -25,7 +25,8 @@ export default {
     pathNodeTooltip: 'Drag node to move it. Double-click node to change segment type',
     pathCtrlPtTooltip: 'Drag control point to adjust curve properties',
     pick_stroke_paint_opacity: 'Pick a Stroke Paint and Opacity',
-    pick_fill_paint_opacity: 'Pick a Fill Paint and Opacity'
+    pick_fill_paint_opacity: 'Pick a Fill Paint and Opacity',
+    group_identify_label: 'Group identification label'
   },
   properties: {
     id: 'Identify the element',
@@ -73,7 +74,16 @@ export default {
     italic: 'Kursīvs',
     text_anchor_start: 'Align the text in start',
     text_anchor_middle: 'Align the text in middle',
-    text_anchor_end: 'Align the text in end'
+    text_anchor_end: 'Align the text in end',
+    class: 'Element class',
+    serif: 'Serif',
+    sans_serif: 'Sans-serif',
+    cursive: 'Cursive',
+    fantasy: 'Fantasy',
+    monospace: 'Monospace',
+    courier: 'Courier',
+    helvetica: 'Helvetica',
+    times: 'times'
   },
   tools: {
     main_menu: 'Main Menu',
@@ -147,7 +157,10 @@ export default {
     move_front: 'Bring to Front',
     move_up: 'Bring Forward',
     move_down: 'Send Backward',
-    move_back: 'Send to Back'
+    move_back: 'Send to Back',
+    tool_unlink_use: 'Break link to reference element (make unique)',
+    ellipse_circle_tool: 'Ellipse/Circle Tool',
+    square_rect_tool: 'Square/Rect Tool'
   },
   layers: {
     layer: 'Layer',
diff --git a/src/editor/locale/lang.mk.js b/src/editor/locale/lang.mk.js
index fdd4cb0b3..baaaa941b 100644
--- a/src/editor/locale/lang.mk.js
+++ b/src/editor/locale/lang.mk.js
@@ -25,7 +25,8 @@ export default {
     pathNodeTooltip: 'Drag node to move it. Double-click node to change segment type',
     pathCtrlPtTooltip: 'Drag control point to adjust curve properties',
     pick_stroke_paint_opacity: 'Pick a Stroke Paint and Opacity',
-    pick_fill_paint_opacity: 'Pick a Fill Paint and Opacity'
+    pick_fill_paint_opacity: 'Pick a Fill Paint and Opacity',
+    group_identify_label: 'Group identification label'
   },
   properties: {
     id: 'Identify the element',
@@ -73,7 +74,16 @@ export default {
     italic: 'Italic текст',
     text_anchor_start: 'Align the text in start',
     text_anchor_middle: 'Align the text in middle',
-    text_anchor_end: 'Align the text in end'
+    text_anchor_end: 'Align the text in end',
+    class: 'Element class',
+    serif: 'Serif',
+    sans_serif: 'Sans-serif',
+    cursive: 'Cursive',
+    fantasy: 'Fantasy',
+    monospace: 'Monospace',
+    courier: 'Courier',
+    helvetica: 'Helvetica',
+    times: 'times'
   },
   tools: {
     main_menu: 'Main Menu',
@@ -147,7 +157,10 @@ export default {
     move_front: 'Bring to Front',
     move_up: 'Bring Forward',
     move_down: 'Send Backward',
-    move_back: 'Send to Back'
+    move_back: 'Send to Back',
+    tool_unlink_use: 'Break link to reference element (make unique)',
+    ellipse_circle_tool: 'Ellipse/Circle Tool',
+    square_rect_tool: 'Square/Rect Tool'
   },
   layers: {
     layer: 'Layer',
diff --git a/src/editor/locale/lang.ms.js b/src/editor/locale/lang.ms.js
index 520f1612d..9d1b6f8e6 100644
--- a/src/editor/locale/lang.ms.js
+++ b/src/editor/locale/lang.ms.js
@@ -25,7 +25,8 @@ export default {
     pathNodeTooltip: 'Drag node to move it. Double-click node to change segment type',
     pathCtrlPtTooltip: 'Drag control point to adjust curve properties',
     pick_stroke_paint_opacity: 'Pick a Stroke Paint and Opacity',
-    pick_fill_paint_opacity: 'Pick a Fill Paint and Opacity'
+    pick_fill_paint_opacity: 'Pick a Fill Paint and Opacity',
+    group_identify_label: 'Group identification label'
   },
   properties: {
     id: 'Identify the element',
@@ -73,7 +74,16 @@ export default {
     italic: 'Italic Teks',
     text_anchor_start: 'Align the text in start',
     text_anchor_middle: 'Align the text in middle',
-    text_anchor_end: 'Align the text in end'
+    text_anchor_end: 'Align the text in end',
+    class: 'Element class',
+    serif: 'Serif',
+    sans_serif: 'Sans-serif',
+    cursive: 'Cursive',
+    fantasy: 'Fantasy',
+    monospace: 'Monospace',
+    courier: 'Courier',
+    helvetica: 'Helvetica',
+    times: 'times'
   },
   tools: {
     main_menu: 'Main Menu',
@@ -147,7 +157,10 @@ export default {
     move_front: 'Bring to Front',
     move_up: 'Bring Forward',
     move_down: 'Send Backward',
-    move_back: 'Send to Back'
+    move_back: 'Send to Back',
+    tool_unlink_use: 'Break link to reference element (make unique)',
+    ellipse_circle_tool: 'Ellipse/Circle Tool',
+    square_rect_tool: 'Square/Rect Tool'
   },
   layers: {
     layer: 'Layer',
diff --git a/src/editor/locale/lang.mt.js b/src/editor/locale/lang.mt.js
index 9ef7d9d13..5c6444bea 100644
--- a/src/editor/locale/lang.mt.js
+++ b/src/editor/locale/lang.mt.js
@@ -25,7 +25,8 @@ export default {
     pathNodeTooltip: 'Drag node to move it. Double-click node to change segment type',
     pathCtrlPtTooltip: 'Drag control point to adjust curve properties',
     pick_stroke_paint_opacity: 'Pick a Stroke Paint and Opacity',
-    pick_fill_paint_opacity: 'Pick a Fill Paint and Opacity'
+    pick_fill_paint_opacity: 'Pick a Fill Paint and Opacity',
+    group_identify_label: 'Group identification label'
   },
   properties: {
     id: 'Identify the element',
@@ -73,7 +74,16 @@ export default {
     italic: 'Test korsiv',
     text_anchor_start: 'Align the text in start',
     text_anchor_middle: 'Align the text in middle',
-    text_anchor_end: 'Align the text in end'
+    text_anchor_end: 'Align the text in end',
+    class: 'Element class',
+    serif: 'Serif',
+    sans_serif: 'Sans-serif',
+    cursive: 'Cursive',
+    fantasy: 'Fantasy',
+    monospace: 'Monospace',
+    courier: 'Courier',
+    helvetica: 'Helvetica',
+    times: 'times'
   },
   tools: {
     main_menu: 'Main Menu',
@@ -147,7 +157,10 @@ export default {
     move_front: 'Bring to Front',
     move_up: 'Bring Forward',
     move_down: 'Send Backward',
-    move_back: 'Send to Back'
+    move_back: 'Send to Back',
+    tool_unlink_use: 'Break link to reference element (make unique)',
+    ellipse_circle_tool: 'Ellipse/Circle Tool',
+    square_rect_tool: 'Square/Rect Tool'
   },
   layers: {
     layer: 'Layer',
diff --git a/src/editor/locale/lang.nl.js b/src/editor/locale/lang.nl.js
index a9f92bc05..318e557b5 100644
--- a/src/editor/locale/lang.nl.js
+++ b/src/editor/locale/lang.nl.js
@@ -25,7 +25,8 @@ export default {
     pathNodeTooltip: 'Drag node to move it. Double-click node to change segment type',
     pathCtrlPtTooltip: 'Drag control point to adjust curve properties',
     pick_stroke_paint_opacity: 'Pick a Stroke Paint and Opacity',
-    pick_fill_paint_opacity: 'Pick a Fill Paint and Opacity'
+    pick_fill_paint_opacity: 'Pick a Fill Paint and Opacity',
+    group_identify_label: 'Group identification label'
   },
   properties: {
     id: 'Identificeer het element',
@@ -73,7 +74,16 @@ export default {
     italic: 'Cursief',
     text_anchor_start: 'Align the text in start',
     text_anchor_middle: 'Align the text in middle',
-    text_anchor_end: 'Align the text in end'
+    text_anchor_end: 'Align the text in end',
+    class: 'Element class',
+    serif: 'Serif',
+    sans_serif: 'Sans-serif',
+    cursive: 'Cursive',
+    fantasy: 'Fantasy',
+    monospace: 'Monospace',
+    courier: 'Courier',
+    helvetica: 'Helvetica',
+    times: 'times'
   },
   tools: {
     main_menu: 'Hoofdmenu',
@@ -147,7 +157,10 @@ export default {
     move_front: 'Bring to Front',
     move_up: 'Bring Forward',
     move_down: 'Send Backward',
-    move_back: 'Send to Back'
+    move_back: 'Send to Back',
+    tool_unlink_use: 'Break link to reference element (make unique)',
+    ellipse_circle_tool: 'Ellipse/Circle Tool',
+    square_rect_tool: 'Square/Rect Tool'
   },
   layers: {
     layer: 'Laag',
diff --git a/src/editor/locale/lang.no.js b/src/editor/locale/lang.no.js
index 868808258..d95124d43 100644
--- a/src/editor/locale/lang.no.js
+++ b/src/editor/locale/lang.no.js
@@ -25,7 +25,8 @@ export default {
     pathNodeTooltip: 'Drag node to move it. Double-click node to change segment type',
     pathCtrlPtTooltip: 'Drag control point to adjust curve properties',
     pick_stroke_paint_opacity: 'Pick a Stroke Paint and Opacity',
-    pick_fill_paint_opacity: 'Pick a Fill Paint and Opacity'
+    pick_fill_paint_opacity: 'Pick a Fill Paint and Opacity',
+    group_identify_label: 'Group identification label'
   },
   properties: {
     id: 'Identify the element',
@@ -73,7 +74,16 @@ export default {
     italic: 'Kursiv tekst',
     text_anchor_start: 'Align the text in start',
     text_anchor_middle: 'Align the text in middle',
-    text_anchor_end: 'Align the text in end'
+    text_anchor_end: 'Align the text in end',
+    class: 'Element class',
+    serif: 'Serif',
+    sans_serif: 'Sans-serif',
+    cursive: 'Cursive',
+    fantasy: 'Fantasy',
+    monospace: 'Monospace',
+    courier: 'Courier',
+    helvetica: 'Helvetica',
+    times: 'times'
   },
   tools: {
     main_menu: 'Main Menu',
@@ -147,7 +157,10 @@ export default {
     move_front: 'Bring to Front',
     move_up: 'Bring Forward',
     move_down: 'Send Backward',
-    move_back: 'Send to Back'
+    move_back: 'Send to Back',
+    tool_unlink_use: 'Break link to reference element (make unique)',
+    ellipse_circle_tool: 'Ellipse/Circle Tool',
+    square_rect_tool: 'Square/Rect Tool'
   },
   layers: {
     layer: 'Layer',
diff --git a/src/editor/locale/lang.pl.js b/src/editor/locale/lang.pl.js
index 2ab816c25..a36afa74f 100644
--- a/src/editor/locale/lang.pl.js
+++ b/src/editor/locale/lang.pl.js
@@ -26,7 +26,8 @@ export default {
     pathNodeTooltip: 'Drag node to move it. Double-click node to change segment type',
     pathCtrlPtTooltip: 'Drag control point to adjust curve properties',
     pick_stroke_paint_opacity: 'Pick a Stroke Paint and Opacity',
-    pick_fill_paint_opacity: 'Pick a Fill Paint and Opacity'
+    pick_fill_paint_opacity: 'Pick a Fill Paint and Opacity',
+    group_identify_label: 'Group identification label'
   },
   properties: {
     id: 'Identyfikator elementu',
@@ -74,7 +75,16 @@ export default {
     italic: 'Kursywa',
     text_anchor_start: 'Align the text in start',
     text_anchor_middle: 'Align the text in middle',
-    text_anchor_end: 'Align the text in end'
+    text_anchor_end: 'Align the text in end',
+    class: 'Element class',
+    serif: 'Serif',
+    sans_serif: 'Sans-serif',
+    cursive: 'Cursive',
+    fantasy: 'Fantasy',
+    monospace: 'Monospace',
+    courier: 'Courier',
+    helvetica: 'Helvetica',
+    times: 'times'
   },
   tools: {
     main_menu: 'Menu główne',
@@ -148,7 +158,10 @@ export default {
     move_front: 'Przenieś do przodu',
     move_up: 'Przenieś warstwę w górę',
     move_down: 'Przenieś warstwę w dół',
-    move_back: 'Przenieś do tyłu'
+    move_back: 'Przenieś do tyłu',
+    tool_unlink_use: 'Break link to reference element (make unique)',
+    ellipse_circle_tool: 'Ellipse/Circle Tool',
+    square_rect_tool: 'Square/Rect Tool'
   },
   layers: {
     layer: 'Warstwa',
diff --git a/src/editor/locale/lang.pt-BR.js b/src/editor/locale/lang.pt-BR.js
index 5161c3ebd..4a7307936 100644
--- a/src/editor/locale/lang.pt-BR.js
+++ b/src/editor/locale/lang.pt-BR.js
@@ -25,7 +25,8 @@ export default {
     pathNodeTooltip: 'Drag node to move it. Double-click node to change segment type',
     pathCtrlPtTooltip: 'Drag control point to adjust curve properties',
     pick_stroke_paint_opacity: 'Pick a Stroke Paint and Opacity',
-    pick_fill_paint_opacity: 'Pick a Fill Paint and Opacity'
+    pick_fill_paint_opacity: 'Pick a Fill Paint and Opacity',
+    group_identify_label: 'Group identification label'
   },
   properties: {
     id: 'Identifica o elemento',
@@ -73,7 +74,16 @@ export default {
     italic: 'Italico',
     text_anchor_start: 'Align the text in start',
     text_anchor_middle: 'Align the text in middle',
-    text_anchor_end: 'Align the text in end'
+    text_anchor_end: 'Align the text in end',
+    class: 'Element class',
+    serif: 'Serif',
+    sans_serif: 'Sans-serif',
+    cursive: 'Cursive',
+    fantasy: 'Fantasy',
+    monospace: 'Monospace',
+    courier: 'Courier',
+    helvetica: 'Helvetica',
+    times: 'times'
   },
   tools: {
     main_menu: 'Menu Principal',
@@ -147,7 +157,10 @@ export default {
     move_front: 'Trazer para Frente',
     move_up: 'Avançar',
     move_down: 'Recuar',
-    move_back: 'Enviar para Trás'
+    move_back: 'Enviar para Trás',
+    tool_unlink_use: 'Break link to reference element (make unique)',
+    ellipse_circle_tool: 'Ellipse/Circle Tool',
+    square_rect_tool: 'Square/Rect Tool'
   },
   layers: {
     layer: 'Camada',
diff --git a/src/editor/locale/lang.pt-PT.js b/src/editor/locale/lang.pt-PT.js
index e7ebd2302..9329037be 100644
--- a/src/editor/locale/lang.pt-PT.js
+++ b/src/editor/locale/lang.pt-PT.js
@@ -25,7 +25,8 @@ export default {
     pathNodeTooltip: 'Drag node to move it. Double-click node to change segment type',
     pathCtrlPtTooltip: 'Drag control point to adjust curve properties',
     pick_stroke_paint_opacity: 'Pick a Stroke Paint and Opacity',
-    pick_fill_paint_opacity: 'Pick a Fill Paint and Opacity'
+    pick_fill_paint_opacity: 'Pick a Fill Paint and Opacity',
+    group_identify_label: 'Group identification label'
   },
   properties: {
     id: 'Identify the element',
@@ -73,7 +74,16 @@ export default {
     italic: 'Texto em itálico',
     text_anchor_start: 'Align the text in start',
     text_anchor_middle: 'Align the text in middle',
-    text_anchor_end: 'Align the text in end'
+    text_anchor_end: 'Align the text in end',
+    class: 'Element class',
+    serif: 'Serif',
+    sans_serif: 'Sans-serif',
+    cursive: 'Cursive',
+    fantasy: 'Fantasy',
+    monospace: 'Monospace',
+    courier: 'Courier',
+    helvetica: 'Helvetica',
+    times: 'times'
   },
   tools: {
     main_menu: 'Main Menu',
@@ -147,7 +157,10 @@ export default {
     move_front: 'Bring to Front',
     move_up: 'Bring Forward',
     move_down: 'Send Backward',
-    move_back: 'Send to Back'
+    move_back: 'Send to Back',
+    tool_unlink_use: 'Break link to reference element (make unique)',
+    ellipse_circle_tool: 'Ellipse/Circle Tool',
+    square_rect_tool: 'Square/Rect Tool'
   },
   layers: {
     layer: 'Layer',
diff --git a/src/editor/locale/lang.ro.js b/src/editor/locale/lang.ro.js
index cd27d7082..1e8ac950f 100644
--- a/src/editor/locale/lang.ro.js
+++ b/src/editor/locale/lang.ro.js
@@ -25,7 +25,8 @@ export default {
     pathNodeTooltip: 'Drag node to move it. Double-click node to change segment type',
     pathCtrlPtTooltip: 'Drag control point to adjust curve properties',
     pick_stroke_paint_opacity: 'Pick a Stroke Paint and Opacity',
-    pick_fill_paint_opacity: 'Pick a Fill Paint and Opacity'
+    pick_fill_paint_opacity: 'Pick a Fill Paint and Opacity',
+    group_identify_label: 'Group identification label'
   },
   properties: {
     id: 'Identificare element',
@@ -73,7 +74,16 @@ export default {
     italic: 'Text Înclinat',
     text_anchor_start: 'Align the text in start',
     text_anchor_middle: 'Align the text in middle',
-    text_anchor_end: 'Align the text in end'
+    text_anchor_end: 'Align the text in end',
+    class: 'Element class',
+    serif: 'Serif',
+    sans_serif: 'Sans-serif',
+    cursive: 'Cursive',
+    fantasy: 'Fantasy',
+    monospace: 'Monospace',
+    courier: 'Courier',
+    helvetica: 'Helvetica',
+    times: 'times'
   },
   tools: {
     main_menu: 'Menu Principal',
@@ -147,7 +157,10 @@ export default {
     move_front: 'Pune in faţa',
     move_up: 'Pune in spate',
     move_down: 'Trimite in faţa',
-    move_back: 'Trimite in spate'
+    move_back: 'Trimite in spate',
+    tool_unlink_use: 'Break link to reference element (make unique)',
+    ellipse_circle_tool: 'Ellipse/Circle Tool',
+    square_rect_tool: 'Square/Rect Tool'
   },
   layers: {
     layer: 'Strat',
diff --git a/src/editor/locale/lang.ru.js b/src/editor/locale/lang.ru.js
index 8668ccc4e..42120d96c 100644
--- a/src/editor/locale/lang.ru.js
+++ b/src/editor/locale/lang.ru.js
@@ -25,7 +25,17 @@ export default {
     pathNodeTooltip: 'Drag node to move it. Double-click node to change segment type',
     pathCtrlPtTooltip: 'Drag control point to adjust curve properties',
     pick_stroke_paint_opacity: 'Pick a Stroke Paint and Opacity',
-    pick_fill_paint_opacity: 'Pick a Fill Paint and Opacity'
+    pick_fill_paint_opacity: 'Pick a Fill Paint and Opacity',
+    group_identify_label: 'Group identification label',
+    class: 'Element class',
+    serif: 'Serif',
+    sans_serif: 'Sans-serif',
+    cursive: 'Cursive',
+    fantasy: 'Fantasy',
+    monospace: 'Monospace',
+    courier: 'Courier',
+    helvetica: 'Helvetica',
+    times: 'times'
   },
   properties: {
     id: 'Идентификатор элемента',
@@ -147,7 +157,10 @@ export default {
     move_front: 'На передний план',
     move_up: 'Передвинуть вперед',
     move_down: 'Передвинуть назад',
-    move_back: 'На задний план'
+    move_back: 'На задний план',
+    tool_unlink_use: 'Break link to reference element (make unique)',
+    ellipse_circle_tool: 'Ellipse/Circle Tool',
+    square_rect_tool: 'Square/Rect Tool'
   },
   layers: {
     layer: 'Слой',
diff --git a/src/editor/locale/lang.sk.js b/src/editor/locale/lang.sk.js
index df0aa3d70..2a654965d 100644
--- a/src/editor/locale/lang.sk.js
+++ b/src/editor/locale/lang.sk.js
@@ -25,7 +25,8 @@ export default {
     pathNodeTooltip: 'Drag node to move it. Double-click node to change segment type',
     pathCtrlPtTooltip: 'Drag control point to adjust curve properties',
     pick_stroke_paint_opacity: 'Pick a Stroke Paint and Opacity',
-    pick_fill_paint_opacity: 'Pick a Fill Paint and Opacity'
+    pick_fill_paint_opacity: 'Pick a Fill Paint and Opacity',
+    group_identify_label: 'Group identification label'
   },
   properties: {
     id: 'Zmeniť ID elementu',
@@ -73,7 +74,16 @@ export default {
     italic: 'Kurzíva',
     text_anchor_start: 'Align the text in start',
     text_anchor_middle: 'Align the text in middle',
-    text_anchor_end: 'Align the text in end'
+    text_anchor_end: 'Align the text in end',
+    class: 'Element class',
+    serif: 'Serif',
+    sans_serif: 'Sans-serif',
+    cursive: 'Cursive',
+    fantasy: 'Fantasy',
+    monospace: 'Monospace',
+    courier: 'Courier',
+    helvetica: 'Helvetica',
+    times: 'times'
   },
   tools: {
     main_menu: 'Hlavné menu',
@@ -147,7 +157,10 @@ export default {
     move_front: 'Vysuň navrch',
     move_up: 'Vysuň vpred',
     move_down: 'Zasuň na spodok',
-    move_back: 'Zasuň dozadu'
+    move_back: 'Zasuň dozadu',
+    tool_unlink_use: 'Break link to reference element (make unique)',
+    ellipse_circle_tool: 'Ellipse/Circle Tool',
+    square_rect_tool: 'Square/Rect Tool'
   },
   layers: {
     layer: 'Vrstva',
diff --git a/src/editor/locale/lang.sl.js b/src/editor/locale/lang.sl.js
index ee4fe3103..8e5307f90 100644
--- a/src/editor/locale/lang.sl.js
+++ b/src/editor/locale/lang.sl.js
@@ -25,7 +25,8 @@ export default {
     pathNodeTooltip: 'Drag node to move it. Double-click node to change segment type',
     pathCtrlPtTooltip: 'Drag control point to adjust curve properties',
     pick_stroke_paint_opacity: 'Pick a Stroke Paint and Opacity',
-    pick_fill_paint_opacity: 'Pick a Fill Paint and Opacity'
+    pick_fill_paint_opacity: 'Pick a Fill Paint and Opacity',
+    group_identify_label: 'Group identification label'
   },
   properties: {
     id: 'ID elementa',
@@ -73,7 +74,16 @@ export default {
     italic: 'Poševno',
     text_anchor_start: 'Align the text in start',
     text_anchor_middle: 'Align the text in middle',
-    text_anchor_end: 'Align the text in end'
+    text_anchor_end: 'Align the text in end',
+    class: 'Element class',
+    serif: 'Serif',
+    sans_serif: 'Sans-serif',
+    cursive: 'Cursive',
+    fantasy: 'Fantasy',
+    monospace: 'Monospace',
+    courier: 'Courier',
+    helvetica: 'Helvetica',
+    times: 'times'
   },
   tools: {
     main_menu: 'Glavni meni',
@@ -147,7 +157,10 @@ export default {
     move_front: 'Postavi v ospredje',
     move_up: 'Pomakni naporej',
     move_down: 'Pomakni nazaj',
-    move_back: 'Postavi v ozadje'
+    move_back: 'Postavi v ozadje',
+    tool_unlink_use: 'Break link to reference element (make unique)',
+    ellipse_circle_tool: 'Ellipse/Circle Tool',
+    square_rect_tool: 'Square/Rect Tool'
   },
   layers: {
     layer: 'Sloj',
diff --git a/src/editor/locale/lang.sq.js b/src/editor/locale/lang.sq.js
index 34df0832d..11a2d58a5 100644
--- a/src/editor/locale/lang.sq.js
+++ b/src/editor/locale/lang.sq.js
@@ -25,7 +25,8 @@ export default {
     pathNodeTooltip: 'Drag node to move it. Double-click node to change segment type',
     pathCtrlPtTooltip: 'Drag control point to adjust curve properties',
     pick_stroke_paint_opacity: 'Pick a Stroke Paint and Opacity',
-    pick_fill_paint_opacity: 'Pick a Fill Paint and Opacity'
+    pick_fill_paint_opacity: 'Pick a Fill Paint and Opacity',
+    group_identify_label: 'Group identification label'
   },
   properties: {
     id: 'Identify the element',
@@ -73,7 +74,16 @@ export default {
     italic: 'Italic Text',
     text_anchor_start: 'Align the text in start',
     text_anchor_middle: 'Align the text in middle',
-    text_anchor_end: 'Align the text in end'
+    text_anchor_end: 'Align the text in end',
+    class: 'Element class',
+    serif: 'Serif',
+    sans_serif: 'Sans-serif',
+    cursive: 'Cursive',
+    fantasy: 'Fantasy',
+    monospace: 'Monospace',
+    courier: 'Courier',
+    helvetica: 'Helvetica',
+    times: 'times'
   },
   tools: {
     main_menu: 'Main Menu',
@@ -147,7 +157,10 @@ export default {
     move_front: 'Bring to Front',
     move_up: 'Bring Forward',
     move_down: 'Send Backward',
-    move_back: 'Send to Back'
+    move_back: 'Send to Back',
+    tool_unlink_use: 'Break link to reference element (make unique)',
+    ellipse_circle_tool: 'Ellipse/Circle Tool',
+    square_rect_tool: 'Square/Rect Tool'
   },
   layers: {
     layer: 'Layer',
diff --git a/src/editor/locale/lang.sr.js b/src/editor/locale/lang.sr.js
index 0ed665a06..743e94dd7 100644
--- a/src/editor/locale/lang.sr.js
+++ b/src/editor/locale/lang.sr.js
@@ -25,7 +25,8 @@ export default {
     pathNodeTooltip: 'Drag node to move it. Double-click node to change segment type',
     pathCtrlPtTooltip: 'Drag control point to adjust curve properties',
     pick_stroke_paint_opacity: 'Pick a Stroke Paint and Opacity',
-    pick_fill_paint_opacity: 'Pick a Fill Paint and Opacity'
+    pick_fill_paint_opacity: 'Pick a Fill Paint and Opacity',
+    group_identify_label: 'Group identification label'
   },
   properties: {
     id: 'Identify the element',
@@ -73,7 +74,16 @@ export default {
     italic: 'Италиц текст',
     text_anchor_start: 'Align the text in start',
     text_anchor_middle: 'Align the text in middle',
-    text_anchor_end: 'Align the text in end'
+    text_anchor_end: 'Align the text in end',
+    class: 'Element class',
+    serif: 'Serif',
+    sans_serif: 'Sans-serif',
+    cursive: 'Cursive',
+    fantasy: 'Fantasy',
+    monospace: 'Monospace',
+    courier: 'Courier',
+    helvetica: 'Helvetica',
+    times: 'times'
   },
   tools: {
     main_menu: 'Main Menu',
@@ -147,7 +157,10 @@ export default {
     move_front: 'Bring to Front',
     move_up: 'Bring Forward',
     move_down: 'Send Backward',
-    move_back: 'Send to Back'
+    move_back: 'Send to Back',
+    tool_unlink_use: 'Break link to reference element (make unique)',
+    ellipse_circle_tool: 'Ellipse/Circle Tool',
+    square_rect_tool: 'Square/Rect Tool'
   },
   layers: {
     layer: 'Layer',
diff --git a/src/editor/locale/lang.sv.js b/src/editor/locale/lang.sv.js
index 162cb3058..c7be7324f 100644
--- a/src/editor/locale/lang.sv.js
+++ b/src/editor/locale/lang.sv.js
@@ -25,7 +25,8 @@ export default {
     pathNodeTooltip: 'Drag node to move it. Double-click node to change segment type',
     pathCtrlPtTooltip: 'Drag control point to adjust curve properties',
     pick_stroke_paint_opacity: 'Pick a Stroke Paint and Opacity',
-    pick_fill_paint_opacity: 'Pick a Fill Paint and Opacity'
+    pick_fill_paint_opacity: 'Pick a Fill Paint and Opacity',
+    group_identify_label: 'Group identification label'
   },
   properties: {
     id: 'Identify the element',
@@ -73,7 +74,16 @@ export default {
     italic: 'Kursiv text',
     text_anchor_start: 'Align the text in start',
     text_anchor_middle: 'Align the text in middle',
-    text_anchor_end: 'Align the text in end'
+    text_anchor_end: 'Align the text in end',
+    class: 'Element class',
+    serif: 'Serif',
+    sans_serif: 'Sans-serif',
+    cursive: 'Cursive',
+    fantasy: 'Fantasy',
+    monospace: 'Monospace',
+    courier: 'Courier',
+    helvetica: 'Helvetica',
+    times: 'times'
   },
   tools: {
     main_menu: 'Main Menu',
@@ -147,7 +157,10 @@ export default {
     move_front: 'Bring to Front',
     move_up: 'Bring Forward',
     move_down: 'Send Backward',
-    move_back: 'Send to Back'
+    move_back: 'Send to Back',
+    tool_unlink_use: 'Break link to reference element (make unique)',
+    ellipse_circle_tool: 'Ellipse/Circle Tool',
+    square_rect_tool: 'Square/Rect Tool'
   },
   layers: {
     layer: 'Layer',
diff --git a/src/editor/locale/lang.sw.js b/src/editor/locale/lang.sw.js
index cac3dc76b..32adecdd3 100644
--- a/src/editor/locale/lang.sw.js
+++ b/src/editor/locale/lang.sw.js
@@ -25,7 +25,8 @@ export default {
     pathNodeTooltip: 'Drag node to move it. Double-click node to change segment type',
     pathCtrlPtTooltip: 'Drag control point to adjust curve properties',
     pick_stroke_paint_opacity: 'Pick a Stroke Paint and Opacity',
-    pick_fill_paint_opacity: 'Pick a Fill Paint and Opacity'
+    pick_fill_paint_opacity: 'Pick a Fill Paint and Opacity',
+    group_identify_label: 'Group identification label'
   },
   properties: {
     id: 'Identify the element',
@@ -73,7 +74,16 @@ export default {
     italic: 'Italiki Nakala',
     text_anchor_start: 'Align the text in start',
     text_anchor_middle: 'Align the text in middle',
-    text_anchor_end: 'Align the text in end'
+    text_anchor_end: 'Align the text in end',
+    class: 'Element class',
+    serif: 'Serif',
+    sans_serif: 'Sans-serif',
+    cursive: 'Cursive',
+    fantasy: 'Fantasy',
+    monospace: 'Monospace',
+    courier: 'Courier',
+    helvetica: 'Helvetica',
+    times: 'times'
   },
   tools: {
     main_menu: 'Main Menu',
@@ -147,7 +157,10 @@ export default {
     move_front: 'Bring to Front',
     move_up: 'Bring Forward',
     move_down: 'Send Backward',
-    move_back: 'Send to Back'
+    move_back: 'Send to Back',
+    tool_unlink_use: 'Break link to reference element (make unique)',
+    ellipse_circle_tool: 'Ellipse/Circle Tool',
+    square_rect_tool: 'Square/Rect Tool'
   },
   layers: {
     layer: 'Layer',
diff --git a/src/editor/locale/lang.th.js b/src/editor/locale/lang.th.js
index fa14e1923..877d1585a 100644
--- a/src/editor/locale/lang.th.js
+++ b/src/editor/locale/lang.th.js
@@ -25,7 +25,8 @@ export default {
     pathNodeTooltip: 'Drag node to move it. Double-click node to change segment type',
     pathCtrlPtTooltip: 'Drag control point to adjust curve properties',
     pick_stroke_paint_opacity: 'Pick a Stroke Paint and Opacity',
-    pick_fill_paint_opacity: 'Pick a Fill Paint and Opacity'
+    pick_fill_paint_opacity: 'Pick a Fill Paint and Opacity',
+    group_identify_label: 'Group identification label'
   },
   properties: {
     id: 'Identify the element',
@@ -73,7 +74,16 @@ export default {
     italic: 'ข้อความตัวเอียง',
     text_anchor_start: 'Align the text in start',
     text_anchor_middle: 'Align the text in middle',
-    text_anchor_end: 'Align the text in end'
+    text_anchor_end: 'Align the text in end',
+    class: 'Element class',
+    serif: 'Serif',
+    sans_serif: 'Sans-serif',
+    cursive: 'Cursive',
+    fantasy: 'Fantasy',
+    monospace: 'Monospace',
+    courier: 'Courier',
+    helvetica: 'Helvetica',
+    times: 'times'
   },
   tools: {
     main_menu: 'Main Menu',
@@ -147,7 +157,10 @@ export default {
     move_front: 'Bring to Front',
     move_up: 'Bring Forward',
     move_down: 'Send Backward',
-    move_back: 'Send to Back'
+    move_back: 'Send to Back',
+    tool_unlink_use: 'Break link to reference element (make unique)',
+    ellipse_circle_tool: 'Ellipse/Circle Tool',
+    square_rect_tool: 'Square/Rect Tool'
   },
   layers: {
     layer: 'Layer',
diff --git a/src/editor/locale/lang.tl.js b/src/editor/locale/lang.tl.js
index 5127745db..9578bdf0d 100644
--- a/src/editor/locale/lang.tl.js
+++ b/src/editor/locale/lang.tl.js
@@ -25,7 +25,8 @@ export default {
     pathNodeTooltip: 'Drag node to move it. Double-click node to change segment type',
     pathCtrlPtTooltip: 'Drag control point to adjust curve properties',
     pick_stroke_paint_opacity: 'Pick a Stroke Paint and Opacity',
-    pick_fill_paint_opacity: 'Pick a Fill Paint and Opacity'
+    pick_fill_paint_opacity: 'Pick a Fill Paint and Opacity',
+    group_identify_label: 'Group identification label'
   },
   properties: {
     id: 'Identify the element',
@@ -73,7 +74,16 @@ export default {
     italic: 'Italic Text',
     text_anchor_start: 'Align the text in start',
     text_anchor_middle: 'Align the text in middle',
-    text_anchor_end: 'Align the text in end'
+    text_anchor_end: 'Align the text in end',
+    class: 'Element class',
+    serif: 'Serif',
+    sans_serif: 'Sans-serif',
+    cursive: 'Cursive',
+    fantasy: 'Fantasy',
+    monospace: 'Monospace',
+    courier: 'Courier',
+    helvetica: 'Helvetica',
+    times: 'times'
   },
   tools: {
     main_menu: 'Main Menu',
@@ -147,7 +157,10 @@ export default {
     move_front: 'Bring to Front',
     move_up: 'Bring Forward',
     move_down: 'Send Backward',
-    move_back: 'Send to Back'
+    move_back: 'Send to Back',
+    tool_unlink_use: 'Break link to reference element (make unique)',
+    ellipse_circle_tool: 'Ellipse/Circle Tool',
+    square_rect_tool: 'Square/Rect Tool'
   },
   layers: {
     layer: 'Layer',
diff --git a/src/editor/locale/lang.tr.js b/src/editor/locale/lang.tr.js
index 8eb8abf0c..7b91ee5df 100644
--- a/src/editor/locale/lang.tr.js
+++ b/src/editor/locale/lang.tr.js
@@ -25,7 +25,8 @@ export default {
     pathNodeTooltip: 'Drag node to move it. Double-click node to change segment type',
     pathCtrlPtTooltip: 'Drag control point to adjust curve properties',
     pick_stroke_paint_opacity: 'Pick a Stroke Paint and Opacity',
-    pick_fill_paint_opacity: 'Pick a Fill Paint and Opacity'
+    pick_fill_paint_opacity: 'Pick a Fill Paint and Opacity',
+    group_identify_label: 'Group identification label'
   },
   properties: {
     id: 'Identify the element',
@@ -73,7 +74,16 @@ export default {
     italic: 'Italik yazı',
     text_anchor_start: 'Align the text in start',
     text_anchor_middle: 'Align the text in middle',
-    text_anchor_end: 'Align the text in end'
+    text_anchor_end: 'Align the text in end',
+    class: 'Element class',
+    serif: 'Serif',
+    sans_serif: 'Sans-serif',
+    cursive: 'Cursive',
+    fantasy: 'Fantasy',
+    monospace: 'Monospace',
+    courier: 'Courier',
+    helvetica: 'Helvetica',
+    times: 'times'
   },
   tools: {
     main_menu: 'Main Menu',
@@ -147,7 +157,10 @@ export default {
     move_front: 'Bring to Front',
     move_up: 'Bring Forward',
     move_down: 'Send Backward',
-    move_back: 'Send to Back'
+    move_back: 'Send to Back',
+    tool_unlink_use: 'Break link to reference element (make unique)',
+    ellipse_circle_tool: 'Ellipse/Circle Tool',
+    square_rect_tool: 'Square/Rect Tool'
   },
   layers: {
     layer: 'Layer',
diff --git a/src/editor/locale/lang.uk.js b/src/editor/locale/lang.uk.js
index 3c0724db2..5399031c4 100644
--- a/src/editor/locale/lang.uk.js
+++ b/src/editor/locale/lang.uk.js
@@ -25,7 +25,8 @@ export default {
     pathNodeTooltip: 'Drag node to move it. Double-click node to change segment type',
     pathCtrlPtTooltip: 'Drag control point to adjust curve properties',
     pick_stroke_paint_opacity: 'Pick a Stroke Paint and Opacity',
-    pick_fill_paint_opacity: 'Pick a Fill Paint and Opacity'
+    pick_fill_paint_opacity: 'Pick a Fill Paint and Opacity',
+    group_identify_label: 'Group identification label'
   },
   properties: {
     id: 'Identify the element',
@@ -73,7 +74,16 @@ export default {
     italic: 'Похилий текст',
     text_anchor_start: 'Align the text in start',
     text_anchor_middle: 'Align the text in middle',
-    text_anchor_end: 'Align the text in end'
+    text_anchor_end: 'Align the text in end',
+    class: 'Element class',
+    serif: 'Serif',
+    sans_serif: 'Sans-serif',
+    cursive: 'Cursive',
+    fantasy: 'Fantasy',
+    monospace: 'Monospace',
+    courier: 'Courier',
+    helvetica: 'Helvetica',
+    times: 'times'
   },
   tools: {
     main_menu: 'Main Menu',
@@ -147,7 +157,10 @@ export default {
     move_front: 'Bring to Front',
     move_up: 'Bring Forward',
     move_down: 'Send Backward',
-    move_back: 'Send to Back'
+    move_back: 'Send to Back',
+    tool_unlink_use: 'Break link to reference element (make unique)',
+    ellipse_circle_tool: 'Ellipse/Circle Tool',
+    square_rect_tool: 'Square/Rect Tool'
   },
   layers: {
     layer: 'Layer',
diff --git a/src/editor/locale/lang.vi.js b/src/editor/locale/lang.vi.js
index 21d1124fb..d39a910eb 100644
--- a/src/editor/locale/lang.vi.js
+++ b/src/editor/locale/lang.vi.js
@@ -25,7 +25,8 @@ export default {
     pathNodeTooltip: 'Drag node to move it. Double-click node to change segment type',
     pathCtrlPtTooltip: 'Drag control point to adjust curve properties',
     pick_stroke_paint_opacity: 'Pick a Stroke Paint and Opacity',
-    pick_fill_paint_opacity: 'Pick a Fill Paint and Opacity'
+    pick_fill_paint_opacity: 'Pick a Fill Paint and Opacity',
+    group_identify_label: 'Group identification label'
   },
   properties: {
     id: 'Identify the element',
@@ -73,7 +74,16 @@ export default {
     italic: 'Italic Text',
     text_anchor_start: 'Align the text in start',
     text_anchor_middle: 'Align the text in middle',
-    text_anchor_end: 'Align the text in end'
+    text_anchor_end: 'Align the text in end',
+    class: 'Element class',
+    serif: 'Serif',
+    sans_serif: 'Sans-serif',
+    cursive: 'Cursive',
+    fantasy: 'Fantasy',
+    monospace: 'Monospace',
+    courier: 'Courier',
+    helvetica: 'Helvetica',
+    times: 'times'
   },
   tools: {
     main_menu: 'Main Menu',
@@ -147,7 +157,10 @@ export default {
     move_front: 'Bring to Front',
     move_up: 'Bring Forward',
     move_down: 'Send Backward',
-    move_back: 'Send to Back'
+    move_back: 'Send to Back',
+    tool_unlink_use: 'Break link to reference element (make unique)',
+    ellipse_circle_tool: 'Ellipse/Circle Tool',
+    square_rect_tool: 'Square/Rect Tool'
   },
   layers: {
     layer: 'Layer',
diff --git a/src/editor/locale/lang.yi.js b/src/editor/locale/lang.yi.js
index 96b5ded5f..4b38ff09c 100644
--- a/src/editor/locale/lang.yi.js
+++ b/src/editor/locale/lang.yi.js
@@ -25,7 +25,8 @@ export default {
     pathNodeTooltip: 'Drag node to move it. Double-click node to change segment type',
     pathCtrlPtTooltip: 'Drag control point to adjust curve properties',
     pick_stroke_paint_opacity: 'Pick a Stroke Paint and Opacity',
-    pick_fill_paint_opacity: 'Pick a Fill Paint and Opacity'
+    pick_fill_paint_opacity: 'Pick a Fill Paint and Opacity',
+    group_identify_label: 'Group identification label'
   },
   properties: {
     id: 'Identify the element',
@@ -73,7 +74,16 @@ export default {
     italic: 'יטאַליק טעקסט',
     text_anchor_start: 'Align the text in start',
     text_anchor_middle: 'Align the text in middle',
-    text_anchor_end: 'Align the text in end'
+    text_anchor_end: 'Align the text in end',
+    class: 'Element class',
+    serif: 'Serif',
+    sans_serif: 'Sans-serif',
+    cursive: 'Cursive',
+    fantasy: 'Fantasy',
+    monospace: 'Monospace',
+    courier: 'Courier',
+    helvetica: 'Helvetica',
+    times: 'times'
   },
   tools: {
     main_menu: 'Main Menu',
@@ -147,7 +157,10 @@ export default {
     move_front: 'Bring to Front',
     move_up: 'Bring Forward',
     move_down: 'Send Backward',
-    move_back: 'Send to Back'
+    move_back: 'Send to Back',
+    tool_unlink_use: 'Break link to reference element (make unique)',
+    ellipse_circle_tool: 'Ellipse/Circle Tool',
+    square_rect_tool: 'Square/Rect Tool'
   },
   layers: {
     layer: 'Layer',
diff --git a/src/editor/locale/lang.zh-CN.js b/src/editor/locale/lang.zh-CN.js
index 5b398faa9..c720284d2 100644
--- a/src/editor/locale/lang.zh-CN.js
+++ b/src/editor/locale/lang.zh-CN.js
@@ -25,7 +25,8 @@ export default {
     pathNodeTooltip: 'Drag node to move it. Double-click node to change segment type',
     pathCtrlPtTooltip: 'Drag control point to adjust curve properties',
     pick_stroke_paint_opacity: 'Pick a Stroke Paint and Opacity',
-    pick_fill_paint_opacity: 'Pick a Fill Paint and Opacity'
+    pick_fill_paint_opacity: 'Pick a Fill Paint and Opacity',
+    group_identify_label: 'Group identification label'
   },
   properties: {
     id: '元素ID',
@@ -73,7 +74,16 @@ export default {
     italic: '斜体',
     text_anchor_start: 'Align the text in start',
     text_anchor_middle: 'Align the text in middle',
-    text_anchor_end: 'Align the text in end'
+    text_anchor_end: 'Align the text in end',
+    class: 'Element class',
+    serif: 'Serif',
+    sans_serif: 'Sans-serif',
+    cursive: 'Cursive',
+    fantasy: 'Fantasy',
+    monospace: 'Monospace',
+    courier: 'Courier',
+    helvetica: 'Helvetica',
+    times: 'times'
   },
   tools: {
     main_menu: '主菜单',
@@ -147,7 +157,10 @@ export default {
     move_front: '移至顶部',
     move_up: '向上移动',
     move_down: '向下移动',
-    move_back: '移至底部'
+    move_back: '移至底部',
+    tool_unlink_use: 'Break link to reference element (make unique)',
+    ellipse_circle_tool: 'Ellipse/Circle Tool',
+    square_rect_tool: 'Square/Rect Tool'
   },
   layers: {
     layer: '图层',
diff --git a/src/editor/locale/lang.zh-HK.js b/src/editor/locale/lang.zh-HK.js
index 82a282461..e90f02242 100644
--- a/src/editor/locale/lang.zh-HK.js
+++ b/src/editor/locale/lang.zh-HK.js
@@ -25,7 +25,8 @@ export default {
     pathNodeTooltip: 'Drag node to move it. Double-click node to change segment type',
     pathCtrlPtTooltip: 'Drag control point to adjust curve properties',
     pick_stroke_paint_opacity: 'Pick a Stroke Paint and Opacity',
-    pick_fill_paint_opacity: 'Pick a Fill Paint and Opacity'
+    pick_fill_paint_opacity: 'Pick a Fill Paint and Opacity',
+    group_identify_label: 'Group identification label'
   },
   properties: {
     id: 'Identify the element',
@@ -73,7 +74,16 @@ export default {
     italic: '斜体文本',
     text_anchor_start: 'Align the text in start',
     text_anchor_middle: 'Align the text in middle',
-    text_anchor_end: 'Align the text in end'
+    text_anchor_end: 'Align the text in end',
+    class: 'Element class',
+    serif: 'Serif',
+    sans_serif: 'Sans-serif',
+    cursive: 'Cursive',
+    fantasy: 'Fantasy',
+    monospace: 'Monospace',
+    courier: 'Courier',
+    helvetica: 'Helvetica',
+    times: 'times'
   },
   tools: {
     main_menu: 'Main Menu',
@@ -147,7 +157,10 @@ export default {
     move_front: 'Bring to Front',
     move_up: 'Bring Forward',
     move_down: 'Send Backward',
-    move_back: 'Send to Back'
+    move_back: 'Send to Back',
+    tool_unlink_use: 'Break link to reference element (make unique)',
+    ellipse_circle_tool: 'Ellipse/Circle Tool',
+    square_rect_tool: 'Square/Rect Tool'
   },
   layers: {
     layer: 'Layer',
diff --git a/src/editor/locale/lang.zh-TW.js b/src/editor/locale/lang.zh-TW.js
index a2c1a1cd6..dfa5e8a82 100644
--- a/src/editor/locale/lang.zh-TW.js
+++ b/src/editor/locale/lang.zh-TW.js
@@ -25,7 +25,8 @@ export default {
     pathNodeTooltip: 'Drag node to move it. Double-click node to change segment type',
     pathCtrlPtTooltip: 'Drag control point to adjust curve properties',
     pick_stroke_paint_opacity: 'Pick a Stroke Paint and Opacity',
-    pick_fill_paint_opacity: 'Pick a Fill Paint and Opacity'
+    pick_fill_paint_opacity: 'Pick a Fill Paint and Opacity',
+    group_identify_label: 'Group identification label'
   },
   properties: {
     id: 'Identify the element',
@@ -73,7 +74,16 @@ export default {
     italic: '斜體',
     text_anchor_start: 'Align the text in start',
     text_anchor_middle: 'Align the text in middle',
-    text_anchor_end: 'Align the text in end'
+    text_anchor_end: 'Align the text in end',
+    class: 'Element class',
+    serif: 'Serif',
+    sans_serif: 'Sans-serif',
+    cursive: 'Cursive',
+    fantasy: 'Fantasy',
+    monospace: 'Monospace',
+    courier: 'Courier',
+    helvetica: 'Helvetica',
+    times: 'times'
   },
   tools: {
     main_menu: 'Main Menu',
@@ -147,7 +157,10 @@ export default {
     move_front: 'Bring to Front',
     move_up: 'Bring Forward',
     move_down: 'Send Backward',
-    move_back: 'Send to Back'
+    move_back: 'Send to Back',
+    tool_unlink_use: 'Break link to reference element (make unique)',
+    ellipse_circle_tool: 'Ellipse/Circle Tool',
+    square_rect_tool: 'Square/Rect Tool'
   },
   layers: {
     layer: '圖層',
diff --git a/src/editor/panels/BottomPanel.js b/src/editor/panels/BottomPanel.js
index 03304c90f..341914403 100644
--- a/src/editor/panels/BottomPanel.js
+++ b/src/editor/panels/BottomPanel.js
@@ -28,7 +28,6 @@ class BottomPanel {
     get multiselected () {
       return this.editor.multiselected;
     }
-
     /**
     * @type {module}
     */
@@ -39,7 +38,6 @@ class BottomPanel {
       }
       this.editor.svgCanvas.setStrokeWidth(val);
     }
-
     /**
     * @type {module}
     */
@@ -60,7 +58,6 @@ class BottomPanel {
         }
         const zoom = this.editor.svgCanvas.getZoom();
         const wArea = this.editor.workarea;
-
         this.editor.zoomChanged(window, {
           width: 0,
           height: 0,
@@ -98,7 +95,6 @@ class BottomPanel {
           $id(btn).disabled = false;
         });
       }
-
       if (bNoStroke && bNoFill) {
         // eslint-disable-next-line sonarjs/no-identical-functions
         buttonsNeedingFillAndStroke.forEach((btn) => {
@@ -113,7 +109,6 @@ class BottomPanel {
           $id(btn).disabled = false;
         });
       }
-
       this.editor.svgCanvas.runExtensions(
         'toolButtonStateUpdate',
         /** @type {module:svgcanvas.SvgCanvas#event:ext_toolButtonStateUpdate} */ {
@@ -122,7 +117,6 @@ class BottomPanel {
         }
       );
     }
-
     /**
     * @type {module}
     */
@@ -164,13 +158,14 @@ class BottomPanel {
     }
     this.updateToolButtonState();
   }
-
   /**
   * @type {module}
   */
   init () {
     // register actions for Bottom panel
     const template = document.createElement('template');
+    const { i18next } = this.editor;
+    // eslint-disable-next-line no-unsanitized/property
     template.innerHTML = `
       <div id="tools_bottom">
         <!-- Zoom buttons -->
@@ -181,39 +176,39 @@ class BottomPanel {
           <div value="100">100</div>
           <div value="50">50</div>
           <div value="25">25</div>
-          <div value="canvas">Fit to canvas</div>
-          <div value="selection">Fit to selection</div>
-          <div value="layer">Fit to layer content</div>
-          <div value="content">Fit to all content</div>
+          <div value="canvas">${i18next.t('tools.fit_to_canvas')}</div>
+          <div value="selection">${i18next.t('tools.fit_to_sel')}</div>
+          <div value="layer">${i18next.t('tools.fit_to_layer_content')}</div>
+          <div value="content">${i18next.t('tools.fit_to_all')}</div>
         </se-zoom>
-        <se-colorpicker id="fill_color" src="./images/fill.svg" title="Change fill color" type="fill"></se-colorpicker>
-        <se-colorpicker id="stroke_color" src="./images/stroke.svg" title="Change stroke color" type="stroke">
+        <se-colorpicker id="fill_color" src="./images/fill.svg" title="${i18next.t('properties.fill_color')}" type="fill"></se-colorpicker>
+        <se-colorpicker id="stroke_color" src="./images/stroke.svg" title="${i18next.t('properties.stroke_color')}" type="stroke">
         </se-colorpicker>
-        <se-spin-input id="stroke_width" min=0 max=99 step=1 title="Change stroke width" label=""></se-spin-input>
-        <se-list id="stroke_style" title="Change stroke dash style" label="" width="22px" height="24px">
+        <se-spin-input id="stroke_width" min=0 max=99 step=1 title="${i18next.t('properties.stroke_width')}" label=""></se-spin-input>
+        <se-list id="stroke_style" title="${i18next.t('properties.stroke_style')}" label="" width="22px" height="24px">
           <se-list-item value="none">&#8212;</se-list-item>
           <se-list-item value="2,2">...</se-list-item>
           <se-list-item value="5,5">- -</se-list-item>
           <se-list-item value="5,2,2,2">- .</se-list-item>
           <se-list-item value="5,2,2,2,2,2">- ..</se-list-item>
         </se-list>
-        <se-list id="stroke_linejoin" title="Linejoin: Miter" label="" width="22px" height="24px">
-          <se-list-item id="linejoin_miter" value="miter"><img title="Linejoin: Miter" src="./images/linejoin_miter.svg"
+        <se-list id="stroke_linejoin" title="${i18next.t('properties.linejoin_miter')}" label="" width="22px" height="24px">
+          <se-list-item id="linejoin_miter" value="miter"><img title="${i18next.t('properties.linejoin_miter')}" src="./images/linejoin_miter.svg"
               height="22px"></img></se-list-item>
-          <se-list-item id="linejoin_round" value="round"><img title="Linejoin: Round" src="./images/linejoin_round.svg"
+          <se-list-item id="linejoin_round" value="round"><img title="${i18next.t('properties.linejoin_round')}" src="./images/linejoin_round.svg"
               height="22px"></img></se-list-item>
-          <se-list-item id="linejoin_bevel" value="bevel"><img title="Linejoin: Bevel" src="./images/linejoin_bevel.svg"
+          <se-list-item id="linejoin_bevel" value="bevel"><img title="${i18next.t('properties.linejoin_bevel')}" src="./images/linejoin_bevel.svg"
               height="22px"></img></se-list-item>
         </se-list>
-        <se-list id="stroke_linecap" title="Linecap: Butt" label="" width="22px" height="24px">
-          <se-list-item id="linecap_butt" value="butt"><img title="Linecap: Butt" src="./images/linecap_butt.svg"
+        <se-list id="stroke_linecap" title="${i18next.t('properties.linecap_butt')}" label="" width="22px" height="24px">
+          <se-list-item id="linecap_butt" value="butt"><img title="${i18next.t('properties.linecap_butt')}" src="./images/linecap_butt.svg"
               height="22px"></img></se-list-item>
-          <se-list-item id="linecap_square" value="square"><img title="Linecap: Square" src="./images/linecap_square.svg"
+          <se-list-item id="linecap_square" value="square"><img title="${i18next.t('properties.linecap_square')}" src="./images/linecap_square.svg"
               height="22px"></img></se-list-item>
-          <se-list-item id="linecap_round" value="round"><img title="Linecap: Round" src="./images/linecap_round.svg"
+          <se-list-item id="linecap_round" value="round"><img title="${i18next.t('properties.linecap_round')}" src="./images/linecap_round.svg"
               height="22px"></img></se-list-item>
         </se-list>
-        <se-spin-input size="3" id="opacity" min=0 max=100 step=5 title="Change selected item opacity"
+        <se-spin-input size="3" id="opacity" min=0 max=100 step=5 title="${i18next.t('properties.opacity')}"
           src="./images/opacity.svg"></se-spin-input>
         <se-palette id="palette"></se-palette>
       </div> <!-- tools_bottom -->
diff --git a/src/editor/panels/LayersPanel.js b/src/editor/panels/LayersPanel.js
index 0d8f25913..b374ea9a9 100644
--- a/src/editor/panels/LayersPanel.js
+++ b/src/editor/panels/LayersPanel.js
@@ -113,17 +113,19 @@ class LayersPanel {
    */
   init() {
     const template = document.createElement("template");
+    const { i18next } = this.editor;
+    // eslint-disable-next-line no-unsanitized/property
     template.innerHTML = `
     <div id="sidepanels">
     <div id="layerpanel">
-      <h3 id="layersLabel">Layers</h3>
+      <h3 id="layersLabel">${i18next.t('layers.layers')}</h3>
       <fieldset id="layerbuttons">
-        <se-button id="layer_new" title="New Layer" size="small" src="./images/new.svg"></se-button>
-        <se-button id="layer_delete" title="Delete Layer" size="small" src="./images/delete.svg"></se-button>
-        <se-button id="layer_rename" title="Rename Layer" size="small" src="./images/text.svg"></se-button>
-        <se-button id="layer_up" title="Move Layer Up" size="small" src="./images/go_up.svg"></se-button>
-        <se-button id="layer_down" title="Move Layer Down" size="small" src="./images/go_down.svg"></se-button>
-        <se-button id="layer_moreopts" title="More Options" size="small" src="./images/context_menu.svg">
+        <se-button id="layer_new" title="${i18next.t('layers.new')}" size="small" src="./images/new.svg"></se-button>
+        <se-button id="layer_delete" title="${i18next.t('layers.del')}" size="small" src="./images/delete.svg"></se-button>
+        <se-button id="layer_rename" title="${i18next.t('layers.rename')}" size="small" src="./images/text.svg"></se-button>
+        <se-button id="layer_up" title="${i18next.t('layers.move_up')}" size="small" src="./images/go_up.svg"></se-button>
+        <se-button id="layer_down" title="${i18next.t('layers.move_down')}" size="small" src="./images/go_down.svg"></se-button>
+        <se-button id="layer_moreopts" title="${i18next.t('common.more_opts')}" size="small" src="./images/context_menu.svg">
         </se-button>
       </fieldset>
       <table id="layerlist">
@@ -132,12 +134,12 @@ class LayersPanel {
           <td class="layername">Layer 1</td>
         </tr>
       </table>
-      <span id="selLayerLabel">Move elements to:</span>
-      <select id="selLayerNames" title="Move selected elements to a different layer" disabled="disabled">
+      <span id="selLayerLabel">${i18next.t('layers.move_elems_to')}</span>
+      <select id="selLayerNames" title="${i18next.t('layers.move_selected')}" disabled="disabled">
         <option selected="selected" value="layer1">Layer 1</option>
       </select>
     </div>
-    <div id="sidepanel_handle" title="Drag left/right to resize side panel [X]">L a y e r s
+    <div id="sidepanel_handle" title="${i18next.t('ui.panel_drag')}">L a y e r s
     </div>
   </div>
     `;
diff --git a/src/editor/panels/LeftPanel.js b/src/editor/panels/LeftPanel.js
index 88c5a122d..c48fdb58c 100644
--- a/src/editor/panels/LeftPanel.js
+++ b/src/editor/panels/LeftPanel.js
@@ -194,27 +194,29 @@ class LeftPanel {
    * @type {module}
    */
   init() {
+    const { i18next } = this.editor
     // add Left panel
     const template = document.createElement("template");
+    // eslint-disable-next-line no-unsanitized/property
     template.innerHTML = `
     <div id="tools_left">
-     <se-button id="tool_select" title="Select Tool" src="./images/select.svg"></se-button>
-     <se-button id="tool_zoom" title="Zoom Tool" src="./images/zoom.svg" shortcut="Z"></se-button>
-     <se-button id="tool_fhpath" title="Pencil Tool" src="./images/pencil.svg" shortcut="Q"></se-button>
-     <se-button id="tool_line" title="Line Tool" src="./images/pen.svg" shortcut="L"></se-button>
-     <se-button id="tool_path" title="Path Tool" src="./images/path.svg" shortcut="P"></se-button>
-     <se-flyingbutton id="tools_rect" title="Square/Rect Tool">
-       <se-button id="tool_rect" title="Rectangle" src="./images/rect.svg" shortcut="R"></se-button>
-       <se-button id="tool_square" title="Square" src="./images/square.svg"></se-button>
-       <se-button id="tool_fhrect" title="Free-Hand Rectangle" src="./images/fh_rect.svg"></se-button>
+     <se-button id="tool_select" title="${i18next.t('tools.mode_select')}" src="./images/select.svg"></se-button>
+     <se-button id="tool_zoom" title="${i18next.t('tools.mode_zoom')}" src="./images/zoom.svg" shortcut="Z"></se-button>
+     <se-button id="tool_fhpath" title="${i18next.t('tools.mode_fhpath')}" src="./images/pencil.svg" shortcut="Q"></se-button>
+     <se-button id="tool_line" title="${i18next.t('tools.mode_line')}" src="./images/pen.svg" shortcut="L"></se-button>
+     <se-button id="tool_path" title="${i18next.t('tools.mode_path')}" src="./images/path.svg" shortcut="P"></se-button>
+     <se-flyingbutton id="tools_rect" title="${i18next.t('tools.square_rect_tool')}">
+       <se-button id="tool_rect" title="${i18next.t('tools.mode_rect')}" src="./images/rect.svg" shortcut="R"></se-button>
+       <se-button id="tool_square" title="${i18next.t('tools.mode_square')}" src="./images/square.svg"></se-button>
+       <se-button id="tool_fhrect" title="${i18next.t('tools.mode_fhrect')}" src="./images/fh_rect.svg"></se-button>
      </se-flyingbutton>
-     <se-flyingbutton id="tools_ellipse" title="Ellipse/Circle Tool">
-       <se-button id="tool_ellipse" title="Rectangle" src="./images/ellipse.svg" shortcut="E"></se-button>
-       <se-button id="tool_circle" title="Square" src="./images/circle.svg"></se-button>
-       <se-button id="tool_fhellipse" title="Free-Hand Rectangle" src="./images/fh_ellipse.svg"></se-button>
+     <se-flyingbutton id="tools_ellipse" title="${i18next.t('tools.ellipse_circle_tool')}">
+       <se-button id="tool_ellipse" title="${i18next.t('tools.mode_ellipse')}" src="./images/ellipse.svg" shortcut="E"></se-button>
+       <se-button id="tool_circle" title="${i18next.t('tools.mode_circle')}" src="./images/circle.svg"></se-button>
+       <se-button id="tool_fhellipse" title="${i18next.t('tools.mode_fhellipse')}" src="./images/fh_ellipse.svg"></se-button>
      </se-flyingbutton>
-     <se-button id="tool_text" title="Text Tool" src="./images/text.svg" shortcut="T"></se-button>
-     <se-button id="tool_image" title="Image Tool" src="./images/image.svg"></se-button>
+     <se-button id="tool_text" title="${i18next.t('tools.mode_text')}" src="./images/text.svg" shortcut="T"></se-button>
+     <se-button id="tool_image" title="${i18next.t('tools.mode_image')}" src="./images/image.svg"></se-button>
     </div> <!-- tools_left -->
      `;
     this.editor.$svgEditor.append(template.content.cloneNode(true));
diff --git a/src/editor/panels/TopPanel.js b/src/editor/panels/TopPanel.js
index bf93ff59c..09edeb7d1 100644
--- a/src/editor/panels/TopPanel.js
+++ b/src/editor/panels/TopPanel.js
@@ -762,58 +762,58 @@ class TopPanel {
        </div> <!-- editor_panel -->
        <div id="history_panel">
          <div class="tool_sep"></div>
-         <se-button id="tool_undo" title="Undo" shortcut="Z" src="./images/undo.svg" disabled></se-button>
-         <se-button id="tool_redo" title="Redo" shortcut="Y" src="./images/redo.svg" disabled></se-button>
+         <se-button id="tool_undo" title="${i18next.t('tools.undo')}" shortcut="Z" src="./images/undo.svg" disabled></se-button>
+         <se-button id="tool_redo" title="${i18next.t('tools.redo')}" shortcut="Y" src="./images/redo.svg" disabled></se-button>
        </div> <!-- history_panel -->
        <!-- Buttons when a single element is selected -->
        <div id="selected_panel">
          <div class="toolset">
            <div class="tool_sep"></div>
-           <se-button id="tool_clone" title="Duplicate Element" shortcut="D" src="./images/clone.svg"></se-button>
-           <se-button id="tool_delete" title="Delete Element" shortcut="Backspace" src="./images/delete.svg">
+           <se-button id="tool_clone" title="${i18next.t('tools.clone')}" shortcut="D" src="./images/clone.svg"></se-button>
+           <se-button id="tool_delete" title="${i18next.t('tools.del')}" shortcut="Backspace" src="./images/delete.svg">
            </se-button>
          </div>
          <div class="toolset">
            <div class="tool_sep"></div>
-           <se-button id="tool_move_top" title="Bring to Front" shortcut="Ctrl+Shift+]" src="./images/move_top.svg">
+           <se-button id="tool_move_top" title="${i18next.t('tools.move_top')}" shortcut="Ctrl+Shift+]" src="./images/move_top.svg">
            </se-button>
-           <se-button id="tool_move_bottom" title="Send to Back" shortcut="Ctrl+Shift+[" src="./images/move_bottom.svg">
+           <se-button id="tool_move_bottom" title="${i18next.t('tools.move_bottom')}" shortcut="Ctrl+Shift+[" src="./images/move_bottom.svg">
            </se-button>
          </div>
          <div class="toolset">
-           <se-button id="tool_topath" title="Convert to Path" src="./images/to_path.svg"></se-button>
-           <se-button id="tool_reorient" title="Reorient path" src="./images/reorient.svg"></se-button>
-           <se-button id="tool_make_link" title="Make (hyper)link" src="./images/globe_link.svg"></se-button>
+           <se-button id="tool_topath" title="${i18next.t('tools.to_path')}" src="./images/to_path.svg"></se-button>
+           <se-button id="tool_reorient" title="${i18next.t('tools.reorient_path')}" src="./images/reorient.svg"></se-button>
+           <se-button id="tool_make_link" title="${i18next.t('tools.make_link')}" src="./images/globe_link.svg"></se-button>
          </div>
          <div class="toolset">
            <div class="tool_sep"></div>
-           <se-input id="elem_id" data-attr="id" size="10" label="id" title="Identify the element"></se-input>
+           <se-input id="elem_id" data-attr="id" size="10" label="id" title="${i18next.t('properties.id')}"></se-input>
          </div>
          <div class="toolset">
-           <se-input id="elem_class" data-attr="class" size="10" label="class" title="Element class"></se-input>
+           <se-input id="elem_class" data-attr="class" size="10" label="class" title="${i18next.t('properties.class')}"></se-input>
          </div>
          <se-spin-input size="3" id="angle" min=-180 max=180 step=5 src="./images/angle.svg"
-           title="Change rotation angle"></se-spin-input>
+           title="${i18next.t('properties.angle')}"></se-spin-input>
          <se-spin-input size="2" id="blur" min=0 max=100 step=5 src="./images/blur.svg"
-           title="Change gaussian blur value"></se-spin-input>
-         <se-list id="tool_position" title="Align Element to Page" label="" width="22px" height="24px">
+           title="${i18next.t('properties.blur')}"></se-spin-input>
+         <se-list id="tool_position" title="${i18next.t('tools.align_to_page')}" label="" width="22px" height="24px">
            <se-list-item id="tool_posleft" value="l">
-             <img title="align left" src="./images/align_left.svg" height="22px">
+             <img title="${i18next.t('tools.align_left')}" src="./images/align_left.svg" height="22px">
            </se-list-item>
            <se-list-item id="tool_poscenter" value="c">
-             <img title="align center" src="./images/align_center.svg" height="22px">
+             <img title="${i18next.t('tools.align_center')}" src="./images/align_center.svg" height="22px">
            </se-list-item>
            <se-list-item id="tool_posright" value="r">
-             <img title="align right" src="./images/align_right.svg" height="22px">
+             <img title="${i18next.t('tools.align_right')}" src="./images/align_right.svg" height="22px">
            </se-list-item>
            <se-list-item id="tool_postop" value="t">
-             <img title="align top" src="./images/align_top.svg" height="22px">
+             <img title="${i18next.t('tools.align_top')}" src="./images/align_top.svg" height="22px">
            </se-list-item>
            <se-list-item id="tool_posmiddle" value="m">
-             <img title="align middle" src="./images/align_middle.svg" height="22px">
+             <img title="${i18next.t('tools.align_middle')}" src="./images/align_middle.svg" height="22px">
            </se-list-item>
            <se-list-item id="tool_posbottom" value="b">
-             <img title="align bottom" src="./images/align_bottom.svg" height="22px">
+             <img title="${i18next.t('tools.align_bottom')}" src="./images/align_bottom.svg" height="22px">
            </se-list-item>
          </se-list>
          <div id="xy_panel" class="toolset">
@@ -826,52 +826,52 @@ class TopPanel {
        <!-- Buttons when multiple elements are selected -->
        <div id="multiselected_panel">
          <div class="tool_sep"></div>
-         <se-button id="tool_clone_multi" title="Clone Elements" shortcut="C" src="./images/clone.svg"></se-button>
-         <se-button id="tool_delete_multi" title="Delete Selected Elements" shortcut="Delete/Backspace"
+         <se-button id="tool_clone_multi" title="${i18next.t('tools.clone')}" shortcut="C" src="./images/clone.svg"></se-button>
+         <se-button id="tool_delete_multi" title="${i18next.t('tools.del')}" shortcut="Delete/Backspace"
            src="./images/delete.svg"></se-button>
          <div class="tool_sep"></div>
-         <se-button id="tool_group_elements" title="Group Elements" shortcut="G" src="./images/group_elements.svg">
+         <se-button id="tool_group_elements" title="${i18next.t('tools.group_elements')}" shortcut="G" src="./images/group_elements.svg">
          </se-button>
-         <se-button id="tool_make_link_multi" title="Make (hyper)link" src="./images/globe_link.svg"></se-button>
-         <se-button id="tool_align_left" title="Align Left" src="./images/align_left.svg"></se-button>
-         <se-button id="tool_align_center" title="Align Center" src="./images/align_center.svg"></se-button>
-         <se-button id="tool_align_right" title="Align Right" src="./images/align_right.svg"></se-button>
-         <se-button id="tool_align_top" title="Align Top" src="./images/align_top.svg"></se-button>
-         <se-button id="tool_align_middle" title="Align Middle" src="./images/align_middle.svg"></se-button>
-         <se-button id="tool_align_bottom" title="Align Bottom" src="./images/align_bottom.svg"></se-button>
+         <se-button id="tool_make_link_multi" title="${i18next.t('tools.make_link')}" src="./images/globe_link.svg"></se-button>
+         <se-button id="tool_align_left" title="${i18next.t('tools.align_left')}" src="./images/align_left.svg"></se-button>
+         <se-button id="tool_align_center" title="${i18next.t('tools.align_center')}" src="./images/align_center.svg"></se-button>
+         <se-button id="tool_align_right" title="${i18next.t('tools.align_right')}" src="./images/align_right.svg"></se-button>
+         <se-button id="tool_align_top" title="${i18next.t('tools.align_top')}" src="./images/align_top.svg"></se-button>
+         <se-button id="tool_align_middle" title="${i18next.t('tools.align_middle')}" src="./images/align_middle.svg"></se-button>
+         <se-button id="tool_align_bottom" title="${i18next.t('tools.align_bottom')}" src="./images/align_bottom.svg"></se-button>
          <se-list id="tool_align_relative" label="relative to:">
-           <se-list-item id="selected_objects" value="selected">selected objects</se-list-item>
-           <se-list-item id="largest_object" value="largest">largest object</se-list-item>
-           <se-list-item id="smallest_object" value="smallest">smallest object</se-list-item>
-           <se-list-item id="page" value="page">page</se-list-item>
+           <se-list-item id="selected_objects" value="selected">${i18next.t('tools.selected_objects')}</se-list-item>
+           <se-list-item id="largest_object" value="largest">${i18next.t('tools.largest_object')}</se-list-item>
+           <se-list-item id="smallest_object" value="smallest">${i18next.t('tools.smallest_object')}</se-list-item>
+           <se-list-item id="page" value="page">${i18next.t('tools.page')}</se-list-item>
          </se-list>
          <div class="tool_sep"></div>
        </div> <!-- multiselected_panel -->
        <div id="rect_panel">
          <div class="toolset">
-           <se-spin-input id="rect_width" data-attr="width" size="4" label="w" title="Change rectangle width">
+           <se-spin-input id="rect_width" data-attr="width" size="4" label="w" title="${i18next.t('properties.rect_width')}">
            </se-spin-input>
-           <se-spin-input id="rect_height" data-attr="height" size="4" label="h" title="Change rectangle height">
+           <se-spin-input id="rect_height" data-attr="height" size="4" label="h" title="${i18next.t('properties.rect_height')}">
            </se-spin-input>
          </div>
-         <se-spin-input id="rect_rx" min=0 max=1000 step=1 size="3" title="Change Rectangle Corner Radius"
+         <se-spin-input id="rect_rx" min=0 max=1000 step=1 size="3" title="${i18next.t('properties.corner_radius')}"
            data-attr="Corner Radius" src="./images/c_radius.svg"></se-spin-input>
        </div> <!-- rect_panel -->
        <div id="image_panel">
          <div class="toolset">
-           <se-spin-input id="image_width" data-attr="width" size="4" type="text" label="w" title="Change image width">
+           <se-spin-input id="image_width" data-attr="width" size="4" type="text" label="w" title="${i18next.t('properties.image_width')}">
            </se-spin-input>
            <se-spin-input id="image_height" data-attr="height" size="4" type="text" label="h"
-             title="Change image height"></se-spin-input>
+             title="${i18next.t('properties.image_height')}"></se-spin-input>
          </div>
          <div class="toolset">
            <label id="tool_image_url">url:
-             <input id="image_url" type="text" title="Change URL" size="35" />
+             <input id="image_url" type="text" title="${i18next.t('properties.image_url')}" size="35" />
            </label>
            <label id="tool_change_image">
              <button id="change_image_url" style="display: none;">Change Image</button>
              <span id="url_notice"
-               title="NOTE: This image cannot be embedded. It will depend on this path to be displayed"></span>
+               title="${i18next.t('tools.no_embed')}"></span>
            </label>
          </div>
        </div> <!-- image_panel -->
@@ -886,52 +886,52 @@ class TopPanel {
        </div> <!-- circle_panel -->
        <div id="ellipse_panel">
          <div class="toolset">
-           <se-spin-input id="ellipse_cx" data-attr="cx" size="4" title="Change ellipse's cx coordinate" label="cx">
+           <se-spin-input id="ellipse_cx" data-attr="cx" size="4" title="${i18next.t('properties.ellipse_cx')}" label="cx">
            </se-spin-input>
-           <se-spin-input id="ellipse_cy" data-attr="cy" size="4" title="Change ellipse's cy coordinate" label="cy">
+           <se-spin-input id="ellipse_cy" data-attr="cy" size="4" title="${i18next.t('properties.ellipse_cy')}" label="cy">
            </se-spin-input>
          </div>
          <div class="toolset">
-           <se-spin-input id="ellipse_rx" data-attr="rx" size="4" title="Change ellipse's x radius" label="rx">
+           <se-spin-input id="ellipse_rx" data-attr="rx" size="4" title="${i18next.t('properties.ellipse_rx')}" label="rx">
            </se-spin-input>
-           <se-spin-input id="ellipse_ry" data-attr="ry" size="4" title="Change ellipse's y radius" label="ry">
+           <se-spin-input id="ellipse_ry" data-attr="ry" size="4" title="${i18next.t('properties.ellipse_ry')}" label="ry">
            </se-spin-input>
          </div>
        </div> <!-- ellipse_panel -->
        <div id="line_panel">
          <div class="toolset">
-           <se-spin-input id="line_x1" data-attr="x1" size="4" title="Change line's starting x coordinate" label="x1">
+           <se-spin-input id="line_x1" data-attr="x1" size="4" title="${i18next.t('properties.line_x1')}" label="x1">
            </se-spin-input>
-           <se-spin-input id="line_y1" data-attr="y1" size="4" title="Change line's starting y coordinate" label="y1">
+           <se-spin-input id="line_y1" data-attr="y1" size="4" title="${i18next.t('properties.line_y1')}" label="y1">
            </se-spin-input>
-           <se-spin-input id="line_x2" data-attr="x2" size="4" title="Change line's ending x coordinate" label="x2">
+           <se-spin-input id="line_x2" data-attr="x2" size="4" title="${i18next.t('properties.line_x2')}" label="x2">
            </se-spin-input>
-           <se-spin-input id="line_y2" data-attr="y2" size="4" title="Change line's ending y coordinate" label="y2">
+           <se-spin-input id="line_y2" data-attr="y2" size="4" title="${i18next.t('properties.line_y2')}" label="y2">
            </se-spin-input>
          </div>
        </div> <!-- line_panel -->
        <div id="text_panel">
          <div class="toolset">
-           <se-button id="tool_bold" title="Bold Text [B]" src="./images/bold.svg" shortcut="B"></se-button>
-           <se-button id="tool_italic" title="Italic Text [I]" src="./images/italic.svg" shortcut="I"></se-button>
-           <se-button id="tool_text_anchor_start" title="Align the text from start" src="./images/anchor_start.svg">
+           <se-button id="tool_bold" title="${i18next.t('properties.bold')}" src="./images/bold.svg" shortcut="B"></se-button>
+           <se-button id="tool_italic" title="${i18next.t('properties.italic')}" src="./images/italic.svg" shortcut="I"></se-button>
+           <se-button id="tool_text_anchor_start" title="${i18next.t('properties.text_anchor_start')}" src="./images/anchor_start.svg">
            </se-button>
-           <se-button id="tool_text_anchor_middle" title="Align the text from middle" src="./images/anchor_middle.svg">
+           <se-button id="tool_text_anchor_middle" title="${i18next.t('properties.text_anchor_middle')}" src="./images/anchor_middle.svg">
            </se-button>
-           <se-button id="tool_text_anchor_end" title="Align the text from end" src="./images/anchor_end.svg">
+           <se-button id="tool_text_anchor_end" title="${i18next.t('properties.text_anchor_end')}" src="./images/anchor_end.svg">
            </se-button>
          </div>
          <se-list id="tool_font_family" label="Font:">
-           <se-list-item value="Serif" style="font-family:serif;"> Serif</se-list-item>
-           <se-list-item value="Sans-serif" style="font-family:sans-serif;"> Sans-serif</se-list-item>
-           <se-list-item value="Cursive" style="font-family:cursive;"> Cursive</se-list-item>
-           <se-list-item value="Fantasy" style="font-family:fantasy;"> Fantasy</se-list-item>
-           <se-list-item value="Monospace" style="font-family:monospace;"> Monospace</se-list-item>
-           <se-list-item value="Courier" style="font-family:courier;"> Courier</se-list-item>
-           <se-list-item value="Helvetica" style="font-family:helvetica;">Helvetica</se-list-item>
-           <se-list-item value="Times" style="font-family:times;">Times</se-list-item>
+           <se-list-item value="Serif" style="font-family:serif;">${i18next.t('properties.serif')}</se-list-item>
+           <se-list-item value="Sans-serif" style="font-family:sans-serif;">${i18next.t('properties.sans_serif')}</se-list-item>
+           <se-list-item value="Cursive" style="font-family:cursive;">${i18next.t('properties.cursive')}</se-list-item>
+           <se-list-item value="Fantasy" style="font-family:fantasy;">${i18next.t('properties.fantasy')}</se-list-item>
+           <se-list-item value="Monospace" style="font-family:monospace;">${i18next.t('properties.monospace')}</se-list-item>
+           <se-list-item value="Courier" style="font-family:courier;">${i18next.t('properties.courier')} </se-list-item>
+           <se-list-item value="Helvetica" style="font-family:helvetica;">${i18next.t('properties.helvetica')}</se-list-item>
+           <se-list-item value="Times" style="font-family:times;">${i18next.t('properties.times')}</se-list-item>
          </se-list>
-         <se-spin-input size="2" id="font_size" min=1 max=1000 step=1 title="Change Font Size"
+         <se-spin-input size="2" id="font_size" min=1 max=1000 step=1 title="${i18next.t('properties.font_size')}"
            src="./images/fontsize.svg"></se-spin-input>
          <!-- Not visible, but still used -->
          <input id="text" type="text" size="35" />
@@ -940,45 +940,45 @@ class TopPanel {
        <div id="container_panel">
          <div class="tool_sep"></div>
          <!-- Add viewBox field here? -->
-         <label id="group_title" title="Group identification label">
+         <label id="group_title" title="${i18next.t('ui.group_identify_label')}">
            <span>label</span>
            <input id="g_title" data-attr="title" size="10" type="text" />
          </label>
        </div> <!-- container_panel -->
        <div id="use_panel">
-         <se-button id="tool_unlink_use" title="Break link to reference element (make unique)"
+         <se-button id="tool_unlink_use" title="${i18next.t('tools.tool_unlink_use')}"
            src="./images/unlink_use.svg">
          </se-button>
        </div> <!-- use_panel -->
        <div id="g_panel">
-         <se-button id="tool_ungroup" title="Ungroup Elements [G]" src="./images/ungroup.svg">
+         <se-button id="tool_ungroup" title="${i18next.t('tools.ungroup')}" src="./images/ungroup.svg">
          </se-button>
        </div> <!-- g_panel -->
        <!-- For anchor elements -->
        <div id="a_panel">
-         <label id="tool_link_url" title="Set link URL (leave empty to remove)">
+         <label id="tool_link_url" title="${i18next.t('tools.set_link_url')}">
            <span id="linkLabel" class="icon_label"></span>
            <input id="link_url" type="text" size="35" />
          </label>
        </div> <!-- a_panel -->
        <div id="path_node_panel">
          <div class="tool_sep"></div>
-         <se-button id="tool_node_link" title="Link Control Points" src="./images/tool_node_link.svg" pressed>
+         <se-button id="tool_node_link" title="${i18next.t('tools.node_link')}" src="./images/tool_node_link.svg" pressed>
          </se-button>
          <div class="tool_sep"></div>
-         <se-spin-input id="path_node_x" data-attr="x" size="4" title="Change node's x coordinate" label="x:">
+         <se-spin-input id="path_node_x" data-attr="x" size="4" title="${i18next.t('properties.node_x')}" label="x:">
          </se-spin-input>
-         <se-spin-input id="path_node_y" data-attr="y" size="4" title="Change node's y coordinate" label="y:">
+         <se-spin-input id="path_node_y" data-attr="y" size="4" title="${i18next.t('properties.node_y')}" label="y:">
          </se-spin-input>
-         <select id="seg_type" title="Change Segment type">
-           <option id="straight_segments" selected="selected" value="4">Straight</option>
-           <option id="curve_segments" value="6">Curve</option>
+         <select id="seg_type" title="${i18next.t('tools.seg_type')}">
+           <option id="straight_segments" selected="selected" value="4">${i18next.t('properties.straight_segments')}</option>
+           <option id="curve_segments" value="6">${i18next.t('properties.curve_segments')}</option>
          </select>
-         <se-button id="tool_node_clone" title="Clone Node" src="./images/tool_node_clone.svg"></se-button>
-         <se-button id="tool_node_delete" title="Delete Node" src="./images/tool_node_delete.svg"></se-button>
-         <se-button id="tool_openclose_path" title="Open/close sub-path" src="./images/tool_openclose_path.svg">
+         <se-button id="tool_node_clone" title="${i18next.t('tools.node_clone')}" src="./images/tool_node_clone.svg"></se-button>
+         <se-button id="tool_node_delete" title="${i18next.t('tools.node_delete')}" src="./images/tool_node_delete.svg"></se-button>
+         <se-button id="tool_openclose_path" title="${i18next.t('tools.openclose_path')}" src="./images/tool_openclose_path.svg">
          </se-button>
-         <se-button id="tool_add_subpath" title="Add sub-path" src="./images/tool_add_subpath.svg"></se-button>
+         <se-button id="tool_add_subpath" title="${i18next.t('tools.add_subpath')}" src="./images/tool_add_subpath.svg"></se-button>
        </div> <!-- path_node_panel -->
        <div id="cur_context_panel"></div>
      </div> <!-- tools_top -->


Test patch:
diff --git a/src/editor/locale/lang.test.js b/src/editor/locale/lang.test.js
index fa9898e5b..9f2d2165f 100644
--- a/src/editor/locale/lang.test.js
+++ b/src/editor/locale/lang.test.js
@@ -25,7 +25,8 @@ export default {
     pathNodeTooltip: 'Drag node to move it. Double-click node to change segment type',
     pathCtrlPtTooltip: 'Drag control point to adjust curve properties',
     pick_stroke_paint_opacity: 'Pick a Stroke Paint and Opacity',
-    pick_fill_paint_opacity: 'Pick a Fill Paint and Opacity'
+    pick_fill_paint_opacity: 'Pick a Fill Paint and Opacity',
+    group_identify_label: 'Group identification label'
   },
   properties: {
     id: 'Identify the element',
@@ -73,7 +74,16 @@ export default {
     italic: 'Italic Text',
     text_anchor_start: 'Align the text in start',
     text_anchor_middle: 'Align the text in middle',
-    text_anchor_end: 'Align the text in end'
+    text_anchor_end: 'Align the text in end',
+    class: 'Element class',
+    serif: 'Serif',
+    sans_serif: 'Sans-serif',
+    cursive: 'Cursive',
+    fantasy: 'Fantasy',
+    monospace: 'Monospace',
+    courier: 'Courier',
+    helvetica: 'Helvetica',
+    times: 'times'
   },
   tools: {
     main_menu: 'Main Menu',
@@ -147,7 +157,10 @@ export default {
     move_front: 'Bring to Front',
     move_up: 'Move Layer Up',
     move_down: 'Move Layer Down',
-    move_back: 'Send to Back'
+    move_back: 'Send to Back',
+    tool_unlink_use: 'Break link to reference element (make unique)',
+    ellipse_circle_tool: 'Ellipse/Circle Tool',
+    square_rect_tool: 'Square/Rect Tool'
   },
   layers: {
     layer: 'Layer',