diff --git a/front/src/static/assets/css/workspace.css b/front/src/static/assets/css/workspace.css index acdf7de7..84b4e09f 100644 --- a/front/src/static/assets/css/workspace.css +++ b/front/src/static/assets/css/workspace.css @@ -28,11 +28,15 @@ .ws-ask-button { padding: 10px; - width: 180px; + width: 210px; background-color: white; border-radius: 7px; box-shadow: 0px 0px 15px rgba(0, 0, 0, 0.1); text-align: center; + display: flex; + align-items: center; + justify-content: center; + gap: 8px; } diff --git a/front/src/static/config.js b/front/src/static/config.js index 50861d2d..554c6085 100755 --- a/front/src/static/config.js +++ b/front/src/static/config.js @@ -17,13 +17,19 @@ const config_edge_main_form_id = "#config_edge_main_form"; const config_content_save_tag = "#config_content_save"; const config_content_save_id = "config_content_save"; -const ClearConfigForm = function (text) { - - let txt = '' - - if (!text) { - txt = 'Тут будут настройки устройств. Выделите любое на схеме.'; +// Хелпер для безопасного получения перевода на лету +const t = function(key, fallback) { + if (typeof window !== 'undefined' && typeof window.getTranslation === 'function') { + const translated = window.getTranslation(key); + if (translated && translated !== key) { + return translated; + } } + return fallback; +}; + +const ClearConfigForm = function (text) { + let txt = text || t('settingsPlaceholder', 'Тут будут настройки устройств. Выделите любое на схеме.'); // Clear all child $(config_content_id).empty(); @@ -38,14 +44,12 @@ const ClearConfigForm = function (text) { } const HostWarningMsg = function (msg) { - let warning_msg = ''; $(config_content_id).prepend(warning_msg); } const SwitchWarningMsg = function (msg) { - let warning_msg = ''; @@ -53,7 +57,6 @@ const SwitchWarningMsg = function (msg) { } const ServerWarningMsg = function (msg) { - let warning_msg = ''; @@ -61,7 +64,6 @@ const ServerWarningMsg = function (msg) { } const HostErrorMsg = function (msg) { - $(config_content_id).find('.alert-info, .alert-danger').remove(); let error_msg = ' - + \ No newline at end of file diff --git a/front/src/static/config_hub.html b/front/src/static/config_hub.html index 0a93b015..6bf482d3 100644 --- a/front/src/static/config_hub.html +++ b/front/src/static/config_hub.html @@ -7,23 +7,23 @@ + \ No newline at end of file diff --git a/front/src/static/config_router.html b/front/src/static/config_router.html index 51ff6fdf..b1576b4f 100755 --- a/front/src/static/config_router.html +++ b/front/src/static/config_router.html @@ -7,7 +7,7 @@ @@ -70,7 +68,7 @@
- +
@@ -121,12 +119,12 @@ @@ -137,7 +135,7 @@ + class="text-sm" data-i18n="ipAddressMask">IP-адрес / Маска
+ class="text-sm" data-i18n="vlanLabel">VLAN + value="1" data-i18n-attr="placeholder:placeholderVlanId">
@@ -186,7 +183,7 @@ + \ No newline at end of file diff --git a/front/src/static/config_server.html b/front/src/static/config_server.html index 32e7aa60..241ccba4 100644 --- a/front/src/static/config_server.html +++ b/front/src/static/config_server.html @@ -7,7 +7,7 @@ @@ -52,13 +52,13 @@ - + \ No newline at end of file diff --git a/front/src/static/config_switch.html b/front/src/static/config_switch.html index c345a0ea..e604c1e7 100644 --- a/front/src/static/config_switch.html +++ b/front/src/static/config_switch.html @@ -8,12 +8,12 @@ @@ -66,7 +66,7 @@ @@ -79,7 +79,7 @@ @@ -95,8 +95,8 @@ @@ -130,11 +130,11 @@ + \ No newline at end of file diff --git a/front/src/static/config_vlan.html b/front/src/static/config_vlan.html index ce5081ec..17c3bbb1 100644 --- a/front/src/static/config_vlan.html +++ b/front/src/static/config_vlan.html @@ -10,8 +10,8 @@
@@ -34,12 +34,12 @@ - + @@ -47,4 +47,4 @@
УстройствоУстройство - VLAN ID + VLAN ID - Тип подключения + Тип подключения
- + \ No newline at end of file diff --git a/front/src/static/config_vxlan.html b/front/src/static/config_vxlan.html index 39621657..e3f51391 100644 --- a/front/src/static/config_vxlan.html +++ b/front/src/static/config_vxlan.html @@ -1,10 +1,10 @@ @@ -14,8 +14,8 @@ @@ -42,18 +42,16 @@
- +
- +

@@ -64,23 +62,21 @@
- +
- +
- +
\ No newline at end of file diff --git a/front/src/static/js/i18n.js b/front/src/static/js/i18n.js new file mode 100644 index 00000000..39d9a376 --- /dev/null +++ b/front/src/static/js/i18n.js @@ -0,0 +1,248 @@ +class I18nManager { + #translations = {}; + #currentLanguage = 'ru'; + #supportedLanguages = ['ru', 'en']; + #defaultLanguage = 'ru'; + #observer = null; + + constructor() { + this.DEBUG = typeof window !== 'undefined' && window.I18N_DEBUG === true; + } + + /** + * Логирование информационных сообщений (только в режиме отладки) + * @param {...any} args + */ + #logInfo(...args) { + if (this.DEBUG) { + // eslint-disable-next-line no-console + console.info('[i18n]', ...args); + } + } + + /** + * Логирование ошибок (всегда включено) + * @param {...any} args + */ + #logError(...args) { + // eslint-disable-next-line no-console + console.error('[i18n]', ...args); + } + + /** + * Текущий выбранный язык + * @returns {string} + */ + get currentLanguage() { + return this.#currentLanguage; + } + + /** + * Инициализация локализации при загрузке страницы + */ + async init() { + const savedLang = localStorage.getItem('language'); + const browserLang = navigator.language.slice(0, 2); + const langToSet = savedLang || browserLang || this.#defaultLanguage; + + await this.setLanguage(langToSet); + this.#startMutationObserver(); + } + + /** + * Устанавливает язык и применяет переводы + * @param {string} lang + */ + async setLanguage(lang) { + if (!this.#supportedLanguages.includes(lang)) { + lang = this.#defaultLanguage; + } + + if (!this.#translations[lang]) { + const isLoaded = await this.#loadTranslations(lang); + if (!isLoaded && lang !== this.#defaultLanguage) { + lang = this.#defaultLanguage; // Fallback + } + } + + this.#currentLanguage = lang; + this.#applyTranslations(lang); + localStorage.setItem('language', lang); + } + + /** + * Загружает JSON-файл с переводами с сервера + * @param {string} lang + * @returns {Promise} Успешна ли загрузка + */ + async #loadTranslations(lang) { + try { + const response = await fetch(`/locales/${lang}.json?v=${new Date().getTime()}`); + if (!response.ok) { + throw new Error(`HTTP ${response.status} - Could not load ${lang}.json`); + } + this.#translations[lang] = await response.json(); + this.#logInfo(`Translations for ${lang} loaded.`); + return true; + } catch (error) { + this.#logError(`Failed to load translations for ${lang}:`, error); + + // Fallback механизм + if (lang !== this.#defaultLanguage && !this.#translations[this.#defaultLanguage]) { + this.#logInfo(`Attempting fallback to ${this.#defaultLanguage}...`); + try { + const fallbackResp = await fetch(`/locales/${this.#defaultLanguage}.json?v=${new Date().getTime()}`); + if (!fallbackResp.ok) throw new Error(`Could not load ${this.#defaultLanguage}.json`); + this.#translations[this.#defaultLanguage] = await fallbackResp.json(); + this.#logInfo(`Fallback to ${this.#defaultLanguage} applied.`); + } catch (fallbackError) { + this.#logError(`Fallback to ${this.#defaultLanguage} failed:`, fallbackError); + } + } + return false; + } + } + + /** + * Применяет загруженные переводы к текущему DOM + * @param {string} lang + */ + #applyTranslations(lang) { + if (!this.#translations[lang]) return; + + this.translateNode(document); + this.#updateNetworkButtonsTooltipsAria(); + document.documentElement.lang = lang; + } + + /** + * Переводит все элементы внутри переданного узла + * @param {HTMLElement|Document} node + */ + translateNode(node) { + if (!node || !node.querySelectorAll) return; + const trans = this.#translations[this.#currentLanguage]; + if (!trans) return; + + node.querySelectorAll('[data-i18n]').forEach(element => { + const key = element.getAttribute('data-i18n'); + if (trans[key]) { + element.innerHTML = trans[key]; + } + }); + + node.querySelectorAll('[data-i18n-attr]').forEach(element => { + const attrString = element.getAttribute('data-i18n-attr'); + if (attrString) { + attrString.split(';').forEach(attrPair => { + const[attr, key] = attrPair.split(':'); + if (attr && key && trans[key]) { + element.setAttribute(attr.trim(), trans[key]); + } + }); + } + }); + } + + /** + * Возвращает переведенную строку по ключу + * @param {string} key + * @returns {string} + */ + getTranslation(key) { + const trans = this.#translations[this.#currentLanguage]; + if (trans && Object.prototype.hasOwnProperty.call(trans, key)) { + return trans[key]; + } + return key; + } + + /** + * Запускает MutationObserver для перевода динамически добавляемых элементов (замена setTimeout) + */ + #startMutationObserver() { + if (typeof MutationObserver === 'undefined') return; + + this.#observer = new MutationObserver((mutations) => { + mutations.forEach(mutation => { + mutation.addedNodes.forEach(node => { + if (node.nodeType === Node.ELEMENT_NODE) { + // Если сам узел имеет атрибуты перевода + if (node.hasAttribute('data-i18n') || node.hasAttribute('data-i18n-attr')) { + this.translateNode(node.parentElement || node); + } else { + // Ищем дочерние элементы + this.translateNode(node); + } + } + }); + }); + }); + + this.#observer.observe(document.body, { childList: true, subtree: true }); + } + + /** + * Адаптер для обновления тултипов Bootstrap + */ + #updateNetworkButtonsTooltipsAria() { + if (typeof document === 'undefined') return; + + const map = { + 'share-network': 'tooltipUrlAvailable', + 'copy-network': 'tooltipSaveChanges', + 'net-settings': 'tooltipNetSettings', + }; + + Object.keys(map).forEach(id => { + const el = document.getElementById(id); + if (!el) return; + + const key = map[id]; + const text = this.getTranslation(key); + if (!text || text === key) return; + + // Безопасная работа с jQuery и Bootstrap + try { + if (typeof jQuery !== 'undefined' && typeof jQuery(el).tooltip === 'function') { + if (jQuery(el).data('bs.tooltip')) { + jQuery(el).attr('data-original-title', text).tooltip('fixTitle'); + } else { + jQuery(el).attr('title', text); + jQuery(el).tooltip('dispose'); + jQuery(el).tooltip({ trigger: 'hover', title: text }); + } + } + } catch (err) { + this.#logError(`Tooltip update failed for #${id}`, err); + } + + // Обновление aria-describedby + const describedById = el.getAttribute('aria-describedby'); + if (describedById) { + const tooltipEl = document.getElementById(describedById); + if (tooltipEl) { + const inner = tooltipEl.querySelector('.tooltip-inner') || tooltipEl; + inner.textContent = text; + } + } + }); + } +} + +// Создаем глобальный инстанс для работы приложения +const i18nManager = new I18nManager(); + +// Обратная совместимость для существующего кода +if (typeof window !== 'undefined') { + window.i18n = i18nManager; + // Оставляем глобальные функции, чтобы не сломать другие скрипты, которые их вызывают + window.setLanguage = (lang) => i18nManager.setLanguage(lang); + window.getTranslation = (key) => i18nManager.getTranslation(key); + // Заменяем старую функцию на надежный метод класса (хотя Observer сделает все сам) + window.translateDynamicContent = (el) => i18nManager.translateNode(el); +} + +document.addEventListener('DOMContentLoaded', () => { + i18nManager.init(); +}); \ No newline at end of file diff --git a/front/src/static/locales/en.json b/front/src/static/locales/en.json new file mode 100644 index 00000000..b5e59e6f --- /dev/null +++ b/front/src/static/locales/en.json @@ -0,0 +1,284 @@ +{ + "docTitle": "Computer Network Emulator", + "docDescription": "Computer network emulator for educational purposes based on Linux OS", + "mainTitle": "Computer network emulator for educational purposes based on Linux OS", + "createNetworkBtn": "Create a network", + "featuresTitle": "Features", + "feature1Title": "All work in the browser", + "feature1Desc": "No need to install additional programs. Network creation and configuration are performed directly in the browser.", + "feature2Title": "Accurate network emulation", + "feature2Desc": "The Linux OS network stack is used for network emulation. The whole process takes only 10-15 seconds, depending on the network settings.", + "feature3Title": "Network animation", + "feature3Desc": "Detailed animation helps to thoroughly study the operation of the network and specific protocols. The animation can be scrolled to a specific step.", + "feature4Title": "Variety of network settings", + "feature4Desc": "You can run a TCP/UDP server and client, study the entire TCP/IP stack in detail, and set channel losses. There is support for NAT, VLAN, IPIP, GRE, VxLAN, STP/RSTP.", + "feature5Title": "Knowledge check (testing)", + "feature5Desc": "Automatic testing on theory and practice. Contact us to add your own questions and tasks!", + "whoUsesTitle": "Who uses Miminet?", + "whoUsesDesc": "Miminet has already been used by 15,000+ students.", + "useCasesTitle": "Use Cases", + "useCase1Title": "In lectures", + "useCase1Desc": "An interactive tool for the lecturer. You can draw network diagrams directly in the browser and immediately animate them, demonstrating the work of various protocols and devices.", + "useCase1Device1": "Host", + "useCase1Device2": "Switch", + "useCase1Device3": "Router", + "useCase1Device4": "Server", + "useCase2Title": "In practical classes", + "useCase2Desc": "A large set of configurable parameters. You can assemble simple and complex configurations, emulate them, and study in detail the operation of individual protocols: ARP, IP, TCP, ICMP, GRE, and many others.", + "useCase3Title": "Knowledge check", + "useCase3Desc": "The \"Testing\" section helps to conduct interviews and check the knowledge of students. You can create your own sets of tests from theoretical questions and practical tasks with automatic verification.", + "useCase3Fact1": "100+ Questions in the database", + "useCase3Fact2": "10+ Practical tasks", + "howItWorksTitle": "How does it work?", + "step1Title": "Draw your network", + "step1Desc": "You can create a computer network with hosts, switches, hubs, routers, and servers. You can also specify settings for each device.", + "step2Title": "Send the network for emulation", + "step2Desc": "On our server, your network will be emulated using the Linux network subsystem. A network interface with its own unique MAC address and other settings is created for each device.", + "step3Title": "Wait for emulation on the server", + "step3Desc": "During network emulation, all network traffic is captured, which is then processed for visualization.", + "step4Title": "Start the animation", + "step4Desc": "Start the animation and watch the network traffic in a convenient format.", + "videoTitle": "How it works (video)", + "contactTitle": "Still have questions?", + "contactSubtitle": "You can contact us", + "workspaceDocDescription": "Web-based computer network emulation", + "myNetworksLink": "My Networks", + "tooltipNetSettings": "Network Settings", + "tooltipSaveChanges": "Save a copy", + "tooltipUrlAvailable": "Network is available via URL", + "loginButton": "Login", + "devicesHeader": "Devices", + "deviceSwitch": "Switch (L2)", + "deviceSwitchPopover": "A switch operates at the second layer of the OSI model.", + "deviceHost": "Host", + "deviceHostPopover": "A host is a network end-device.", + "deviceHub": "Hub (L1)", + "deviceHubPopover": "A hub is the simplest network device.", + "deviceRouter": "Router (L3)", + "deviceRouterPopover": "A router operates at the 3rd layer of the OSI model, allowing different networks to be connected.", + "deviceServer": "Server", + "deviceServerPopover": "A server handles client requests.", + "settingsHeader": "Settings", + "settingsPlaceholder": "Device settings will be here. Select any device on the scheme.", + "emulationCheckButton": "Emulation check", + "emulationWaitLabel": "Waiting 10-30 sec.", + "askQuestionLink": "Ask a question", + "navCourses": "Courses", + "navTesting": "Testing", + "navExamples": "Examples", + "modalNetSettingsTitle": "Network Settings", + "closeBtn": "Close", + "modalNetNameLabel": "Network name", + "modalNetDescLabel": "Network description", + "deleteNetworkBtn": "Delete network", + "saveBtn": "Save", + "modalDeleteConfirmTitle": "Are you sure you want to delete this network?", + "yesDeleteBtn": "Yes, delete it!", + "noBtn": "No", + "modalWhatToDoTitle": "What to do?", + "modalNoJobsP1": "Why emulate a network if it does nothing?!", + "modalNoJobsP2": "In the host settings, add a command. For example, you can execute the 'ping' command to a specific IP address or hostname.", + "modalNoJobsP3": "And don't forget to configure the IP address and mask for the hosts.", + "okBtn": "OK", + "modalTooLargeTitle": "Network is too large!", + "modalTooLargeP1": "The current network is too large for emulation.", + "modalTooLargeP2": "The maximum network size available for emulation in Miminet is 80 nodes (hosts, switches, servers, routers).", + "modalCopySuccessTitle": "You have successfully saved the network", + "goToEditBtn": "Go to editing", + "continueHereBtn": "Continue here", + "alertLinkCopied": "Network link copied", + "docTitleHome": "Web-Emulator", + "docDescHome": "Computer network emulator for educational purposes based on Linux OS", + "newNetwork": "New network", + "docTitleCourses": "Training Courses", + "docDescCourses": "Training courses on computer networks using the Miminet emulator", + "coursesTitle": "Computer Networks Courses", + "freeBadge": "Free", + "course1Title": "Fundamentals of Computer Networks", + "course1Desc": "The course introduces the student to the world of computer networks, how your home network, a network in a cafe, or the global Internet works. This is a basic course and is mandatory before a deeper study of computer networks. For better understanding and assimilation of the material, the course contains many examples from the Miminet web emulator. This course is taught at the Faculty of Mathematics and Mechanics of St. Petersburg State University.", + "studentsLabel": "students", + "course2Title": "Computer Network Programming (Python)", + "course2Desc": "A practical course for those who want to learn how to write their own network applications in Python under Linux. In the course, you will learn to: write your own TCP/UDP server and client, establish a secure (SSL) connection, write your own sniffer (raw socket, scapy), manage network settings in Linux OS directly from Python (pyroute2), work with tun/tap devices and write your own tunnels (VPN). This course is taught at the Faculty of Mathematics and Mechanics of St. Petersburg State University.", + "docTitleExamples": "Computer Network Examples", + "docDescExamples": "Examples of computer network emulations based on Linux OS", + "examplesTitle": "Computer Network Examples", + "ariaOpenNetwork": "Open network", + "consentTitle": "CONSENT TO THE PROCESSING OF PERSONAL DATA", + "consentP1": "Acting freely, by his own will and in his own interest, as well as confirming his legal capacity, the individual gives his consent to Ilya Valerievich Zelenchuk, registered at: 198206, St. Petersburg, Novobelitskaya street, building 6, block 2, 8th floor, apt. 68 (hereinafter - the Operator) for the processing of his personal data under the following conditions:", + "consentLi1": "This Consent is given for the processing of personal data, both without the use of automation tools and with their use.", + "consentLi2": "Consent is given to the processing of my following personal data: data available from the social network profile used for authorization on the site; email address; nickname.", + "consentLi3": "The purpose of processing personal data: user registration on the site/in the application.", + "consentLi4": "In the course of processing, the following actions will be performed with personal data: collection, systematization; storage; use; extraction; blocking; destruction; recording; deletion; accumulation; updating; modification.", + "consentLi5": "Personal data is processed until the user deletes his personal account on the site.", + "consentLi6": "Consent may be withdrawn by the subject of personal data or his representative by sending an application to the email address: ilya@hackerdom.ru.", + "docTitleMimiShark": "MimiShark", + "downloadPcap": "Download pcap", + "tableColTime": "Time", + "tableColSource": "Source", + "tableColDestination": "Destination", + "tableColProtocol": "Protocol", + "tableColLength": "Length", + "docTitleLogin": "Login/Registration in Web-Emulator", + "welcome": "Welcome!", + "loginWith": "Login with", + "consentAgreement": "By registering, I give my consent to data processing.", + "adminCreateTask": "Create Check Task", + "adminGuidsLabel": "GUIDs", + "adminRequirementsLabel": "Requirements", + "adminSubmitBtn": "Submit", + "adminCheckByQuestion": "Check Tasks by Question", + "adminQuestionIdLabel": "Question ID", + "adminRunCheckBtn": "Run Check", + "cancelBtn": "Cancel", + "adminCheckByQuestionBtn": "Check by Question", + "testNotFinishedTitle": "You have not completed the test", + "testNotFinishedText": "It seems you started the test but did not complete it.", + "backToTestsBtn": "Back to tests", + "modalPracticeNoJobsP1": "Why simulate a network if it does nothing?!", + "modalPracticeNoJobsP2": "Read the task and configure the network: add devices, configure the IP address and mask for the hosts, and add the necessary command in the host settings.", + "counterQuestion": "Question", + "counterOf": "of", + "finishTestBtn": "Finish test", + "emulateBtn": "Emulate", + "hints": "Hints", + "answerBtn": "Answer", + "nextQuestionBtn": "Next question", + "answerSaved": "Answer saved", + "yourScore": "You scored", + "points": "points", + "imageQuestion": "Question image", + "imageEnlarged": "Enlarged image", + "resultsTitle": "Results", + "resultsAnswersLater": "Answers will be available later", + "resultsExamFinished": "You have completed the exam. The results will be available on", + "resultsPending": "Your answers are under review. Please wait for the results.", + "viewMyAnswer": "View my answer", + "resultsTestResults": "Test results", + "resultsOnCheck": "Answer is under review", + "resultsPoints": "Points", + "resultsTimeSpent": "Test completed in:", + "resultsFinal": "Final result:", + "resultsPracticeTasks": "Practical tasks:", + "resultsPracticeTask": "Practical task", + "resultsTheoryTitle": "Theory test results:", + "resultsCorrectAnswers": "Correct answers:", + "quizTitle": "Computer Networks Testing", + "quizControlTask": "Control task: this section is an exam. No hints are provided.", + "quizTrainingTask": "Training task: this section is for learning. Explanations will be given for incorrect answers.", + "resultsPageLink": "To the results page", + "resultsLastResultLink": "View last result", + "resultsLabel": "Results", + "correctLabel": "correct", + "questionsCount_one": "question", + "questionsCount_few": "questions", + "questionsCount_many": "questions", + "minutesCount_one": "minute", + "minutesCount_few": "minutes", + "minutesCount_many": "minutes", + "sectionsCount_one": "section", + "sectionsCount_few": "sections", + "sectionsCount_many": "sections", + "resultsDeadline": "Until", + "resultsFinished": "Testing is over", + "resultsNoTimeLimit": "No time limit", + "modalTestPassed": "You have already passed this section", + "modalOneAttemptTitle": "The test can only be taken once.", + "modalOneAttemptP1": "You can return to the test at any time until you submit an answer and start another test.", + "modalOneAttemptP2": "The test can only be taken once.", + "modalOneAttemptP3": "The time to complete is limited. Are you ready to start?", + "startBtn": "Start", + "edgeFrom": "From", + "edgeTo": "To", + "edgePacketLoss": "Packet Loss (%)", + "hostName": "Hostname", + "executeCommand": "Execute command", + "cmdNone": "---", + "cmdPing1": "ping (1 packet)", + "cmdPingWithOptions": "ping (with options)", + "cmdTraceroute": "traceroute (with options)", + "cmdUdp": "Send data (UDP)", + "cmdTcp": "Send data (TCP)", + "cmdAddRoute": "Add route", + "cmdAddArp": "Add ARP-cache entry", + "cmdDhclient": "Request IP address automatically", + "placeholderPingIp": "Enter host IP", + "placeholderOptions": "example: -c 1 -t 5", + "placeholderIpAddress": "192.168.1.1", + "placeholderTracerouteOptions": "example: -n", + "placeholderBytes": "Size in bytes (1-65535)", + "recipientIpPort": "Recipient (IP / port)", + "placeholderIp": "IP address", + "placeholderPort": "Port", + "placeholderMacAddress": "01:02:03:04:05:06", + "ipAddressMask": "IP Address / Mask", + "gatewayIp": "Gateway IP address", + "linkTo": "Link to", + "defaultGateway": "Default Gateway", + "hubName": "Hub Name", + "routerName": "Router Name", + "cmdAddIp": "Add IP address", + "cmdEnableNat": "Enable NAT on interface", + "cmdAddVlanSubif": "Add VLAN subinterface", + "cmdAddIpip": "Add IPIP interface", + "cmdAddGre": "Add GRE interface", + "cmdEnableArpProxy": "Enable ARP Proxy on interface", + "vlanLabel": "VLAN", + "placeholderVlanId": "Enter VLAN ID", + "ipipInterfaceParams": "IPIP Interface Parameters", + "endpointIp": "Endpoint IP", + "ipipInterfaceIp": "IPIP Interface IP", + "ipipInterfaceName": "IPIP Interface Name", + "greInterfaceParams": "GRE Interface Parameters", + "greInterfaceIp": "GRE Interface IP", + "greInterfaceName": "GRE Interface Name", + "arpProxyParams": "ARP Proxy Interface Parameters", + "serverName": "Server Name", + "cmdUdpServer": "Start UDP server", + "cmdTcpServer": "Start TCP server", + "cmdBlockPort": "Block TCP/UDP port", + "cmdDhcpServer": "Start DHCP server", + "ipAddressPort": "IP Address / Port", + "tcpUdpPort": "TCP/UDP port", + "ipRange": "IP address range", + "maskIpAddress": "IP address mask", + "switchName": "Switch Name", + "stpWarning": "Enabling STP protocol increases emulation time by 30 seconds.", + "rstpWarning": "Enabling RSTP protocol increases emulation time by 10 seconds.", + "stpSelection": "STP/RSTP Selection", + "disableStp": "Disable STP", + "priority": "Priority", + "vlanSetup": "VLAN Configuration", + "device": "Device", + "tooltipVlanId": "Allowed values: from 1 to 4094.", + "connectionType": "Connection Type", + "tooltipTrunk": "For Trunk connection, enter values separated by comma or space. E.g.: \"10 20\"", + "vxlanSetup": "VXLAN Configuration", + "closeAndSave": "Close and Save", + "clientLink": "Client Link", + "addDevice": "Add Device", + "targetVtepIp": "Target VTEP IP", + "networkLink": "Network Link", + "addInterface": "Add Interface", + "tooltipVxlanConfig": "Configure VTEP by adding client devices with corresponding VNIs and specify the interface for sending packets to other VTEPs.", + "tooltipVxlanHelp": "Configure VTEP by adding client devices associated with a VNI and specify the interface and remote router IP to send packets with the corresponding VNI to the specified VTEP.", + "emulatingStatus": "Emulating...", + "commandsLabel": "Commands", + "stepsLabel": "Step:", + "ofLabel": "of", + "step": "step", + "steps_few": "steps", + "steps_many": "steps", + "packet": "packet", + "packets_few": "packets", + "packets_many": "packets", + "packets_zero": "0 packets", + "queuePosition": "Queue position", + "langSelectorAria": "Language selector", + "loadingError": "Data loading error", + "translationMissing": "Translation missing", + "savingBtn": "Saving...", + "selectLink": "Select link", + "startInterface": "Start interface", + "notEnoughInterfaces": "Not enough interfaces", + "profileTitle": "Profile" +} \ No newline at end of file diff --git a/front/src/static/locales/ru.json b/front/src/static/locales/ru.json new file mode 100644 index 00000000..f24aee83 --- /dev/null +++ b/front/src/static/locales/ru.json @@ -0,0 +1,284 @@ +{ + "docTitle": "Эмулятор компьютерной сети", + "docDescription": "Эмулятор компьютерной сети для образовательных целей на базе ОС Linux", + "mainTitle": "Эмулятор компьютерной сети для образовательных целей на базе ОС Linux", + "createNetworkBtn": "Создать сеть", + "featuresTitle": "Возможности", + "feature1Title": "Вся работа в браузере", + "feature1Desc": "Нет нужды устанавливать дополнительные программы. Создание и настройка сети выполняется прямо в браузере.", + "feature2Title": "Точная эмуляция сети", + "feature2Desc": "Для эмуляции сети используется сетевой стек ОС Linux. Весь процесс занимает всего 10-15 секунд, в зависимости от настроек сети.", + "feature3Title": "Анимация сети", + "feature3Desc": "Детальная анимация помогает досконально изучать работу сети и конкретных протоколов. Анимацию можно проматывать до конкретного шага.", + "feature4Title": "Разнообразие сетевых настроек", + "feature4Desc": "Можно запускать TCP/UDP сервер и клиент, детально изучать работу всего TCP/IP стека, устанавливать потери в канале. Есть поддержка NAT, VLAN, IPIP, GRE, VxLAN, STP/RSTP.", + "feature5Title": "Проверка знаний (тестирование)", + "feature5Desc": "Автоматическое тестирование по теории и практике. Свяжитесь с нами для добавления своих вопросов и задач!", + "whoUsesTitle": "Кто использует Miminet?", + "whoUsesDesc": "Miminet уже воспользовались 15 000+ обучающихся.", + "useCasesTitle": "Примеры использования", + "useCase1Title": "На лекциях", + "useCase1Desc": "Интерактивный инструмент для лектора. Можно прямо в браузере рисовать схемы сетей и сразу анимировать их, демонстрируя работу различных протоколов и устройств.", + "useCase1Device1": "Хост", + "useCase1Device2": "Коммутатор", + "useCase1Device3": "Маршрутизатор", + "useCase1Device4": "Сервер", + "useCase2Title": "На практиках", + "useCase2Desc": "Большой набор конфигурируемых параметров. Вы можете собирать простые и сложные конфигурации, эмулировать их и детально изучать работу отдельных протоколов: ARP, IP, TCP, ICMP, GRE и многих других.", + "useCase3Title": "Проверка знаний", + "useCase3Desc": "Раздел \"Тестирование\" помогает проводить собеседования и проверку знаний у обучающихся. Можно создавать свои наборы тестов из теоретических вопросов и практических заданий с автоматической проверкой.", + "useCase3Fact1": "100+ Вопросов в базе", + "useCase3Fact2": "10+ Практических задач", + "howItWorksTitle": "Как это работает?", + "step1Title": "Нарисуйте свою сеть", + "step1Desc": "Вы можете создать компьютерную сеть, в которой будут хосты, свитчи, хабы, раутеры и сервера. Также можете задать настройки для каждого устройства.", + "step2Title": "Отправьте сеть на эмуляцию", + "step2Desc": "На нашем сервере ваша сеть будет эмулироваться с помощью сетевой подсистемы Linux. Для каждого устройства создается сетевой интерфейс со своим уникальным MAC адресом и другими настройками.", + "step3Title": "Дождитесь эмуляции на сервере", + "step3Desc": "Во время эмуляции сети идет захват всего сетевого трафика, который затем обрабатывается для визуализации.", + "step4Title": "Запустите анимацию", + "step4Desc": "Запустите анимацию и наблюдайте за сетевым трафиком в удобном формате.", + "videoTitle": "Как это работает (видео)", + "contactTitle": "Остались вопросы?", + "contactSubtitle": "Можете связаться с нами", + "workspaceDocDescription": "Эмуляция компьютерной сети в вебе", + "myNetworksLink": "Мои сети", + "tooltipNetSettings": "Настройки сети", + "tooltipSaveChanges": "Сохранить копию", + "tooltipUrlAvailable": "Сеть доступна по URL", + "loginButton": "Вход", + "devicesHeader": "Устройства", + "deviceSwitch": "Свитч (L2)", + "deviceSwitchPopover": "Свитч (коммутатор) работает на втором уровне модели OSI.", + "deviceHost": "Хост", + "deviceHostPopover": "Хост - конечное сетевое устройство.", + "deviceHub": "Хаб (L1)", + "deviceHubPopover": "Хаб (концентратор) — простейшее сетевое устойство.", + "deviceRouter": "Роутер (L3)", + "deviceRouterPopover": "Маршрутизатор (роутер, раутер) — работает на 3-м уровне модели OSI, позволяет объединять различные сети.", + "deviceServer": "Сервер", + "deviceServerPopover": "Сервер — обслуживает клиентские запросы.", + "settingsHeader": "Настройки", + "settingsPlaceholder": "Тут будут настройки устройств. Выделите любое на схеме.", + "emulationCheckButton": "Проверка эмуляции", + "emulationWaitLabel": "Ожидание 10-30 сек.", + "askQuestionLink": "Задать вопрос", + "navCourses": "Учебные курсы", + "navTesting": "Тестирование", + "navExamples": "Примеры сетей", + "modalNetSettingsTitle": "Настройки сети", + "closeBtn": "Закрыть", + "modalNetNameLabel": "Название сети", + "modalNetDescLabel": "Описание сети", + "deleteNetworkBtn": "Удалить сеть", + "saveBtn": "Сохранить", + "modalDeleteConfirmTitle": "Вы точно хотите удалить эту сеть?", + "yesDeleteBtn": "Да, удалить!", + "noBtn": "Нет", + "modalWhatToDoTitle": "Что делать?", + "modalNoJobsP1": "Зачем эмулировать сеть, если она ничего не делает?!", + "modalNoJobsP2": "В настройках хоста добавьте команду, например, вы можете выполнить команду 'ping' на определенный IP адрес или имя хоста.", + "modalNoJobsP3": "И не забывайте конфигурировать IP адрес и маску у хостов.", + "okBtn": "Ок", + "modalTooLargeTitle": "Сеть слишком большая!", + "modalTooLargeP1": "Текущая сеть слишком большая для эмуляции.", + "modalTooLargeP2": "Максимальный размер сети доступный для эмуляции в Miminet составляет 80 узлов (хосты, коммутаторы, сервера, маршрутизацторы).", + "modalCopySuccessTitle": "Вы успешно сохранили сеть к себе", + "goToEditBtn": "Перейти к редактированию", + "continueHereBtn": "Продолжить здесь", + "alertLinkCopied": "Ссылка на сеть скопирована", + "docTitleHome": "Веб-эмулятор", + "docDescHome": "Эмулятор компьютерной сети для образовательных целей на базе ОС Linux", + "newNetwork": "Новая сеть", + "docTitleCourses": "Учебные курсы", + "docDescCourses": "Учебные курсы по компьютерным сетям с использованием эмулятора Miminet", + "coursesTitle": "Курсы по компьютерным сетям", + "freeBadge": "Бесплатно", + "course1Title": "Основы компьютерных сетей", + "course1Desc": "Курс знакомит слушателя с миром компьютерных сетей, как работает ваша домашняя сеть, сеть в кафе или глобальная сеть Интернет. Это базовый курс и он является обязательным перед более глубоким изучением компьютерных сетей. Для лучшего понимания и усваивания материала в курсе содержится множество примеров из веб-эмулятора Miminet. Данный курс читается на мат-мехе СПбГУ.", + "studentsLabel": "учащихся", + "course2Title": "Программирование компьютерных сетей (Python)", + "course2Desc": "Практический курс для тех, кто хочет научиться писать свои собственные сетевые приложения на Python под Linux. На курсе вы научитесь: писать свой TCP/UDP сервер и клиент, устанавливать безопасное (SSL) соединение, писать свой сниффер (raw socket, scapy), управлять сетевыми настройками в ОС Linux прямо из Python (pyroute2), работать с tun/tap устройствами и писать собственные туннели (VPN). Данный курс читается на мат-мехе СПбГУ.", + "docTitleExamples": "Примеры компьютерных сетей", + "docDescExamples": "Примеры эмуляций компьютерных сетей на базе ОС Linux", + "examplesTitle": "Примеры компьютерных сетей", + "ariaOpenNetwork": "Открыть сеть", + "consentTitle": "СОГЛАСИЕ НА ОБРАБОТКУ ПЕРСОНАЛЬНЫХ ДАННЫХ", + "consentP1": "Действуя свободно, своей волей и в своем интересе, а также подтверждая свою дееспособность, физическое лицо дает свое согласие Зеленчуку Илье Валерьевичу, зарегистрированный по адресу: 198206, г. Санкт-Петербург, улица Новобелицкая, дом 6, корпус 2, 8 этаж, кв. 68 (Далее - Оператор) на обработку своих персональных данных со следующими условиями:", + "consentLi1": "Данное Согласие дается на обработку персональных данных, как без использования средств автоматизации, так и с их использованием", + "consentLi2": "Согласие дается на обработку следующих моих персональных данных: данные доступные из профиля в социальной сети, с помощью которой происходит авторизация на сайте; адрес электронной почты; никнейм.", + "consentLi3": "Цель обработки персональных данных: регистрация пользователя на сайте/в приложении.", + "consentLi4": "В ходе обработки с персональными данными будут совершены следующие действия: сбор, систематизация; хранение; использование; извлечение; блокирование; уничтожение; запись; удаление; накопление; обновление; изменение.", + "consentLi5": "Персональные данные обрабатываются до удаления пользователем личного кабинета на сайте.", + "consentLi6": "Согласие может быть отозвано субъектом персональных данных или его представителем путем направления заявления по адресу электронной почты: ilya@hackerdom.ru.", + "docTitleMimiShark": "MimiShark", + "downloadPcap": "Скачать pcap", + "tableColTime": "Time", + "tableColSource": "Source", + "tableColDestination": "Destination", + "tableColProtocol": "Protocol", + "tableColLength": "Length", + "docTitleLogin": "Вход/Регистрация в веб-эмулятор", + "welcome": "Добро пожаловать!", + "loginWith": "Вход с помощью", + "consentAgreement": "Регистрируясь, я даю согласие на обработку данных.", + "adminCreateTask": "Создать задачу проверки", + "adminGuidsLabel": "GUIDs", + "adminRequirementsLabel": "Требования", + "adminSubmitBtn": "Отправить", + "adminCheckByQuestion": "Проверка заданий по вопросу", + "adminQuestionIdLabel": "ID Вопроса", + "adminRunCheckBtn": "Запустить проверку", + "cancelBtn": "Отмена", + "adminCheckByQuestionBtn": "Проверить по вопросу", + "testNotFinishedTitle": "Вы не завершили тест", + "testNotFinishedText": "Похоже, вы начали тест, но не завершили его.", + "backToTestsBtn": "Вернуться к тестам", + "modalPracticeNoJobsP1": "Зачем симулировать сеть, если она ничего не делает?!", + "modalPracticeNoJobsP2": "Прочитайте задание и настройте сеть: добавьте устройства, сконфигурируйте IP адрес и маску у хостов, в настройках хоста добавьте необходимую команду.", + "counterQuestion": "Вопрос", + "counterOf": "из", + "finishTestBtn": "Завершить тест", + "emulateBtn": "Эмулировать", + "hints": "Подсказки", + "answerBtn": "Ответить", + "nextQuestionBtn": "Следующий вопрос", + "answerSaved": "Ответ сохранён", + "yourScore": "Вы набрали", + "points": "баллов", + "imageQuestion": "Картинка вопроса", + "imageEnlarged": "Увеличенное изображение", + "resultsTitle": "Результаты", + "resultsAnswersLater": "Ответы появятся позже", + "resultsExamFinished": "Вы завершили экзамен. Результаты станут доступны", + "resultsPending": "Ваши ответы находятся на проверке. Пожалуйста, ожидайте результатов.", + "viewMyAnswer": "Посмотреть мой ответ", + "resultsTestResults": "Результаты теста", + "resultsOnCheck": "Ответ на проверке", + "resultsPoints": "Баллы", + "resultsTimeSpent": "Тест пройден за:", + "resultsFinal": "Итоговый результат:", + "resultsPracticeTasks": "Практические задания:", + "resultsPracticeTask": "Практическое задание", + "resultsTheoryTitle": "Результаты теоретического теста:", + "resultsCorrectAnswers": "Правильных ответов:", + "quizTitle": "Тестирование по компьютерным сетям", + "quizControlTask": "Контрольное задание: этот раздел является экзаменом. Подсказки не предусмотрены.", + "quizTrainingTask": "Учебное задание: этот раздел предназначен для обучения. При неверном выполнении задания будут даны разъяснения.", + "resultsPageLink": "На страницу результатов", + "resultsLastResultLink": "Посмотреть последний результат", + "resultsLabel": "Результаты", + "correctLabel": "верно", + "questionsCount_one": "вопрос", + "questionsCount_few": "вопроса", + "questionsCount_many": "вопросов", + "minutesCount_one": "минута", + "minutesCount_few": "минуты", + "minutesCount_many": "минут", + "sectionsCount_one": "раздел", + "sectionsCount_few": "раздела", + "sectionsCount_many": "разделов", + "resultsDeadline": "До", + "resultsFinished": "Тестирование завершено", + "resultsNoTimeLimit": "Нет ограничения по времени", + "modalTestPassed": "Данный раздел уже пройден вами", + "modalOneAttemptTitle": "Тест можно пройти только один раз.", + "modalOneAttemptP1": "Вы можете вернуться к прохождению теста в любое время, пока не отправили ответ и не начали выполнение другого теста.", + "modalOneAttemptP2": "Пройти тест можно только один раз.", + "modalOneAttemptP3": "Время на решение ограничено. Вы готовы начать?", + "startBtn": "Начать прохождение", + "edgeFrom": "Из", + "edgeTo": "В", + "edgePacketLoss": "Потери пакетов (%)", + "hostName": "Имя хоста", + "executeCommand": "Выполнить команду", + "cmdNone": "---", + "cmdPing1": "ping (1 пакет)", + "cmdPingWithOptions": "ping (с опциями)", + "cmdTraceroute": "traceroute (с опциями)", + "cmdUdp": "Отправить данные (UDP)", + "cmdTcp": "Отправить данные (TCP)", + "cmdAddRoute": "Добавить маршрут", + "cmdAddArp": "Добавить запись в ARP-cache", + "cmdDhclient": "Запросить IP адрес автоматически", + "placeholderPingIp": "Введите IP хоста", + "placeholderOptions": "пример: -c 1 -t 5", + "placeholderIpAddress": "192.168.1.1", + "placeholderTracerouteOptions": "пример: -n", + "placeholderBytes": "Объём в байтах (1-65535)", + "recipientIpPort": "Получатель (IP / порт)", + "placeholderIp": "IP адрес", + "placeholderPort": "Порт", + "placeholderMacAddress": "01:02:03:04:05:06", + "ipAddressMask": "IP-адрес / Маска", + "gatewayIp": "IP адрес шлюза", + "linkTo": "Линк к", + "defaultGateway": "Шлюз по умолчанию", + "hubName": "Имя хаба", + "routerName": "Имя роутера", + "cmdAddIp": "Добавить IP адрес", + "cmdEnableNat": "Включить NAT на интерфейсе", + "cmdAddVlanSubif": "Добавить сабинтерфейс с VLAN", + "cmdAddIpip": "Добавить IPIP-интерфейс", + "cmdAddGre": "Добавить GRE-интерфейс", + "cmdEnableArpProxy": "Включить ARP Proxy на интерфейсе", + "vlanLabel": "VLAN", + "placeholderVlanId": "Введите VLAN ID", + "ipipInterfaceParams": "Параметры IPIP-интерфейса", + "endpointIp": "IP конечной точки", + "ipipInterfaceIp": "IP IPIP-интерфейса", + "ipipInterfaceName": "Название IPIP-интерфейса", + "greInterfaceParams": "Параметры GRE-интерфейса", + "greInterfaceIp": "IP GRE-интерфейса", + "greInterfaceName": "Название GRE-интерфейса", + "arpProxyParams": "Параметры ARP Proxy-интерфейса", + "serverName": "Имя сервера", + "cmdUdpServer": "Запустить UDP сервер", + "cmdTcpServer": "Запустить TCP сервер", + "cmdBlockPort": "Блокировать TCP/UDP порт", + "cmdDhcpServer": "Запустить DHCP сервер", + "ipAddressPort": "IP-адрес / Порт", + "tcpUdpPort": "TCP/UDP порт", + "ipRange": "Диапазон IP адресов", + "maskIpAddress": "Маска IP адреса", + "switchName": "Имя Свитча", + "stpWarning": "Включение протокола STP увеличивает эмуляцию на 30 секунд.", + "rstpWarning": "Включение протокола RSTP увеличивает эмуляцию на 10 секунд.", + "stpSelection": "Выбор STP/RSTP", + "disableStp": "Отключить STP", + "priority": "Приоритет", + "vlanSetup": "Настройка VLAN", + "device": "Устройство", + "tooltipVlanId": "Допустимые значения: от 1 до 4094.", + "connectionType": "Тип подключения", + "tooltipTrunk": "Для Trunk подключения вводите значения через запятую или пробел. Например: \"10 20\"", + "vxlanSetup": "Настройка VXLAN", + "closeAndSave": "Закрыть и сохранить", + "clientLink": "Клиентский линк", + "addDevice": "Добавить устройство", + "targetVtepIp": "IP целевого VTEP", + "networkLink": "Сетевой линк", + "addInterface": "Добавить интерфейс", + "tooltipVxlanConfig": "Настройте VTEP, добавив клиентские устройства с соответствующими VNI и укажите интерфейс для отправки пакетов на другие VTEP.", + "tooltipVxlanHelp": "Настройте VTEP, добавив клиентские устройства ассоциированные с VNI и укажите интерфейс и ip удаленного роутера для отправки пакетов с соответствующим VNI на заданный VTEP.", + "emulatingStatus": "Эмулируется...", + "commandsLabel": "Команды", + "stepsLabel": "Шаг:", + "ofLabel": "из", + "step": "шаг", + "steps_few": "шага", + "steps_many": "шагов", + "packet": "пакет", + "packets_few": "пакета", + "packets_many": "пакетов", + "packets_zero": "0 пакетов", + "queuePosition": "Место в очереди", + "langSelectorAria": "Выбор языка", + "loadingError": "Ошибка загрузки данных", + "translationMissing": "Перевод отсутствует", + "savingBtn": "Сохранение...", + "selectLink": "Выберите линк", + "startInterface": "Интерфейс начальной точки", + "notEnoughInterfaces": "Мало интерфейсов", + "profileTitle": "Профиль" +} \ No newline at end of file diff --git a/front/src/static/netfront_f.js b/front/src/static/netfront_f.js index 7acfec3d..9a15a4a5 100644 --- a/front/src/static/netfront_f.js +++ b/front/src/static/netfront_f.js @@ -1570,18 +1570,19 @@ const InsertWaitingTimeHelper = function(time_filter) { data: '', success: function(data) { const queue_size = parseInt(data.size); + if (!$('#NetworkPlayer button:first').prop('disabled')) { console.log($('#NetworkPlayer button:first').prop('disabled')) return; } else if (queue_size <= 1) { - $('#NetworkPlayerLabel').text("Ожидание 10-15 сек."); + $('#NetworkPlayerLabel').html('Ожидание 10-30 сек.'); } else { - $('#NetworkPlayerLabel').text(`Место в очереди ${queue_size}`); - - // Update waiting time - setTimeout(() => InsertWaitingTimeHelper(time_filter), 500); + $('#NetworkPlayerLabel').html('Место в очереди ' + queue_size); } + if (typeof translateDynamicContent === 'function') { + translateDynamicContent(document.getElementById('NetworkPlayerLabel')); + } }, error: function(err) { console.error("Failed to fetch queue size:", err); @@ -2311,7 +2312,16 @@ const SetNetworkPlayerState = function (simulation_id) { $('#PacketSliderInput').show(); const pkt_count = packets.reduce((currentCount, row) => currentCount + row.length, 0); - $('#NetworkPlayerLabel').text(packets.length + ' ' + NumWord(packets.length, ['шаг', 'шага', 'шагов']) + ' / ' + pkt_count + ' ' + NumWord(pkt_count, ['пакет', 'пакета', 'пакетов'])); + + const stepsWords = [getTranslation('step'), getTranslation('steps_few'), getTranslation('steps_many')]; + const packetsWords = [getTranslation('packet'), getTranslation('packets_few'), getTranslation('packets_many')]; + + const stepsLabel = NumWord(packets.length, stepsWords); + const packetsLabel = NumWord(pkt_count, packetsWords); + + $('#NetworkPlayerLabel').text( + packets.length + ' ' + stepsLabel + ' / ' + pkt_count + ' ' + packetsLabel + ); $('#PacketSliderInput')[0].noUiSlider.on('slide', function (e) { if (!e) return; @@ -2323,10 +2333,18 @@ const SetNetworkPlayerState = function (simulation_id) { if (!e) return; let x = Math.round(e[0]); if (packets.length === 0){ - $('#NetworkPlayerLabel').text('0 пакетов'); + $('#NetworkPlayerLabel').text(getTranslation('packets_zero')); return; } - $('#NetworkPlayerLabel').text('Шаг: ' + x + '/' + packets.length + ' (' + packets[x-1].length + ' ' + NumWord(packets[x-1].length, ['пакет', 'пакета', 'пакетов']) + ')'); + const stepsWordsUpdate = [getTranslation('step'), getTranslation('steps_few'), getTranslation('steps_many')]; + const packetsWordsUpdate = [getTranslation('packet'), getTranslation('packets_few'), getTranslation('packets_many')]; + + const stepLabel = getTranslation('stepsLabel'); + const packetWord = NumWord(packets[x-1].length, packetsWordsUpdate); + + $('#NetworkPlayerLabel').text( + stepLabel + ' ' + x + '/' + packets.length + ' (' + packets[x-1].length + ' ' + packetWord + ')' + ); }); // Set click handlers @@ -2400,8 +2418,17 @@ const SetNetworkPlayerState = function (simulation_id) { // Add emulation button. $('#NetworkPlayer').empty(); $('#PacketSliderInput').hide(); - $('#NetworkPlayer').append(''); - $('#NetworkPlayerLabel').empty(); + + $('#NetworkPlayer').append(''); + + $('#NetworkPlayerLabel').html('Ожидание 10-30 сек.'); + if (typeof translateDynamicContent === 'function') { + translateDynamicContent(document.getElementById('NetworkPlayerLabel')); + } + + if (typeof translateDynamicContent === 'function') { + translateDynamicContent(document.getElementById('NetworkPlayer')); + } $('#NetworkEmulateButton').click(function() { @@ -2426,13 +2453,17 @@ const SetNetworkPlayerState = function (simulation_id) { RunSimulation(network_guid); $('#NetworkPlayer').empty(); - $('#NetworkPlayer').append(''); + $('#NetworkPlayer').append(''); + + if (typeof translateDynamicContent === 'function') { + translateDynamicContent(document.getElementById('NetworkPlayer')); + } + InsertWaitingTime(); return; }); return; - } // 2 states: @@ -2469,7 +2500,16 @@ const SetSharedNetworkPlayerState = function() $('#PacketSliderInput').show(); const pkt_count = packets.reduce((currentCount, row) => currentCount + row.length, 0); - $('#NetworkPlayerLabel').text(packets.length + ' ' + NumWord(packets.length, ['шаг', 'шага', 'шагов']) + ' / ' + pkt_count + ' ' + NumWord(pkt_count, ['пакет', 'пакета', 'пакетов'])); + + const stepsWords = [getTranslation('step'), getTranslation('steps_few'), getTranslation('steps_many')]; + const packetsWords = [getTranslation('packet'), getTranslation('packets_few'), getTranslation('packets_many')]; + + const stepsLabel = NumWord(packets.length, stepsWords); + const packetsLabel = NumWord(pkt_count, packetsWords); + + $('#NetworkPlayerLabel').text( + packets.length + ' ' + stepsLabel + ' / ' + pkt_count + ' ' + packetsLabel + ); $('#PacketSliderInput')[0].noUiSlider.on('slide', function (e) { if (!e) return; @@ -2481,10 +2521,18 @@ const SetSharedNetworkPlayerState = function() if (!e) return; let x = Math.round(e[0]); if (packets.length === 0){ - $('#NetworkPlayerLabel').text('0 пакетов'); + $('#NetworkPlayerLabel').text(getTranslation('packets_zero')); return; } - $('#NetworkPlayerLabel').text('Шаг: ' + x + '/' + packets.length + ' (' + packets[x-1].length + ' ' + NumWord(packets[x-1].length, ['пакет', 'пакета', 'пакетов']) + ')'); + const stepsWordsUpdate = [getTranslation('step'), getTranslation('steps_few'), getTranslation('steps_many')]; + const packetsWordsUpdate = [getTranslation('packet'), getTranslation('packets_few'), getTranslation('packets_many')]; + + const stepLabel = getTranslation('stepsLabel'); + const packetWord = NumWord(packets[x-1].length, packetsWordsUpdate); + + $('#NetworkPlayerLabel').text( + stepLabel + ' ' + x + '/' + packets.length + ' (' + packets[x-1].length + ' ' + packetWord + ')' + ); }); // Set click handlers @@ -2546,7 +2594,10 @@ const SetSharedNetworkPlayerState = function() $('#NetworkPlayer').empty(); $('#PacketSliderInput').hide(); $('#NetworkPlayerLabel').empty(); - $('#NetworkPlayer').append(''); + $('#NetworkPlayer').append(''); + if (typeof translateDynamicContent === 'function') { + translateDynamicContent(document.getElementById('NetworkPlayer')); + } return; } diff --git a/front/src/templates/auth/login.html b/front/src/templates/auth/login.html index b0513b5a..beb83609 100644 --- a/front/src/templates/auth/login.html +++ b/front/src/templates/auth/login.html @@ -1,15 +1,21 @@ {% extends "base.html" %} -{% block title %}Вход/Регистрация в веб-эмулятор{% endblock %} -{% block description %}Эмулятор компьютерной сети для образовательных целей на базе ОС Linux{% endblock %} -{% block content %} +{% block title_tag %} + Вход/Регистрация в веб-эмулятор +{% endblock %} + +{% block description %} + +{% endblock %} + +{% block content %}
-

Добро пожаловать!

+

Добро пожаловать!


-
Вход с помощью
+
Вход с помощью
@@ -54,11 +60,10 @@
Вход с помощью
-

Регистрируясь, я даю согласие на обработку данных.

+

Регистрируясь, я даю согласие на обработку данных.

- {% endblock %} diff --git a/front/src/templates/base.html b/front/src/templates/base.html index 10625a93..4725e918 100644 --- a/front/src/templates/base.html +++ b/front/src/templates/base.html @@ -1,35 +1,31 @@ - + - - - + - + {% block description %}{% endblock %} - - {% block title %}{% endblock %} - +{% block title_tag %} + Эмулятор компьютерной сети +{% endblock %} {% block og %}{% endblock %} - - @@ -48,27 +44,47 @@ {% if network %} {% if current_user.is_authenticated %} - -{% endblock %} - +{% endblock %} \ No newline at end of file diff --git a/front/src/templates/examples.html b/front/src/templates/examples.html index a90d6377..b7cb52ad 100644 --- a/front/src/templates/examples.html +++ b/front/src/templates/examples.html @@ -1,11 +1,11 @@ {% extends "base.html" %} -{% block title %}Примеры компьютерных сетей{% endblock %} -{% block description %}Примеры эмуляций компьютерных сетей на базе ОС Linux{% endblock %} +{% block title_tag %}Примеры компьютерных сетей{% endblock %} +{% block description %}{% endblock %} {% block content %}
-

Примеры компьютерных сетей

+

Примеры компьютерных сетей

@@ -13,7 +13,7 @@

Примеры компьютерных сетей

- +
diff --git a/front/src/templates/home.html b/front/src/templates/home.html index d4733983..effd92b7 100644 --- a/front/src/templates/home.html +++ b/front/src/templates/home.html @@ -1,6 +1,6 @@ {% extends "base.html" %} -{% block title %} Веб-эмулятор {% endblock %} -{% block description %}Эмулятор компьютерной сети для образовательных целей на базе ОС Linux{% endblock %} +{% block title_tag %} Веб-эмулятор {% endblock %} +{% block description %}{% endblock %} {% block content %}
@@ -10,7 +10,7 @@
diff --git a/front/src/templates/index.html b/front/src/templates/index.html index b9fa778d..c488cec4 100644 --- a/front/src/templates/index.html +++ b/front/src/templates/index.html @@ -1,6 +1,6 @@ {% extends "base.html" %} -{% block title %}Эмулятор компьютерной сети{% endblock %} -{% block description %}Эмулятор компьютерной сети для образовательных целей на базе ОС Linux{% endblock %} +{% block title_tag %}Эмулятор компьютерной сети{% endblock %} +{% block description %}{% endblock %} {% block content %}
@@ -12,12 +12,11 @@
-

Эмулятор компьютерной сети для образовательных целей на базе ОС Linux -
- Создать сеть -

+

Эмулятор компьютерной сети для образовательных целей на базе ОС Linux +
+ Создать сеть +

-
@@ -25,7 +24,7 @@
-

Возможности

+

Возможности

@@ -33,8 +32,8 @@

Возможнос Icon

-

Вся работа в браузере

-

Нет нужды устанавливать дополнительные программы. Создание и настройка сети выполняется прямо в браузере.

+

Вся работа в браузере

+

Нет нужды устанавливать дополнительные программы. Создание и настройка сети выполняется прямо в браузере.

@@ -45,8 +44,8 @@

Вся работа в брау Icon

-

Точная эмуляция сети

-

Для эмуляции сети используется сетевой стек ОС Linux. Весь процесс занимает всего 10-15 секунд, в зависимости от настроек сети.

+

Точная эмуляция сети

+

Для эмуляции сети используется сетевой стек ОС Linux. Весь процесс занимает всего 10-15 секунд, в зависимости от настроек сети.

@@ -57,8 +56,8 @@

Точная эмуляция с Icon
-

Анимация сети

-

Детальная анимация помогает досконально изучать работу сети и конкретных протоколов. Анимацию можно проматывать до конкретного шага.

+

Анимация сети

+

Детальная анимация помогает досконально изучать работу сети и конкретных протоколов. Анимацию можно проматывать до конкретного шага.

@@ -69,8 +68,8 @@

Анимация сети

Icon
-

Разнообразие сетевых настроек

-

Можно запускать TCP/UDP сервер и клиент, детально изучать работу всего TCP/IP стека, устанавливать потери в канале. Есть поддержка NAT, VLAN, IPIP, GRE, VxLAN, STP/RSTP.

+

Разнообразие сетевых настроек

+

Можно запускать TCP/UDP сервер и клиент, детально изучать работу всего TCP/IP стека, устанавливать потери в канале. Есть поддержка NAT, VLAN, IPIP, GRE, VxLAN, STP/RSTP.

@@ -81,8 +80,8 @@

Разнообразие сет Icon
-

Проверка знаний (тестирование)

-

Автоматическое тестирование по теории и практике. Свяжитесь с нами для добавления своих вопросов и задач!

+

Проверка знаний (тестирование)

+

Автоматическое тестирование по теории и практике. Свяжитесь с нами для добавления своих вопросов и задач!

@@ -93,8 +92,8 @@

Проверка знаний (
-

Кто использует Miminet?

-

Miminet уже воспользовались 15 000+ обучающихся.

+

Кто использует Miminet?

+

Miminet уже воспользовались 15 000+ обучающихся.

@@ -121,19 +120,19 @@

Кто использует Miminet?

-

Примеры использования

+

Примеры использования

-

На лекциях

-

Интерактивный инструмент для лектора. Можно прямо в браузере рисовать схемы сетей и сразу анимировать их, демонстрируя работу различных протоколов и устройств.

+

На лекциях

+

Интерактивный инструмент для лектора. Можно прямо в браузере рисовать схемы сетей и сразу анимировать их, демонстрируя работу различных протоколов и устройств.


    -
  • Хост
  • -
  • Коммутатор
  • -
  • Маршрутизатор
  • -
  • Сервер
  • +
  • Хост
  • +
  • Коммутатор
  • +
  • Маршрутизатор
  • +
  • Сервер
@@ -158,11 +157,10 @@

На лекциях

-

На практиках

-

Большой набор конфигурируемых параметров. Вы можете собирать простые и сложные конфигурации, эмулировать их и детально изучать работу отдельных протоколов: ARP, IP, TCP, ICMP, GRE и многих других.

+

На практиках

+

Большой набор конфигурируемых параметров. Вы можете собирать простые и сложные конфигурации, эмулировать их и детально изучать работу отдельных протоколов: ARP, IP, TCP, ICMP, GRE и многих других.


    -
  • TCP/IP
  • VLAN
  • STP/RSTP
  • @@ -176,13 +174,13 @@

    На практиках

    -

    Проверка знаний

    -

    Раздел "Тестирование" помогает проводить собеседования и проверку знаний у обучающихся. Можно создавать свои наборы тестов из теоретических вопросов и практических заданий с автоматической проверкой.

    +

    Проверка знаний

    +

    Раздел "Тестирование" помогает проводить собеседования и проверку знаний у обучающихся. Можно создавать свои наборы тестов из теоретических вопросов и практических заданий с автоматической проверкой.


      -
    • 100+ Вопросов в базе
    • -
    • 10+ Практических задач
    • +
    • 100+ Вопросов в базе
    • +
    • 10+ Практических задач
    @@ -197,7 +195,7 @@

    Проверка знаний

-

Как это работает?

+

Как это работает?

@@ -208,8 +206,8 @@

Как это работает?

Нарисуйте свою сеть
-

Нарисуйте свою сеть

-

Вы можете создать компьютерную сеть, в которой будут хосты, свитчи, хабы, раутеры и сервера. Также можете задать настройки для каждого устройства.

+

Нарисуйте свою сеть

+

Вы можете создать компьютерную сеть, в которой будут хосты, свитчи, хабы, раутеры и сервера. Также можете задать настройки для каждого устройства.

@@ -222,8 +220,8 @@

Нарисуйте свою сеть

Отправте сеть на эмуляцию
-

Отправьте сеть на эмуляцию

-

На нашем сервере ваша сеть будет эмулироваться с помощью сетевой подсистемы Linux. Для каждого устройства создается сетевой интерфейс со своим уникальным MAC адресом и другими настройками.

+

Отправьте сеть на эмуляцию

+

На нашем сервере ваша сеть будет эмулироваться с помощью сетевой подсистемы Linux. Для каждого устройства создается сетевой интерфейс со своим уникальным MAC адресом и другими настройками.

@@ -236,8 +234,8 @@

Отправьте сеть на эмуляцию

Illustration
-

Дождитесь эмуляции на сервере

-

Во время эмуляции сети идет захват всего сетевого трафика, который затем обрабатывается для визуализации.

+

Дождитесь эмуляции на сервере

+

Во время эмуляции сети идет захват всего сетевого трафика, который затем обрабатывается для визуализации.

@@ -250,60 +248,16 @@

Дождитесь эмуляции на сервере

Illustration
-

Запустите анимацию

-

Запустите анимацию и наблюдайте за сетевым трафиком в удобном формате.

+

Запустите анимацию

+

Запустите анимацию и наблюдайте за сетевым трафиком в удобном формате.

- - - - - - - - - - - -
-

Как это работает (видео)

+

Как это работает (видео)

@@ -313,8 +267,8 @@

Как это работает (
-

Остались вопросы?

-

Можете связаться с нами

+

Остались вопросы?

+

Можете связаться с нами

@@ -331,16 +285,16 @@

10.0.0.2", "type": "packet"}, "config": {"type": "ICMP echo-request\\n10.0.0.1 > 10.0.0.2", "path": "edge_lhaep1dk4d654tvxhl5", "source": "host_1", "target": "l2sw1"}, "timestamp": "1683282739732096"}], [{"data": {"id": "pkt_UPUQXZZW", "label": "ICMP echo-request\\n10.0.0.1 > 10.0.0.2", "type": "packet"}, "config": {"type": "ICMP echo-request\\n10.0.0.1 > 10.0.0.2", "path": "edge_lhaep2k8vhx64z7cubc", "source": "l2sw1", "target": "host_3"}, "timestamp": "1683282739747163"}], [{"data": {"id": "pkt_WSJOYE9F", "label": "ICMP echo-reply\\n10.0.0.2 > 10.0.0.1", "type": "packet"}, "config": {"type": "ICMP echo-reply\\n10.0.0.2 > 10.0.0.1", "path": "edge_lhaep2k8vhx64z7cubc", "source": "host_3", "target": "l2sw1"}, "timestamp": "1683282739762258"}], [{"data": {"id": "pkt_RI2K22SO", "label": "ICMP echo-reply\\n10.0.0.2 > 10.0.0.1", "type": "packet"}, "config": {"type": "ICMP echo-reply\\n10.0.0.2 > 10.0.0.1", "path": "edge_lhaep1dk4d654tvxhl5", "source": "l2sw1", "target": "host_1"}, "timestamp": "1683282739777312"}]]'); + var packets = JSON.parse('[[{"data": {"id": "pkt_G09AFC4L", "label": "ARP-request", "type": "packet"}, "config": {"type": "ARP-request", "path": "edge_lhaep1dk4d654tvxhl5", "source": "host_1", "target": "l2sw1"}, "timestamp": "1683282739671808"}],[{"data": {"id": "pkt_MM7U6MQB", "label": "ARP-request", "type": "packet"}, "config": {"type": "ARP-request", "path": "edge_lhaep2k8vhx64z7cubc", "source": "l2sw1", "target": "host_3"}, "timestamp": "1683282739686887"}],[{"data": {"id": "pkt_D8TBGL1D", "label": "ARP-response", "type": "packet"}, "config": {"type": "ARP-response", "path": "edge_lhaep2k8vhx64z7cubc", "source": "host_3", "target": "l2sw1"}, "timestamp": "1683282739701950"}],[{"data": {"id": "pkt_D51CRKUA", "label": "ARP-response", "type": "packet"}, "config": {"type": "ARP-response", "path": "edge_lhaep1dk4d654tvxhl5", "source": "l2sw1", "target": "host_1"}, "timestamp": "1683282739717026"}],[{"data": {"id": "pkt_2X7HMBD9", "label": "ICMP echo-request\\n10.0.0.1 > 10.0.0.2", "type": "packet"}, "config": {"type": "ICMP echo-request\\n10.0.0.1 > 10.0.0.2", "path": "edge_lhaep1dk4d654tvxhl5", "source": "host_1", "target": "l2sw1"}, "timestamp": "1683282739732096"}],[{"data": {"id": "pkt_UPUQXZZW", "label": "ICMP echo-request\\n10.0.0.1 > 10.0.0.2", "type": "packet"}, "config": {"type": "ICMP echo-request\\n10.0.0.1 > 10.0.0.2", "path": "edge_lhaep2k8vhx64z7cubc", "source": "l2sw1", "target": "host_3"}, "timestamp": "1683282739747163"}],[{"data": {"id": "pkt_WSJOYE9F", "label": "ICMP echo-reply\\n10.0.0.2 > 10.0.0.1", "type": "packet"}, "config": {"type": "ICMP echo-reply\\n10.0.0.2 > 10.0.0.1", "path": "edge_lhaep2k8vhx64z7cubc", "source": "host_3", "target": "l2sw1"}, "timestamp": "1683282739762258"}],[{"data": {"id": "pkt_RI2K22SO", "label": "ICMP echo-reply\\n10.0.0.2 > 10.0.0.1", "type": "packet"}, "config": {"type": "ICMP echo-reply\\n10.0.0.2 > 10.0.0.1", "path": "edge_lhaep1dk4d654tvxhl5", "source": "l2sw1", "target": "host_1"}, "timestamp": "1683282739777312"}]]'); var ns = null; PacketPlayer.getInstance().InitPlayer(packets); @@ -352,5 +306,4 @@

MimiShark{% endblock %} +{% block description %}{% endblock %} {% block og %} {% endblock %} @@ -15,7 +14,7 @@

@@ -32,11 +31,11 @@ # - Time - Source - Destination - Protocol - Length + Time + Source + Destination + Protocol + Length @@ -83,4 +82,4 @@ -{% endblock %} +{% endblock %} \ No newline at end of file diff --git a/front/src/templates/network.html b/front/src/templates/network.html index eb5250ab..4b663c4d 100644 --- a/front/src/templates/network.html +++ b/front/src/templates/network.html @@ -1,11 +1,13 @@ {% extends "base.html" %} -{% block title %}{{ network.title }}{% endblock %} -{% block description %}Эмуляция компьютерной сети в вебе{% endblock %} +{% block title_tag %} + {{ network.title }} +{% endblock %} +{% block description %}{% endblock %} {% block og %} - + @@ -26,44 +28,43 @@
{% if request.path != url_for('web_network_shared') %} - Мои сети + Мои сети / {% endif %} {{ network.title }}
+ -
- - - - - - - -
{% else %} Miminet @@ -73,35 +74,35 @@ {{ network.title }}
- + Вход {% endif %} -
-
Устройства
+
+
Устройства
Свитч (L2) + data-bs-content="Свитч (коммутатор) работает на втором уровне модели OSI." data-i18n="deviceSwitch" data-i18n-attr="data-bs-content:deviceSwitchPopover">Свитч (L2)
Хост + data-bs-content="Хост - конечное сетевое устройство." data-i18n="deviceHost" data-i18n-attr="data-bs-content:deviceHostPopover">Хост
Хаб (L1) + data-bs-content="Хаб (концентратор) — простейшее сетевое устойство." data-i18n="deviceHub" data-i18n-attr="data-bs-content:deviceHubPopover">Хаб (L1)
@@ -109,23 +110,23 @@ src="{{ url_for('static', filename='/images/l3_router.png') }}"/>
Роутер (L3) + data-bs-content="Маршрутизатор (роутер, раутер) — работает на 3-м уровне модели OSI, позволяет объединять различные сети." data-i18n="deviceRouter" data-i18n-attr="data-bs-content:deviceRouterPopover">Роутер (L3)
Сервер + data-bs-content="Сервер — обслуживает клиентские запросы." data-i18n="deviceServer" data-i18n-attr="data-bs-content:deviceServerPopover">Сервер
-
-
Настройки
+
+
Настройки
- Тут будут настройки устройств. Выделите любое на схеме. + Тут будут настройки устройств. Выделите любое на схеме.
@@ -133,7 +134,7 @@
- +
@@ -141,12 +142,12 @@
- Ожидание 10-30 сек. + Ожидание 10-30 сек.
- Задать вопрос + Задать вопрос

@@ -183,4 +184,4 @@ DrawGraph(); -{% endblock %} +{% endblock %} \ No newline at end of file diff --git a/front/src/templates/network_shared.html b/front/src/templates/network_shared.html index 8c7cf9e8..db03cb5b 100644 --- a/front/src/templates/network_shared.html +++ b/front/src/templates/network_shared.html @@ -1,11 +1,13 @@ {% extends "base.html" %} - -{% block description %}Эмуляция компьютерной сети в вебе{% endblock %} +{% block title_tag %} + {{ network.title }} +{% endblock %} +{% block description %}{% endblock %} {% block og %} - + @@ -26,44 +28,42 @@
{% if request.path != url_for('web_network_shared') %} - Мои сети + Мои сети / {% endif %} {{ network.title }}
- -
- - - - - - - -
{% else %} Miminet @@ -71,16 +71,16 @@ - + Вход {% endif %}
-
Настройки
+
Настройки
- Тут будут настройки устройств. Выделите любое на схеме. + Тут будут настройки устройств. Выделите любое на схеме.
@@ -88,7 +88,7 @@
- +
@@ -96,12 +96,12 @@
- Ожидание 10-30 сек. + Ожидание 10-30 сек.
- Задать вопрос + Задать вопрос

@@ -137,4 +137,4 @@ DrawSharedGraph(nodes, edges); -{% endblock %} +{% endblock %} \ No newline at end of file diff --git a/front/src/templates/quiz/networkBase.html b/front/src/templates/quiz/networkBase.html index a58b75b5..0c949932 100644 --- a/front/src/templates/quiz/networkBase.html +++ b/front/src/templates/quiz/networkBase.html @@ -1,7 +1,6 @@ - + - - - + {% block description %}{% endblock %} - - {% block title %}{% endblock %} - + {% block title_tag %} + Эмулятор компьютерной сети +{% endblock %} {% block og %}{% endblock %} - - @@ -56,59 +52,36 @@ Miminet - {# Test name and section name #} -
-
- -
-
- -
-
-

-
+
+
+
/
+

- {% if current_user.is_authenticated %} -
{% endif %} @@ -118,18 +91,17 @@ @@ -138,8 +110,7 @@
{% endif %} -{% block content %} -{% endblock %} +{% block content %}{% endblock %} @@ -149,46 +120,32 @@ - - - - - + -{% block network %} -{% endblock %} +{% block network %}{% endblock %} diff --git a/front/src/templates/quiz/practiceTask.html b/front/src/templates/quiz/practiceTask.html index 21879e19..4546e0cd 100644 --- a/front/src/templates/quiz/practiceTask.html +++ b/front/src/templates/quiz/practiceTask.html @@ -23,9 +23,9 @@
{# Timer and counter #} - @@ -81,7 +81,7 @@
Сервер + data-bs-content="Сервер — обслуживает клиентские запросы." data-i18n="deviceServer" data-i18n-attr="data-bs-content:deviceServerPopover">Сервер
@@ -89,16 +89,16 @@
-
Настройки
+
Настройки
- Тут будут настройки устройств. Выделите любое на схеме. + Тут будут настройки устройств. Выделите любое на схеме.
-
@@ -107,7 +107,7 @@
- Ожидание 10-30 сек. + Ожидание 10-30 сек. @@ -115,13 +115,13 @@ @@ -131,21 +131,21 @@
{% if is_exam and not available_answer %} - + {% else %} - + {% endif %} - + -
-{% endblock %} - + +{% endblock %} \ No newline at end of file diff --git a/front/src/templates/quiz/sessionQuestion.html b/front/src/templates/quiz/sessionQuestion.html index ab905137..063713aa 100644 --- a/front/src/templates/quiz/sessionQuestion.html +++ b/front/src/templates/quiz/sessionQuestion.html @@ -1,5 +1,5 @@ {% extends "quiz/networkBase.html" %} -{% block title %}{% endblock %} +{% block title_tag %}{% endblock %} {% block description %}Сетевой тренажер для обучения компьютерным сетям{% endblock %} {% block content %} diff --git a/front/src/templates/quiz/sessionResult.html b/front/src/templates/quiz/sessionResult.html index 0764b5b4..2d2a7e1f 100644 --- a/front/src/templates/quiz/sessionResult.html +++ b/front/src/templates/quiz/sessionResult.html @@ -1,6 +1,8 @@ {% extends "base.html" %} -{% block title %}Результаты{% endblock %} -{% block description %}Сетевой тренажер для обучения компьютерным сетям{% endblock %} +{% block title_tag %} + Результаты +{% endblock %} +{% block description %}{% endblock %} {% block content %} @@ -24,10 +26,10 @@

- Ответы появятся позже + Ответы появятся позже

- Вы завершили экзамен. Результаты станут доступны + Вы завершили экзамен. Результаты станут доступны {{ data.results_available_from.strftime('%d.%m.%Y %H:%M') }}.

@@ -35,7 +37,7 @@
{% else %} {% endif %} @@ -47,7 +49,8 @@
{{ q.question_text }}
+ class="btn btn-outline-primary btn-sm" + data-i18n="viewMyAnswer"> Посмотреть мой ответ @@ -57,7 +60,7 @@
{{ q.question_text }}
{% else %} -

Результаты теста

+

Результаты теста

{% for q in data.results %} @@ -73,15 +76,16 @@
{{ q.question_text }}
{% if q.max_score == 0 %} -

Ответ на проверке

+

Ответ на проверке

{% else %} -

Баллы: {{ q.score }} из {{ q.max_score }}

+

Баллы: {{ q.score }} из {{ q.max_score }}

{% endif %}
{% if q.network_guid %} + class="btn btn-outline-primary btn-sm" + data-i18n="viewMyAnswer"> Посмотреть мой ответ {% endif %} @@ -92,18 +96,18 @@
-

Тест пройден за: {{ data.time_spent }}

+

Тест пройден за: {{ data.time_spent }}

- Итоговый результат: + Итоговый результат: {% set total_score = data.theory_correct + data.practice_results|sum(attribute='score') %} {% set total_max = data.theory_count + data.practice_results|sum(attribute='max_score') %} - {{ total_score }} / {{ total_max }} баллов + {{ total_score }} / {{ total_max }} баллов

{% endif %}
-
@@ -141,4 +145,4 @@
}); } -{% endblock %} +{% endblock %} \ No newline at end of file diff --git a/front/src/templates/quiz/textTask.html b/front/src/templates/quiz/textTask.html index 233f57cb..a7d508ef 100644 --- a/front/src/templates/quiz/textTask.html +++ b/front/src/templates/quiz/textTask.html @@ -26,7 +26,8 @@
@@ -72,21 +73,21 @@
{% if is_exam and not available_answer %} {% if question.question_type == 'variable' %} - + {% else %} - + {% endif %} {% else %} {% if question.question_type == 'variable' %} - + {% else %} - + {% endif %} {% endif %} - + -
@@ -163,7 +164,6 @@ new bootstrap.Modal(document.getElementById('imageModal')).show(); } - // Enable answer button only when option is selected document.addEventListener('DOMContentLoaded', function () { let answerBtn = document.querySelector('button[name="answerQuestion"]'); let examBtn = document.querySelector('button[name="answerExamQuestion"]'); diff --git a/front/src/templates/quiz/userSessionResult.html b/front/src/templates/quiz/userSessionResult.html index 022029be..3f05801d 100644 --- a/front/src/templates/quiz/userSessionResult.html +++ b/front/src/templates/quiz/userSessionResult.html @@ -1,6 +1,8 @@ {% extends "base.html" %} -{% block title %}Результаты{% endblock %} -{% block description %}Сетевой тренажер для обучения компьютерным сетям{% endblock %} +{% block title_tag %} + Результаты +{% endblock %} +{% block description %}{% endblock %} {% block content %}
@@ -19,32 +21,33 @@

- Ответы появятся позже + Ответы появятся позже

- Вы завершили экзамен. Результаты станут доступны {{ data['results_available_from'].strftime('%d.%m.%Y %H:%M') }}. + Вы завершили экзамен. Результаты станут доступны {{ data['results_available_from'].strftime('%d.%m.%Y %H:%M') }}.

{% else %} -