[FEAT] i18n IT/EN, Help Online contestuale, pagina Architettura

- i18n.js: sistema traduzioni IT/EN con ~150 chiavi, localStorage, data-i18n
- help.js: help contestuale per 10 pagine con riferimenti NIS2
- architecture.html: descrizione architettura sistema completa
- common.js: language toggle sidebar (IT/EN), link Architettura, icone
- Integrato i18n + help in tutte le 14 pagine app + 3 admin

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-18 08:34:37 +01:00
co-authored by Claude Opus 4.6
parent 4e3408e9f6
commit 52fd45fac9
17 changed files with 2343 additions and 1 deletions
+71 -1
View File
@@ -210,6 +210,7 @@ function loadSidebar() {
label: 'Sistema',
items: [
{ name: 'Impostazioni', href: 'settings.html', icon: iconCog() },
{ name: 'Architettura', href: 'architecture.html', icon: iconCubeTransparent() },
]
}
];
@@ -240,9 +241,13 @@ function loadSidebar() {
}
navHTML += '</nav>';
// Footer with user info
// Footer with language toggle + user info
navHTML += `
<div class="sidebar-footer">
<div class="sidebar-lang-toggle" id="sidebar-lang-toggle">
<button class="lang-btn ${_getSavedLang() === 'it' ? 'active' : ''}" onclick="switchLang('it')" title="Italiano">IT</button>
<button class="lang-btn ${_getSavedLang() === 'en' ? 'active' : ''}" onclick="switchLang('en')" title="English">EN</button>
</div>
<div class="sidebar-user">
<div class="sidebar-user-avatar" id="sidebar-user-avatar">--</div>
<div class="sidebar-user-info">
@@ -472,6 +477,14 @@ function iconChartBar() {
return '<svg viewBox="0 0 20 20" fill="currentColor"><path d="M2 11a1 1 0 011-1h2a1 1 0 011 1v5a1 1 0 01-1 1H3a1 1 0 01-1-1v-5zm6-4a1 1 0 011-1h2a1 1 0 011 1v9a1 1 0 01-1 1H9a1 1 0 01-1-1V7zm6-3a1 1 0 011-1h2a1 1 0 011 1v12a1 1 0 01-1 1h-2a1 1 0 01-1-1V4z"/></svg>';
}
function iconCubeTransparent() {
return '<svg viewBox="0 0 20 20" fill="currentColor"><path d="M10 3.5a1 1 0 00-.5.134l-5 2.857A1 1 0 004 7.382v5.236a1 1 0 00.5.866l5 2.857a1 1 0 001 0l5-2.857a1 1 0 00.5-.866V7.382a1 1 0 00-.5-.891l-5-2.857A1 1 0 0010 3.5zM6 8.382l4 2.286 4-2.286v3.236l-4 2.286-4-2.286V8.382z"/></svg>';
}
function iconQuestionMarkCircle() {
return '<svg viewBox="0 0 20 20" fill="currentColor"><path fill-rule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-8-3a1 1 0 00-.867.5 1 1 0 11-1.731-1A3 3 0 0113 8a3.001 3.001 0 01-2 2.83V11a1 1 0 11-2 0v-1a1 1 0 011-1 1 1 0 100-2zm0 8a1 1 0 100-2 1 1 0 000 2z" clip-rule="evenodd"/></svg>';
}
function iconCog() {
return '<svg viewBox="0 0 20 20" fill="currentColor"><path fill-rule="evenodd" d="M11.49 3.17c-.38-1.56-2.6-1.56-2.98 0a1.532 1.532 0 01-2.286.948c-1.372-.836-2.942.734-2.106 2.106.54.886.061 2.042-.947 2.287-1.561.379-1.561 2.6 0 2.978a1.532 1.532 0 01.947 2.287c-.836 1.372.734 2.942 2.106 2.106a1.532 1.532 0 012.287.947c.379 1.561 2.6 1.561 2.978 0a1.533 1.533 0 012.287-.947c1.372.836 2.942-.734 2.106-2.106a1.533 1.533 0 01.947-2.287c1.561-.379 1.561-2.6 0-2.978a1.532 1.532 0 01-.947-2.287c.836-1.372-.734-2.942-2.106-2.106a1.532 1.532 0 01-2.287-.947zM10 13a3 3 0 100-6 3 3 0 000 6z" clip-rule="evenodd"/></svg>';
}
@@ -504,3 +517,60 @@ function iconPlus() {
function iconSparkles() {
return '<svg viewBox="0 0 20 20" fill="currentColor"><path d="M5 2a1 1 0 011 1v1h1a1 1 0 010 2H6v1a1 1 0 01-2 0V6H3a1 1 0 010-2h1V3a1 1 0 011-1zm0 10a1 1 0 011 1v1h1a1 1 0 110 2H6v1a1 1 0 11-2 0v-1H3a1 1 0 110-2h1v-1a1 1 0 011-1zm7-10a1 1 0 01.967.744L14.146 7.2 17.5 8.512a1 1 0 010 1.836l-3.354 1.311-1.18 4.456a1 1 0 01-1.932 0L9.854 11.66 6.5 10.348a1 1 0 010-1.836l3.354-1.311 1.18-4.456A1 1 0 0112 2z"/></svg>';
}
// ═══════════════════════════════════════════════════════════════════
// Language Switcher
// ═══════════════════════════════════════════════════════════════════
function _getSavedLang() {
return localStorage.getItem('nis2_lang') || 'it';
}
function switchLang(lang) {
if (typeof I18n !== 'undefined') {
I18n.setLang(lang);
}
localStorage.setItem('nis2_lang', lang);
// Aggiorna bottoni
document.querySelectorAll('.lang-btn').forEach(btn => {
btn.classList.toggle('active', btn.textContent.trim().toLowerCase() === lang);
});
}
// Inject language toggle CSS
(function _injectLangCSS() {
if (document.getElementById('lang-toggle-css')) return;
const style = document.createElement('style');
style.id = 'lang-toggle-css';
style.textContent = `
.sidebar-lang-toggle {
display: flex;
justify-content: center;
gap: 4px;
padding: 8px 16px 4px;
}
.lang-btn {
padding: 4px 14px;
font-size: 0.75rem;
font-weight: 700;
border: 1px solid var(--gray-300);
background: var(--gray-50);
color: var(--gray-500);
border-radius: 4px;
cursor: pointer;
transition: all var(--transition-fast);
font-family: inherit;
}
.lang-btn:hover {
background: var(--gray-100);
color: var(--gray-700);
}
.lang-btn.active {
background: var(--primary);
color: #fff;
border-color: var(--primary);
}
`;
document.head.appendChild(style);
})();