mirror of
https://github.com/yggdrasil-network/yggdrasil-go.git
synced 2025-08-24 16:05:07 +03:00
Enhance WebUI with multilingual support
This commit is contained in:
parent
a984fba30d
commit
008ac3d864
4 changed files with 362 additions and 45 deletions
|
@ -6,6 +6,8 @@
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<title>Yggdrasil Web Interface</title>
|
<title>Yggdrasil Web Interface</title>
|
||||||
<link rel="stylesheet" href="static/style.css">
|
<link rel="stylesheet" href="static/style.css">
|
||||||
|
<script src="static/lang/ru.js"></script>
|
||||||
|
<script src="static/lang/en.js"></script>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
|
@ -13,57 +15,169 @@
|
||||||
<header>
|
<header>
|
||||||
<div class="header-content">
|
<div class="header-content">
|
||||||
<div>
|
<div>
|
||||||
<h1>🌳 Yggdrasil Web Interface</h1>
|
<h1 data-key="title">🌳 Yggdrasil Web Interface</h1>
|
||||||
<p>Network mesh management dashboard</p>
|
<p data-key="subtitle">Network mesh management dashboard</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="header-actions">
|
<div class="header-actions">
|
||||||
<button onclick="logout()" class="logout-btn">Logout</button>
|
<div class="language-switcher">
|
||||||
|
<button onclick="switchLanguage('ru')" class="lang-btn" id="lang-ru">RU</button>
|
||||||
|
<button onclick="switchLanguage('en')" class="lang-btn" id="lang-en">EN</button>
|
||||||
|
</div>
|
||||||
|
<button onclick="logout()" class="logout-btn" data-key="logout">Logout</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<main>
|
<div class="layout">
|
||||||
|
<aside class="sidebar">
|
||||||
|
<nav class="nav-menu">
|
||||||
|
<div class="nav-item active" onclick="showSection('status')">
|
||||||
|
<span class="nav-icon">📊</span>
|
||||||
|
<span class="nav-text" data-key="nav_status">Состояние</span>
|
||||||
|
</div>
|
||||||
|
<div class="nav-item" onclick="showSection('peers')">
|
||||||
|
<span class="nav-icon">🌐</span>
|
||||||
|
<span class="nav-text" data-key="nav_peers">Пиры</span>
|
||||||
|
</div>
|
||||||
|
<div class="nav-item" onclick="showSection('config')">
|
||||||
|
<span class="nav-icon">⚙️</span>
|
||||||
|
<span class="nav-text" data-key="nav_config">Конфигурация</span>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
</aside>
|
||||||
|
|
||||||
|
<main class="main-content">
|
||||||
|
<div id="status-section" class="content-section active">
|
||||||
<div class="status-card">
|
<div class="status-card">
|
||||||
<h2>Node Status</h2>
|
<h2 data-key="status_title">Состояние узла</h2>
|
||||||
<div class="status-indicator">
|
<div class="status-indicator">
|
||||||
<span class="status-dot active"></span>
|
<span class="status-dot active"></span>
|
||||||
<span>Active</span>
|
<span data-key="status_active">Активен</span>
|
||||||
</div>
|
</div>
|
||||||
<p>WebUI is running and accessible</p>
|
<p data-key="status_description">WebUI запущен и доступен</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="info-grid">
|
<div class="info-grid">
|
||||||
<div class="info-card">
|
<div class="info-card">
|
||||||
<h3>Configuration</h3>
|
<h3 data-key="network_info">Сетевая информация</h3>
|
||||||
<p>Manage node settings and peers</p>
|
<p><span data-key="address">Адрес</span>: 200:1234:5678:9abc::1</p>
|
||||||
<small>Coming soon...</small>
|
<p><span data-key="subnet">Подсеть</span>: 300:1234:5678:9abc::/64</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="info-card">
|
<div class="info-card">
|
||||||
<h3>Peers</h3>
|
<h3 data-key="statistics">Статистика</h3>
|
||||||
<p>View and manage peer connections</p>
|
<p><span data-key="uptime">Время работы</span>: 2д 15ч 42м</p>
|
||||||
<small>Coming soon...</small>
|
<p><span data-key="connections">Активных соединений</span>: 3</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="peers-section" class="content-section">
|
||||||
|
<div class="status-card">
|
||||||
|
<h2 data-key="peers_title">Управление пирами</h2>
|
||||||
|
<p data-key="peers_description">Просмотр и управление соединениями с пирами</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="info-grid">
|
||||||
|
<div class="info-card">
|
||||||
|
<h3 data-key="active_peers">Активные пиры</h3>
|
||||||
|
<p data-key="active_connections">Количество активных соединений</p>
|
||||||
|
<small data-key="coming_soon">Функция в разработке...</small>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="info-card">
|
<div class="info-card">
|
||||||
<h3>Network</h3>
|
<h3 data-key="add_peer">Добавить пир</h3>
|
||||||
<p>Network topology and routing</p>
|
<p data-key="add_peer_description">Подключение к новому узлу</p>
|
||||||
<small>Coming soon...</small>
|
<small data-key="coming_soon">Функция в разработке...</small>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="config-section" class="content-section">
|
||||||
|
<div class="status-card">
|
||||||
|
<h2 data-key="config_title">Конфигурация</h2>
|
||||||
|
<p data-key="config_description">Настройки узла и параметры сети</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="info-grid">
|
||||||
|
<div class="info-card">
|
||||||
|
<h3 data-key="basic_settings">Основные настройки</h3>
|
||||||
|
<p data-key="basic_settings_description">Базовая конфигурация узла</p>
|
||||||
|
<small data-key="coming_soon">Функция в разработке...</small>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="info-card">
|
||||||
|
<h3 data-key="network_settings">Сетевые настройки</h3>
|
||||||
|
<p data-key="network_settings_description">Параметры сетевого взаимодействия</p>
|
||||||
|
<small data-key="coming_soon">Функция в разработке...</small>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
|
</div>
|
||||||
|
|
||||||
<footer>
|
<footer>
|
||||||
<p>Yggdrasil Network • Minimal WebUI v1.0</p>
|
<p data-key="footer_text">Yggdrasil Network • Minimal WebUI v1.0</p>
|
||||||
</footer>
|
</footer>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
let currentLanguage = localStorage.getItem('yggdrasil-language') || 'ru';
|
||||||
|
|
||||||
|
function updateTexts() {
|
||||||
|
const elements = document.querySelectorAll('[data-key]');
|
||||||
|
elements.forEach(element => {
|
||||||
|
const key = element.getAttribute('data-key');
|
||||||
|
if (window.translations && window.translations[currentLanguage] && window.translations[currentLanguage][key]) {
|
||||||
|
element.textContent = window.translations[currentLanguage][key];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function switchLanguage(lang) {
|
||||||
|
currentLanguage = lang;
|
||||||
|
localStorage.setItem('yggdrasil-language', lang);
|
||||||
|
|
||||||
|
// Update active button
|
||||||
|
document.querySelectorAll('.lang-btn').forEach(btn => btn.classList.remove('active'));
|
||||||
|
document.getElementById('lang-' + lang).classList.add('active');
|
||||||
|
|
||||||
|
updateTexts();
|
||||||
|
}
|
||||||
|
|
||||||
function logout() {
|
function logout() {
|
||||||
if (confirm('Are you sure you want to logout?')) {
|
const confirmText = window.translations[currentLanguage]['logout_confirm'];
|
||||||
|
if (confirm(confirmText)) {
|
||||||
window.location.href = '/auth/logout';
|
window.location.href = '/auth/logout';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function showSection(sectionName) {
|
||||||
|
// Hide all sections
|
||||||
|
const sections = document.querySelectorAll('.content-section');
|
||||||
|
sections.forEach(section => section.classList.remove('active'));
|
||||||
|
|
||||||
|
// Remove active class from all nav items
|
||||||
|
const navItems = document.querySelectorAll('.nav-item');
|
||||||
|
navItems.forEach(item => item.classList.remove('active'));
|
||||||
|
|
||||||
|
// Show selected section
|
||||||
|
const targetSection = document.getElementById(sectionName + '-section');
|
||||||
|
if (targetSection) {
|
||||||
|
targetSection.classList.add('active');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add active class to clicked nav item
|
||||||
|
event.target.closest('.nav-item').classList.add('active');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize language on page load
|
||||||
|
document.addEventListener('DOMContentLoaded', function () {
|
||||||
|
// Set active language button
|
||||||
|
document.getElementById('lang-' + currentLanguage).classList.add('active');
|
||||||
|
// Update all texts
|
||||||
|
updateTexts();
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
|
|
33
src/webui/static/lang/en.js
Normal file
33
src/webui/static/lang/en.js
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
window.translations = window.translations || {};
|
||||||
|
window.translations.en = {
|
||||||
|
'title': '🌳 Yggdrasil Web Interface',
|
||||||
|
'subtitle': 'Network mesh management dashboard',
|
||||||
|
'logout': 'Logout',
|
||||||
|
'nav_status': 'Status',
|
||||||
|
'nav_peers': 'Peers',
|
||||||
|
'nav_config': 'Configuration',
|
||||||
|
'status_title': 'Node Status',
|
||||||
|
'status_active': 'Active',
|
||||||
|
'status_description': 'WebUI is running and accessible',
|
||||||
|
'network_info': 'Network Information',
|
||||||
|
'address': 'Address',
|
||||||
|
'subnet': 'Subnet',
|
||||||
|
'statistics': 'Statistics',
|
||||||
|
'uptime': 'Uptime',
|
||||||
|
'connections': 'Active connections',
|
||||||
|
'peers_title': 'Peer Management',
|
||||||
|
'peers_description': 'View and manage peer connections',
|
||||||
|
'active_peers': 'Active Peers',
|
||||||
|
'active_connections': 'Number of active connections',
|
||||||
|
'add_peer': 'Add Peer',
|
||||||
|
'add_peer_description': 'Connect to a new node',
|
||||||
|
'config_title': 'Configuration',
|
||||||
|
'config_description': 'Node settings and network parameters',
|
||||||
|
'basic_settings': 'Basic Settings',
|
||||||
|
'basic_settings_description': 'Basic node configuration',
|
||||||
|
'network_settings': 'Network Settings',
|
||||||
|
'network_settings_description': 'Network interaction parameters',
|
||||||
|
'coming_soon': 'Coming soon...',
|
||||||
|
'footer_text': 'Yggdrasil Network • Minimal WebUI v1.0',
|
||||||
|
'logout_confirm': 'Are you sure you want to logout?'
|
||||||
|
};
|
33
src/webui/static/lang/ru.js
Normal file
33
src/webui/static/lang/ru.js
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
window.translations = window.translations || {};
|
||||||
|
window.translations.ru = {
|
||||||
|
'title': '🌳 Yggdrasil Web Interface',
|
||||||
|
'subtitle': 'Панель управления mesh-сетью',
|
||||||
|
'logout': 'Выход',
|
||||||
|
'nav_status': 'Состояние',
|
||||||
|
'nav_peers': 'Пиры',
|
||||||
|
'nav_config': 'Конфигурация',
|
||||||
|
'status_title': 'Состояние узла',
|
||||||
|
'status_active': 'Активен',
|
||||||
|
'status_description': 'WebUI запущен и доступен',
|
||||||
|
'network_info': 'Сетевая информация',
|
||||||
|
'address': 'Адрес',
|
||||||
|
'subnet': 'Подсеть',
|
||||||
|
'statistics': 'Статистика',
|
||||||
|
'uptime': 'Время работы',
|
||||||
|
'connections': 'Активных соединений',
|
||||||
|
'peers_title': 'Управление пирами',
|
||||||
|
'peers_description': 'Просмотр и управление соединениями с пирами',
|
||||||
|
'active_peers': 'Активные пиры',
|
||||||
|
'active_connections': 'Количество активных соединений',
|
||||||
|
'add_peer': 'Добавить пир',
|
||||||
|
'add_peer_description': 'Подключение к новому узлу',
|
||||||
|
'config_title': 'Конфигурация',
|
||||||
|
'config_description': 'Настройки узла и параметры сети',
|
||||||
|
'basic_settings': 'Основные настройки',
|
||||||
|
'basic_settings_description': 'Базовая конфигурация узла',
|
||||||
|
'network_settings': 'Сетевые настройки',
|
||||||
|
'network_settings_description': 'Параметры сетевого взаимодействия',
|
||||||
|
'coming_soon': 'Функция в разработке...',
|
||||||
|
'footer_text': 'Yggdrasil Network • Minimal WebUI v1.0',
|
||||||
|
'logout_confirm': 'Вы уверены, что хотите выйти?'
|
||||||
|
};
|
|
@ -29,6 +29,41 @@ header {
|
||||||
text-align: left;
|
text-align: left;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.header-actions {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.language-switcher {
|
||||||
|
display: flex;
|
||||||
|
background: rgba(255, 255, 255, 0.15);
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 4px;
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.lang-btn {
|
||||||
|
background: transparent;
|
||||||
|
color: white;
|
||||||
|
border: none;
|
||||||
|
padding: 6px 12px;
|
||||||
|
border-radius: 6px;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 500;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.lang-btn:hover {
|
||||||
|
background: rgba(255, 255, 255, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.lang-btn.active {
|
||||||
|
background: rgba(255, 255, 255, 0.3);
|
||||||
|
box-shadow: 0 2px 4px rgba(0,0,0,0.2);
|
||||||
|
}
|
||||||
|
|
||||||
.header-content > div:first-child {
|
.header-content > div:first-child {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
|
@ -60,20 +95,88 @@ header p {
|
||||||
background: rgba(255, 255, 255, 0.3);
|
background: rgba(255, 255, 255, 0.3);
|
||||||
}
|
}
|
||||||
|
|
||||||
main {
|
.layout {
|
||||||
background: white;
|
display: flex;
|
||||||
border-radius: 12px;
|
gap: 20px;
|
||||||
padding: 30px;
|
|
||||||
box-shadow: 0 8px 32px rgba(0,0,0,0.1);
|
|
||||||
margin-bottom: 20px;
|
margin-bottom: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.status-card {
|
.sidebar {
|
||||||
background: #f8f9fa;
|
min-width: 250px;
|
||||||
border-radius: 8px;
|
background: rgba(255, 255, 255, 0.15);
|
||||||
|
backdrop-filter: blur(10px);
|
||||||
|
border-radius: 12px;
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
|
box-shadow: 0 8px 32px rgba(0,0,0,0.1);
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.18);
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-menu {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 12px;
|
||||||
|
padding: 15px 18px;
|
||||||
|
background: rgba(255, 255, 255, 0.1);
|
||||||
|
border-radius: 10px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
color: white;
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-item:hover {
|
||||||
|
background: rgba(255, 255, 255, 0.2);
|
||||||
|
transform: translateY(-2px);
|
||||||
|
box-shadow: 0 4px 15px rgba(0,0,0,0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-item.active {
|
||||||
|
background: rgba(255, 255, 255, 0.25);
|
||||||
|
border-color: rgba(255, 255, 255, 0.4);
|
||||||
|
box-shadow: 0 4px 15px rgba(0,0,0,0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-icon {
|
||||||
|
font-size: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-text {
|
||||||
|
font-weight: 500;
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.main-content {
|
||||||
|
flex: 1;
|
||||||
|
background: rgba(255, 255, 255, 0.95);
|
||||||
|
border-radius: 12px;
|
||||||
|
padding: 30px;
|
||||||
|
box-shadow: 0 8px 32px rgba(0,0,0,0.1);
|
||||||
|
backdrop-filter: blur(10px);
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.18);
|
||||||
|
}
|
||||||
|
|
||||||
|
.content-section {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content-section.active {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-card {
|
||||||
|
background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%);
|
||||||
|
border-radius: 15px;
|
||||||
|
padding: 25px;
|
||||||
margin-bottom: 30px;
|
margin-bottom: 30px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
box-shadow: 0 4px 20px rgba(0,0,0,0.08);
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.5);
|
||||||
}
|
}
|
||||||
|
|
||||||
.status-indicator {
|
.status-indicator {
|
||||||
|
@ -109,17 +212,19 @@ main {
|
||||||
}
|
}
|
||||||
|
|
||||||
.info-card {
|
.info-card {
|
||||||
background: #ffffff;
|
background: linear-gradient(135deg, #ffffff 0%, #f8f9fa 100%);
|
||||||
border: 1px solid #e9ecef;
|
border: 1px solid rgba(233, 236, 239, 0.6);
|
||||||
border-radius: 8px;
|
border-radius: 15px;
|
||||||
padding: 20px;
|
padding: 25px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
transition: transform 0.2s ease, box-shadow 0.2s ease;
|
transition: all 0.3s ease;
|
||||||
|
box-shadow: 0 2px 10px rgba(0,0,0,0.05);
|
||||||
}
|
}
|
||||||
|
|
||||||
.info-card:hover {
|
.info-card:hover {
|
||||||
transform: translateY(-2px);
|
transform: translateY(-5px);
|
||||||
box-shadow: 0 4px 12px rgba(0,0,0,0.1);
|
box-shadow: 0 8px 25px rgba(0,0,0,0.15);
|
||||||
|
border-color: rgba(102, 126, 234, 0.3);
|
||||||
}
|
}
|
||||||
|
|
||||||
.info-card h3 {
|
.info-card h3 {
|
||||||
|
@ -153,6 +258,11 @@ footer {
|
||||||
gap: 20px;
|
gap: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.header-actions {
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
.header-content > div:first-child {
|
.header-content > div:first-child {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
@ -161,8 +271,35 @@ footer {
|
||||||
font-size: 2rem;
|
font-size: 2rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
main {
|
.layout {
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar {
|
||||||
|
min-width: auto;
|
||||||
|
order: 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-menu {
|
||||||
|
flex-direction: row;
|
||||||
|
overflow-x: auto;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-item {
|
||||||
|
min-width: 120px;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 12px 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-text {
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.main-content {
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
|
order: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.info-grid {
|
.info-grid {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue