From 2cda7b887464ce5480eaac6cb40730351aaf16c8 Mon Sep 17 00:00:00 2001 From: rainjke Date: Fri, 19 Dec 2025 07:51:38 +0300 Subject: [PATCH] Add internationalization support with English and Russian translations - Implemented dynamic translation loading in i18n.js for better user experience. - Added English (en.json) and Russian (ru.json) translation files with extensive content for the application. - Created a mechanism to detect user language based on IP and browser settings. - Updated UI elements to utilize translation keys for multilingual support. - Included error handling for translation loading failures. --- front/src/static/assets/css/workspace.css | 6 +- front/src/static/config.js | 234 ++++++++++---- front/src/static/config_edge.html | 8 +- front/src/static/config_host.html | 68 ++-- front/src/static/config_hub.html | 8 +- front/src/static/config_router.html | 65 ++-- front/src/static/config_server.html | 41 ++- front/src/static/config_switch.html | 24 +- front/src/static/config_vlan.html | 16 +- front/src/static/config_vxlan.html | 24 +- front/src/static/js/i18n.js | 163 ++++++++++ front/src/static/locales/en.json | 276 ++++++++++++++++ front/src/static/locales/ru.json | 276 ++++++++++++++++ front/src/static/netfront_f.js | 84 ++++- front/src/templates/auth/login.html | 19 +- front/src/templates/base.html | 299 +++++++----------- front/src/templates/cookie_consent.html | 24 +- front/src/templates/course.html | 26 +- front/src/templates/examples.html | 8 +- front/src/templates/home.html | 6 +- front/src/templates/index.html | 139 +++----- front/src/templates/mimishark.html | 19 +- front/src/templates/network.html | 70 ++-- front/src/templates/network_shared.html | 32 +- front/src/templates/quiz/networkBase.html | 121 +++---- front/src/templates/quiz/practiceTask.html | 40 +-- front/src/templates/quiz/quiz.html | 112 ++++--- front/src/templates/quiz/quizzes.html | 60 +++- front/src/templates/quiz/sessionQuestion.html | 2 +- front/src/templates/quiz/sessionResult.html | 34 +- front/src/templates/quiz/textTask.html | 16 +- .../src/templates/quiz/userSessionResult.html | 69 ++-- 32 files changed, 1580 insertions(+), 809 deletions(-) create mode 100644 front/src/static/js/i18n.js create mode 100644 front/src/static/locales/en.json create mode 100644 front/src/static/locales/ru.json 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 b807dd47..f267578f 100755 --- a/front/src/static/config.js +++ b/front/src/static/config.js @@ -22,7 +22,12 @@ const ClearConfigForm = function (text) { let txt = '' if (!text) { - txt = 'Тут будут настройки устройств. Выделите любое на схеме.'; + const defaultTextKey = 'settingsPlaceholder'; + txt = (window.translations && window.translations[currentLanguage] && window.translations[currentLanguage][defaultTextKey]) + ? window.translations[currentLanguage][defaultTextKey] + : 'Тут будут настройки устройств. Выделите любое на схеме.'; + } else { + txt = text; } // Clear all child @@ -56,7 +61,7 @@ const UpdateHostConfigurationForm = function(host_id) { // Set loading spinner $('#config_host_main_form_submit_button').text(''); - $('#config_host_main_form_submit_button').append('Сохранение...'); + $('#config_host_main_form_submit_button').append('Сохранение...'); // TODO: Localize "Сохранение..." UpdateHostConfiguration(data, host_id); }; @@ -75,6 +80,11 @@ const ConfigHostForm = function(host_id){ $(config_content_id).append(form); $(config_content_save_tag).append(button); + if (typeof translateDynamicContent === 'function') { + translateDynamicContent(document.getElementById('config_content')); + translateDynamicContent(document.getElementById('config_content_save')); + } + addIpFieldHandlers(); // Set host_id @@ -103,6 +113,11 @@ const ConfigRouterForm = function (router_id) { $(config_content_id).append(form); $(config_content_save_tag).append(button); + if (typeof translateDynamicContent === 'function') { + translateDynamicContent(document.getElementById('config_content')); + translateDynamicContent(document.getElementById('config_content_save')); + } + addIpFieldHandlers(); // Set host_id @@ -118,7 +133,7 @@ const ConfigRouterForm = function (router_id) { // Set loading spinner $('#config_router_main_form_submit_button').text(''); - $('#config_router_main_form_submit_button').append('Сохранение...'); + $('#config_router_main_form_submit_button').append('Сохранение...'); // TODO: Localize UpdateRouterConfiguration(data, router_id); } @@ -140,6 +155,11 @@ const ConfigServerForm = function (server_id) { $(config_content_id).append(form); $(config_content_save_tag).append(button); + if (typeof translateDynamicContent === 'function') { + translateDynamicContent(document.getElementById('config_content')); + translateDynamicContent(document.getElementById('config_content_save')); + } + addIpFieldHandlers(); // Set host_id @@ -155,7 +175,7 @@ const ConfigServerForm = function (server_id) { // Set loading spinner $('#config_server_main_form_submit_button').text(''); - $('#config_server_main_form_submit_button').append('Сохранение...'); + $('#config_server_main_form_submit_button').append('Сохранение...'); // TODO: Localize UpdateServerConfiguration(data, server_id); } @@ -177,6 +197,11 @@ const ConfigHubForm = function (hub_id) { $(config_content_id).append(form); $(config_content_save_tag).append(button); + if (typeof translateDynamicContent === 'function') { + translateDynamicContent(document.getElementById('config_content')); + translateDynamicContent(document.getElementById('config_content_save')); + } + addIpFieldHandlers(); // Set host_id @@ -192,7 +217,7 @@ const ConfigHubForm = function (hub_id) { // Set loading spinner $('#config_hub_main_form_submit_button').text(''); - $('#config_hub_main_form_submit_button').append('Сохранение...'); + $('#config_hub_main_form_submit_button').append('Сохранение...'); // TODO: Localize UpdateHubConfiguration(data, hub_id); } @@ -214,6 +239,11 @@ const ConfigSwitchForm = function (switch_id) { $(config_content_id).append(form); $(config_content_save_tag).append(button); + if (typeof translateDynamicContent === 'function') { + translateDynamicContent(document.getElementById('config_content')); + translateDynamicContent(document.getElementById('config_content_save')); + } + addIpFieldHandlers(); // Add href for mimishark @@ -234,7 +264,7 @@ const ConfigSwitchForm = function (switch_id) { // Set loading spinner $('#config_switch_main_form_submit_button').text(''); - $('#config_switch_main_form_submit_button').append('Сохранение...'); + $('#config_switch_main_form_submit_button').append('Сохранение...'); // TODO: Localize UpdateSwitchConfiguration(data, switch_id); } @@ -258,6 +288,11 @@ const ConfigEdgeForm = function (edge_id) { $(config_content_id).append(form); $(config_content_save_tag).append(button); + if (typeof translateDynamicContent === 'function') { + translateDynamicContent(document.getElementById('config_content')); + translateDynamicContent(document.getElementById('config_content_save')); + } + // Set host_id $('#edge_id').val(edge_id); $('#net_guid').val(network_guid); @@ -281,7 +316,7 @@ const ConfigEdgeForm = function (edge_id) { inputsToDisable.prop("disabled", true); $('#config_edge_main_form_submit_button').html( - ' Сохранение...' + ' Сохранение...' // TODO: Localize ); edgeSaveXHR = UpdateEdgeConfiguration(data); @@ -291,42 +326,42 @@ const ConfigEdgeForm = function (edge_id) { $('#config_edge_main_form_submit_button, #config_edge_end_form').off('click').on('click', handleEdgeClick); } -const ConfigHubName = function (hostname) { - - var text = document.getElementById('config_hub_name_script').innerHTML; +const appendAndTranslate = function(formId, elementId) { + const template = document.getElementById(elementId); + if (template) { + $(formId).prepend(template.innerHTML); + if (typeof translateDynamicContent === 'function') { + const formElement = $(formId).get(0); + if (formElement) { + translateDynamicContent(formElement); + } + } + } +} - $(config_hub_main_form_id).prepend((text)); +const ConfigHubName = function (hostname) { + appendAndTranslate(config_hub_main_form_id, 'config_hub_name_script'); $('#config_hub_name').val(hostname); } const ConfigEdgePercentage = function (edge_loss) { - - var text = document.getElementById('config_edge_save_loss_script').innerHTML; - - $(config_edge_main_form_id).prepend(text); + appendAndTranslate(config_edge_main_form_id, 'config_edge_save_loss_script'); $('#edge_loss').val(edge_loss); } const ConfigEdgeEndpoints = function (edge_source, edge_target) { - - var text = document.getElementById('config_edge_edpoint_script').innerHTML; - - $(config_edge_main_form_id).prepend((text)); + appendAndTranslate(config_edge_main_form_id, 'config_edge_edpoint_script'); $('#edge_source').val(edge_source); $('#edge_target').val(edge_target); } const ConfigSwitchName = function (hostname) { - - var text = document.getElementById('config_switch_name_script').innerHTML; - - $(config_switch_main_form_id).prepend((text)); + appendAndTranslate(config_switch_main_form_id, 'config_switch_name_script'); $('#switch_name').val(hostname); } const ConfigSwtichSTP = function (stp) { var elem = document.getElementById('config_switch_checkbox_stp_script'); - $(elem.innerHTML).insertBefore('#config_switch_end_form'); if (stp === 1) { @@ -337,6 +372,9 @@ const ConfigSwtichSTP = function (stp) { $('#config_switch_stp').on('click', function () { if ($(this).is(':checked')) { $(warning_text).insertBefore('#config_switch_end_form'); + if (typeof translateDynamicContent === 'function') { + translateDynamicContent(document.getElementById('config_content')); + } } else { $('#config_warning_stp').remove(); } @@ -345,7 +383,6 @@ const ConfigSwtichSTP = function (stp) { const ConfigSwtichRSTP = function (rstp) { var elem = document.getElementById('config_switch_checkbox_rstp_script'); - $(elem.innerHTML).insertBefore('#config_switch_end_form'); if (rstp === 1) { @@ -356,6 +393,9 @@ const ConfigSwtichRSTP = function (rstp) { $('#config_switch_rstp').on('click', function () { if ($(this).is(':checked')) { $(warning_text).insertBefore('#config_switch_end_form'); + if (typeof translateDynamicContent === 'function') { + translateDynamicContent(document.getElementById('config_content')); + } } else { $('#config_warning_rstp').remove(); } @@ -372,6 +412,10 @@ const SharedConfigHostForm = function(host_id){ // Add new form $(config_content_id).append(form); + + if (typeof translateDynamicContent === 'function') { + translateDynamicContent(document.getElementById('config_content')); + } // Set host_id $('#host_id').val( host_id ); @@ -390,6 +434,10 @@ const SharedConfigRouterForm = function (router_id) { // Add new form $(config_content_id).append(form); + if (typeof translateDynamicContent === 'function') { + translateDynamicContent(document.getElementById('config_content')); + } + // Set host_id $('#router_id').val(router_id); $('#net_guid').val(network_guid); @@ -408,6 +456,10 @@ const SharedConfigServerForm = function (router_id) { // Add new form $(config_content_id).append(form); + if (typeof translateDynamicContent === 'function') { + translateDynamicContent(document.getElementById('config_content')); + } + // Set host_id $('#router_id').val(router_id); $('#net_guid').val(network_guid); @@ -425,6 +477,11 @@ const SharedConfigHubForm = function (hub_id) { // Add new form $(config_content_id).append(form); + + if (typeof translateDynamicContent === 'function') { + translateDynamicContent(document.getElementById('config_content')); + } + $('#config_hub_main_form_submit_button').prop('disabled', true); } @@ -438,6 +495,11 @@ const SharedConfigSwitchForm = function (switch_id) { // Add new form $(config_content_id).append(form); + + if (typeof translateDynamicContent === 'function') { + translateDynamicContent(document.getElementById('config_content')); + } + $('#config_switch_main_form_submit_button').prop('disabled', true); } @@ -451,36 +513,31 @@ const SharedConfigEdgeForm = function (edge_id) { // Add new form $(config_content_id).append(form); + + if (typeof translateDynamicContent === 'function') { + translateDynamicContent(document.getElementById('config_content')); + } + $('#config_edge_main_form_submit_button').prop('disabled', true); } const ConfigHostName = function (hostname) { - - var text = document.getElementById('config_host_name_script').innerHTML; - - $(config_main_form_id).prepend((text)); + appendAndTranslate(config_main_form_id, 'config_host_name_script'); $('#config_host_name').val(hostname); } const ConfigRouterName = function (hostname) { - - var text = document.getElementById('config_router_name_script').innerHTML; - - $(config_main_form_id).prepend((text)); + appendAndTranslate(config_main_form_id, 'config_router_name_script'); $('#config_router_name').val(hostname); } const ConfigServerName = function (hostname) { - - var text = document.getElementById('config_server_name_script').innerHTML; - - $(config_main_form_id).prepend((text)); + appendAndTranslate(config_main_form_id, 'config_server_name_script'); $('#config_server_name').val(hostname); } const ConfigItemInterface = function (name, ip, netmask, connected_to, item) { - let conf_item = 'config_' + item; let elem = document.getElementById(conf_item + '_interface_script'); let eth = jQuery.extend({}, elem); @@ -498,13 +555,29 @@ const ConfigItemInterface = function (name, ip, netmask, connected_to, item) { let text = eth.innerHTML; $(text).insertBefore(tag + '_end_form'); + if (typeof translateDynamicContent === 'function') { + const formElement = $(`#${conf_item}_end_form`).closest('form').get(0); + if (formElement) { + translateDynamicContent(formElement); + } + } + $('').insertBefore(tag + ids[1] + name); $(tag + ids[1] + name).attr("placeholder", connected_to); $(tag + ids[2] + name).val(ip); $(tag + ids[3] + name).val(netmask); if (Array.isArray(pcaps) && pcaps.includes(name)) { - $(tag + '_iface_name_label_' + name).html('Линк к (pcap)'); + const linkLabel = getTranslation('linkTo'); + $(tag + '_iface_name_label_' + name).html( + '' + linkLabel + ' (pcap)' + ); + if (typeof translateDynamicContent === 'function') { + const labelEl = document.querySelector(tag + '_iface_name_label_' + name); + if (labelEl) { + translateDynamicContent(labelEl); + } + } } else { console.warn('pcaps не определен или не является массивом:', pcaps); } @@ -568,6 +641,13 @@ const UpdateHostForm = function(name) { $('div[name="config_host_select_input"]').remove(); $(elem).insertBefore(host_job_list); + + if (typeof translateDynamicContent === 'function') { + const parentForm = $(host_job_list).closest('form').get(0); + if (parentForm) { + translateDynamicContent(parentForm); + } + } }; const ConfigHostJobOnChange = function (evnt) { @@ -625,7 +705,6 @@ const ConfigHostJobOnChange = function (evnt) { } const ConfigHostJob = function (host_jobs, shared = 0) { - let elem = document.getElementById('config_host_job_script').innerHTML; let host_id = document.getElementById('host_id'); @@ -634,6 +713,13 @@ const ConfigHostJob = function (host_jobs, shared = 0) { } $(elem).insertBefore(host_id); + + if (typeof translateDynamicContent === 'function') { + const parentForm = $(host_id).closest('form').get(0); + if (parentForm) { + translateDynamicContent(parentForm); + } + } // Set onchange document.getElementById('config_host_job_select_field').addEventListener('change', ConfigHostJobOnChange); @@ -654,7 +740,10 @@ const ConfigHostJob = function (host_jobs, shared = 0) { let jid = host_jobs[i].id; if (i == 0) { - $('#config_host_job_list').append(''); + $('#config_host_job_list').append(''); + if (typeof translateDynamicContent === 'function') { + translateDynamicContent(document.getElementById('config_host_job_list')); + } } elem = document.getElementById('config_host_job_list_elem_script'); @@ -682,26 +771,17 @@ const ConfigHostJob = function (host_jobs, shared = 0) { } const ConfigHostGateway = function (gw) { - - var text = document.getElementById('config_host_default_gw_script').innerHTML; - - $(text).insertBefore('#config_host_end_form'); + appendAndTranslate('#config_host_end_form', 'config_host_default_gw_script'); $('#config_host_default_gw').val(gw); } const ConfigRouterGateway = function (gw) { - - var text = document.getElementById('config_router_default_gw_script').innerHTML; - - $(text).insertBefore('#config_router_end_form'); + appendAndTranslate('#config_router_end_form', 'config_router_default_gw_script'); $('#config_router_default_gw').val(gw); } const ConfigServerGateway = function (gw) { - - var text = document.getElementById('config_server_default_gw_script').innerHTML; - - $(text).insertBefore('#config_server_end_form'); + appendAndTranslate('#config_server_end_form', 'config_server_default_gw_script'); $('#config_server_default_gw').val(gw); } @@ -756,7 +836,6 @@ const ConfigRouterJobOnChange = function(evnt) { } const ConfigRouterJob = function (router_jobs, shared = 0) { - let elem = document.getElementById('config_router_job_script').innerHTML; let router_id = document.getElementById('router_id'); @@ -766,6 +845,13 @@ const ConfigRouterJob = function (router_jobs, shared = 0) { $(elem).insertBefore(router_id); + if (typeof translateDynamicContent === 'function') { + const parentForm = $(router_id).closest('form').get(0); + if (parentForm) { + translateDynamicContent(parentForm); + } + } + // Set onchange document.getElementById('config_router_job_select_field').addEventListener('change', ConfigRouterJobOnChange); @@ -785,7 +871,10 @@ const ConfigRouterJob = function (router_jobs, shared = 0) { let jid = router_jobs[i].id; if (i == 0) { - $('#config_router_job_list').append(''); + $('#config_router_job_list').append(''); + if (typeof translateDynamicContent === 'function') { + translateDynamicContent(document.getElementById('config_router_job_list')); + } } elem = document.getElementById('config_router_job_list_elem_script'); @@ -799,7 +888,6 @@ const ConfigRouterJob = function (router_jobs, shared = 0) { job_elem.innerHTML = job_elem.innerHTML.replace(/justify-content-between align-items-center\">/, 'justify-content-between align-items-center\">' + router_jobs[i].print_cmd + ''); let text = job_elem.innerHTML; - //$(text).insertBefore(host_id); $('#config_router_job_list').append(text); $('#config_router_job_delete_' + jid).click(function (event) { @@ -812,7 +900,6 @@ const ConfigRouterJob = function (router_jobs, shared = 0) { } const ConfigServerJob = function (server_jobs, shared = 0) { - let elem = document.getElementById('config_server_job_script').innerHTML; let server_id = document.getElementById('server_id'); @@ -821,6 +908,13 @@ const ConfigServerJob = function (server_jobs, shared = 0) { } $(elem).insertBefore(server_id); + + if (typeof translateDynamicContent === 'function') { + const parentForm = $(server_id).closest('form').get(0); + if (parentForm) { + translateDynamicContent(parentForm); + } + } // Set onchange document.getElementById('config_server_job_select_field').addEventListener('change', ConfigServerJobOnChange); @@ -841,7 +935,10 @@ const ConfigServerJob = function (server_jobs, shared = 0) { let jid = server_jobs[i].id; if (i == 0) { - $('#config_server_job_list').append(''); + $('#config_server_job_list').append(''); + if (typeof translateDynamicContent === 'function') { + translateDynamicContent(document.getElementById('config_server_job_list')); + } } elem = document.getElementById('config_server_job_list_elem_script'); @@ -855,7 +952,6 @@ const ConfigServerJob = function (server_jobs, shared = 0) { job_elem.innerHTML = job_elem.innerHTML.replace(/justify-content-between align-items-center\">/, 'justify-content-between align-items-center\">' + server_jobs[i].print_cmd + ''); let text = job_elem.innerHTML; - //$(text).insertBefore(host_id); $('#config_server_job_list').append(text); $('#config_server_job_delete_' + jid).click(function (event) { @@ -879,10 +975,16 @@ const UpdateServerForm = function(name) { $('div[name="config_server_select_input"]').remove(); $(elem).insertBefore(server_job_list); + + if (typeof translateDynamicContent === 'function') { + const parentForm = $(server_job_list).closest('form').get(0); + if (parentForm) { + translateDynamicContent(parentForm); + } + } } const ConfigServerJobOnChange = function (evnt) { - let elem = null; let server_job_list = null; let n = null; @@ -916,7 +1018,6 @@ const ConfigServerJobOnChange = function (evnt) { default: console.log("Unknown target.value"); } - } const DisableFormInputs = function () { @@ -948,9 +1049,16 @@ const UpdateRouterForm = function(name) { $('div[name="config_router_select_input"]').remove(); $(elem).insertBefore(router_job_list); + + if (typeof translateDynamicContent === 'function') { + const parentForm = $(router_job_list).closest('form').get(0); + if (parentForm) { + translateDynamicContent(parentForm); + } + } } -const FillRouterSelect = function(select_id, field_msg = 'Интерфейс начальной точки', return_ip = true) { +const FillRouterSelect = function(select_id, field_msg = 'Интерфейс начальной точки', return_ip = true) { // TODO: Localize /** * Fill select element with network hosts. * @param {String} select_id ID(name) of the element to which you need to add data. @@ -1047,4 +1155,4 @@ const DisableVXLANInputs = function (n) { $('#' + modalId).off('hidden.bs.modal.myNamespace'); }); -}; +}; \ No newline at end of file diff --git a/front/src/static/config_edge.html b/front/src/static/config_edge.html index b50c4fb1..70682e8a 100644 --- a/front/src/static/config_edge.html +++ b/front/src/static/config_edge.html @@ -7,24 +7,24 @@ @@ -101,13 +100,12 @@ - + \ 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 73635476..6247c103 100755 --- a/front/src/static/config_router.html +++ b/front/src/static/config_router.html @@ -7,22 +7,22 @@ @@ -63,7 +63,7 @@
- +
@@ -80,12 +80,12 @@ @@ -96,7 +96,7 @@ + class="text-sm" data-i18n="ipAddressMask">IP-адрес / Маска
+ class="text-sm" data-i18n="vlanLabel">VLAN + value="1" data-i18n-attr="placeholder:placeholderVlanId">
@@ -145,7 +144,7 @@ + \ No newline at end of file diff --git a/front/src/static/config_server.html b/front/src/static/config_server.html index fd8ee42e..0c0a93bd 100644 --- a/front/src/static/config_server.html +++ b/front/src/static/config_server.html @@ -7,26 +7,26 @@ @@ -44,13 +44,13 @@ - + \ No newline at end of file diff --git a/front/src/static/config_switch.html b/front/src/static/config_switch.html index 086f9800..e861d269 100644 --- a/front/src/static/config_switch.html +++ b/front/src/static/config_switch.html @@ -8,12 +8,12 @@ @@ -27,7 +27,7 @@ @@ -40,7 +40,7 @@ @@ -56,8 +56,8 @@ @@ -91,11 +91,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..232f7b8c --- /dev/null +++ b/front/src/static/js/i18n.js @@ -0,0 +1,163 @@ + + +const translations = {}; +// Делаем объект переводов доступным через window, чтобы getTranslation работал корректно +if (typeof window !== 'undefined') { + window.translations = translations; +} +let currentLanguage = navigator.language.slice(0, 2) || 'ru'; + + +async function loadTranslations(lang) { + try { + + const response = await fetch(`/locales/${lang}.json?v=${new Date().getTime()}`); + if (!response.ok) throw new Error(`Could not load ${lang}.json`); + translations[lang] = await response.json(); + if (typeof window !== 'undefined') { + window.translations = translations; + } + console.log(`Translations for ${lang} loaded.`); + } catch (error) { + console.error(error); + } +} + + +function applyTranslations(lang) { + if (!translations[lang]) return; + const trans = translations[lang]; + + + document.querySelectorAll('[data-i18n]').forEach(element => { + const key = element.getAttribute('data-i18n'); + if (trans[key]) { + element.innerHTML = trans[key]; + } + }); + + + document.querySelectorAll('[data-i18n-attr]').forEach(element => { + const [attr, key] = element.getAttribute('data-i18n-attr').split(':'); + if (trans[key]) { + element.setAttribute(attr, trans[key]); + } + }); + + // Обновляем aria-describedby / tooltip‑тексты для специальных кнопок сети + updateNetworkButtonsTooltipsAria(); + + document.documentElement.lang = lang; +} + +async function setLanguage(lang) { + if (!['ru', 'en'].includes(lang)) { + lang = 'ru'; + } + + if (!translations[lang]) { + await loadTranslations(lang); + } + + currentLanguage = lang; + applyTranslations(lang); + localStorage.setItem('language', lang); +} + +async function detectLanguageByIP() { + try { + const response = await fetch('https://ipapi.co/json/'); + if (!response.ok) throw new Error('Could not fetch IP geolocation data'); + const data = await response.json(); + const country = data.country_code; + + await setLanguage(country !== 'RU' ? 'en' : 'ru'); + } catch (error) { + console.error('IP detection failed, defaulting to browser language:', error); + await setLanguage(currentLanguage); + } +} + + +document.addEventListener('DOMContentLoaded', async () => { + const savedLang = localStorage.getItem('language'); + + if (savedLang) { + await setLanguage(savedLang); + } else { + await detectLanguageByIP(); + } +}); +function translateDynamicContent(parentElement) { + if (!parentElement) return; + if (!translations[currentLanguage]) { + setTimeout(() => translateDynamicContent(parentElement), 100); + return; + } + const trans = translations[currentLanguage]; + + parentElement.querySelectorAll('[data-i18n]').forEach(element => { + const key = element.getAttribute('data-i18n'); + if (trans[key]) { + element.innerHTML = trans[key]; + } + }); + + parentElement.querySelectorAll('[data-i18n-attr]').forEach(element => { + const attrString = element.getAttribute('data-i18n-attr'); + attrString.split(';').forEach(attrPair => { + const [attr, key] = attrPair.split(':'); + if (trans[key]) { + element.setAttribute(attr.trim(), trans[key]); + } + }); + }); +} + +// Обновляет текст элементов, на которые ссылается aria-describedby для кнопок share/copy/settings +function 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 = getTranslation(key); + + if (!text || text === key) return; + + if (typeof $ !== 'undefined' && $(el).data('bs.tooltip')) { + $(el).attr('data-original-title', text).tooltip('fixTitle'); + } else if (typeof $ !== 'undefined') { + $(el).attr('title', text); + $(el).tooltip('dispose'); + $(el).tooltip({trigger: 'hover', title: text}); + } + + 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; + } + } + }); +} + +function getTranslation(key) { + if (window.translations && window.translations[currentLanguage] && window.translations[currentLanguage][key]) { + return window.translations[currentLanguage][key]; + } + if (translations[currentLanguage] && translations[currentLanguage][key]) { + return translations[currentLanguage][key]; + } + return key; +} \ 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..9e735ada --- /dev/null +++ b/front/src/static/locales/en.json @@ -0,0 +1,276 @@ +{ + "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" +} \ 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..dbd03e49 --- /dev/null +++ b/front/src/static/locales/ru.json @@ -0,0 +1,276 @@ +{ + "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": "Место в очереди" +} \ No newline at end of file diff --git a/front/src/static/netfront_f.js b/front/src/static/netfront_f.js index 6e80aba8..99d06afe 100644 --- a/front/src/static/netfront_f.js +++ b/front/src/static/netfront_f.js @@ -1343,17 +1343,17 @@ const InsertWaitingTimeHelper = function(time_filter) { success: function(data) { const queue_size = parseInt(data.size); - if ($('#NetworkPlayerLabel').text().startsWith("Шаг:")) { + if ($('#NetworkPlayerLabel').text().startsWith("Шаг:") || $('#NetworkPlayerLabel').text().startsWith("Step:")) { 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); @@ -1839,7 +1839,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; @@ -1851,10 +1860,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 @@ -1928,8 +1945,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() { @@ -1954,13 +1980,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: @@ -1997,7 +2027,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; @@ -2009,10 +2048,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 @@ -2074,7 +2121,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 698f7973..aa40976f 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 %} - - @@ -44,71 +40,67 @@ Miminet + {% if network %} - {% if current_user.is_authenticated %} - {% endif %} @@ -120,25 +112,25 @@ @@ -148,14 +140,14 @@ @@ -165,16 +157,16 @@ @@ -204,12 +196,12 @@
Наше компьюнити
- - - - - - + - -{% block network %} -{% endblock %} +{% block network %}{% endblock %} @@ -383,4 +312,4 @@
Наше компьюнити
- + \ No newline at end of file diff --git a/front/src/templates/cookie_consent.html b/front/src/templates/cookie_consent.html index 1a0e1441..57cbe66d 100644 --- a/front/src/templates/cookie_consent.html +++ b/front/src/templates/cookie_consent.html @@ -1,26 +1,26 @@ {% extends "base.html" %} -{% block title %}СОГЛАСИЕ НА ОБРАБОТКУ ПЕРСОНАЛЬНЫХ ДАННЫХ{% endblock %} -{% block description %}СОГЛАСИЕ НА ОБРАБОТКУ ПЕРСОНАЛЬНЫХ ДАННЫХ{% endblock %} +{% block title_tag %}СОГЛАСИЕ НА ОБРАБОТКУ ПЕРСОНАЛЬНЫХ ДАННЫХ{% endblock %} +{% block description %}{% endblock %} {% block content %}
-

СОГЛАСИЕ НА ОБРАБОТКУ ПЕРСОНАЛЬНЫХ ДАННЫХ

+

СОГЛАСИЕ НА ОБРАБОТКУ ПЕРСОНАЛЬНЫХ ДАННЫХ

-

+

Действуя свободно, своей волей и в своем интересе, а также подтверждая свою дееспособность, физическое лицо дает свое согласие Зеленчуку Илье Валерьевичу, зарегистрированный по адресу: 198206, г. Санкт-Петербург, улица Новобелицкая, дом 6, корпус 2, 8 этаж, кв. 68 (Далее - Оператор) на обработку своих персональных данных со следующими условиями: +

    -
  1. Данное Согласие дается на обработку персональных данных, как без использования средств автоматизации, так и с их использованием
  2. -
  3. Согласие дается на обработку следующих моих персональных данных: данные доступные из профиля в социальной сети, с помощью которой происходит авторизация на сайте; адрес электронной почты; никнейм.
  4. -
  5. Цель обработки персональных данных: регистрация пользователя на сайте/в приложении.
  6. -
  7. В ходе обработки с персональными данными будут совершены следующие действия: сбор, систематизация; хранение; использование; извлечение; блокирование; уничтожение; запись; удаление; накопление; обновление; изменение.
  8. -
  9. Персональные данные обрабатываются до удаления пользователем личного кабинета на сайте.
  10. -
  11. Согласие может быть отозвано субъектом персональных данных или его представителем путем направления заявления по адресу электронной почты: ilya@hackerdom.ru.
  12. +
  13. Данное Согласие дается на обработку персональных данных, как без использования средств автоматизации, так и с их использованием
  14. +
  15. Согласие дается на обработку следующих моих персональных данных: данные доступные из профиля в социальной сети, с помощью которой происходит авторизация на сайте; адрес электронной почты; никнейм.
  16. +
  17. Цель обработки персональных данных: регистрация пользователя на сайте/в приложении.
  18. +
  19. В ходе обработки с персональными данными будут совершены следующие действия: сбор, систематизация; хранение; использование; извлечение; блокирование; уничтожение; запись; удаление; накопление; обновление; изменение.
  20. +
  21. Персональные данные обрабатываются до удаления пользователем личного кабинета на сайте.
  22. +
  23. Согласие может быть отозвано субъектом персональных данных или его представителем путем направления заявления по адресу электронной почты: ilya@hackerdom.ru.
-{% endblock %} - +{% endblock %} \ No newline at end of file diff --git a/front/src/templates/course.html b/front/src/templates/course.html index b8e4a15e..81b0befe 100644 --- a/front/src/templates/course.html +++ b/front/src/templates/course.html @@ -1,13 +1,13 @@ {% extends "base.html" %} -{% block title %}Учебные курсы{% endblock %} -{% block description %}Учебные курсы по компьютерным сетям с использованием эмулятора Miminet{% endblock %} +{% block title_tag %}Учебные курсы{% endblock %} +{% block description %}{% endblock %} {% block content %}
-

Курсы по компьютерным сетям

+

Курсы по компьютерным сетям

@@ -16,12 +16,12 @@

Курсы по компьютерным сетям

- Основы компьютерных сетей + Основы компьютерных сетей

-

Курс знакомит слушателя с миром компьютерных сетей, как работает ваша домашняя сеть, сеть в кафе или глобальная сеть Интернет. Это базовый курс и он является обязательным перед более глубоким изучением компьютерных сетей. Для лучшего понимания и усваивания материала в курсе содержится множество примеров из веб-эмулятора Miminet. Данный курс читается на мат-мехе СПбГУ.

+

Курс знакомит слушателя с миром компьютерных сетей, как работает ваша домашняя сеть, сеть в кафе или глобальная сеть Интернет. Это базовый курс и он является обязательным перед более глубоким изучением компьютерных сетей. Для лучшего понимания и усваивания материала в курсе содержится множество примеров из веб-эмулятора Miminet. Данный курс читается на мат-мехе СПбГУ.

@@ -32,7 +32,7 @@

(4.9)

- 14 000 учащихся + 14 000 учащихся
@@ -45,12 +45,12 @@

- Программирование компьютерных сетей (Python) + Программирование компьютерных сетей (Python)

-

Практический курс для тех, кто хочет научиться писать свои собственные сетевые приложения на Python под Linux. На курсе вы научитесь: писать свой TCP/UDP сервер и клиент, устанавливать безопасное (SSL) соединение, писать свой сниффер (raw socket, scapy), управлять сетевыми настройками в ОС Linux прямо из Python (pyroute2), работать с tun/tap устройствами и писать собственные туннели (VPN). Данный курс читается на мат-мехе СПбГУ.

+

Практический курс для тех, кто хочет научиться писать свои собственные сетевые приложения на Python под Linux. На курсе вы научитесь: писать свой TCP/UDP сервер и клиент, устанавливать безопасное (SSL) соединение, писать свой сниффер (raw socket, scapy), управлять сетевыми настройками в ОС Linux прямо из Python (pyroute2), работать с tun/tap устройствами и писать собственные туннели (VPN). Данный курс читается на мат-мехе СПбГУ.

@@ -61,12 +61,12 @@

(5)

- 300 учащихся + 300 учащихся
+

-{% 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..d4760c59 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 @@

Как это работает (
@@ -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 b7338ac0..e91deb49 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,47 +28,19 @@
{% if request.path != url_for('web_network_shared') %} - Мои сети + Мои сети / {% endif %} {{ 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)
@@ -113,23 +87,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">Сервер
-
Настройки
+
Настройки
- Тут будут настройки устройств. Выделите любое на схеме. + Тут будут настройки устройств. Выделите любое на схеме.
@@ -137,7 +111,7 @@
- +
@@ -145,12 +119,12 @@
- Ожидание 10-30 сек. + Ожидание 10-30 сек.
- Задать вопрос + Задать вопрос

diff --git a/front/src/templates/network_shared.html b/front/src/templates/network_shared.html index 7c319b1d..acc13d96 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,7 +28,7 @@
{% if request.path != url_for('web_network_shared') %} - Мои сети + Мои сети / {% endif %} {{ network.title }} @@ -41,13 +43,14 @@ data-bs-toggle="modal" data-target="#netConfigModal" data-toggle="tooltip" - data-bs-placement="bottom" title="Настройки сети"> + data-bs-placement="bottom" title="Настройки сети" data-i18n-attr="title:tooltipNetSettings"> @@ -55,18 +58,19 @@ data-toggle="tooltip" data-bs-placement="bottom" title="Сеть доступна по URL" + data-i18n-attr="title:tooltipUrlAvailable" onclick="urlCopyButton()" style="margin-left: 16px; cursor: pointer;">
{% else %} @@ -75,16 +79,16 @@ - + Вход {% endif %}
-
Настройки
+
Настройки
- Тут будут настройки устройств. Выделите любое на схеме. + Тут будут настройки устройств. Выделите любое на схеме.
@@ -92,7 +96,7 @@
- +
@@ -100,12 +104,12 @@
- Ожидание 10-30 сек. + Ожидание 10-30 сек.
- Задать вопрос + Задать вопрос

diff --git a/front/src/templates/quiz/networkBase.html b/front/src/templates/quiz/networkBase.html index 9a46c667..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,49 +52,36 @@ Miminet - {# Test name and section name #} -
-
- -
-
- -
-
-

-
+
+
+
/
+

- {% if current_user.is_authenticated %} - - {% else %} - {% if request.path != url_for('login_index') %} - +
{% endif %} @@ -108,18 +91,17 @@ @@ -128,8 +110,7 @@
{% endif %} -{% block content %} -{% endblock %} +{% block content %}{% endblock %} @@ -139,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 %} -