diff --git a/.gitignore b/.gitignore
index 5a077bd6b2..570c932d28 100644
--- a/.gitignore
+++ b/.gitignore
@@ -42,6 +42,7 @@ plugins/FlatProfileExporter
plugins/ProfileFlattener
plugins/cura-god-mode-plugin
plugins/cura-big-flame-graph
+plugins/cura-siemensnx-plugin
#Build stuff
CMakeCache.txt
diff --git a/cura/CrashHandler.py b/cura/CrashHandler.py
index 7008ba64d2..04f04a1c37 100644
--- a/cura/CrashHandler.py
+++ b/cura/CrashHandler.py
@@ -1,18 +1,27 @@
+# Copyright (c) 2017 Ultimaker B.V.
+# Cura is released under the terms of the LGPLv3 or higher.
+
import sys
import platform
import traceback
-import webbrowser
import faulthandler
import tempfile
import os
-import urllib
+import os.path
+import time
+import json
+import ssl
+import urllib.request
+import urllib.error
-from PyQt5.QtCore import QT_VERSION_STR, PYQT_VERSION_STR, Qt, QCoreApplication
-from PyQt5.QtGui import QPixmap
-from PyQt5.QtWidgets import QDialog, QDialogButtonBox, QHBoxLayout, QVBoxLayout, QLabel, QTextEdit
+from PyQt5.QtCore import QT_VERSION_STR, PYQT_VERSION_STR, QCoreApplication
+from PyQt5.QtWidgets import QDialog, QDialogButtonBox, QVBoxLayout, QLabel, QTextEdit, QGroupBox, QPushButton
from UM.Logger import Logger
+from UM.View.GL.OpenGL import OpenGL
from UM.i18n import i18nCatalog
+from UM.Platform import Platform
+
catalog = i18nCatalog("cura")
MYPY = False
@@ -35,83 +44,238 @@ fatal_exception_types = [
SystemError,
]
-def show(exception_type, value, tb):
- Logger.log("c", "An uncaught exception has occurred!")
- for line in traceback.format_exception(exception_type, value, tb):
- for part in line.rstrip("\n").split("\n"):
- Logger.log("c", part)
+class CrashHandler:
+ crash_url = "https://stats.ultimaker.com/api/cura"
- if not CuraDebugMode and exception_type not in fatal_exception_types:
- return
+ def __init__(self, exception_type, value, tb):
- application = QCoreApplication.instance()
- if not application:
- sys.exit(1)
+ self.exception_type = exception_type
+ self.value = value
+ self.traceback = tb
- dialog = QDialog()
- dialog.setMinimumWidth(640)
- dialog.setMinimumHeight(640)
- dialog.setWindowTitle(catalog.i18nc("@title:window", "Crash Report"))
+ # While we create the GUI, the information will be stored for sending afterwards
+ self.data = dict()
+ self.data["time_stamp"] = time.time()
- layout = QVBoxLayout(dialog)
+ Logger.log("c", "An uncaught exception has occurred!")
+ for line in traceback.format_exception(exception_type, value, tb):
+ for part in line.rstrip("\n").split("\n"):
+ Logger.log("c", part)
- #label = QLabel(dialog)
- #pixmap = QPixmap()
- #try:
- # data = urllib.request.urlopen("http://www.randomkittengenerator.com/cats/rotator.php").read()
- # pixmap.loadFromData(data)
- #except:
- # try:
- # from UM.Resources import Resources
- # path = Resources.getPath(Resources.Images, "kitten.jpg")
- # pixmap.load(path)
- # except:
- # pass
- #pixmap = pixmap.scaled(150, 150)
- #label.setPixmap(pixmap)
- #label.setAlignment(Qt.AlignCenter)
- #layout.addWidget(label)
+ if not CuraDebugMode and exception_type not in fatal_exception_types:
+ return
- label = QLabel(dialog)
- layout.addWidget(label)
+ application = QCoreApplication.instance()
+ if not application:
+ sys.exit(1)
- #label.setScaledContents(True)
- label.setText(catalog.i18nc("@label", """
A fatal exception has occurred that we could not recover from!
- Please use the information below to post a bug report at http://github.com/Ultimaker/Cura/issues
- """))
+ self._createDialog()
- textarea = QTextEdit(dialog)
- layout.addWidget(textarea)
+ ## Creates a modal dialog.
+ def _createDialog(self):
- try:
- from UM.Application import Application
- version = Application.getInstance().getVersion()
- except:
- version = "Unknown"
+ self.dialog = QDialog()
+ self.dialog.setMinimumWidth(640)
+ self.dialog.setMinimumHeight(640)
+ self.dialog.setWindowTitle(catalog.i18nc("@title:window", "Crash Report"))
- trace = "".join(traceback.format_exception(exception_type, value, tb))
+ layout = QVBoxLayout(self.dialog)
- crash_info = "Version: {0}\nPlatform: {1}\nQt: {2}\nPyQt: {3}\n\nException:\n{4}"
- crash_info = crash_info.format(version, platform.platform(), QT_VERSION_STR, PYQT_VERSION_STR, trace)
+ layout.addWidget(self._messageWidget())
+ layout.addWidget(self._informationWidget())
+ layout.addWidget(self._exceptionInfoWidget())
+ layout.addWidget(self._logInfoWidget())
+ layout.addWidget(self._userDescriptionWidget())
+ layout.addWidget(self._buttonsWidget())
- tmp_file_fd, tmp_file_path = tempfile.mkstemp(prefix = "cura-crash", text = True)
- os.close(tmp_file_fd)
- with open(tmp_file_path, "w") as f:
- faulthandler.dump_traceback(f, all_threads=True)
- with open(tmp_file_path, "r") as f:
- data = f.read()
+ def _messageWidget(self):
+ label = QLabel()
+ label.setText(catalog.i18nc("@label crash message", """A fatal exception has occurred. Please send us this Crash Report to fix the problem
+ Please use the "Send report" button to post a bug report automatically to our servers
+ """))
- msg = "-------------------------\n"
- msg += data
- crash_info += "\n\n" + msg
+ return label
- textarea.setText(crash_info)
+ def _informationWidget(self):
+ group = QGroupBox()
+ group.setTitle(catalog.i18nc("@title:groupbox", "System information"))
+ layout = QVBoxLayout()
+ label = QLabel()
- buttons = QDialogButtonBox(QDialogButtonBox.Close, dialog)
- layout.addWidget(buttons)
- buttons.addButton(catalog.i18nc("@action:button", "Open Web Page"), QDialogButtonBox.HelpRole)
- buttons.rejected.connect(dialog.close)
- buttons.helpRequested.connect(lambda: webbrowser.open("http://github.com/Ultimaker/Cura/issues"))
+ try:
+ from UM.Application import Application
+ self.cura_version = Application.getInstance().getVersion()
+ except:
+ self.cura_version = catalog.i18nc("@label unknown version of Cura", "Unknown")
- dialog.exec_()
- sys.exit(1)
+ crash_info = catalog.i18nc("@label Cura version", "Cura version: {version}
").format(version = self.cura_version)
+ crash_info += catalog.i18nc("@label Platform", "Platform: {platform}
").format(platform = platform.platform())
+ crash_info += catalog.i18nc("@label Qt version", "Qt version: {qt}
").format(qt = QT_VERSION_STR)
+ crash_info += catalog.i18nc("@label PyQt version", "PyQt version: {pyqt}
").format(pyqt = PYQT_VERSION_STR)
+ crash_info += catalog.i18nc("@label OpenGL", "OpenGL: {opengl}
").format(opengl = self._getOpenGLInfo())
+ label.setText(crash_info)
+
+ layout.addWidget(label)
+ group.setLayout(layout)
+
+ self.data["cura_version"] = self.cura_version
+ self.data["os"] = {"type": platform.system(), "version": platform.version()}
+ self.data["qt_version"] = QT_VERSION_STR
+ self.data["pyqt_version"] = PYQT_VERSION_STR
+
+ return group
+
+ def _getOpenGLInfo(self):
+ info = ""
+ info += catalog.i18nc("@label OpenGL version", "- OpenGL Version: {version}
").format(version = OpenGL.getInstance().getOpenGLVersion())
+ info += catalog.i18nc("@label OpenGL vendor", "- OpenGL Vendor: {vendor}
").format(vendor = OpenGL.getInstance().getGPUVendorName())
+ info += catalog.i18nc("@label OpenGL renderer", "- OpenGL Renderer: {renderer}
").format(renderer = OpenGL.getInstance().getGPUType())
+ info += "
"
+
+ self.data["opengl"] = {"version": OpenGL.getInstance().getOpenGLVersion(), "vendor": OpenGL.getInstance().getGPUVendorName(), "type": OpenGL.getInstance().getGPUType()}
+
+ return info
+
+ def _exceptionInfoWidget(self):
+ group = QGroupBox()
+ group.setTitle(catalog.i18nc("@title:groupbox", "Exception traceback"))
+ layout = QVBoxLayout()
+
+ text_area = QTextEdit()
+ trace_dict = traceback.format_exception(self.exception_type, self.value, self.traceback)
+ trace = "".join(trace_dict)
+ text_area.setText(trace)
+ text_area.setReadOnly(True)
+
+ layout.addWidget(text_area)
+ group.setLayout(layout)
+
+ # Parsing all the information to fill the dictionary
+ summary = trace_dict[len(trace_dict)-1].rstrip("\n")
+ module = trace_dict[len(trace_dict)-2].rstrip("\n").split("\n")
+ module_split = module[0].split(", ")
+ filepath = module_split[0].split("\"")[1]
+ directory, filename = os.path.split(filepath)
+ line = int(module_split[1].lstrip("line "))
+ function = module_split[2].lstrip("in ")
+ code = module[1].lstrip(" ")
+
+ # Using this workaround for a cross-platform path splitting
+ split_path = []
+ folder_name = ""
+ # Split until reach folder "cura"
+ while folder_name != "cura":
+ directory, folder_name = os.path.split(directory)
+ if not folder_name:
+ break
+ split_path.append(folder_name)
+
+ # Look for plugins. If it's not a plugin, the current cura version is set
+ isPlugin = False
+ module_version = self.cura_version
+ module_name = "Cura"
+ if split_path.__contains__("plugins"):
+ isPlugin = True
+ # Look backwards until plugin.json is found
+ directory, name = os.path.split(filepath)
+ while not os.listdir(directory).__contains__("plugin.json"):
+ directory, name = os.path.split(directory)
+
+ json_metadata_file = os.path.join(directory, "plugin.json")
+ try:
+ with open(json_metadata_file, "r") as f:
+ try:
+ metadata = json.loads(f.read())
+ module_version = metadata["version"]
+ module_name = metadata["name"]
+ except json.decoder.JSONDecodeError:
+ # Not throw new exceptions
+ Logger.logException("e", "Failed to parse plugin.json for plugin %s", name)
+ except:
+ # Not throw new exceptions
+ pass
+
+ exception_dict = dict()
+ exception_dict["traceback"] = {"summary": summary, "full_trace": trace}
+ exception_dict["location"] = {"path": filepath, "file": filename, "function": function, "code": code, "line": line,
+ "module_name": module_name, "version": module_version, "is_plugin": isPlugin}
+ self.data["exception"] = exception_dict
+
+ return group
+
+ def _logInfoWidget(self):
+ group = QGroupBox()
+ group.setTitle(catalog.i18nc("@title:groupbox", "Logs"))
+ layout = QVBoxLayout()
+
+ text_area = QTextEdit()
+ tmp_file_fd, tmp_file_path = tempfile.mkstemp(prefix = "cura-crash", text = True)
+ os.close(tmp_file_fd)
+ with open(tmp_file_path, "w") as f:
+ faulthandler.dump_traceback(f, all_threads=True)
+ with open(tmp_file_path, "r") as f:
+ logdata = f.read()
+
+ text_area.setText(logdata)
+ text_area.setReadOnly(True)
+
+ layout.addWidget(text_area)
+ group.setLayout(layout)
+
+ self.data["log"] = logdata
+
+ return group
+
+
+ def _userDescriptionWidget(self):
+ group = QGroupBox()
+ group.setTitle(catalog.i18nc("@title:groupbox", "User description"))
+ layout = QVBoxLayout()
+
+ # When sending the report, the user comments will be collected
+ self.user_description_text_area = QTextEdit()
+ self.user_description_text_area.setFocus(True)
+
+ layout.addWidget(self.user_description_text_area)
+ group.setLayout(layout)
+
+ return group
+
+ def _buttonsWidget(self):
+ buttons = QDialogButtonBox()
+ buttons.addButton(QDialogButtonBox.Close)
+ buttons.addButton(catalog.i18nc("@action:button", "Send report"), QDialogButtonBox.AcceptRole)
+ buttons.rejected.connect(self.dialog.close)
+ buttons.accepted.connect(self._sendCrashReport)
+
+ return buttons
+
+ def _sendCrashReport(self):
+ # Before sending data, the user comments are stored
+ self.data["user_info"] = self.user_description_text_area.toPlainText()
+
+ # Convert data to bytes
+ binary_data = json.dumps(self.data).encode("utf-8")
+
+ # Submit data
+ kwoptions = {"data": binary_data, "timeout": 5}
+
+ if Platform.isOSX():
+ kwoptions["context"] = ssl._create_unverified_context()
+
+ Logger.log("i", "Sending crash report info to [%s]...", self.crash_url)
+
+ try:
+ f = urllib.request.urlopen(self.crash_url, **kwoptions)
+ Logger.log("i", "Sent crash report info.")
+ f.close()
+ except urllib.error.HTTPError:
+ Logger.logException("e", "An HTTP error occurred while trying to send crash report")
+ except Exception: # We don't want any exception to cause problems
+ Logger.logException("e", "An exception occurred while trying to send crash report")
+
+ os._exit(1)
+
+ def show(self):
+ self.dialog.exec_()
+ os._exit(1)
\ No newline at end of file
diff --git a/cura/Settings/ExtrudersModel.py b/cura/Settings/ExtrudersModel.py
index ab91e79789..b13e51723b 100644
--- a/cura/Settings/ExtrudersModel.py
+++ b/cura/Settings/ExtrudersModel.py
@@ -10,7 +10,6 @@ from UM.Application import Application
import UM.FlameProfiler
from cura.Settings.ExtruderManager import ExtruderManager
from cura.Settings.ExtruderStack import ExtruderStack #To listen to changes on the extruders.
-from cura.Settings.MachineManager import MachineManager #To listen to changes on the extruders of the currently active machine.
catalog = i18nCatalog("cura")
diff --git a/cura_app.py b/cura_app.py
index 6869fd2111..d725bc1200 100755
--- a/cura_app.py
+++ b/cura_app.py
@@ -41,8 +41,9 @@ if "PYTHONPATH" in os.environ.keys(): # If PYTHONPATH is u
sys.path.insert(1, PATH_real) # Insert it at 1 after os.curdir, which is 0.
def exceptHook(hook_type, value, traceback):
- import cura.CrashHandler
- cura.CrashHandler.show(hook_type, value, traceback)
+ from cura.CrashHandler import CrashHandler
+ _crash_handler = CrashHandler(hook_type, value, traceback)
+ _crash_handler.show()
sys.excepthook = exceptHook
diff --git a/plugins/MachineSettingsAction/MachineSettingsAction.py b/plugins/MachineSettingsAction/MachineSettingsAction.py
index 80258ef548..4343c2c780 100755
--- a/plugins/MachineSettingsAction/MachineSettingsAction.py
+++ b/plugins/MachineSettingsAction/MachineSettingsAction.py
@@ -79,7 +79,7 @@ class MachineSettingsAction(MachineAction):
@pyqtSlot()
def onFinishAction(self):
# Restore autoslicing when the machineaction is dismissed
- if self._backend.determineAutoSlicing():
+ if self._backend and self._backend.determineAutoSlicing():
self._backend.tickle()
def _onActiveExtruderStackChanged(self):
diff --git a/plugins/VersionUpgrade/VersionUpgrade27to30/VersionUpgrade27to30.py b/plugins/VersionUpgrade/VersionUpgrade27to30/VersionUpgrade27to30.py
index d0b78168b4..972d238921 100644
--- a/plugins/VersionUpgrade/VersionUpgrade27to30/VersionUpgrade27to30.py
+++ b/plugins/VersionUpgrade/VersionUpgrade27to30/VersionUpgrade27to30.py
@@ -118,24 +118,23 @@ class VersionUpgrade27to30(VersionUpgrade):
if not parser.has_section("general"):
parser.add_section("general")
- # Need to exclude the following names:
- # - ultimaker2_plus
- # - ultimaker2_go
- # - ultimaker2_extended
- # - ultimaker2_extended_plus
- exclude_prefix_list = ["ultimaker2_plus_",
- "ultimaker2_go_",
- "ultimaker2_extended_",
- "ultimaker2_extended_plus_"]
+ # The ultimaker 2 family
+ ultimaker2_prefix_list = ["ultimaker2_extended_",
+ "ultimaker2_go_",
+ "ultimaker2_"]
+ # ultimaker 2+ is a different family, so don't do anything with those
+ exclude_prefix_list = ["ultimaker2_extended_plus_",
+ "ultimaker2_plus_"]
+
+ # set machine definition to "ultimaker2" for the custom quality profiles that can be for the ultimaker 2 family
file_base_name = os.path.basename(filename)
- if file_base_name.startswith("ultimaker2_"):
- skip_this = False
- for exclude_prefix in exclude_prefix_list:
- if file_base_name.startswith(exclude_prefix):
- skip_this = True
- break
- if not skip_this:
- parser["general"]["definition"] = "ultimaker2"
+ is_ultimaker2_family = False
+ if not any(file_base_name.startswith(ep) for ep in exclude_prefix_list):
+ is_ultimaker2_family = any(file_base_name.startswith(ep) for ep in ultimaker2_prefix_list)
+
+ # ultimaker2 family quality profiles used to set as "fdmprinter" profiles
+ if is_ultimaker2_family and parser["general"]["definition"] == "fdmprinter":
+ parser["general"]["definition"] = "ultimaker2"
# Update version numbers
parser["general"]["version"] = "2"
diff --git a/resources/i18n/cura.pot b/resources/i18n/cura.pot
index 9135cfc381..fb4b9ed0d6 100644
--- a/resources/i18n/cura.pot
+++ b/resources/i18n/cura.pot
@@ -63,6 +63,21 @@ msgctxt "@info:status"
msgid "Print finished"
msgstr ""
+#: Manually added for resources/Cura/Cura.qml
+msgctxt "@title:menu menubar:toplevel"
+msgid "P&lugins"
+msgstr ""
+
+#: Manually added for resources/Cura/Actions.qml
+msgctxt "@action:menu"
+msgid "Browse plugins..."
+msgstr ""
+
+#: Manually added for resources/Cura/Actions.qml
+msgctxt "@action:menu"
+msgid "Installed plugins..."
+msgstr ""
+
#: /home/ruben/Projects/Cura/plugins/MachineSettingsAction/MachineSettingsAction.py:29
msgctxt "@action"
msgid "Machine Settings"
diff --git a/resources/i18n/de_DE/cura.po b/resources/i18n/de_DE/cura.po
index a18361802b..6bb71a5ae0 100644
--- a/resources/i18n/de_DE/cura.po
+++ b/resources/i18n/de_DE/cura.po
@@ -61,6 +61,21 @@ msgctxt "@info:status"
msgid "Print finished"
msgstr "Druck vollendet"
+#: Manually added for resources/Cura/Cura.qml
+msgctxt "@title:menu menubar:toplevel"
+msgid "P&lugins"
+msgstr "&Plugins"
+
+#: Manually added for resources/Cura/Actions.qml
+msgctxt "@action:menu"
+msgid "Browse plugins..."
+msgstr "Plugins durchsuchen..."
+
+#: Manually added for resources/Cura/Actions.qml
+msgctxt "@action:menu"
+msgid "Installed plugins..."
+msgstr "Installierte plugins..."
+
#: /home/ruben/Projects/Cura/plugins/MachineSettingsAction/MachineSettingsAction.py:29
msgctxt "@action"
msgid "Machine Settings"
diff --git a/resources/i18n/es_ES/cura.po b/resources/i18n/es_ES/cura.po
index fe88603c5e..f8f379d3d9 100644
--- a/resources/i18n/es_ES/cura.po
+++ b/resources/i18n/es_ES/cura.po
@@ -61,6 +61,21 @@ msgctxt "@info:status"
msgid "Print finished"
msgstr "Impresión terminada"
+#: Manually added for resources/Cura/Cura.qml
+msgctxt "@title:menu menubar:toplevel"
+msgid "P&lugins"
+msgstr "&Complementos"
+
+#: Manually added for resources/Cura/Actions.qml
+msgctxt "@action:menu"
+msgid "Browse plugins..."
+msgstr "Examinar complementos..."
+
+#: Manually added for resources/Cura/Actions.qml
+msgctxt "@action:menu"
+msgid "Installed plugins..."
+msgstr "Complementos instalados..."
+
#: /home/ruben/Projects/Cura/plugins/MachineSettingsAction/MachineSettingsAction.py:29
msgctxt "@action"
msgid "Machine Settings"
diff --git a/resources/i18n/fi_FI/cura.po b/resources/i18n/fi_FI/cura.po
index 6c4dc4fdc8..ae60ff98c6 100644
--- a/resources/i18n/fi_FI/cura.po
+++ b/resources/i18n/fi_FI/cura.po
@@ -61,6 +61,21 @@ msgctxt "@info:status"
msgid "Print finished"
msgstr "Tulosta valmis"
+#: Manually added for resources/Cura/Cura.qml
+msgctxt "@title:menu menubar:toplevel"
+msgid "P&lugins"
+msgstr "&Lisäosat"
+
+#: Manually added for resources/Cura/Actions.qml
+msgctxt "@action:menu"
+msgid "Browse plugins..."
+msgstr "Selaa lisäosia..."
+
+#: Manually added for resources/Cura/Actions.qml
+msgctxt "@action:menu"
+msgid "Installed plugins..."
+msgstr "Asennetut lisäoset..."
+
#: /home/ruben/Projects/Cura/plugins/MachineSettingsAction/MachineSettingsAction.py:29
msgctxt "@action"
msgid "Machine Settings"
diff --git a/resources/i18n/fr_FR/cura.po b/resources/i18n/fr_FR/cura.po
index 5724cb3192..76ad2ae4a6 100644
--- a/resources/i18n/fr_FR/cura.po
+++ b/resources/i18n/fr_FR/cura.po
@@ -61,6 +61,21 @@ msgctxt "@info:status"
msgid "Print finished"
msgstr "Impression terminée"
+#: Manually added for resources/Cura/Cura.qml
+msgctxt "@title:menu menubar:toplevel"
+msgid "P&lugins"
+msgstr "&Plug-ins"
+
+#: Manually added for resources/Cura/Actions.qml
+msgctxt "@action:menu"
+msgid "Browse plugins..."
+msgstr "Parcourir les plug-ins..."
+
+#: Manually added for resources/Cura/Actions.qml
+msgctxt "@action:menu"
+msgid "Installed plugins..."
+msgstr "Plug-ins installés..."
+
#: /home/ruben/Projects/Cura/plugins/MachineSettingsAction/MachineSettingsAction.py:29
msgctxt "@action"
msgid "Machine Settings"
diff --git a/resources/i18n/it_IT/cura.po b/resources/i18n/it_IT/cura.po
index ec85f0c2c8..7354379afa 100644
--- a/resources/i18n/it_IT/cura.po
+++ b/resources/i18n/it_IT/cura.po
@@ -61,6 +61,21 @@ msgctxt "@info:status"
msgid "Print finished"
msgstr "Stampa finita"
+#: Manually added for resources/Cura/Cura.qml
+msgctxt "@title:menu menubar:toplevel"
+msgid "P&lugins"
+msgstr "&Plugin"
+
+#: Manually added for resources/Cura/Actions.qml
+msgctxt "@action:menu"
+msgid "Browse plugins..."
+msgstr "Sfoglia plugin..."
+
+#: Manually added for resources/Cura/Actions.qml
+msgctxt "@action:menu"
+msgid "Installed plugins..."
+msgstr "Plugin installati..."
+
#: /home/ruben/Projects/Cura/plugins/MachineSettingsAction/MachineSettingsAction.py:29
msgctxt "@action"
msgid "Machine Settings"
diff --git a/resources/i18n/nl_NL/cura.po b/resources/i18n/nl_NL/cura.po
index c43d557c71..680106d321 100644
--- a/resources/i18n/nl_NL/cura.po
+++ b/resources/i18n/nl_NL/cura.po
@@ -61,6 +61,21 @@ msgctxt "@info:status"
msgid "Print finished"
msgstr "Print klaar"
+#: Manually added for resources/Cura/Cura.qml
+msgctxt "@title:menu menubar:toplevel"
+msgid "P&lugins"
+msgstr "&Plugins"
+
+#: Manually added for resources/Cura/Actions.qml
+msgctxt "@action:menu"
+msgid "Browse plugins..."
+msgstr "Door invoegtoepassingen bladeren..."
+
+#: Manually added for resources/Cura/Actions.qml
+msgctxt "@action:menu"
+msgid "Installed plugins..."
+msgstr "Geïnstalleerde plugins..."
+
#: /home/ruben/Projects/Cura/plugins/MachineSettingsAction/MachineSettingsAction.py:29
msgctxt "@action"
msgid "Machine Settings"
diff --git a/resources/i18n/pl_PL/cura.po b/resources/i18n/pl_PL/cura.po
index 9021cbcdde..fd840d9fc2 100644
--- a/resources/i18n/pl_PL/cura.po
+++ b/resources/i18n/pl_PL/cura.po
@@ -63,6 +63,21 @@ msgctxt "@info:status"
msgid "Print finished"
msgstr "Drukowanie zakończone"
+#: Manually added for resources/Cura/Cura.qml
+msgctxt "@title:menu menubar:toplevel"
+msgid "P&lugins"
+msgstr "W&tyczki"
+
+#: Manually added for resources/Cura/Actions.qml
+msgctxt "@action:menu"
+msgid "Browse plugins..."
+msgstr "Przeglądaj wtyczki..."
+
+#: Manually added for resources/Cura/Actions.qml
+msgctxt "@action:menu"
+msgid "Installed plugins..."
+msgstr "Zainstalowane wtyczki..."
+
#: /home/ruben/Projects/Cura/plugins/MachineSettingsAction/MachineSettingsAction.py:29
msgctxt "@action"
msgid "Machine Settings"
diff --git a/resources/i18n/pt_BR/cura.po b/resources/i18n/pt_BR/cura.po
index 868fe5e552..db4aa72f0b 100644
--- a/resources/i18n/pt_BR/cura.po
+++ b/resources/i18n/pt_BR/cura.po
@@ -62,6 +62,21 @@ msgctxt "@info:status"
msgid "Print finished"
msgstr "Impressão Concluída"
+#: Manually added for resources/Cura/Cura.qml
+msgctxt "@title:menu menubar:toplevel"
+msgid "P&lugins"
+msgstr "&Complementos"
+
+#: Manually added for resources/Cura/Actions.qml
+msgctxt "@action:menu"
+msgid "Browse plugins..."
+msgstr "Navegar complementos..."
+
+#: Manually added for resources/Cura/Actions.qml
+msgctxt "@action:menu"
+msgid "Installed plugins..."
+msgstr "Complementos instalados..."
+
#: /home/ruben/Projects/Cura/plugins/MachineSettingsAction/MachineSettingsAction.py:29
msgctxt "@action"
msgid "Machine Settings"
diff --git a/resources/i18n/ru_RU/cura.po b/resources/i18n/ru_RU/cura.po
index 7fe9e17073..34f7262a1f 100755
--- a/resources/i18n/ru_RU/cura.po
+++ b/resources/i18n/ru_RU/cura.po
@@ -63,6 +63,21 @@ msgctxt "@info:status"
msgid "Print finished"
msgstr "Печать завершена"
+#: Manually added for resources/Cura/Cura.qml
+msgctxt "@title:menu menubar:toplevel"
+msgid "P&lugins"
+msgstr "Плагины"
+
+#: Manually added for resources/Cura/Actions.qml
+msgctxt "@action:menu"
+msgid "Browse plugins..."
+msgstr "Просмотр плагинов..."
+
+#: Manually added for resources/Cura/Actions.qml
+msgctxt "@action:menu"
+msgid "Installed plugins..."
+msgstr "Установленные плагины..."
+
#: /home/ruben/Projects/Cura/plugins/MachineSettingsAction/MachineSettingsAction.py:29
msgctxt "@action"
msgid "Machine Settings"
diff --git a/resources/i18n/tr_TR/cura.po b/resources/i18n/tr_TR/cura.po
index dca9e1ce86..129c2bbe6b 100644
--- a/resources/i18n/tr_TR/cura.po
+++ b/resources/i18n/tr_TR/cura.po
@@ -61,6 +61,21 @@ msgctxt "@info:status"
msgid "Print finished"
msgstr "Baskı tamamlandı"
+#: Manually added for resources/Cura/Cura.qml
+msgctxt "@title:menu menubar:toplevel"
+msgid "P&lugins"
+msgstr "&Eklentiler"
+
+#: Manually added for resources/Cura/Actions.qml
+msgctxt "@action:menu"
+msgid "Browse plugins..."
+msgstr "Eklentilere göz at..."
+
+#: Manually added for resources/Cura/Actions.qml
+msgctxt "@action:menu"
+msgid "Installed plugins..."
+msgstr "Yüklü eklentiler..."
+
#: /home/ruben/Projects/Cura/plugins/MachineSettingsAction/MachineSettingsAction.py:29
msgctxt "@action"
msgid "Machine Settings"
diff --git a/resources/i18n/zh_CN/cura.po b/resources/i18n/zh_CN/cura.po
index 98357b94dc..092ce5120b 100644
--- a/resources/i18n/zh_CN/cura.po
+++ b/resources/i18n/zh_CN/cura.po
@@ -63,6 +63,21 @@ msgctxt "@info:status"
msgid "Print finished"
msgstr "打印完成"
+#: Manually added for resources/Cura/Cura.qml
+msgctxt "@title:menu menubar:toplevel"
+msgid "P&lugins"
+msgstr "插件"
+
+#: Manually added for resources/Cura/Actions.qml
+msgctxt "@action:menu"
+msgid "Browse plugins..."
+msgstr "浏览插件..."
+
+#: Manually added for resources/Cura/Actions.qml
+msgctxt "@action:menu"
+msgid "Installed plugins..."
+msgstr "已安装插件..."
+
#: /home/ruben/Projects/Cura/plugins/MachineSettingsAction/MachineSettingsAction.py:29
msgctxt "@action"
msgid "Machine Settings"