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 = "" + + 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"