diff --git a/.github/scripts/Test-CompiledScript.ps1 b/.github/scripts/Test-CompiledScript.ps1
index d9d4a5f0..62b9c1b6 100644
--- a/.github/scripts/Test-CompiledScript.ps1
+++ b/.github/scripts/Test-CompiledScript.ps1
@@ -20,7 +20,7 @@ param(
[string]$ScriptPath = "WinToolkit.ps1",
[Parameter(Mandatory = $false)]
- [string]$ToolPath = "tool",
+ [string]$ToolPath = "tools",
[Parameter(Mandatory = $false)]
[string]$TemplatePath = "WinToolkit-template.ps1"
@@ -104,7 +104,7 @@ try {
# ========================================
Write-TestLog -Message "`n🔍 Test 2: Verifica funzioni..." -Type Info
- # Rilevamento automatico delle funzioni dalla cartella tool/
+ # Rilevamento automatico delle funzioni dalla cartella tools/
$toolFiles = Get-ChildItem -Path $ToolPath -Filter "*.ps1" -ErrorAction SilentlyContinue | Where-Object { $_.Name -notlike "*start-*" }
$expectedFunctions = $toolFiles | ForEach-Object { [System.IO.Path]::GetFileNameWithoutExtension($_.Name) }
@@ -174,7 +174,7 @@ try {
else {
$script:TestResults += "❌ Funzioni: $($presentFunctions.Count)/$($expectedFunctions.Count) presenti"
$script:TotalErrors++
- $errorMsg = "MODULI MANCANTI NEL TEMPLATE: I seguenti script in /tool non hanno un placeholder (nemmeno commentato) in WinToolkit-template.ps1: $($missingFunctions -join ', ')"
+ $errorMsg = "MODULI MANCANTI NEL TEMPLATE: I seguenti script in /tools non hanno un placeholder (nemmeno commentato) in WinToolkit-template.ps1: $($missingFunctions -join ', ')"
$script:CriticalErrors += $errorMsg
}
diff --git a/.github/workflows/Release_Wintoolkit.yml b/.github/workflows/Release_Wintoolkit.yml
index c0808561..f77917b2 100644
--- a/.github/workflows/Release_Wintoolkit.yml
+++ b/.github/workflows/Release_Wintoolkit.yml
@@ -127,8 +127,8 @@ jobs:
"README.md",
"CHANGELOG.md",
"LICENSE",
- "img/",
- "asset/",
+ "images/",
+ "assets/",
".github/ISSUE_TEMPLATE/bug_report.yml",
".github/ISSUE_TEMPLATE/feature_request.yml",
".github/workflows/Release_Wintoolkit.yml",
@@ -143,10 +143,10 @@ jobs:
exit 1
}
- # Esclusione specifica della cartella png in asset
- if (Test-Path -Path 'asset/png') {
- Write-Host "🗑️ Rimoziene cartella asset/png esclusa dal rilascio..."
- Remove-Item -Recurse -Force 'asset/png'
+ # Esclusione specifica della cartella png in assets
+ if (Test-Path -Path 'assets/png') {
+ Write-Host "🗑️ Rimoziene cartella assets/png esclusa dal rilascio..."
+ Remove-Item -Recurse -Force 'assets/png'
}
- name: Commit delle Modifiche
@@ -158,8 +158,8 @@ jobs:
"README.md",
"CHANGELOG.md",
"LICENSE",
- "img/",
- "asset/",
+ "images/",
+ "assets/",
".github/ISSUE_TEMPLATE/bug_report.yml",
".github/ISSUE_TEMPLATE/feature_request.yml",
".github/workflows/Release_Wintoolkit.yml",
diff --git a/README.md b/README.md
index b1bfde59..ebad4659 100644
--- a/README.md
+++ b/README.md
@@ -1,238 +1,242 @@
-
-
WinToolkit: Il Tool Powershell definitivo per sopravvivere a Windows
+
+ WinToolkit: the ultimate PowerShell tool to survive Windows
-
+
-
+
-
+
-WinToolkit è una suite di script PowerShell potente e compatta, progettata per offrire a professionisti IT, amministratori di sistema e utenti esperti un controllo granulare sulla manutenzione e sulla risoluzione dei problemi di Windows e della Suite Office. Questo toolkit intuitivo aggrega gli strumenti di riparazione di sistema più efficaci in un'unica interfaccia, automatizzando i processi complessi per ottimizzare le prestazioni e ripristinare la stabilità del sistema con pochi passaggi automatizzati. Questo progetto è traslitterato tramite un workflow AI.
+WinToolkit is a powerful, compact PowerShell script suite built to give IT professionals, system administrators, and advanced users granular control over Windows and Office suite maintenance and troubleshooting. This intuitive toolkit brings the most effective system repair tools into a single interface, automating complex processes to optimize performance and restore system stability in just a few automated steps. This project is transliterated through an AI workflow.
---
-## ⚙️ Requisiti minimi
+## ⚙️ Minimum Requirements
> [!IMPORTANT]
-> Prima di avviare il toolkit, assicurati di soddisfare i seguenti requisiti:
+> Before starting the toolkit, make sure you meet these requirements:
>
-> - **connessione ad Internet**;
-> - **spazio libero su disco**: >= 50 GB [(vedere la sezione F.A.Q.)](#-faq---domande-frequenti);
+> - **Internet connection**;
+> - **free disk space**: >= 50 GB [(see the FAQ section)](#-faq---frequently-asked-questions);
> - **Windows >= 8.1**.
-| Versioni di Windows | Supportato |
-| :-------------------- | :-------------- |
-| Windows 11 >= 22H2 | 🟢 Sì |
-| Windows 10 >= 1809 | 🟢 Sì |
-| Windows 11 <= 21H2 | 🟡 Parzialmente |
-| Windows 10 <= 1809 | 🟡 Parzialmente |
-| Windows 8.1 | 🟡 Parzialmente |
-| Windows 8 e inferiori | 🔴 No |
+| Windows Versions | Supported |
+| :-------------------- | :------------------ |
+| Windows 11 >= 22H2 | 🟢 Yes |
+| Windows 10 >= 1809 | 🟢 Yes |
+| Windows 11 <= 21H2 | 🟡 Partially |
+| Windows 10 <= 1809 | 🟡 Partially |
+| Windows 8.1 | 🟡 Partially |
+| Windows 8 and earlier | 🔴 No |
-## 🚀 Come eseguire WinToolkit
+## 🚀 How To Run WinToolkit
-Installa l'eseguibile di WinToolkit sul desktop seguendo queste istruzioni:
+Install the WinToolkit executable on your desktop by following these steps:
-1. Premi il tasto `Windows` sulla tastiera oppure clicca sulla ricerca di Windows.
-2. Digita `Powershell` nel campo della ricerca.
-3. Clicca col tasto destro del mouse sulla voce `Powershell`.
-4. Clicca sulla voce `Esegui come amministratore` dal menù a tendina.
-5. Copia e incolla nella finestra di Powershell il comando seguente:
+1. Press the `Windows` key on your keyboard or open Windows Search.
+2. Type `PowerShell` in the search field.
+3. Right-click `PowerShell`.
+4. Click `Run as administrator` from the drop-down menu.
+5. Copy and paste the following command into the PowerShell window:
```powershell
irm https://magnetarman.com/winstart | iex
```
-6. Al riavvio del tuo PC troverai la scorciatoia `Win Toolkit` sul desktop da cui avviare comodamente lo script in modalità amministratore con un semplice doppio click sull'icona.
+6. After your PC restarts, you will find the `Win Toolkit` shortcut on the desktop. Use it to start the script as administrator with a simple double-click.
-### ⚙️ Per utenti esperti
+### ⚙️ For Advanced Users
> [!WARNING]
-> Se stai avviando WinToolkit da versioni parzialmente supportate è consigliabile utilizzare PowerShell 7 o versioni successive. Questa versione moderna è necessaria per garantire la massima compatibilità, eseguire correttamente le operazioni del tool e prevenire errori di runtime o l'errata applicazione delle modifiche.
+> If you are starting WinToolkit from a partially supported Windows version, PowerShell 7 or later is recommended. This modern version is required for maximum compatibility, correct tool execution, and to prevent runtime errors or incorrect application of changes.
-1. Installa Powershell 7 (o versioni successive) da [Microsoft Store](https://www.microsoft.com/store/apps/9MZ1SNWT0N5D) oppure da [GitHub](https://learn.microsoft.com/it-it/powershell/scripting/install/install-powershell-on-windows?view=powershell-7.5#msi).
-2. Premi il tasto `Windows` sulla tastiera oppure clicca sulla ricerca di Windows.
-3. Digita `Powershell` nel campo della ricerca.
-4. Clicca col tasto destro del mouse sulla voce `Powershell`.
-5. Clicca sulla voce `Esegui come amministratore` dal menù a tendina.
-6. Copia e incolla nella finestra di Powershell il comando seguente:
+1. Install PowerShell 7 or later from the [Microsoft Store](https://www.microsoft.com/store/apps/9MZ1SNWT0N5D) or from [GitHub](https://learn.microsoft.com/powershell/scripting/install/install-powershell-on-windows?view=powershell-7.5#msi).
+2. Press the `Windows` key on your keyboard or open Windows Search.
+3. Type `PowerShell` in the search field.
+4. Right-click `PowerShell`.
+5. Click `Run as administrator` from the drop-down menu.
+6. Copy and paste the following command into the PowerShell window:
```powershell
irm https://magnetarman.com/WinToolkit | iex
```
-### 👨💻 Per i beta-tester
+### 👨💻 For Beta Testers
> [!CAUTION]
-> Avviare le versioni in fase di sviluppo è **rischioso e potrebbe causare danni al tuo sistema.** Sono presenti funzionalità in corso di sviluppo e/o in fase di test. Se non sei sicuro o non sai cosa stai facendo, vai alla sezione "Esecuzione consigliata".
+> Running development versions is **risky and may damage your system.** These builds include features that are still under development and/or testing. If you are unsure or do not know what you are doing, use the recommended execution path.
```powershell
irm https://magnetarman.com/winstart-dev | iex
```
-## 🪟 GUI - Interfaccia grafica
+## 🪟 GUI - Graphical Interface
> [!CAUTION]
-> La Versione GUI è disponibile in versione ALPHA, pertanto potrà subire modifiche anche significative. Il funzionamento di questa versione è fortemente instabile, utilizzare a proprio rischio e pericolo.
+> The GUI version is available as an ALPHA build, so it may change significantly. This version is highly unstable; use it at your own risk.
```powershell
irm https://magnetarman.com/Wintoolkit-gui | iex
```
+## 🌐 Languages
+
+WinToolkit loads every JSON language file found in the `languages` folder. To add a new language, copy the schema from `en-US.json` or `it-IT.json`, translate the `strings` values, and save it as `xx-XX.json`. The `code` value inside the file must match the file name, for example `fr-FR.json` with `"code": "fr-FR"`.
+
---
-## 👾 Componenti
-
-- **Sezione Windows**:
- - **Windows Repair Toolkit**: Avvia una sequenza automatizzata di comandi standard di Windows, come: sfc, chkdsk e DISM per individuare e correggere la corruzione dei file di sistema e i problemi del disco.
- - **Windows Update Reset**: Risolve in modo efficiente i problemi comuni di Windows Update resettando i componenti chiave e ripristinando le impostazioni dei servizi.
-- **Sezione Office**:
- - **Install Office**: Consente di installare una versione "Basic" di Microsoft Office in modo semi-automatico.
- - **Repair Office**: Ripara le installazioni esistenti offrendo la modalità rapida offline o completa online.
- - **Uninstall Office**: Rimuove completamente la suite dal sistema utilizzando l'efficace strumento ufficiale "GetHelpCMD" (EX saRA).
-- **Windows Store Repair**: Esegue una reinstallazione di componenti critici come: Microsoft Store, WinGet, e UniGet UI (Utile per aggiornare e gestire le app in modo grafico utilizzando WinGet).
-- **Win Backup Driver**: Semplifica il processo di backup dei driver, che automatizza l'esportazione di tutti i driver di terze parti installati utilizzando il comando DISM per garantire un'operazione completa e affidabile.
-- **Cleaner Toolkit**: Libera spazio su disco e ottimizzare le prestazioni eseguendo una pulizia profonda.
-- **Video Driver Install**: Semplifica l'installazione, l'aggiornamento, la reinstallazione e la configurazione ottimale dei driver video (GPU) per sistemi NVIDIA e AMD, gestendo anche la pulizia precedente e bloccando gli aggiornamenti automatici dei driver da Windows Update, spesso fonte di instabilità.
-
-> [!Note]
+## 👾 Components
+
+- **Windows section**:
+ - **Windows Repair Toolkit**: Runs an automated sequence of standard Windows commands, such as SFC, CHKDSK, and DISM, to detect and repair system file corruption and disk issues.
+ - **Windows Update Reset**: Efficiently fixes common Windows Update issues by resetting key components and restoring service settings.
+- **Office section**:
+ - **Install Office**: Lets you install a "Basic" Microsoft Office version semi-automatically.
+ - **Repair Office**: Repairs existing installations with either quick offline mode or full online mode.
+ - **Uninstall Office**: Fully removes the suite from the system by using the official "GetHelpCMD" tool (formerly SaRA).
+- **Windows Store Repair**: Reinstalls critical components such as Microsoft Store, WinGet, and UniGet UI, which is useful for updating and managing apps graphically through WinGet.
+- **Win Backup Driver**: Simplifies driver backup by automating the export of all installed third-party drivers through DISM for a complete and reliable operation.
+- **Cleaner Toolkit**: Frees disk space and optimizes performance through deep cleanup.
+- **Video Driver Install**: Simplifies installation, updates, reinstallation, and optimal configuration of GPU drivers for NVIDIA and AMD systems. It also handles previous-driver cleanup and blocks automatic driver updates from Windows Update, which are often a source of instability.
+
+> [!NOTE]
>
-> Esegui lo script. Al riavvio del computer, il sistema entrerà automaticamente in **Modalità Provvisoria**.
+> Run the script. After the computer restarts, the system will automatically enter **Safe Mode**.
>
-> Una volta terminate le tue operazioni, come la pulizia dei driver obsoleti tramite DDU, troverai un file chiamato "Switch To Normal Mode.bat" sul tuo Desktop. Per tornare alla modalità di avvio standard di Windows, fai doppio click su questo file e poi riavvia normalmente il computer.
+> Once you have finished your work, such as removing obsolete drivers with DDU, you will find a file named "Switch To Normal Mode.bat" on your desktop. To return to the standard Windows boot mode, double-click this file and restart the computer normally.
-- **Gaming Toolkit**: Progettato per ottimizzare rapidamente il tuo PC Windows per le massime prestazioni di gioco. Si occupa di installare tutti i componenti essenziali (come DirectX, .NET e Visual C++ redistributables), installa i client di gioco più comuni (Steam, Epic, GOG, ecc.), attiva il profilo energetico 'Performance Massime' e disattiva le interruzioni con la modalità "Non disturbare". Essenzialmente, prepara il tuo sistema per giocare senza distrazioni e con la massima potenza.
+- **Gaming Toolkit**: Designed to quickly optimize your Windows PC for maximum gaming performance. It installs essential components such as DirectX, .NET, and Visual C++ Redistributables; installs the most common game clients such as Steam, Epic, and GOG; enables the "Ultimate Performance" power plan; and disables interruptions with "Do not disturb" mode. In short, it prepares your system for distraction-free gaming at full power.
-> [!Note]
+> [!NOTE]
>
-> In Windows 11 22H2 o inferiori, WinToolkit consiglierà di effettuare prima la funzione di riparazione di WinGet.
+> On Windows 11 22H2 or earlier, WinToolkit will recommend running the WinGet repair function first.
-- **BitLocker Toolkit**: Avvia un processo automatizzato per disattivare la crittografia BitLocker sul drive di sistema (C:). Lo strumento verifica lo stato attuale e, se attivo, esegue il comando per avviare la decrittografia del volume in modo controllato. Infine aggiunge al registro di sistema una voce per cercare di contrastare future e possibili ri-attivazione occulte da parte di Microsoft.
+- **BitLocker Toolkit**: Starts an automated process to disable BitLocker encryption on the system drive (C:). The tool checks the current state and, if BitLocker is active, runs the command to start controlled volume decryption. It also adds a registry entry to help counter possible hidden future reactivation attempts by Microsoft.
---
## 📌 Changelog
-- 📄 **[Changelog.md - Leggi le modifiche introdotte.](/CHANGELOG.md)**
+- 📄 **[Changelog.md - Read the introduced changes.](/CHANGELOG.md)**
---
-## 📽️ Parlano di WinToolkit
+## 📽️ WinToolkit In The Media
-| Img Canale | Link |
-| :-------------------------------------------------------------------- | :---------------------------------------------------------------------------------------------------------------------------- |
-| | WINTOOLKIT 2.5: Dominare Windows 11 con PowerShell by MagnetarMan |
+| Channel Image | Link |
+| :----------------------------------------------------------------------- | :---------------------------------------------------------------------------------------------------------------------------- |
+| | WINTOOLKIT 2.5: Dominare Windows 11 con PowerShell by MagnetarMan |
---
-## 💀 Dove tutto è iniziato (ver. 1.0)
+## 💀 Where It All Started (ver. 1.0)
| | |
| :----------------------------------------------------------: | :-------------------------------------------------------------------------------: |
-|
|
|
+|
|
|
---
-## 🤔 F.A.Q. - Domande Frequenti
+## 🤔 F.A.Q. - Frequently Asked Questions
-### Perché avviare WinToolkit?
+### Why Run WinToolkit?
-Sia che tu stia gestendo un parco macchine aziendale o che tu voglia semplicemente mantenere il tuo PC personale in perfette condizioni, WinToolkit ti permette di:
+Whether you manage a company fleet or simply want to keep your personal PC in perfect shape, WinToolkit lets you:
-- **risparmiare tempo**: automatizza ore di lavoro manuale di diagnostica e riparazione.
-- **prevenire malfunzionamenti**: esegui manutenzioni preventive per evitare problemi futuri.
-- **agire da esperto**: sfrutta la potenza degli strumenti ufficiali Microsoft di sistema con un'interfaccia semplice e sicura.
+- **save time**: automates hours of manual diagnostic and repair work.
+- **prevent malfunctions**: performs preventive maintenance to avoid future issues.
+- **act like an expert**: uses the power of official Microsoft system tools through a simple and safe interface.
-### Perché servono almeno 50 GB di spazio libero sul disco?
+### Why Is At Least 50 GB Of Free Disk Space Required?
-I 50 GB non servono allo strumento che pesa pochi KB, né ai download, ma solo a Windows per mantenere stabilità e funzionare correttamente durante le riparazioni.
+The 50 GB is not required by the tool itself, which is only a few KB, nor by its downloads. It is required by Windows to remain stable and work correctly during repairs.
-Quando il sistema operativo lavora su componenti critici, ha bisogno di spazio vitale per gestire diversi processi in background:
+When the operating system works on critical components, it needs breathing room to handle several background processes:
-- File temporanei e backup interni: Windows crea e gestisce file temporanei, copie di backup interne e cache durante la manutenzione.
-- Gestione del file di paging (memoria virtuale): Lo spazio è cruciale per il file di paging, che Windows utilizza come "sostituto" temporaneo della RAM quando la memoria fisica si esaurisce. Se questo spazio è insufficiente, si possono verificare gravi errori di sistema.
-- Prevenzione di malfunzionamenti: Operare con poco spazio libero (tipicamente meno del 10–15% dello spazio totale) è una causa comune di rallentamenti e malfunzionamenti generici in Windows. Avere un margine così ampio previene questi problemi e assicura che il sistema non diventi instabile.
+- Temporary files and internal backups: Windows creates and manages temporary files, internal backup copies, and caches during maintenance.
+- Page file management (virtual memory): disk space is crucial for the page file, which Windows uses as a temporary RAM substitute when physical memory runs out. If this space is insufficient, severe system errors may occur.
+- Malfunction prevention: operating with little free space, typically less than 10-15% of total capacity, is a common cause of slowdowns and generic Windows malfunctions. Keeping this large margin helps prevent those problems and ensures the system does not become unstable.
-In sintesi, i 50 GB sono una misura cautelativa per fornire a Windows l'ambiente di lavoro ideale e completare le operazioni senza interruzioni o errori dovuti alla gestione inefficiente dello spazio su disco.
+In short, 50 GB is a precautionary measure that gives Windows the ideal working environment and lets operations complete without interruptions or errors caused by inefficient disk space management.
-### Dov'è la cartella di lavoro di WinToolkit?
+### Where Is The WinToolkit Working Folder?
-Il percorso della cartella di lavoro di WinToolkit è:
+The WinToolkit working folder is:
`%localappdata%\WinToolkit`
-### Dove sono i file di log?
+### Where Are The Log Files?
-Il percorso dove si trovano i file di log di WinToolkit è:
+The WinToolkit log files are located at:
`%localappdata%\WinToolkit\logs`
---
-## 💖 Supporta il progetto WinToolkit!
+## 💖 Support The WinToolkit Project!
-Se WinToolkit ti è stato utile, considera di supportare attivamente il progetto attraverso una [donazione](#-fai-una-donazione), oppure puoi [contribuire](#-contribuisci).
+If WinToolkit has helped you, consider actively supporting the project through a [donation](#-make-a-donation), or you can [contribute](#-contribute).
-### 👛 Fai una donazione
+### 👛 Make A Donation
-La tua donazione non è solo un ringraziamento, ma un investimento diretto nel futuro e nello sviluppo di questo strumento.
+Your donation is not only a thank-you, but a direct investment in the future and development of this tool.
-Per effettuare una donazione, scopri come cliccand sul pulsante Sponsor in alto a destra.
+To make a donation, click the Sponsor button in the top-right corner to learn how.
-🚀 Sviluppo continuo: le donazioni mi permettono di dedicare più tempo e risorse per mantenere la versione attuale aggiornata e compatibile, e per implementare nuove e potenti funzionalità.
+🚀 Continuous development: donations allow me to dedicate more time and resources to keeping the current version updated and compatible, and to implementing powerful new features.
-🏆 Entra nella Hall of Fame: ogni donatore verrà incluso in una nuova sezione dedicata all'interno della lista dei contributori come ringraziamento per il tuo prezioso supporto.
+🏆 Join the Hall of Fame: every donor will be included in a new dedicated section inside the contributors list as thanks for your valuable support.
---
-## 🏗️ Architettura e sviluppo
+## 🏗️ Architecture And Development
> [!NOTE]
-> Questa sezione è rivolta a **contributor e utenti avanzati** che vogliono capire come funziona WinToolkit internamente, come modificarlo o come testare le proprie modifiche prima di aprire una PR.
+> This section is for **contributors and advanced users** who want to understand how WinToolkit works internally, how to modify it, or how to test changes before opening a PR.
-WinToolkit usa un sistema di build personalizzato: i sorgenti risiedono nel branch **`Dev`** e vengono compilati automaticamente dalla pipeline CI in un unico file distribuibile su **`main`**. Gli utenti finali clonano `main` e ottengono il toolkit pronto all'uso; i contributor lavorano su `Dev`.
+WinToolkit uses a custom build system: sources live on the **`Dev`** branch and are automatically compiled by the CI pipeline into a single distributable file on **`main`**. End users clone `main` and get the toolkit ready to use; contributors work on `Dev`.
-Per orientarsi nell'architettura del progetto — flusso `Dev` → `main`, compilatore, struttura dei moduli, pipeline CI/CD e istruzioni per testare localmente — leggi:
+To understand the project architecture, including the `Dev` → `main` flow, compiler, module structure, CI/CD pipeline, and local testing instructions, read:
📄 **[ARCHITECTURE.md](.github/Docs/ARCHITECTURE.md)**
---
-### 🔰 Contribuisci
+### 🔰 Contribute
-Se non puoi donare, puoi comunque aiutarmi a migliorare WinToolkit attraverso queste azioni:
+If you cannot donate, you can still help me improve WinToolkit through these actions:
-⭐ **Metti una stella**: mettendo una stella al progetto lo farai diventare più popolare su GitHub.
+⭐ **Star the project**: starring the project helps it become more visible on GitHub.
> [!WARNING]
-> Prima di effettuare Pull Request o aprire issue [PER FAVORE LEGGI ATTENTAMENTE LA GUIDA](https://github.com/Magnetarman/WinToolkit/blob/Dev/.github/Docs/PR_And_PullRequest.md).
+> Before opening pull requests or issues, [PLEASE READ THE GUIDE CAREFULLY](https://github.com/Magnetarman/WinToolkit/blob/Dev/.github/Docs/PR_And_PullRequest.md).
-🐛 **[Segnala un problema](https://github.com/Magnetarman/WinToolkit/issues)**: segnala un bug che hai trovato o richiedi nuove funzionalità.
+🐛 **[Report an issue](https://github.com/Magnetarman/WinToolkit/issues)**: report a bug you found or request new features.
-💡 **[Invia Pull Request](https://github.com/Magnetarman/WinToolkit/pulls)**: invia la tua risoluzione di un bug o la tua aggiunta di una nuova funzionalità.
+💡 **[Submit a pull request](https://github.com/Magnetarman/WinToolkit/pulls)**: submit your bug fix or your new feature.
-💬 **[Partecipa alle Discussioni](https://t.me/GlitchTalkGroup)**: condividi le tue idee, fornisci feedback o fai domande.
+💬 **[Join the Discussions](https://t.me/GlitchTalkGroup)**: share your ideas, provide feedback, or ask questions.
-Grazie di cuore per il tuo supporto!
+Thank you from the heart for your support!
---
-## 🎉 Traguardi
+## 🎉 Milestones
[](https://repostars.dev/?repos=Magnetarman%2FWinToolkit&theme=dark)
---
-## 🎗 Autore
+## 🎗 Author
-Creato con ❤️ da [Magnetarman](https://magnetarman.com/).
+Created with ❤️ by [Magnetarman](https://magnetarman.com/).
diff --git a/WinToolkit-template.ps1 b/WinToolkit-template.ps1
index 4a3f85ec..0448bf61 100644
--- a/WinToolkit-template.ps1
+++ b/WinToolkit-template.ps1
@@ -8,7 +8,7 @@
Autore: MagnetarMan
#>
-param([int]$CountdownSeconds = 30, [switch]$ImportOnly)
+param([int]$CountdownSeconds = 30, [switch]$ImportOnly, [string]$Language = 'en-US')
# ==============================================================================
# SEZIONE 1 · BOOTSTRAP
@@ -94,22 +94,22 @@ $ToolkitVersion = "2.5.4 (Build 47)"
$AppConfig = @{
URLs = @{
- GitHubAssetBaseUrl = "https://raw.githubusercontent.com/Magnetarman/WinToolkit/refs/heads/main/asset/"
- GitHubAssetDevBaseUrl = "https://raw.githubusercontent.com/Magnetarman/WinToolkit/refs/heads/Dev/asset/"
+ GitHubAssetBaseUrl = "https://raw.githubusercontent.com/Magnetarman/WinToolkit/refs/heads/main/assets/"
+ GitHubAssetDevBaseUrl = "https://raw.githubusercontent.com/Magnetarman/WinToolkit/refs/heads/Dev/assets/"
# Office
- OfficeSetup = "https://raw.githubusercontent.com/Magnetarman/WinToolkit/refs/heads/main/asset/Setup.exe"
- OfficeBasicConfig = "https://raw.githubusercontent.com/Magnetarman/WinToolkit/refs/heads/main/asset/Basic.xml"
+ OfficeSetup = "https://raw.githubusercontent.com/Magnetarman/WinToolkit/refs/heads/main/assets/Setup.exe"
+ OfficeBasicConfig = "https://raw.githubusercontent.com/Magnetarman/WinToolkit/refs/heads/main/assets/Basic.xml"
GetHelpInstaller = "https://aka.ms/SaRA_EnterpriseVersionFiles"
# Video Driver
AMDInstaller = "https://drivers.amd.com/drivers/installer/26.10/whql/amd-software-adrenalin-edition-26.5.2-minimalsetup-260513_web.exe"
- NVCleanstall = "https://raw.githubusercontent.com/Magnetarman/WinToolkit/refs/heads/main/asset/NVCleanstall_1.19.0.exe"
- DDUZip = "https://raw.githubusercontent.com/Magnetarman/WinToolkit/refs/heads/main/asset/DDU.zip"
- DriverOverridesJson = "https://raw.githubusercontent.com/Magnetarman/WinToolkit/refs/heads/Dev/asset/DriverOverrides.json"
+ NVCleanstall = "https://raw.githubusercontent.com/Magnetarman/WinToolkit/refs/heads/main/assets/NVCleanstall_1.19.0.exe"
+ DDUZip = "https://raw.githubusercontent.com/Magnetarman/WinToolkit/refs/heads/main/assets/DDU.zip"
+ DriverOverridesJson = "https://raw.githubusercontent.com/Magnetarman/WinToolkit/refs/heads/Dev/assets/DriverOverrides.json"
# Gaming
- DirectXWebSetup = "https://raw.githubusercontent.com/Magnetarman/WinToolkit/refs/heads/main/asset/dxwebsetup.exe"
+ DirectXWebSetup = "https://raw.githubusercontent.com/Magnetarman/WinToolkit/refs/heads/main/assets/dxwebsetup.exe"
BattleNetInstaller = "https://downloader.battle.net/download/getInstallerForGame?os=win&gameProgram=BATTLENET_APP&version=Live"
# 7-Zip
@@ -175,6 +175,282 @@ $Global:MsgStyles = @{
}
$Global:ExecutionLog = @()
$Global:NeedsFinalReboot = $false
+$Global:ToolkitLanguage = 'en-US'
+$Global:ToolkitLanguageData = $null
+$Global:ToolkitDefaultLanguageData = $null
+
+function Get-ToolkitLanguageDirectory {
+ $candidate = Join-Path $PSScriptRoot 'languages'
+ if (Test-Path $candidate) { return $candidate }
+
+ $repoCandidate = Join-Path (Get-Location) 'languages'
+ if (Test-Path $repoCandidate) { return $repoCandidate }
+
+ return $candidate
+}
+
+function Get-AvailableToolkitLanguages {
+ $languageDir = Get-ToolkitLanguageDirectory
+ if (-not (Test-Path $languageDir)) { return @() }
+
+ Get-ChildItem -Path $languageDir -File -ErrorAction SilentlyContinue |
+ Where-Object { $_.Extension -eq '.json' } |
+ ForEach-Object {
+ try {
+ $data = Get-Content -LiteralPath $_.FullName -Raw -Encoding UTF8 | ConvertFrom-Json
+ [pscustomobject]@{
+ Code = $data.code
+ Name = $data.name
+ NativeName = $data.nativeName
+ Path = $_.FullName
+ }
+ }
+ catch {
+ Write-Verbose "Invalid language file '$($_.FullName)': $($_.Exception.Message)"
+ }
+ } | Sort-Object Code
+}
+
+function Import-ToolkitLanguageFile {
+ param([string]$LanguageCode)
+
+ $languageDir = Get-ToolkitLanguageDirectory
+ $languageFile = @(
+ (Join-Path $languageDir "$LanguageCode.json")
+ ) | Where-Object { Test-Path $_ } | Select-Object -First 1
+ if (-not $languageFile) { return $null }
+
+ return (Get-Content -LiteralPath $languageFile -Raw -Encoding UTF8 | ConvertFrom-Json)
+}
+
+function Set-ToolkitLanguage {
+ param([string]$LanguageCode = 'en-US')
+
+ $defaultData = Import-ToolkitLanguageFile -LanguageCode 'en-US'
+ if ($defaultData) { $Global:ToolkitDefaultLanguageData = $defaultData }
+
+ $languageData = Import-ToolkitLanguageFile -LanguageCode $LanguageCode
+ if (-not $languageData) {
+ $LanguageCode = 'en-US'
+ $languageData = $defaultData
+ }
+
+ if ($languageData) {
+ $Global:ToolkitLanguage = $LanguageCode
+ $Global:ToolkitLanguageData = $languageData
+ }
+}
+
+function Get-Loc {
+ param(
+ [Parameter(Mandatory = $true)][string]$Key,
+ [object[]]$Args = @()
+ )
+
+ $value = $null
+ if ($Global:ToolkitLanguageData -and $Global:ToolkitLanguageData.strings.PSObject.Properties.Name -contains $Key) {
+ $value = [string]$Global:ToolkitLanguageData.strings.$Key
+ }
+ elseif ($Global:ToolkitDefaultLanguageData -and $Global:ToolkitDefaultLanguageData.strings.PSObject.Properties.Name -contains $Key) {
+ $value = [string]$Global:ToolkitDefaultLanguageData.strings.$Key
+ }
+ else {
+ $value = $Key
+ }
+
+ if ($Args -and $Args.Count -gt 0) { return [string]::Format($value, $Args) }
+ return $value
+}
+
+function Convert-ToolkitSourceText {
+ param([string]$Text)
+
+ if ([string]::IsNullOrWhiteSpace($Text) -or $Global:ToolkitLanguage -eq 'it-IT') { return $Text }
+
+ $translated = $Text
+ $replacements = [ordered]@{
+ 'Errore durante' = 'Error during'
+ 'Errore critico' = 'Critical error'
+ 'Errore imprevisto' = 'Unexpected error'
+ 'Errore sconosciuto' = 'Unknown error'
+ 'Avvio installazione' = 'Starting installation'
+ 'Avvio riparazione' = 'Starting repair'
+ 'Avvio rimozione' = 'Starting removal'
+ 'Avvio processo' = 'Starting process'
+ 'Avvio servizio' = 'Starting service'
+ 'Avvio ' = 'Starting '
+ 'Attesa avvio' = 'Waiting for startup'
+ 'Caricamento moduli' = 'Loading modules'
+ 'Installazione' = 'Installation'
+ 'Installato' = 'Installed'
+ 'Disinstallazione' = 'Uninstallation'
+ 'Riparazione' = 'Repair'
+ 'Rimozione' = 'Removal'
+ 'Pulizia' = 'Cleanup'
+ 'Eliminazione' = 'Deleting'
+ 'Verifica' = 'Checking'
+ 'Validazione' = 'Validation'
+ 'Rilevamento' = 'Detecting'
+ 'Configurazione' = 'Configuration'
+ 'Abilitazione' = 'Enabling'
+ 'Riabilitazione' = 'Re-enabling'
+ 'Aggiornamento' = 'Updating'
+ 'Ripristino' = 'Restoring'
+ 'Reinstallazione' = 'Reinstallation'
+ 'Preparazione' = 'Preparing'
+ 'Estrazione' = 'Extracting'
+ 'Ricerca' = 'Searching'
+ 'Arresto' = 'Stopping'
+ 'Riavvio del sistema' = 'System restart'
+ 'Riavvio individuale soppresso' = 'Individual restart suppressed'
+ 'Verrà gestito un riavvio finale' = 'A final restart will be handled'
+ 'Riavvio non necessario' = 'Restart not required'
+ 'Riavvio necessario' = 'Restart required'
+ 'Riavvio sistema' = 'System restart'
+ 'Riavvio in' = 'Restart in'
+ ' tra ' = ' in '
+ 'Premi un tasto per continuare' = 'Press any key to continue'
+ 'Premere un tasto per uscire' = 'Press any key to exit'
+ 'Premi un tasto qualsiasi per annullare' = 'Press any key to cancel'
+ 'Completato' = 'Completed'
+ 'completata' = 'completed'
+ 'completati' = 'completed'
+ 'terminato' = 'finished'
+ 'fallito' = 'failed'
+ 'fallita' = 'failed'
+ 'falliti' = 'failed'
+ 'annullata' = 'cancelled'
+ 'annullato' = 'cancelled'
+ 'già presente' = 'already present'
+ 'non presente' = 'not present'
+ 'non trovato' = 'not found'
+ 'non trovata' = 'not found'
+ 'Nessuna riparazione necessaria' = 'No repair required'
+ 'non disponibile' = 'not available'
+ 'non accessibili' = 'not accessible'
+ 'Impossibile' = 'Unable to'
+ 'Avviso' = 'Warning'
+ 'Attenzione' = 'Warning'
+ 'Suggerimento' = 'Tip'
+ 'Nota' = 'Note'
+ 'Trovati' = 'Found'
+ 'Rimosso' = 'Removed'
+ 'Rimossi' = 'Removed'
+ 'rimosse' = 'removed'
+ 'eliminata' = 'deleted'
+ 'eliminati' = 'deleted'
+ 'eliminato' = 'deleted'
+ 'rilevata' = 'detected'
+ 'rilevato' = 'detected'
+ 'scaricato' = 'downloaded'
+ 'scaricata' = 'downloaded'
+ 'scaricare' = 'download'
+ 'scarica' = 'download'
+ 'creato' = 'created'
+ 'creata' = 'created'
+ 'attivato' = 'enabled'
+ 'attiva' = 'enabled'
+ 'abilitato' = 'enabled'
+ 'abilitati' = 'enabled'
+ 'configurato' = 'configured'
+ 'configurata' = 'configured'
+ 'ripristinate' = 'restored'
+ 'ripristinato' = 'restored'
+ 'reimpostato' = 'reset'
+ 'avviato' = 'started'
+ 'arrestato' = 'stopped'
+ 'in uso' = 'in use'
+ 'in corso' = 'in progress'
+ 'può richiedere alcuni minuti' = 'may take a few minutes'
+ 'può impiegare 1-2 minuti' = 'may take 1-2 minutes'
+ 'cartella' = 'folder'
+ 'cartelle' = 'folders'
+ 'Directory' = 'Directory'
+ 'chiavi registro' = 'registry keys'
+ 'chiave di registro' = 'registry key'
+ 'registro' = 'registry'
+ 'collegamenti' = 'shortcuts'
+ 'attività' = 'tasks'
+ 'attività pianificate' = 'scheduled tasks'
+ 'criteri di gruppo' = 'group policies'
+ 'criteri locali' = 'local policies'
+ 'Criteri' = 'Policies'
+ 'sorgenti' = 'sources'
+ 'pacchetto' = 'package'
+ 'pacchetti' = 'packages'
+ 'Versione' = 'Version'
+ 'configurazione GPU' = 'GPU configuration'
+ 'GPU rilevata' = 'Detected GPU'
+ 'GPU non rilevata' = 'GPU not detected'
+ 'driver non disponibile per l''installazione automatica' = 'driver not available for automatic installation'
+ 'modalità provvisoria' = 'Safe Mode'
+ 'modalità normale' = 'normal mode'
+ 'prossimo avvio' = 'next boot'
+ 'sistema' = 'system'
+ 'servizio' = 'service'
+ 'servizi' = 'services'
+ 'Stato' = 'Status'
+ 'Avvio:' = 'Startup:'
+ 'codice' = 'code'
+ 'Codice uscita' = 'Exit code'
+ 'Eccezione' = 'Exception'
+ 'Tentativo' = 'Attempt'
+ 'Riepilogo' = 'Summary'
+ 'operazione' = 'operation'
+ 'modifiche' = 'changes'
+ 'valori predefiniti' = 'default values'
+ 'driver video' = 'video driver'
+ 'operazioni pendenti' = 'pending operations'
+ 'operazioni in sospeso' = 'pending operations'
+ 'Questo non è un errore critico' = 'This is not a critical error'
+ 'Proseguo comunque' = 'Continuing anyway'
+ 'Annullamento' = 'Cancelling'
+ 'metodo alternativo' = 'alternative method'
+ 'finestra esterna' = 'external window'
+ 'Attesa completamento' = 'Waiting for completion'
+ 'metodo forzato' = 'forced method'
+ 'file ignorati perché in uso o non accessibili' = 'files skipped because they are in use or not accessible'
+ 'Rilevate operazioni pendenti che richiedono riavvio' = 'Pending operations requiring a restart detected'
+ 'DISM potrebbe fallire' = 'DISM may fail'
+ 'Sistema in salute' = 'System is healthy'
+ 'Riparazione profonda non necessaria' = 'Deep repair not required'
+ 'riparazione profonda' = 'deep repair'
+ 'controllo schedulato al prossimo riavvio' = 'check scheduled at next restart'
+ }
+
+ foreach ($entry in $replacements.GetEnumerator()) {
+ $translated = [regex]::Replace($translated, [regex]::Escape([string]$entry.Key), [string]$entry.Value, [System.Text.RegularExpressions.RegexOptions]::IgnoreCase)
+ }
+
+ return $translated
+}
+
+function Get-ToolkitMenuText {
+ param([object]$Item)
+
+ if ($Item -is [System.Collections.IDictionary]) {
+ if ($Item.Contains('DescriptionKey') -and $Item['DescriptionKey']) {
+ return (Get-Loc $Item['DescriptionKey'])
+ }
+ if ($Item.Contains('CategoryKey') -and $Item['CategoryKey']) {
+ return (Get-Loc $Item['CategoryKey'])
+ }
+ if ($Item.Contains('Description')) { return $Item['Description'] }
+ if ($Item.Contains('Name')) { return $Item['Name'] }
+ }
+
+ if ($Item.PSObject.Properties.Name -contains 'DescriptionKey' -and $Item.DescriptionKey) {
+ return (Get-Loc $Item.DescriptionKey)
+ }
+ if ($Item.PSObject.Properties.Name -contains 'CategoryKey' -and $Item.CategoryKey) {
+ return (Get-Loc $Item.CategoryKey)
+ }
+ if ($Item.PSObject.Properties.Name -contains 'Description') { return $Item.Description }
+ if ($Item.PSObject.Properties.Name -contains 'Name') { return $Item.Name }
+ return [string]$Item
+}
+
+Set-ToolkitLanguage -LanguageCode $Language
# ==============================================================================
@@ -211,12 +487,13 @@ function Write-StyledMessage {
)
$style = $Global:MsgStyles[$Type]
$timestamp = Get-Date -Format "HH:mm:ss"
- Write-Host "[$timestamp] $($style.Icon) $Text" -ForegroundColor $style.Color -NoNewline:$NoNewline
+ $displayText = Convert-ToolkitSourceText -Text $Text
+ Write-Host "[$timestamp] $($style.Icon) $displayText" -ForegroundColor $style.Color -NoNewline:$NoNewline
$logLevel = switch ($Type) {
'Success' { 'SUCCESS' } 'Warning' { 'WARNING' } 'Error' { 'ERROR' } default { 'INFO' }
}
- Write-ToolkitLog -Level $logLevel -Message $Text
+ Write-ToolkitLog -Level $logLevel -Message $displayText
}
function Show-ProgressBar {
@@ -229,8 +506,10 @@ function Show-ProgressBar {
$filled = '█' * [math]::Floor($safePercent * 30 / 100)
$empty = '░' * (30 - $filled.Length)
$bar = "[$filled$empty] {0,3}%" -f $safePercent
+ $displayActivity = Convert-ToolkitSourceText -Text $Activity
+ $displayStatus = Convert-ToolkitSourceText -Text $Status
if (-not $Global:GuiSessionActive) {
- Write-Host "`r$Spinner $Icon $Activity $bar $Status" -NoNewline -ForegroundColor $Color
+ Write-Host "`r$Spinner $Icon $displayActivity $bar $displayStatus" -NoNewline -ForegroundColor $Color
if ($Percent -ge 100) { Write-Host '' }
}
}
@@ -496,13 +775,40 @@ function Get-SystemInfo {
catch { return $null }
}
+function Convert-BitlockerStatusToKey {
+ param([string]$StatusText)
+
+ if ([string]::IsNullOrWhiteSpace($StatusText)) { return 'bitlocker.status.notConfigured' }
+
+ $normalized = $StatusText.Trim().ToLowerInvariant()
+ if ($normalized -match 'decritt|decrypt') { return 'bitlocker.status.decrypting' }
+ if ($normalized -match 'crittografia in corso|encrypt') { return 'bitlocker.status.encrypting' }
+ if ($normalized -match 'sospes|suspend') { return 'bitlocker.status.suspended' }
+ if ($normalized -match 'non configur|not configured') { return 'bitlocker.status.notConfigured' }
+ if ($normalized -match 'disattiv|protection off|off|disabled') { return 'bitlocker.status.off' }
+ if ($normalized -match 'attiv|protection on|on|enabled') { return 'bitlocker.status.on' }
+
+ return 'bitlocker.status.unknown'
+}
+
function Get-BitlockerStatus {
+ param([switch]$Key)
+
try {
$out = & manage-bde -status C: 2>&1
- if ($out -match "Stato protezione:\s*(.*)") { return $matches[1].Trim() }
- return "Non configurato"
+ $statusText = $null
+ if ($out -match "(?im)^\s*(Stato protezione|Protection Status):\s*(.*)$") {
+ $statusText = $matches[2].Trim()
+ }
+
+ $statusKey = Convert-BitlockerStatusToKey -StatusText $statusText
+ if ($Key) { return $statusKey }
+ return (Get-Loc $statusKey)
+ }
+ catch {
+ if ($Key) { return 'bitlocker.status.off' }
+ return (Get-Loc 'bitlocker.status.off')
}
- catch { return "Disattivato" }
}
@@ -1748,11 +2054,11 @@ function Test-WindowsUpdateStatus {
Write-Host (Center-Text "⚠️ AVVISO IMPORTANTE ⚠️") -ForegroundColor Yellow
Write-Host ""
Write-Host " Sono stati rilevati aggiornamenti di sistema pendenti:" -ForegroundColor Yellow
- if ($pendingReboot) { Write-Host " ✓ Riavvio del sistema richiesto per completare aggiornamenti" -ForegroundColor Yellow }
- if ($installerRunning) { Write-Host " ✓ Servizio installazione aggiornamenti Windows in corso" -ForegroundColor Yellow }
+ if ($pendingReboot) { Write-Host " ✓ System restart required to complete updates" -ForegroundColor Yellow }
+ if ($installerRunning) { Write-Host " ✓ Windows update installation service is running" -ForegroundColor Yellow }
Write-Host ""
Write-Host " Questo potrebbe causare malfunzionamenti, errori o comportamenti" -ForegroundColor Yellow
- Write-Host " imprevisti in alcune o tutte le funzionalità di WinToolkit." -ForegroundColor Yellow
+ Write-Host " unexpected behavior in some or all WinToolkit features." -ForegroundColor Yellow
Write-Host ""
Write-Host (Center-Text "⚠️ PROCEDERE CON CAUTELA ⚠️") -ForegroundColor Red
Write-Host ""
@@ -1852,7 +2158,7 @@ function VcardAnalizer {
[string]$OverridesPath
)
- $assetCacheDir = Join-Path $AppConfig.Paths.Root 'asset'
+ $assetCacheDir = Join-Path $AppConfig.Paths.Root 'assets'
if (-not (Test-Path $assetCacheDir)) {
$null = New-Item -Path $assetCacheDir -ItemType Directory -Force
}
@@ -1978,7 +2284,7 @@ function VcardAnalizer {
# ==============================================================================
# SEZIONE 12 · PLACEHOLDER COMPILATORE
-# Stub vuoti sostituiti dal compiler.ps1 con i contenuti di /tool/*.ps1.
+# Stub vuoti sostituiti dal compiler.ps1 con i contenuti di /tools/*.ps1.
# Ordine: Windows → Office → Driver/Gaming → Supporto (segue $menuStructure).
# ==============================================================================
@@ -1991,6 +2297,7 @@ function WinDriverInstall {}
function WinDebloat {}
function WinCleaner {}
function DisableBitlocker {}
+function WinDeleteUserProfiles {}
# Office
function Install-Office {}
@@ -2012,29 +2319,30 @@ function WinExportLog {}
# ==============================================================================
$menuStructure = @(
- @{ 'Name' = 'Windows'; 'Icon' = '🔧'; 'Scripts' = @(
- [pscustomobject]@{Name = 'WinRepairToolkit'; Description = 'Riparazione Windows'; Action = 'RunFunction' },
- [pscustomobject]@{Name = 'WinUpdateReset'; Description = 'Reset Windows Update'; Action = 'RunFunction' },
- [pscustomobject]@{Name = 'WinReinstallStore'; Description = 'Winget/WinStore Reset'; Action = 'RunFunction' },
- [pscustomobject]@{Name = 'WinBackupDriver'; Description = 'Backup Driver PC'; Action = 'RunFunction' },
- [pscustomobject]@{Name = 'WinCleaner'; Description = 'Pulizia File Temporanei'; Action = 'RunFunction' },
- [pscustomobject]@{Name = 'DisableBitlocker'; Description = 'Disabilita Bitlocker'; Action = 'RunFunction' }
+ @{ 'Name' = 'Windows'; 'CategoryKey' = 'category.windows'; 'Icon' = '🔧'; 'Scripts' = @(
+ [pscustomobject]@{Name = 'WinRepairToolkit'; Description = 'Riparazione Windows'; DescriptionKey = 'script.WinRepairToolkit'; Action = 'RunFunction' },
+ [pscustomobject]@{Name = 'WinUpdateReset'; Description = 'Reset Windows Update'; DescriptionKey = 'script.WinUpdateReset'; Action = 'RunFunction' },
+ [pscustomobject]@{Name = 'WinReinstallStore'; Description = 'Winget/WinStore Reset'; DescriptionKey = 'script.WinReinstallStore'; Action = 'RunFunction' },
+ [pscustomobject]@{Name = 'WinBackupDriver'; Description = 'Backup Driver PC'; DescriptionKey = 'script.WinBackupDriver'; Action = 'RunFunction' },
+ [pscustomobject]@{Name = 'WinCleaner'; Description = 'Pulizia File Temporanei'; DescriptionKey = 'script.WinCleaner'; Action = 'RunFunction' },
+ [pscustomobject]@{Name = 'DisableBitlocker'; Description = 'Disabilita Bitlocker'; DescriptionKey = 'script.DisableBitlocker'; Action = 'RunFunction' },
+ [pscustomobject]@{Name = 'WinDeleteUserProfiles'; Description = 'Cancella profili utenti di Windows'; DescriptionKey = 'script.WinDeleteUserProfiles'; Action = 'RunFunction' }
)
},
- @{ 'Name' = 'Office'; 'Icon' = '🏢'; 'Scripts' = @(
- [pscustomobject]@{Name = 'Install-Office'; Description = 'Installa Office Basic'; Action = 'RunFunction' },
- [pscustomobject]@{Name = 'Repair-Office'; Description = 'Ripara Office'; Action = 'RunFunction' },
- [pscustomobject]@{Name = 'Uninstall-Office'; Description = 'Rimuovi Office'; Action = 'RunFunction' }
+ @{ 'Name' = 'Office'; 'CategoryKey' = 'category.office'; 'Icon' = '🏢'; 'Scripts' = @(
+ [pscustomobject]@{Name = 'Install-Office'; Description = 'Installa Office Basic'; DescriptionKey = 'script.Install-Office'; Action = 'RunFunction' },
+ [pscustomobject]@{Name = 'Repair-Office'; Description = 'Ripara Office'; DescriptionKey = 'script.Repair-Office'; Action = 'RunFunction' },
+ [pscustomobject]@{Name = 'Uninstall-Office'; Description = 'Rimuovi Office'; DescriptionKey = 'script.Uninstall-Office'; Action = 'RunFunction' }
)
},
- @{ 'Name' = 'Driver & Gaming'; 'Icon' = '🎮'; 'Scripts' = @(
- [pscustomobject]@{Name = 'AutoVideoDriverInstall'; Description = 'Auto Install Driver Video [Nvidia-AMD]'; Action = 'RunFunction' },
- [pscustomobject]@{Name = 'VideoDriverReinstall'; Description = 'Reinstalla Driver Video'; Action = 'RunFunction' },
- [pscustomobject]@{Name = 'GamingToolkit'; Description = 'Gaming Toolkit'; Action = 'RunFunction' }
+ @{ 'Name' = 'Driver & Gaming'; 'CategoryKey' = 'category.driverGaming'; 'Icon' = '🎮'; 'Scripts' = @(
+ [pscustomobject]@{Name = 'AutoVideoDriverInstall'; Description = 'Auto Install Driver Video [Nvidia-AMD]'; DescriptionKey = 'script.AutoVideoDriverInstall'; Action = 'RunFunction' },
+ [pscustomobject]@{Name = 'VideoDriverReinstall'; Description = 'Reinstalla Driver Video'; DescriptionKey = 'script.VideoDriverReinstall'; Action = 'RunFunction' },
+ [pscustomobject]@{Name = 'GamingToolkit'; Description = 'Gaming Toolkit'; DescriptionKey = 'script.GamingToolkit'; Action = 'RunFunction' }
)
},
- @{ 'Name' = 'Supporto'; 'Icon' = '🕹️'; 'Scripts' = @(
- [pscustomobject]@{Name = 'WinExportLog'; Description = 'Esporta Log WinToolkit'; Action = 'RunFunction' }
+ @{ 'Name' = 'Supporto'; 'CategoryKey' = 'category.support'; 'Icon' = '🕹️'; 'Scripts' = @(
+ [pscustomobject]@{Name = 'WinExportLog'; Description = 'Esporta Log WinToolkit'; DescriptionKey = 'script.WinExportLog'; Action = 'RunFunction' }
)
}
)
@@ -2061,61 +2369,128 @@ if (-not $ImportOnly) {
if (-not $ImportOnly -and -not $Global:GuiSessionActive) {
Write-Host ""
- Write-StyledMessage -Type 'Info' -Text '💎 WinToolkit avviato in modalità interattiva'
+ Write-StyledMessage -Type 'Info' -Text (Get-Loc 'menu.startedInteractive')
Write-Host ""
- while ($true) {
- Show-Header -SubTitle "Menu Principale"
+ function Confirm-UserProfileDeletion {
+ Write-Host ''
+ Write-StyledMessage -Type 'Error' -Text (Get-Loc 'confirm.profile.warn1')
+ Write-StyledMessage -Type 'Error' -Text (Get-Loc 'confirm.profile.warn2')
+ Write-Host ''
+ Write-Host "💎 [1] $(Get-Loc 'confirm.profile.yes')" -ForegroundColor White
+ Write-Host "[INVIO] $(Get-Loc 'menu.back')" -ForegroundColor Gray
+ $firstConfirm = Microsoft.PowerShell.Utility\Read-Host (Get-Loc 'menu.choice')
+
+ if ($firstConfirm -ne '1') {
+ return $false
+ }
+
+ Write-Host ''
+ Write-StyledMessage -Type 'Error' -Text (Get-Loc 'confirm.profile.sure')
+ Write-Host ''
+ Write-Host "💎 [1] $(Get-Loc 'confirm.profile.accept')" -ForegroundColor White
+ Write-Host "[INVIO] $(Get-Loc 'menu.back')" -ForegroundColor Gray
+ $secondConfirm = Microsoft.PowerShell.Utility\Read-Host (Get-Loc 'menu.choice')
+
+ return ($secondConfirm -eq '1')
+ }
+
+ function Show-LanguageMenu {
+ while ($true) {
+ Show-Header -SubTitle (Get-Loc 'menu.language')
+ Write-Host ''
+ Write-Host "==== 🌐 $(Get-Loc 'menu.chooseLanguage') 🌐 ====" -ForegroundColor Cyan
+ Write-Host ''
+
+ $languages = @(Get-AvailableToolkitLanguages)
+ if ($languages.Count -eq 0) {
+ Write-StyledMessage -Type 'Warning' -Text (Get-Loc 'menu.noLanguages')
+ Start-Sleep -Seconds 2
+ return
+ }
+
+ for ($i = 0; $i -lt $languages.Count; $i++) {
+ $marker = if ($languages[$i].Code -eq $Global:ToolkitLanguage) { '*' } else { ' ' }
+ Write-Host "💎 [$($i + 1)] $marker $($languages[$i].NativeName) ($($languages[$i].Code))" -ForegroundColor White
+ }
+
+ Write-Host ''
+ Write-Host "↩️ [0] $(Get-Loc 'menu.back')" -ForegroundColor Gray
+ Write-Host ''
+
+ $choice = Microsoft.PowerShell.Utility\Read-Host (Get-Loc 'menu.choice')
+ if ([string]::IsNullOrWhiteSpace($choice) -or $choice -eq '0') { return }
+
+ $parsed = 0
+ if ([int]::TryParse($choice, [ref]$parsed) -and $parsed -ge 1 -and $parsed -le $languages.Count) {
+ $selectedLanguage = $languages[$parsed - 1]
+ Set-ToolkitLanguage -LanguageCode $selectedLanguage.Code
+ Write-StyledMessage -Type 'Success' -Text (Get-Loc 'menu.languageChanged' -Args @($selectedLanguage.NativeName))
+ Start-Sleep -Seconds 1
+ return
+ }
+
+ Write-StyledMessage -Type 'Warning' -Text (Get-Loc 'menu.invalidSelection')
+ Start-Sleep -Seconds 1
+ }
+ }
+
+ :MainMenu while ($true) {
+ Show-Header -SubTitle (Get-Loc 'menu.main')
# ── Informazioni di sistema ───────────────────────────────────────────
$width = try { $Host.UI.RawUI.BufferSize.Width } catch { 80 }
Write-Host ''
- Write-Host "==== 💻 INFORMAZIONI DI SISTEMA 💻 ====" -ForegroundColor Cyan
+ Write-Host "==== 💻 $(Get-Loc 'system.infoTitle') 💻 ====" -ForegroundColor Cyan
Write-Host ''
$si = Get-SystemInfo
if ($si) {
$editionIcon = if ($si.ProductName -match "Pro") { "🔧" } else { "💻" }
- Write-Host "💻 Edizione: $editionIcon $($si.ProductName)" -ForegroundColor White
- Write-Host "🆔 Versione: " -NoNewline -ForegroundColor White
+ Write-Host "💻 $(Get-Loc 'system.edition'): $editionIcon $($si.ProductName)" -ForegroundColor White
+ Write-Host "🆔 $(Get-Loc 'system.version'): " -NoNewline -ForegroundColor White
Write-Host "Ver. $($si.DisplayVersion) (Build $($si.BuildNumber))" -ForegroundColor Green
- Write-Host "🔑 Architettura: $($si.Architecture)" -ForegroundColor White
- Write-Host "🔧 Nome PC: $($si.ComputerName)" -ForegroundColor White
+ Write-Host "🔑 $(Get-Loc 'system.architecture'): $($si.Architecture)" -ForegroundColor White
+ Write-Host "🔧 $(Get-Loc 'system.computerName'): $($si.ComputerName)" -ForegroundColor White
Write-Host "🧠 RAM: $($si.TotalRAM) GB" -ForegroundColor White
- Write-Host "💾 Disco: " -NoNewline -ForegroundColor White
+ Write-Host "💾 $(Get-Loc 'system.disk'): " -NoNewline -ForegroundColor White
$diskFreeGB = $si.FreeDisk
- $displayString = "$($si.FreePercentage)% Libero ($($diskFreeGB) GB)"
+ $displayString = "$($si.FreePercentage)% $(Get-Loc 'system.free') ($($diskFreeGB) GB)"
$diskColor = if ($diskFreeGB -lt 50) { "Red" } elseif ($diskFreeGB -le 80) { "Yellow" } else { "Green" }
Write-Host $displayString -ForegroundColor $diskColor -NoNewline
Write-Host ""
- $blStatus = Get-BitlockerStatus
- $blColor = if ($blStatus -match 'Disattivato|Non configurato|Off') { 'Green' } else { 'Red' }
- Write-Host "🔒 Stato Bitlocker: " -NoNewline -ForegroundColor White
+ $blStatusKey = Get-BitlockerStatus -Key
+ $blStatus = Get-Loc $blStatusKey
+ $blColor = if ($blStatusKey -in @('bitlocker.status.off', 'bitlocker.status.notConfigured')) { 'Green' } elseif ($blStatusKey -in @('bitlocker.status.suspended', 'bitlocker.status.decrypting')) { 'Yellow' } else { 'Red' }
+ Write-Host "🔒 $(Get-Loc 'system.bitlockerStatus'): " -NoNewline -ForegroundColor White
Write-Host "$blStatus" -ForegroundColor $blColor
}
Write-Host ('*' * 50) -ForegroundColor Red
Write-Host ""
# ── Voci di menu ─────────────────────────────────────────────────────
- $allScripts = @(); $idx = 1
+ Write-Host "🌐 [1] $(Get-Loc 'menu.changeLanguage')" -ForegroundColor White
+ Write-Host ''
+
+ $allScripts = @(); $idx = 2
foreach ($cat in $menuStructure) {
- Write-Host "==== $($cat.Icon) $($cat.Name) $($cat.Icon) ====" -ForegroundColor Cyan
+ Write-Host "==== $($cat.Icon) $(Get-ToolkitMenuText $cat) $($cat.Icon) ====" -ForegroundColor Cyan
Write-Host ""
foreach ($s in $cat.Scripts) {
$allScripts += $s
- Write-Host "💎 [$idx] $($s.Description)" -ForegroundColor White
+ Write-Host "💎 [$idx] $(Get-ToolkitMenuText $s)" -ForegroundColor White
$idx++
}
Write-Host ""
}
- Write-Host "==== Uscita ====" -ForegroundColor Red
+ Write-Host "==== $(Get-Loc 'menu.exitSection') ====" -ForegroundColor Red
Write-Host ""
- Write-Host "❌ [0] Esci dal Toolkit" -ForegroundColor Red
+ Write-Host "❌ [0] $(Get-Loc 'menu.exitToolkit')" -ForegroundColor Red
Write-Host ""
# ── Input utente ──────────────────────────────────────────────────────
- $rawInput = Microsoft.PowerShell.Utility\Read-Host 'Inserisci uno o più numeri (es: 1 2 3 oppure 1,2,3) per eseguire le operazioni in sequenza'
+ $rawInput = Microsoft.PowerShell.Utility\Read-Host (Get-Loc 'menu.multiPrompt')
# Secret check
if ($rawInput -eq [System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String('V2luZG93cyDDqCB1bmEgbWVyZGE='))) {
@@ -2123,20 +2498,26 @@ if (-not $ImportOnly -and -not $Global:GuiSessionActive) {
continue
}
- $rawSelections = Read-ValidatedChoice -Prompt 'Inserisci uno o più numeri' -Min 0 -Max $allScripts.Count -AllowZero -RawInput $rawInput
+ $maxMenuOption = $allScripts.Count + 1
+ $rawSelections = Read-ValidatedChoice -Prompt (Get-Loc 'menu.multiPromptShort') -Min 0 -Max $maxMenuOption -AllowZero -RawInput $rawInput
$c = if ($rawSelections.Count -gt 0) { $rawSelections[0] } else { '' }
if ($c -eq 0 -or $c -eq '0') {
- Write-StyledMessage -type 'Warning' -text 'Per supporto: Github.com/Magnetarman'
- Write-StyledMessage -type 'Success' -text 'Chiusura in corso...'
+ Write-StyledMessage -type 'Warning' -text (Get-Loc 'menu.support')
+ Write-StyledMessage -type 'Success' -text (Get-Loc 'menu.closing')
Write-ToolkitLog -Level INFO -Message "Sessione WinToolkit terminata dall'utente."
Start-Sleep -Seconds 3
break
}
- $selections = @($rawSelections | Where-Object { $_ -ge 1 -and $_ -le $allScripts.Count })
+ if ($rawSelections -contains 1) {
+ Show-LanguageMenu
+ continue
+ }
+
+ $selections = @($rawSelections | Where-Object { $_ -ge 2 -and $_ -le $maxMenuOption })
if ($selections.Count -eq 0) {
- Write-StyledMessage -Type 'Warning' -Text '⚠️ Nessuna selezione valida. Riprova.'
+ Write-StyledMessage -Type 'Warning' -Text (Get-Loc 'menu.invalidSelection')
Start-Sleep -Seconds 2
continue
}
@@ -2148,22 +2529,29 @@ if (-not $ImportOnly -and -not $Global:GuiSessionActive) {
Write-Host ''
if ($isMultiScript) {
- Write-StyledMessage -Type 'Info' -Text "🚀 Esecuzione sequenziale di $($selections.Count) operazioni..."
+ Write-StyledMessage -Type 'Info' -Text (Get-Loc 'run.sequence' -Args @($selections.Count))
Write-Host ''
}
foreach ($sel in $selections) {
- $scriptToRun = $allScripts[$sel - 1]
- Write-StyledMessage -Type 'Progress' -Text "▶️ Avvio: $($scriptToRun.Description)"
+ $scriptToRun = $allScripts[$sel - 2]
+ $scriptDescription = Get-ToolkitMenuText $scriptToRun
+ if ($scriptToRun.Name -eq 'WinDeleteUserProfiles' -and -not (Confirm-UserProfileDeletion)) {
+ Write-StyledMessage -Type 'Warning' -Text (Get-Loc 'run.cancelled')
+ Start-Sleep -Seconds 2
+ continue MainMenu
+ }
+
+ Write-StyledMessage -Type 'Progress' -Text (Get-Loc 'run.start' -Args @($scriptDescription))
Write-Host ''
try {
if ($isMultiScript) { & ([scriptblock]::Create("$($scriptToRun.Name) -SuppressIndividualReboot")) }
else { & $ExecutionContext.InvokeCommand.GetCommand($scriptToRun.Name, 'Function') }
- $Global:ExecutionLog += @{ Name = $scriptToRun.Description; Success = $true }
+ $Global:ExecutionLog += @{ Name = $scriptDescription; Success = $true }
}
catch {
- Write-StyledMessage -Type 'Error' -Text "❌ Errore durante $($scriptToRun.Description): $($_.Exception.Message)"
- $Global:ExecutionLog += @{ Name = $scriptToRun.Description; Success = $false; Error = $_.Exception.Message }
+ Write-StyledMessage -Type 'Error' -Text (Get-Loc 'run.error' -Args @($scriptDescription, $_.Exception.Message))
+ $Global:ExecutionLog += @{ Name = $scriptDescription; Success = $false; Error = $_.Exception.Message }
}
Write-Host ''
}
@@ -2172,29 +2560,29 @@ if (-not $ImportOnly -and -not $Global:GuiSessionActive) {
if ($isMultiScript) {
Write-Host ''
$tableRows = $Global:ExecutionLog | ForEach-Object {
- @{ Operazione = $_.Name; Stato = if ($_.Success) { '✅ Completato' } else { '❌ Errore' }; Dettaglio = if ($_.Error) { $_.Error } else { '' } }
+ @{ Operation = $_.Name; Status = if ($_.Success) { "✅ $(Get-Loc 'summary.completed')" } else { "❌ $(Get-Loc 'summary.error')" }; Detail = if ($_.Error) { $_.Error } else { '' } }
}
Show-ConsoleTable -Rows $tableRows -Columns @(
- @{ Header = 'Operazione'; Key = 'Operazione' },
- @{ Header = 'Stato'; Key = 'Stato' },
- @{ Header = 'Dettaglio'; Key = 'Dettaglio' }
- ) -Title '📊 Riepilogo Esecuzione'
+ @{ Header = (Get-Loc 'summary.operation'); Key = 'Operation' },
+ @{ Header = (Get-Loc 'summary.status'); Key = 'Status' },
+ @{ Header = (Get-Loc 'summary.detail'); Key = 'Detail' }
+ ) -Title "📊 $(Get-Loc 'summary.title')"
Write-Host ''
}
# ── Riavvio finale ────────────────────────────────────────────────────
if ($Global:NeedsFinalReboot) {
- Write-StyledMessage -Type 'Warning' -Text '🔄 È necessario un riavvio per completare le operazioni.'
- if (Start-InterruptibleCountdown -Seconds $CountdownSeconds -Message 'Riavvio sistema in') {
+ Write-StyledMessage -Type 'Warning' -Text (Get-Loc 'reboot.required')
+ if (Start-InterruptibleCountdown -Seconds $CountdownSeconds -Message (Get-Loc 'reboot.countdown')) {
Restart-Computer -Force
}
else {
Write-Host ''
- Write-StyledMessage -Type 'Info' -Text '💡 Ricorda di riavviare il sistema manualmente per completare le operazioni.'
+ Write-StyledMessage -Type 'Info' -Text (Get-Loc 'reboot.reminder')
}
}
- Write-Host "`nPremi INVIO per tornare al menu..." -ForegroundColor Gray
+ Write-Host "`n$(Get-Loc 'menu.pressEnter')" -ForegroundColor Gray
$null = Read-Host
}
}
diff --git a/WinToolkit.ps1 b/WinToolkit.ps1
index ea1cdbff..50fbd972 100644
--- a/WinToolkit.ps1
+++ b/WinToolkit.ps1
@@ -59,16 +59,16 @@ try { $Host.UI.RawUI.WindowTitle = "WinToolkit by MagnetarMan" } catch {}
$ToolkitVersion = "2.5.4 (Build 47)"
$AppConfig = @{
URLs = @{
- GitHubAssetBaseUrl = "https://raw.githubusercontent.com/Magnetarman/WinToolkit/refs/heads/main/asset/"
- GitHubAssetDevBaseUrl = "https://raw.githubusercontent.com/Magnetarman/WinToolkit/refs/heads/Dev/asset/"
- OfficeSetup = "https://raw.githubusercontent.com/Magnetarman/WinToolkit/refs/heads/main/asset/Setup.exe"
- OfficeBasicConfig = "https://raw.githubusercontent.com/Magnetarman/WinToolkit/refs/heads/main/asset/Basic.xml"
+ GitHubAssetBaseUrl = "https://raw.githubusercontent.com/Magnetarman/WinToolkit/refs/heads/main/assets/"
+ GitHubAssetDevBaseUrl = "https://raw.githubusercontent.com/Magnetarman/WinToolkit/refs/heads/Dev/assets/"
+ OfficeSetup = "https://raw.githubusercontent.com/Magnetarman/WinToolkit/refs/heads/main/assets/Setup.exe"
+ OfficeBasicConfig = "https://raw.githubusercontent.com/Magnetarman/WinToolkit/refs/heads/main/assets/Basic.xml"
GetHelpInstaller = "https://aka.ms/SaRA_EnterpriseVersionFiles"
AMDInstaller = "https://drivers.amd.com/drivers/installer/26.10/whql/amd-software-adrenalin-edition-26.5.2-minimalsetup-260513_web.exe"
- NVCleanstall = "https://raw.githubusercontent.com/Magnetarman/WinToolkit/refs/heads/main/asset/NVCleanstall_1.19.0.exe"
- DDUZip = "https://raw.githubusercontent.com/Magnetarman/WinToolkit/refs/heads/main/asset/DDU.zip"
- DriverOverridesJson = "https://raw.githubusercontent.com/Magnetarman/WinToolkit/refs/heads/Dev/asset/DriverOverrides.json"
- DirectXWebSetup = "https://raw.githubusercontent.com/Magnetarman/WinToolkit/refs/heads/main/asset/dxwebsetup.exe"
+ NVCleanstall = "https://raw.githubusercontent.com/Magnetarman/WinToolkit/refs/heads/main/assets/NVCleanstall_1.19.0.exe"
+ DDUZip = "https://raw.githubusercontent.com/Magnetarman/WinToolkit/refs/heads/main/assets/DDU.zip"
+ DriverOverridesJson = "https://raw.githubusercontent.com/Magnetarman/WinToolkit/refs/heads/Dev/assets/DriverOverrides.json"
+ DirectXWebSetup = "https://raw.githubusercontent.com/Magnetarman/WinToolkit/refs/heads/main/assets/dxwebsetup.exe"
BattleNetInstaller = "https://downloader.battle.net/download/getInstallerForGame?os=win&gameProgram=BATTLENET_APP&version=Live"
SevenZipOfficial = "https://www.7-zip.org/a/7zr.exe"
WingetInstaller = "https://aka.ms/getwinget"
@@ -3527,6 +3527,499 @@ function DisableBitlocker {
Write-ToolkitLog -Level INFO -Message "DisableBitlocker sessione terminata."
}
}
+function WinDeleteUserProfiles {
+ [CmdletBinding()]
+ param(
+ [ValidateRange(1, 16)]
+ [int]$MaxThreads = [Math]::Min(2, [Environment]::ProcessorCount),
+ [ValidateRange(0, 3600)]
+ [int]$CountdownSeconds = 30,
+ [switch]$SuppressIndividualReboot,
+ [ValidateNotNullOrEmpty()]
+ [string]$UsersRoot = 'C:\Users',
+ [ValidateNotNullOrEmpty()]
+ [string]$LogFolder = 'C:\Temp',
+ [ValidateRange(0, 3650)]
+ [int]$MinimumProfileAgeDays = 0,
+ [switch]$SkipResidualFolderCleanup,
+ [switch]$SuppressToolkitSession
+ )
+ begin {
+ $script:ToolName = 'WinDeleteUserProfiles'
+ $script:ToolVersion = '3.1'
+ $script:SessionStart = Get-Date
+ $script:UsersRoot = [System.IO.Path]::GetFullPath($UsersRoot.TrimEnd('\') + '\')
+ $script:LogFolder = [System.IO.Path]::GetFullPath($LogFolder)
+ $script:LogFile = Join-Path $script:LogFolder ("{0}_{1}.log" -f $script:ToolName, (Get-Date -Format 'yyyyMMdd_HHmmss'))
+ $script:CurrentUser = $env:USERNAME
+ $script:ComputerName = $env:COMPUTERNAME
+ $script:CountdownSeconds = $CountdownSeconds
+ $script:SuppressIndividualReboot = $SuppressIndividualReboot
+ $script:RebootRecommended = $false
+ $script:MinimumLastUseDate = if ($MinimumProfileAgeDays -gt 0) { (Get-Date).AddDays(-$MinimumProfileAgeDays) } else { $null }
+ $script:ProtectedProfileNames = @(
+ 'Public',
+ 'Pubblica',
+ 'Default',
+ 'Default User',
+ 'All Users',
+ 'defaultuser0',
+ 'WDAGUtilityAccount',
+ 'Administrator',
+ 'Guest',
+ $script:CurrentUser
+ ) | Where-Object { -not [string]::IsNullOrWhiteSpace($_) }
+ $savedErrorActionPreference = $ErrorActionPreference
+ $savedProgressPreference = $ProgressPreference
+ $savedConfirmPreference = $ConfirmPreference
+ $ErrorActionPreference = 'Stop'
+ $ProgressPreference = 'Continue'
+ $ConfirmPreference = 'None'
+ $script:LogQueue = [System.Collections.Concurrent.ConcurrentQueue[string]]::new()
+ }
+ process {
+ if (-not (Get-Command -Name Write-StyledMessage -ErrorAction SilentlyContinue)) {
+ function Write-StyledMessage {
+ param(
+ [ValidateSet('Info', 'Success', 'Warning', 'Error', 'Progress')]
+ [string]$Type = 'Info',
+ [Parameter(Mandatory = $true)]
+ [string]$Text
+ )
+ $color = switch ($Type) {
+ 'Success' { 'Green' }
+ 'Warning' { 'Yellow' }
+ 'Error' { 'Red' }
+ default { 'Cyan' }
+ }
+ Write-Host $Text -ForegroundColor $color
+ }
+ }
+ function Add-ProfileCleanupLog {
+ param(
+ [Parameter(Mandatory = $true)]
+ [string]$Text,
+ [ValidateSet('INFO', 'SUCCESS', 'WARNING', 'ERROR')]
+ [string]$Level = 'INFO'
+ )
+ $script:LogQueue.Enqueue(
+ ('{0} [{1}] {2}' -f (Get-Date -Format 'yyyy-MM-dd HH:mm:ss'), $Level, $Text)
+ )
+ }
+ function Set-ProfileCleanupRebootRecommended {
+ param(
+ [Parameter(Mandatory = $true)]
+ [string]$Reason
+ )
+ $script:RebootRecommended = $true
+ Add-ProfileCleanupLog -Level 'WARNING' -Text "Riavvio consigliato: $Reason"
+ }
+ function Invoke-ProfileCleanupReboot {
+ if (-not $script:RebootRecommended) {
+ return
+ }
+ if (Get-Command -Name Invoke-ToolkitReboot -ErrorAction SilentlyContinue) {
+ Invoke-ToolkitReboot -Message 'Riavvio consigliato dopo pulizia profili' -Seconds $script:CountdownSeconds -SuppressIndividualReboot:$script:SuppressIndividualReboot
+ return
+ }
+ if ($script:SuppressIndividualReboot) {
+ $Global:NeedsFinalReboot = $true
+ Write-StyledMessage -Type 'Info' -Text '🚫 Riavvio individuale soppresso. Verrà gestito un riavvio finale.'
+ return
+ }
+ if (Get-Command -Name Start-InterruptibleCountdown -ErrorAction SilentlyContinue) {
+ if (Start-InterruptibleCountdown -Seconds $script:CountdownSeconds -Message 'Riavvio consigliato dopo pulizia profili') {
+ Restart-Computer -Force
+ }
+ return
+ }
+ Write-StyledMessage -Type 'Warning' -Text 'Riavvio consigliato per completare la pulizia dei profili non rimossi.'
+ }
+ function Test-IsAdministrator {
+ $identity = [Security.Principal.WindowsIdentity]::GetCurrent()
+ $principal = [Security.Principal.WindowsPrincipal]::new($identity)
+ return $principal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)
+ }
+ function Initialize-ProfileCleanupSession {
+ [System.IO.Directory]::CreateDirectory($script:LogFolder) | Out-Null
+ if (-not (Test-IsAdministrator)) {
+ throw 'Lo script deve essere eseguito da una console PowerShell avviata come amministratore.'
+ }
+ if (-not (Test-Path -LiteralPath $script:UsersRoot -PathType Container)) {
+ throw "Il percorso profili non esiste: $script:UsersRoot"
+ }
+ $os = Get-CimInstance -ClassName Win32_OperatingSystem -ErrorAction SilentlyContinue
+ if ($os -and $os.Caption -notmatch 'Windows 11') {
+ Write-StyledMessage -Type 'Warning' -Text "⚠️ Sistema rilevato: $($os.Caption). Lo script è pensato per Windows 11."
+ }
+ if (-not $SuppressToolkitSession -and (Get-Command -Name Start-ToolkitSession -ErrorAction SilentlyContinue)) {
+ Start-ToolkitSession -ToolName $script:ToolName -SubTitle 'Profile Cleanup Toolkit'
+ }
+ else {
+ Write-Host ''
+ Write-Host '====================================================' -ForegroundColor Cyan
+ Write-Host (" {0} v{1}" -f $script:ToolName, $script:ToolVersion)
+ Write-Host '====================================================' -ForegroundColor Cyan
+ Write-Host ''
+ }
+ Write-StyledMessage -Type 'Info' -Text ("🖥️ Computer: {0}" -f $script:ComputerName)
+ Write-StyledMessage -Type 'Info' -Text ("👤 Utente corrente protetto: {0}" -f $script:CurrentUser)
+ Write-StyledMessage -Type 'Info' -Text ("📁 Percorso profili: {0}" -f $script:UsersRoot)
+ Write-StyledMessage -Type 'Info' -Text ("🧵 Thread configurati: {0}" -f $MaxThreads)
+ Write-StyledMessage -Type 'Warning' -Text '⚠️ Modalità non interattiva: nessuna conferma verrà richiesta prima delle cancellazioni.'
+ if ($script:MinimumLastUseDate) {
+ Write-StyledMessage -Type 'Info' -Text ("📅 Soglia ultima attività: profili non usati da almeno {0} giorni." -f $MinimumProfileAgeDays)
+ }
+ Add-ProfileCleanupLog -Text "Sessione avviata su $script:ComputerName."
+ }
+ function New-ProtectedNameSet {
+ $excluded = [System.Collections.Generic.HashSet[string]]::new([System.StringComparer]::OrdinalIgnoreCase)
+ $script:ProtectedProfileNames | ForEach-Object { [void]$excluded.Add($_) }
+ return ,$excluded
+ }
+ function Get-RegisteredProfilePathSet {
+ $pathSet = [System.Collections.Generic.HashSet[string]]::new([System.StringComparer]::OrdinalIgnoreCase)
+ Get-CimInstance -ClassName Win32_UserProfile -ErrorAction SilentlyContinue |
+ Where-Object {
+ $_.LocalPath -and
+ $_.LocalPath.StartsWith($script:UsersRoot, [System.StringComparison]::OrdinalIgnoreCase)
+ } |
+ ForEach-Object {
+ try {
+ [void]$pathSet.Add([System.IO.Path]::GetFullPath($_.LocalPath).TrimEnd('\'))
+ }
+ catch {
+ Add-ProfileCleanupLog -Level 'WARNING' -Text "Impossibile normalizzare LocalPath profilo registrato: $($_.LocalPath)"
+ }
+ }
+ return ,$pathSet
+ }
+ function Get-RemovableUserProfiles {
+ $excluded = New-ProtectedNameSet
+ Write-StyledMessage -Type 'Info' -Text '🔍 Scansione profili locali registrati in corso.'
+ $profiles = Get-CimInstance -ClassName Win32_UserProfile | Where-Object {
+ -not $_.Special -and
+ -not $_.Loaded -and
+ $_.LocalPath -and
+ $_.LocalPath.StartsWith($script:UsersRoot, [System.StringComparison]::OrdinalIgnoreCase)
+ }
+ foreach ($profile in $profiles) {
+ $profileName = [System.IO.Path]::GetFileName($profile.LocalPath)
+ if ($excluded.Contains($profileName)) {
+ Add-ProfileCleanupLog -Text "Profilo escluso: $profileName ($($profile.LocalPath))."
+ continue
+ }
+ if ($script:MinimumLastUseDate -and $profile.LastUseTime) {
+ $lastUse = $profile.LastUseTime
+ if ($lastUse -gt $script:MinimumLastUseDate) {
+ Add-ProfileCleanupLog -Text "Profilo escluso per soglia temporale: $profileName, ultimo uso $lastUse."
+ continue
+ }
+ }
+ $profile
+ }
+ }
+ function Show-ProfileCleanupPreview {
+ param(
+ [Parameter(Mandatory = $true)]
+ [array]$Profiles
+ )
+ Write-Host ''
+ Write-StyledMessage -Type 'Warning' -Text 'Profili registrati selezionati per la rimozione automatica:'
+ Write-Host ''
+ $Profiles |
+ Select-Object @{Name='User'; Expression={ [System.IO.Path]::GetFileName($_.LocalPath) }},
+ @{Name='Loaded'; Expression={ $_.Loaded }},
+ @{Name='LastUseTime'; Expression={ $_.LastUseTime }},
+ @{Name='Path'; Expression={ $_.LocalPath }} |
+ Format-Table -AutoSize
+ Write-Host ''
+ }
+ function Invoke-ProfileRemovalBatch {
+ param(
+ [Parameter(Mandatory = $true)]
+ [array]$Profiles
+ )
+ $pool = [RunspaceFactory]::CreateRunspacePool(1, $MaxThreads)
+ $pool.Open()
+ $jobs = [System.Collections.Generic.List[object]]::new()
+ $scriptBlock = {
+ param($Profile, $LogQueue)
+ $ConfirmPreference = 'None'
+ $ErrorActionPreference = 'Stop'
+ $userPath = $Profile.LocalPath
+ $userName = [System.IO.Path]::GetFileName($userPath)
+ $start = Get-Date
+ $LogQueue.Enqueue(('{0} [INFO] START PROFILE - {1} - {2}' -f $start.ToString('yyyy-MM-dd HH:mm:ss'), $userName, $userPath))
+ try {
+ Remove-CimInstance -InputObject $Profile -ErrorAction Stop -Confirm:$false
+ $LogQueue.Enqueue(('{0} [SUCCESS] CIM profile removed - {1}' -f (Get-Date -Format 'yyyy-MM-dd HH:mm:ss'), $userName))
+ }
+ catch {
+ $LogQueue.Enqueue(('{0} [WARNING] CIM remove failed - {1} - {2}' -f (Get-Date -Format 'yyyy-MM-dd HH:mm:ss'), $userName, $_.Exception.Message))
+ }
+ if ([System.IO.Directory]::Exists($userPath)) {
+ try {
+ $tempEmpty = Join-Path $env:TEMP "EmptyFolder"
+ if (-not (Test-Path $tempEmpty)) {
+ New-Item -ItemType Directory -Path $tempEmpty | Out-Null
+ }
+ robocopy $tempEmpty $userPath /MIR /R:1 /W:1 /NFL /NDL /NJH /NJS /NP | Out-Null
+ Remove-Item -LiteralPath $userPath -Force -Recurse -ErrorAction SilentlyContinue
+ $LogQueue.Enqueue(('{0} [SUCCESS] Folder removed - {1}' -f (Get-Date -Format 'yyyy-MM-dd HH:mm:ss'), $userName))
+ }
+ catch {
+ $LogQueue.Enqueue(('{0} [WARNING] Standard folder cleanup failed - {1} - {2}' -f (Get-Date -Format 'yyyy-MM-dd HH:mm:ss'), $userName, $_.Exception.Message))
+ try {
+ try {
+ & takeown.exe /F $userPath /R /D S | Out-Null
+ }
+ catch {
+ try {
+ & takeown.exe /F $userPath /R /D Y | Out-Null
+ }
+ catch {
+ $LogQueue.Enqueue(('{0} [ERROR] takeown failed - {1} - {2}' -f (Get-Date -Format 'yyyy-MM-dd HH:mm:ss'), $userName, $_.Exception.Message))
+ }
+ }
+ & icacls.exe $userPath /grant Administrators:F /T /C | Out-Null
+ Remove-Item -LiteralPath $userPath -Force -Recurse -ErrorAction Stop -Confirm:$false
+ $LogQueue.Enqueue(('{0} [SUCCESS] Folder removed after ACL reset - {1}' -f (Get-Date -Format 'yyyy-MM-dd HH:mm:ss'), $userName))
+ }
+ catch {
+ $LogQueue.Enqueue(('{0} [ERROR] Cleanup failed - {1} - {2}' -f (Get-Date -Format 'yyyy-MM-dd HH:mm:ss'), $userName, $_.Exception.Message))
+ }
+ }
+ }
+ $success = -not [System.IO.Directory]::Exists($userPath)
+ $duration = New-TimeSpan -Start $start -End (Get-Date)
+ if ($success) {
+ $LogQueue.Enqueue(('{0} [SUCCESS] COMPLETED PROFILE - {1} - {2:hh\:mm\:ss}' -f (Get-Date -Format 'yyyy-MM-dd HH:mm:ss'), $userName, $duration))
+ }
+ else {
+ $LogQueue.Enqueue(('{0} [ERROR] FAILED PROFILE - {1}' -f (Get-Date -Format 'yyyy-MM-dd HH:mm:ss'), $userName))
+ }
+ return [PSCustomObject]@{
+ Type = 'Profile'
+ UserName = $userName
+ Path = $userPath
+ Success = $success
+ Duration = $duration
+ }
+ }
+ try {
+ foreach ($profile in $Profiles) {
+ $ps = [PowerShell]::Create()
+ $ps.RunspacePool = $pool
+ [void]$ps.AddScript($scriptBlock, $true).
+ AddArgument($profile).
+ AddArgument($script:LogQueue)
+ $handle = $ps.BeginInvoke()
+ $jobs.Add([PSCustomObject]@{
+ PowerShell = $ps
+ Handle = $handle
+ })
+ }
+ $total = $jobs.Count
+ $lastPercent = -1
+ do {
+ $completed = ($jobs | Where-Object { $_.Handle.IsCompleted }).Count
+ $percent = if ($total -gt 0) { [math]::Floor(($completed / $total) * 100) } else { 100 }
+ if ($percent -ne $lastPercent) {
+ $lastPercent = $percent
+ Write-Progress -Activity 'Rimozione profili registrati' -Status "$completed / $total completati" -PercentComplete $percent
+ }
+ Start-Sleep -Milliseconds 500
+ } while ($completed -lt $total)
+ Write-Progress -Activity 'Rimozione profili registrati' -Completed
+ $results = foreach ($job in $jobs) {
+ try {
+ $job.PowerShell.EndInvoke($job.Handle)
+ }
+ catch {
+ Add-ProfileCleanupLog -Level 'ERROR' -Text "Errore runspace: $($_.Exception.Message)"
+ }
+ finally {
+ $job.PowerShell.Commands.Clear()
+ $job.PowerShell.Dispose()
+ }
+ }
+ return $results
+ }
+ finally {
+ if ($pool) {
+ $pool.Close()
+ $pool.Dispose()
+ }
+ }
+ }
+ function Get-ResidualUserFolders {
+ $excluded = New-ProtectedNameSet
+ $registeredProfilePaths = Get-RegisteredProfilePathSet
+ Write-StyledMessage -Type 'Info' -Text '🔎 Controllo cartelle residue nella directory utenti.'
+ $folders = Get-ChildItem -Path $UsersRoot -Directory -Force |
+ Where-Object {
+ -not ($_.Attributes -band [IO.FileAttributes]::ReparsePoint)
+ }
+ foreach ($folder in $folders) {
+ $folderName = $folder.Name
+ $folderPath = [System.IO.Path]::GetFullPath($folder.FullName).TrimEnd('\')
+ if ($excluded.Contains($folderName)) {
+ Add-ProfileCleanupLog -Text "Cartella residua esclusa per nome protetto: $folderName ($folderPath)."
+ continue
+ }
+ if ($registeredProfilePaths.Contains($folderPath)) {
+ Add-ProfileCleanupLog -Text "Cartella residua esclusa perché ancora associata a Win32_UserProfile: $folderName ($folderPath)."
+ continue
+ }
+ if ($folder.Attributes -band [System.IO.FileAttributes]::ReparsePoint) {
+ Add-ProfileCleanupLog -Level 'WARNING' -Text "Cartella residua esclusa perché reparse point/symlink: $folderName ($folderPath)."
+ continue
+ }
+ [PSCustomObject]@{
+ Name = $folderName
+ Path = $folderPath
+ }
+ }
+ }
+ function Remove-ResidualUserFolders {
+ param(
+ [Parameter(Mandatory = $true)]
+ [array]$Folders
+ )
+ $results = [System.Collections.Generic.List[object]]::new()
+ $total = $Folders.Count
+ $index = 0
+ foreach ($folder in $Folders) {
+ $index++
+ $percent = if ($total -gt 0) { [math]::Floor(($index / $total) * 100) } else { 100 }
+ Write-Progress `
+ -Activity 'Rimozione cartelle residue in C:\Users' `
+ -Status ("{0} / {1} - {2}" -f $index, $total, $folder.Name) `
+ -PercentComplete $percent
+ $start = Get-Date
+ $success = $false
+ Add-ProfileCleanupLog -Text "START RESIDUAL FOLDER - $($folder.Name) - $($folder.Path)"
+ $folderPath = $folder.Path
+ try {
+ Remove-Item -LiteralPath $folderPath -Force -Recurse -ErrorAction Stop -Confirm:$false
+ $success = -not [System.IO.Directory]::Exists($folderPath)
+ }
+ catch {
+ Add-ProfileCleanupLog -Level 'WARNING' -Text "Rimozione standard cartella residua fallita: $folderPath - $($_.Exception.Message)"
+ try {
+ & takeown.exe /F $folderPath /R /D Y | Out-Null
+ & icacls.exe $folderPath /grant Administrators:F /T /C | Out-Null
+ Remove-Item -LiteralPath $folderPath -Force -Recurse -ErrorAction Stop -Confirm:$false
+ $success = -not [System.IO.Directory]::Exists($folderPath)
+ }
+ catch {
+ Add-ProfileCleanupLog -Level 'ERROR' -Text "Rimozione cartella residua fallita: $folderPath - $($_.Exception.Message)"
+ $success = $false
+ }
+ }
+ $duration = New-TimeSpan -Start $start -End (Get-Date)
+ if ($success) {
+ Add-ProfileCleanupLog -Level 'SUCCESS' -Text "COMPLETED RESIDUAL FOLDER - $($folder.Name) - $($duration.ToString())"
+ }
+ else {
+ Add-ProfileCleanupLog -Level 'ERROR' -Text "FAILED RESIDUAL FOLDER - $($folder.Name)"
+ }
+ $results.Add([PSCustomObject]@{
+ Type = 'ResidualFolder'
+ UserName = $folder.Name
+ Path = $folder.Path
+ Success = $success
+ Duration = $duration
+ }) | Out-Null
+ }
+ Write-Progress -Activity 'Rimozione cartelle residue in C:\Users' -Completed
+ return $results
+ }
+ function Save-ProfileCleanupLog {
+ $logLines = [System.Collections.Generic.List[string]]::new()
+ $line = $null
+ while ($script:LogQueue.TryDequeue([ref]$line)) {
+ $logLines.Add($line)
+ }
+ $logLines | Set-Content -LiteralPath $script:LogFile -Encoding UTF8
+ }
+ try {
+ Initialize-ProfileCleanupSession
+ $profileResults = @()
+ $residualResults = @()
+ $targets = @(Get-RemovableUserProfiles)
+ if ($targets -and $targets.Count -gt 0) {
+ Show-ProfileCleanupPreview -Profiles $targets
+ Write-Host ''
+ Write-StyledMessage -Type 'Info' -Text ("🚀 Avvio rimozione automatica di {0} profili registrati." -f $targets.Count)
+ Write-Host ''
+ $profileResults = @(Invoke-ProfileRemovalBatch -Profiles $targets)
+ }
+ else {
+ Write-Host ''
+ Write-StyledMessage -Type 'Success' -Text '✅ Nessun profilo registrato rimovibile trovato.'
+ Add-ProfileCleanupLog -Level 'SUCCESS' -Text 'Nessun profilo registrato rimovibile trovato.'
+ }
+ if (-not $SkipResidualFolderCleanup) {
+ $residualFolders = @(Get-ResidualUserFolders)
+ if ($residualFolders -and $residualFolders.Count -gt 0) {
+ Write-Host ''
+ Write-StyledMessage -Type 'Warning' -Text ("Cartelle residue selezionate per la rimozione automatica: {0}" -f $residualFolders.Count)
+ $residualFolders | Select-Object Name, Path | Format-Table -AutoSize
+ Write-Host ''
+ Write-StyledMessage -Type 'Info' -Text '🧹 Avvio rimozione cartelle residue.'
+ Write-Host ''
+ $residualResults = @(Remove-ResidualUserFolders -Folders $residualFolders)
+ }
+ else {
+ Write-StyledMessage -Type 'Success' -Text '✅ Nessuna cartella residua rimovibile trovata in C:\Users.'
+ Add-ProfileCleanupLog -Level 'SUCCESS' -Text 'Nessuna cartella residua rimovibile trovata.'
+ }
+ }
+ else {
+ Write-StyledMessage -Type 'Warning' -Text 'Pulizia cartelle residue saltata per parametro SkipResidualFolderCleanup.'
+ Add-ProfileCleanupLog -Level 'WARNING' -Text 'Pulizia cartelle residue saltata.'
+ }
+ $allResults = @($profileResults) + @($residualResults)
+ $successCount = @($allResults | Where-Object { $_.Success }).Count
+ $failedCount = @($allResults | Where-Object { -not $_.Success }).Count
+ $profileSuccessCount = @($profileResults | Where-Object { $_.Success }).Count
+ $residualSuccessCount = @($residualResults | Where-Object { $_.Success }).Count
+ if ($failedCount -gt 0) {
+ Set-ProfileCleanupRebootRecommended -Reason "$failedCount elementi non rimossi potrebbero essere bloccati da sessioni o handle aperti."
+ }
+ $script:SessionEnd = Get-Date
+ $totalDuration = New-TimeSpan -Start $script:SessionStart -End $script:SessionEnd
+ Add-ProfileCleanupLog -Level 'INFO' -Text "Sessione completata. Profili rimossi: $profileSuccessCount. Cartelle residue rimosse: $residualSuccessCount. Errori: $failedCount. Durata: $totalDuration."
+ Save-ProfileCleanupLog
+ Write-Host ''
+ Write-Host '====================================================' -ForegroundColor Green
+ Write-Host ' COMPLETATO'
+ Write-Host '====================================================' -ForegroundColor Green
+ Write-Host ''
+ Write-StyledMessage -Type 'Success' -Text ("✅ Profili registrati rimossi: {0}" -f $profileSuccessCount)
+ Write-StyledMessage -Type 'Success' -Text ("✅ Cartelle residue rimosse: {0}" -f $residualSuccessCount)
+ if ($failedCount -gt 0) {
+ Write-StyledMessage -Type 'Warning' -Text ("⚠️ Elementi non rimossi: {0}" -f $failedCount)
+ }
+ Write-StyledMessage -Type 'Info' -Text ("⏱️ Durata: {0:hh\:mm\:ss}" -f $totalDuration)
+ Write-StyledMessage -Type 'Info' -Text ("📄 Log: {0}" -f $script:LogFile)
+ Invoke-ProfileCleanupReboot
+ }
+ catch {
+ Add-ProfileCleanupLog -Level 'ERROR' -Text $_.Exception.Message
+ try { Save-ProfileCleanupLog } catch { }
+ Write-StyledMessage -Type 'Error' -Text ("❌ Errore: {0}" -f $_.Exception.Message)
+ throw
+ }
+ finally {
+ $ErrorActionPreference = $savedErrorActionPreference
+ $ProgressPreference = $savedProgressPreference
+ $ConfirmPreference = $savedConfirmPreference
+ }
+ }
+}
function Install-Office {
[CmdletBinding()]
param(
@@ -4503,7 +4996,8 @@ $menuStructure = @(
[pscustomobject]@{Name = 'WinReinstallStore'; Description = 'Winget/WinStore Reset'; Action = 'RunFunction' },
[pscustomobject]@{Name = 'WinBackupDriver'; Description = 'Backup Driver PC'; Action = 'RunFunction' },
[pscustomobject]@{Name = 'WinCleaner'; Description = 'Pulizia File Temporanei'; Action = 'RunFunction' },
- [pscustomobject]@{Name = 'DisableBitlocker'; Description = 'Disabilita Bitlocker'; Action = 'RunFunction' }
+ [pscustomobject]@{Name = 'DisableBitlocker'; Description = 'Disabilita Bitlocker'; Action = 'RunFunction' },
+ [pscustomobject]@{Name = 'WinDeleteUserProfiles'; Description = 'Cancella profili utenti di Windows'; Action = 'RunFunction' }
)
},
@{ 'Name' = 'Office'; 'Icon' = '🏢'; 'Scripts' = @(
@@ -4532,7 +5026,26 @@ if (-not $ImportOnly -and -not $Global:GuiSessionActive) {
Write-Host ""
Write-StyledMessage -Type 'Info' -Text '💎 WinToolkit avviato in modalità interattiva'
Write-Host ""
- while ($true) {
+ function Confirm-UserProfileDeletion {
+ Write-Host ''
+ Write-StyledMessage -Type 'Error' -Text 'ATTENZIONE: eseguendo questa opzione verranno cancellati tutti i profili utenti di Windows, escluso l''utente attuale.'
+ Write-StyledMessage -Type 'Error' -Text 'I dati contenuti nei profili eliminati saranno irrecuperabili.'
+ Write-Host ''
+ Write-Host '💎 [1] Sì, cancella i profili utenti' -ForegroundColor White
+ Write-Host '[INVIO] Torna al menu principale' -ForegroundColor Gray
+ $firstConfirm = Microsoft.PowerShell.Utility\Read-Host 'Selezione'
+ if ($firstConfirm -ne '1') {
+ return $false
+ }
+ Write-Host ''
+ Write-StyledMessage -Type 'Error' -Text 'Sei proprio sicuro?'
+ Write-Host ''
+ Write-Host '💎 [1] Sì, sono sicuro di ciò che sto facendo e me ne assumo la responsabilità in caso di cancellazione di file' -ForegroundColor White
+ Write-Host '[INVIO] Torna al menu principale' -ForegroundColor Gray
+ $secondConfirm = Microsoft.PowerShell.Utility\Read-Host 'Selezione'
+ return ($secondConfirm -eq '1')
+ }
+ :MainMenu while ($true) {
Show-Header -SubTitle "Menu Principale"
$width = try { $Host.UI.RawUI.BufferSize.Width } catch { 80 }
Write-Host ''
@@ -4605,6 +5118,11 @@ if (-not $ImportOnly -and -not $Global:GuiSessionActive) {
}
foreach ($sel in $selections) {
$scriptToRun = $allScripts[$sel - 1]
+ if ($scriptToRun.Name -eq 'WinDeleteUserProfiles' -and -not (Confirm-UserProfileDeletion)) {
+ Write-StyledMessage -Type 'Warning' -Text 'Operazione annullata. Ritorno al menu principale.'
+ Start-Sleep -Seconds 2
+ continue MainMenu
+ }
Write-StyledMessage -Type 'Progress' -Text "▶️ Avvio: $($scriptToRun.Description)"
Write-Host ''
try {
diff --git a/WinToolkit_GUI.ps1 b/WinToolkit_GUI.ps1
index 9328325d..707222b8 100644
--- a/WinToolkit_GUI.ps1
+++ b/WinToolkit_GUI.ps1
@@ -23,12 +23,12 @@ $Global:GuiVersion = "3.0.0 (Build 7)" # Format: CoreVersion.GuiBuildNumber
# =============================================================================
# CONFIGURATION AND CONSTANTS
# =============================================================================
-$ScriptTitle = "WinToolkit GUI Edition By MagnetarMan"
+$ScriptTitle = "WinToolkit GUI Edition by MagnetarMan"
$LogDirectory = "$env:LOCALAPPDATA\WinToolkit\logs"
$WindowWidth = 1280 # HD ready resolution in 16:9.
$WindowHeight = 720 # HD ready resolution in 16:9.
$FontFamily = "JetBrains Mono Nerd Font, Cascadia Code, Consolas, Courier New"
-$FontSize = @{Small = 14; Medium = 16; Large = 18; Title = 20; Header = 28; ButtonSmall = 11 }
+$FontSize = @{Small = 14; Medium = 16; Large = 18; Title = 20; Header = 28}
# Emoji mappings for GUI elements
$emojiMappings = @{
@@ -92,8 +92,8 @@ $emojiMappings = @{
# =============================================================================
# EMOJI ICONS CONFIGURATION
# =============================================================================
-$localIconBasePath = Join-Path $env:LOCALAPPDATA "WinToolkit\asset\png"
-$remoteIconBasePath = "https://raw.githubusercontent.com/Magnetarman/WinToolkit/Dev/asset/png"
+$localIconBasePath = Join-Path $env:LOCALAPPDATA "WinToolkit\assets\png"
+$remoteIconBasePath = "https://raw.githubusercontent.com/Magnetarman/WinToolkit/Dev/assets/png"
# =============================================================================
# GLOBAL VARIABLES
@@ -110,6 +110,7 @@ $SysInfoComputerName = $null
$SysInfoRAM = $null
$SysInfoDisk = $null
$SysInfoScriptCompatibility = $null
+$SysInfoScriptCompatibilityImage = $null
$SysInfoBitlocker = $null
$progressBar = $null
$actionsPanel = $null
@@ -147,11 +148,329 @@ $Global:CoreScriptContent = $null
$Global:CoreScriptVersion = "Unknown"
$Global:CoreScriptLoaded = $false
$Global:MenuStructure = @() # Sarà popolato dal Core
+$Global:ToolkitLanguage = 'en-US'
+$Global:ToolkitLanguageData = $null
+$Global:ToolkitDefaultLanguageData = $null
# =============================================================================
# LOGGING AND UTILITY FUNCTIONS
# =============================================================================
+function Get-ToolkitLanguageDirectory {
+ $candidate = Join-Path $PSScriptRoot 'languages'
+ if (Test-Path $candidate) { return $candidate }
+
+ $repoCandidate = Join-Path (Get-Location) 'languages'
+ if (Test-Path $repoCandidate) { return $repoCandidate }
+
+ return $candidate
+}
+
+function Get-AvailableToolkitLanguages {
+ $languageDir = Get-ToolkitLanguageDirectory
+ if (-not (Test-Path $languageDir)) { return @() }
+
+ Get-ChildItem -Path $languageDir -File -ErrorAction SilentlyContinue |
+ Where-Object { $_.Extension -eq '.json' } |
+ ForEach-Object {
+ try {
+ $data = Get-Content -LiteralPath $_.FullName -Raw -Encoding UTF8 | ConvertFrom-Json
+ [pscustomobject]@{
+ Code = $data.code
+ Name = $data.name
+ NativeName = $data.nativeName
+ Path = $_.FullName
+ }
+ }
+ catch {
+ Write-Verbose "Invalid language file '$($_.FullName)': $($_.Exception.Message)"
+ }
+ } | Sort-Object Code
+}
+
+function Import-ToolkitLanguageFile {
+ param([string]$LanguageCode)
+
+ $languageDir = Get-ToolkitLanguageDirectory
+ $languageFile = @(
+ (Join-Path $languageDir "$LanguageCode.json")
+ ) | Where-Object { Test-Path $_ } | Select-Object -First 1
+ if (-not $languageFile) { return $null }
+
+ return (Get-Content -LiteralPath $languageFile -Raw -Encoding UTF8 | ConvertFrom-Json)
+}
+
+function Set-ToolkitLanguage {
+ param([string]$LanguageCode = 'en-US')
+
+ $defaultData = Import-ToolkitLanguageFile -LanguageCode 'en-US'
+ if ($defaultData) { $Global:ToolkitDefaultLanguageData = $defaultData }
+
+ $languageData = Import-ToolkitLanguageFile -LanguageCode $LanguageCode
+ if (-not $languageData) {
+ $LanguageCode = 'en-US'
+ $languageData = $defaultData
+ }
+
+ if ($languageData) {
+ $Global:ToolkitLanguage = $LanguageCode
+ $Global:ToolkitLanguageData = $languageData
+ }
+}
+
+function Get-Loc {
+ param(
+ [Parameter(Mandatory = $true)][string]$Key,
+ [object[]]$Args = @()
+ )
+
+ $value = $null
+ if ($Global:ToolkitLanguageData -and $Global:ToolkitLanguageData.strings.PSObject.Properties.Name -contains $Key) {
+ $value = [string]$Global:ToolkitLanguageData.strings.$Key
+ }
+ elseif ($Global:ToolkitDefaultLanguageData -and $Global:ToolkitDefaultLanguageData.strings.PSObject.Properties.Name -contains $Key) {
+ $value = [string]$Global:ToolkitDefaultLanguageData.strings.$Key
+ }
+ else {
+ $value = $Key
+ }
+
+ if ($Args -and $Args.Count -gt 0) { return [string]::Format($value, $Args) }
+ return $value
+}
+
+function Get-ToolkitMenuText {
+ param([object]$Item)
+
+ if ($Item -is [System.Collections.IDictionary]) {
+ if ($Item.Contains('DescriptionKey') -and $Item['DescriptionKey']) {
+ return (Get-Loc $Item['DescriptionKey'])
+ }
+ if ($Item.Contains('CategoryKey') -and $Item['CategoryKey']) {
+ return (Get-Loc $Item['CategoryKey'])
+ }
+ if ($Item.Contains('Description')) { return $Item['Description'] }
+ if ($Item.Contains('Name')) { return $Item['Name'] }
+ }
+
+ if ($Item.PSObject.Properties.Name -contains 'DescriptionKey' -and $Item.DescriptionKey) {
+ return (Get-Loc $Item.DescriptionKey)
+ }
+ if ($Item.PSObject.Properties.Name -contains 'CategoryKey' -and $Item.CategoryKey) {
+ return (Get-Loc $Item.CategoryKey)
+ }
+ if ($Item.PSObject.Properties.Name -contains 'Description') { return $Item.Description }
+ if ($Item.PSObject.Properties.Name -contains 'Name') { return $Item.Name }
+ return [string]$Item
+}
+
+Set-ToolkitLanguage -LanguageCode 'en-US'
+
+function Convert-GuiSourceText {
+ param([string]$Text)
+ if ([string]::IsNullOrWhiteSpace($Text) -or $Global:ToolkitLanguage -eq 'it-IT') { return $Text }
+
+ $translated = $Text
+ $replacements = [ordered]@{
+ 'INIZIALIZZAZIONE RISORSE - Caricamento Core Script.' = 'RESOURCE INITIALIZATION - Loading Core Script.'
+ 'Attendere prego, operazione in corso.' = 'Please wait, operation in progress.'
+ 'Errore lettura versione cache locale' = 'Error reading local cache version'
+ 'Fallito recupero versione remota' = 'Failed to retrieve remote version'
+ 'Potrebbe essere necessario un download forzato o fallback.' = 'A forced download or fallback may be required.'
+ 'Cache locale scaduta' = 'Local cache expired'
+ 'età' = 'age'
+ 'minuti' = 'minutes'
+ 'Download per aggiornare.' = 'Downloading to update.'
+ 'Core Script scaricato con successo.' = 'Core Script downloaded successfully.'
+ 'Salvato in cache' = 'Saved to cache'
+ 'Impossibile estrarre versione' = 'Unable to extract version'
+ 'Download fallito' = 'Download failed'
+ 'Utilizzo cache locale' = 'Using local cache'
+ 'Core Script content è vuoto dopo i tentativi di caricamento.' = 'Core Script content is empty after loading attempts.'
+ 'INIZIALIZZAZIONE COMPLETATA - GUI pronta all''uso.' = 'INITIALIZATION COMPLETED - GUI ready to use.'
+ 'ERRORE CRITICO durante caricamento Core' = 'CRITICAL ERROR while loading Core'
+ 'Suggerimento: Scarica manualmente WinToolkit.ps1 da:' = 'Tip: manually download WinToolkit.ps1 from:'
+ 'e salvalo in' = 'and save it to'
+ 'Preparazione log errori GUI per la segnalazione.' = 'Preparing GUI error logs for reporting.'
+ 'Nessun file log della GUI o del Core trovato per la segnalazione.' = 'No GUI or Core log file found for reporting.'
+ 'Pacchetto log supporto creato' = 'Support log package created'
+ 'Browser aperto per la segnalazione su GitHub.' = 'Browser opened for reporting on GitHub.'
+ 'Impossibile aprire il browser' = 'Unable to open the browser'
+ 'Operazione completata!' = 'Operation completed!'
+ 'Caricamento funzioni Core in memoria' = 'Loading Core functions into memory'
+ 'Struttura del menu caricata' = 'Menu structure loaded'
+ 'categorie' = 'categories'
+ 'non trovato dopo il caricamento' = 'not found after loading'
+ 'Funzione Get-SystemInfo disponibile.' = 'Get-SystemInfo function available.'
+ 'Funzione Get-SystemInfo NON trovata!' = 'Get-SystemInfo function NOT found!'
+ 'Errore durante dot-sourcing Core' = 'Error while dot-sourcing Core'
+ 'Impossibile caricare o scaricare l''icona della finestra' = 'Unable to load or download the window icon'
+ 'configurato con stile pill-shaped e icona Play.' = 'configured with pill-shaped style and Play icon.'
+ 'Conferma utente bypassata' = 'User confirmation bypassed'
+ 'Risposta predefinita' = 'Default response'
+ 'Input interattivo rilevato' = 'Interactive input detected'
+ 'Non supportato in modalità GUI.' = 'Not supported in GUI mode.'
+ 'Avvio esecuzione' = 'Starting execution'
+ 'Avvio ' = 'Starting '
+ 'Avvio:' = 'Startup:'
+ 'Attesa avvio' = 'Waiting for startup'
+ 'Caricamento moduli' = 'Loading modules'
+ 'Installazione' = 'Installation'
+ 'Installato' = 'Installed'
+ 'Disinstallazione' = 'Uninstallation'
+ 'Riparazione' = 'Repair'
+ 'Rimozione' = 'Removal'
+ 'Pulizia' = 'Cleanup'
+ 'Eliminazione' = 'Deleting'
+ 'Verifica' = 'Checking'
+ 'Validazione' = 'Validation'
+ 'Rilevamento' = 'Detecting'
+ 'Configurazione' = 'Configuration'
+ 'Abilitazione' = 'Enabling'
+ 'Riabilitazione' = 'Re-enabling'
+ 'Aggiornamento' = 'Updating'
+ 'Ripristino' = 'Restoring'
+ 'Reinstallazione' = 'Reinstallation'
+ 'Preparazione' = 'Preparing'
+ 'Estrazione' = 'Extracting'
+ 'Ricerca' = 'Searching'
+ 'Arresto' = 'Stopping'
+ 'Riavvio automatico' = 'Automatic restart'
+ 'Riavvio del sistema' = 'System restart'
+ 'Riavvio individuale soppresso' = 'Individual restart suppressed'
+ 'Verrà gestito un riavvio finale' = 'A final restart will be handled'
+ 'Riavvio non necessario' = 'Restart not required'
+ 'Riavvio necessario' = 'Restart required'
+ 'Riavvio sistema' = 'System restart'
+ 'Riavvio in' = 'Restart in'
+ ' tra ' = ' in '
+ 'Premi un tasto per continuare' = 'Press any key to continue'
+ 'Premere un tasto per uscire' = 'Press any key to exit'
+ 'Premi un tasto qualsiasi per annullare' = 'Press any key to cancel'
+ 'Completato' = 'Completed'
+ 'completata' = 'completed'
+ 'completati' = 'completed'
+ 'terminato' = 'finished'
+ 'Errore durante' = 'Error during'
+ 'Errore critico' = 'Critical error'
+ 'Errore imprevisto' = 'Unexpected error'
+ 'Errore avvio job' = 'Error starting job'
+ 'Errore sconosciuto' = 'Unknown error'
+ 'fallito' = 'failed'
+ 'fallita' = 'failed'
+ 'falliti' = 'failed'
+ 'annullata' = 'cancelled'
+ 'annullato' = 'cancelled'
+ 'già presente' = 'already present'
+ 'non presente' = 'not present'
+ 'non trovato' = 'not found'
+ 'non trovata' = 'not found'
+ 'Nessuna riparazione necessaria' = 'No repair required'
+ 'non disponibile' = 'not available'
+ 'non accessibili' = 'not accessible'
+ 'Impossibile' = 'Unable to'
+ 'Avviso' = 'Warning'
+ 'Attenzione' = 'Warning'
+ 'Suggerimento' = 'Tip'
+ 'Nota' = 'Note'
+ 'Trovati' = 'Found'
+ 'Rimosso' = 'Removed'
+ 'Rimossi' = 'Removed'
+ 'rimosse' = 'removed'
+ 'eliminata' = 'deleted'
+ 'eliminati' = 'deleted'
+ 'eliminato' = 'deleted'
+ 'rilevata' = 'detected'
+ 'rilevato' = 'detected'
+ 'scaricato' = 'downloaded'
+ 'scaricata' = 'downloaded'
+ 'scaricare' = 'download'
+ 'scarica' = 'download'
+ 'creato' = 'created'
+ 'creata' = 'created'
+ 'attivato' = 'enabled'
+ 'attiva' = 'enabled'
+ 'abilitato' = 'enabled'
+ 'abilitati' = 'enabled'
+ 'configurato' = 'configured'
+ 'configurata' = 'configured'
+ 'ripristinate' = 'restored'
+ 'ripristinato' = 'restored'
+ 'reimpostato' = 'reset'
+ 'avviato' = 'started'
+ 'arrestato' = 'stopped'
+ 'in uso' = 'in use'
+ 'in corso' = 'in progress'
+ 'può richiedere alcuni minuti' = 'may take a few minutes'
+ 'può impiegare 1-2 minuti' = 'may take 1-2 minutes'
+ 'cartella' = 'folder'
+ 'cartelle' = 'folders'
+ 'chiavi registro' = 'registry keys'
+ 'chiave di registro' = 'registry key'
+ 'registro' = 'registry'
+ 'collegamenti' = 'shortcuts'
+ 'attività' = 'tasks'
+ 'attività pianificate' = 'scheduled tasks'
+ 'criteri di gruppo' = 'group policies'
+ 'criteri locali' = 'local policies'
+ 'Criteri' = 'Policies'
+ 'sorgenti' = 'sources'
+ 'pacchetto' = 'package'
+ 'pacchetti' = 'packages'
+ 'Versione' = 'Version'
+ 'configurazione GPU' = 'GPU configuration'
+ 'GPU rilevata' = 'Detected GPU'
+ 'GPU non rilevata' = 'GPU not detected'
+ 'driver non disponibile per l''installazione automatica' = 'driver not available for automatic installation'
+ 'modalità provvisoria' = 'Safe Mode'
+ 'modalità normale' = 'normal mode'
+ 'prossimo avvio' = 'next boot'
+ 'sistema' = 'system'
+ 'servizio' = 'service'
+ 'servizi' = 'services'
+ 'Stato' = 'Status'
+ 'codice' = 'code'
+ 'Codice uscita' = 'Exit code'
+ 'Eccezione' = 'Exception'
+ 'Tentativo' = 'Attempt'
+ 'Riepilogo' = 'Summary'
+ 'operazione' = 'operation'
+ 'modifiche' = 'changes'
+ 'valori predefiniti' = 'default values'
+ 'driver video' = 'video driver'
+ 'operazioni pendenti' = 'pending operations'
+ 'operazioni in sospeso' = 'pending operations'
+ 'Questo non è un errore critico' = 'This is not a critical error'
+ 'Proseguo comunque' = 'Continuing anyway'
+ 'Annullamento' = 'Cancelling'
+ 'metodo alternativo' = 'alternative method'
+ 'finestra esterna' = 'external window'
+ 'Attesa completamento' = 'Waiting for completion'
+ 'metodo forzato' = 'forced method'
+ 'file ignorati perché in uso o non accessibili' = 'files skipped because they are in use or not accessible'
+ 'Rilevate operazioni pendenti che richiedono riavvio' = 'Pending operations requiring a restart detected'
+ 'DISM potrebbe fallire' = 'DISM may fail'
+ 'Sistema in salute' = 'System is healthy'
+ 'Riparazione profonda non necessaria' = 'Deep repair not required'
+ 'riparazione profonda' = 'deep repair'
+ 'controllo schedulato al prossimo riavvio' = 'check scheduled at next restart'
+ 'interrotto' = 'stopped'
+ 'Preparazione prossimo script.' = 'Preparing next script.'
+ 'checkbox totali.' = 'total checkboxes.'
+ 'Script selezionato' = 'Selected script'
+ 'Errore lettura checkbox' = 'Error reading checkbox'
+ 'Errore invio log' = 'Error sending logs'
+ 'Errore durante inizializzazione Loaded' = 'Error during Loaded initialization'
+ 'Finestra GUI chiusa. Tentativo di fermare il job in corso.' = 'GUI window closed. Trying to stop the running job.'
+ 'Job in corso fermato e rimosso.' = 'Running job stopped and removed.'
+ 'Errore durante l''interruzione del job' = 'Error while stopping the job'
+ }
+ foreach ($entry in $replacements.GetEnumerator()) {
+ $translated = [regex]::Replace($translated, [regex]::Escape([string]$entry.Key), [string]$entry.Value, [System.Text.RegularExpressions.RegexOptions]::IgnoreCase)
+ }
+ return $translated
+}
+
function Write-UnifiedLog {
param(
[Parameter(Mandatory = $true)][string]$Message,
@@ -167,6 +486,7 @@ function Write-UnifiedLog {
Progress = 'Magenta'
}
+ $Message = Convert-GuiSourceText -Text $Message
$currentDateTime = Get-Date -Format 'HH:mm:ss'
$logPrefix = "[$currentDateTime] [$Type]"
$formattedMessage = "$logPrefix $Message"
@@ -398,7 +718,7 @@ function Initialize-CoreScript {
}
if (-not $coreContent) {
- throw "Core Script content è vuoto dopo i tentativi di caricamento."
+ throw "Core Script content is empty after loading attempts."
}
# NOTE: Loading moved to main scope to fix variable visibility
@@ -677,7 +997,7 @@ Attach this zip file when reporting issues. The CorrelationId links logs across
# in the Core Script (WinToolkit.ps1) and are loaded when the Core Script
# is dot-sourced. The job now only needs to load the Core Script to access
# all tool functions.
-# $Global:ToolScriptsPath = Join-Path $PSScriptRoot "tool"
+# $Global:ToolScriptsPath = Join-Path $PSScriptRoot "tools"
# function Load-AllToolScripts { ... } # REMOVED - All functions are in Core Script
@@ -790,6 +1110,87 @@ catch {
Write-UnifiedLog -Type 'Error' -Message "❌ Errore durante dot-sourcing Core: $($_.Exception.Message)." -GuiColor "#FF0000"
}
+# =============================================================================
+# GUI LOCALIZATION OVERRIDES
+# Re-apply after core dot-sourcing so cached/remote cores cannot overwrite them.
+# =============================================================================
+
+function Get-GuiMenuLocalizationKey {
+ param([object]$Item)
+
+ $name = $null
+ if ($Item -is [System.Collections.IDictionary]) {
+ if ($Item.Contains('CategoryKey') -and $Item['CategoryKey']) { return $Item['CategoryKey'] }
+ if ($Item.Contains('DescriptionKey') -and $Item['DescriptionKey']) { return $Item['DescriptionKey'] }
+ if ($Item.Contains('Name')) { $name = [string]$Item['Name'] }
+ }
+ else {
+ if ($Item.PSObject.Properties.Name -contains 'CategoryKey' -and $Item.CategoryKey) { return $Item.CategoryKey }
+ if ($Item.PSObject.Properties.Name -contains 'DescriptionKey' -and $Item.DescriptionKey) { return $Item.DescriptionKey }
+ if ($Item.PSObject.Properties.Name -contains 'Name') { $name = [string]$Item.Name }
+ }
+
+ switch -Regex ($name) {
+ '^Windows$' { return 'category.windows' }
+ '^Office$' { return 'category.office' }
+ '^Driver & Gaming$' { return 'category.driverGaming' }
+ '^(Supporto|Support)$' { return 'category.support' }
+ default {
+ if (-not [string]::IsNullOrWhiteSpace($name)) { return "script.$name" }
+ }
+ }
+
+ return $null
+}
+
+function Get-ToolkitMenuText {
+ param([object]$Item)
+
+ $key = Get-GuiMenuLocalizationKey -Item $Item
+ if ($key) {
+ $localized = Get-Loc $key
+ if ($localized -ne $key) { return $localized }
+ }
+
+ if ($Item -is [System.Collections.IDictionary]) {
+ if ($Item.Contains('Description')) { return $Item['Description'] }
+ if ($Item.Contains('Name')) { return $Item['Name'] }
+ }
+ else {
+ if ($Item.PSObject.Properties.Name -contains 'Description') { return $Item.Description }
+ if ($Item.PSObject.Properties.Name -contains 'Name') { return $Item.Name }
+ }
+
+ return [string]$Item
+}
+
+function Convert-GuiBitlockerStatusToKey {
+ param([string]$StatusText)
+
+ if ([string]::IsNullOrWhiteSpace($StatusText)) { return 'bitlocker.status.notConfigured' }
+
+ $normalized = $StatusText.Trim().ToLowerInvariant()
+ if ($normalized -match 'decritt|decrypt') { return 'bitlocker.status.decrypting' }
+ if ($normalized -match 'crittografia in corso|encrypt') { return 'bitlocker.status.encrypting' }
+ if ($normalized -match 'sospes|suspend') { return 'bitlocker.status.suspended' }
+ if ($normalized -match 'non configur|not configured') { return 'bitlocker.status.notConfigured' }
+ if ($normalized -match 'disattiv|protection off|off|disabled') { return 'bitlocker.status.off' }
+ if ($normalized -match 'attiv|protection on|on|enabled') { return 'bitlocker.status.on' }
+
+ return 'bitlocker.status.unknown'
+}
+
+function Get-GuiBitlockerStatusKey {
+ $command = Get-Command 'Get-BitlockerStatus' -ErrorAction SilentlyContinue
+ if ($command -and $command.Parameters.ContainsKey('Key')) {
+ $statusKey = Get-BitlockerStatus -Key
+ if ($statusKey -match '^bitlocker\.status\.') { return $statusKey }
+ }
+
+ $statusText = if ($command) { Get-BitlockerStatus } else { $null }
+ return (Convert-GuiBitlockerStatusToKey -StatusText $statusText)
+}
+
# =============================================================================
# WPF GUI DEFINITION
# =============================================================================
@@ -896,7 +1297,7 @@ $xaml = @"
@@ -914,19 +1315,36 @@ $xaml = @"
TextAlignment="Center" Margin="0,4,0,0"/>
-
-
-
-
-
+
+
+
+
+
-
+
+
+
+
+
+
+
@@ -944,9 +1362,9 @@ $xaml = @"
-
@@ -958,10 +1376,10 @@ $xaml = @"
-
-
@@ -975,10 +1393,10 @@ $xaml = @"
-
-
@@ -992,10 +1410,10 @@ $xaml = @"
-
-
@@ -1017,12 +1435,14 @@ $xaml = @"
-
+
-
@@ -1038,14 +1458,14 @@ $xaml = @"
-
-
@@ -1057,9 +1477,9 @@ $xaml = @"
-
@@ -1071,10 +1491,10 @@ $xaml = @"
-
-
@@ -1088,10 +1508,10 @@ $xaml = @"
-
-
@@ -1105,10 +1525,10 @@ $xaml = @"
-
-
@@ -1137,7 +1557,7 @@ $xaml = @"
-
@@ -1162,7 +1582,7 @@ $xaml = @"
-
@@ -1212,7 +1632,7 @@ $xaml = @"
Style="{StaticResource PillButtonStyle}">
-
+
@@ -1228,11 +1648,11 @@ try {
# Setup Window Icon (Favicon & Taskbar) - Remote Fallback
try {
- $localImgDir = Join-Path $env:LOCALAPPDATA "WinToolkit\img"
+ $localImgDir = Join-Path $env:LOCALAPPDATA "WinToolkit\images"
if (-not (Test-Path $localImgDir)) { New-Item -Path $localImgDir -ItemType Directory -Force | Out-Null }
$iconPath = Join-Path $localImgDir "WinToolkit.ico"
- $iconUrl = "https://raw.githubusercontent.com/Magnetarman/WinToolkit/refs/heads/main/img/WinToolkit.ico"
+ $iconUrl = "https://raw.githubusercontent.com/Magnetarman/WinToolkit/refs/heads/main/images/WinToolkit.ico"
if (-not (Test-Path $iconPath)) {
Invoke-WebRequest -Uri $iconUrl -OutFile $iconPath -UseBasicParsing -ErrorAction Stop
@@ -1264,6 +1684,7 @@ $SysInfoComputerName = $window.FindName("SysInfoComputerName")
$SysInfoRAM = $window.FindName("SysInfoRAM")
$SysInfoDisk = $window.FindName("SysInfoDisk")
$SysInfoScriptCompatibility = $window.FindName("SysInfoScriptCompatibility")
+$SysInfoScriptCompatibilityImage = $window.FindName("SysInfoScriptCompatibilityImage")
$SysInfoBitlocker = $window.FindName("SysInfoBitlocker")
$BitlockerImage = $window.FindName("BitlockerImage")
$SysInfoEditionImage = $window.FindName("SysInfoEditionImage")
@@ -1273,6 +1694,21 @@ $SysInfoComputerNameImage = $window.FindName("SysInfoComputerNameImage")
$SysInfoRAMImage = $window.FindName("SysInfoRAMImage")
$SysInfoDiskImage = $window.FindName("SysInfoDiskImage")
$SendErrorLogsButton = $window.FindName("SendErrorLogsButton")
+$SendErrorLogsText = $window.FindName("SendErrorLogsText")
+$LanguageLabelText = $window.FindName("LanguageLabelText")
+$LanguageComboBox = $window.FindName("LanguageComboBox")
+$SysInfoTitleText = $window.FindName("SysInfoTitleText")
+$SysInfoEditionLabel = $window.FindName("SysInfoEditionLabel")
+$SysInfoVersionLabel = $window.FindName("SysInfoVersionLabel")
+$SysInfoArchitectureLabel = $window.FindName("SysInfoArchitectureLabel")
+$SysInfoScriptCompatibilityLabel = $window.FindName("SysInfoScriptCompatibilityLabel")
+$SysInfoBitlockerLabel = $window.FindName("SysInfoBitlockerLabel")
+$SysInfoComputerNameLabel = $window.FindName("SysInfoComputerNameLabel")
+$SysInfoRAMLabel = $window.FindName("SysInfoRAMLabel")
+$SysInfoDiskLabel = $window.FindName("SysInfoDiskLabel")
+$AvailableFunctionsText = $window.FindName("AvailableFunctionsText")
+$OutputLogsText = $window.FindName("OutputLogsText")
+$ExecuteButtonText = $window.FindName("ExecuteButtonText")
$SendErrorLogsImage = $window.FindName("SendErrorLogsImage")
$ToolIconImage = $window.FindName("ToolIconImage")
$ExecuteButtonImage = $window.FindName("ExecuteButtonImage")
@@ -1280,6 +1716,73 @@ $CategorySystemImage = $window.FindName("CategorySystemImage")
$OutputLogImage = $window.FindName("OutputLogImage")
$progressBar = $window.FindName("MainProgressBar")
+function Set-TextBlockText {
+ param([object]$Control, [string]$Text)
+ if ($Control) { $Control.Text = $Text }
+}
+
+function Initialize-LanguageComboBox {
+ if (-not $LanguageComboBox) { return }
+
+ $LanguageComboBox.Items.Clear()
+ foreach ($language in @(Get-AvailableToolkitLanguages)) {
+ $item = New-Object System.Windows.Controls.ComboBoxItem
+ $item.Content = $language.NativeName
+ $item.Tag = $language.Code
+ $LanguageComboBox.Items.Add($item) | Out-Null
+ if ($language.Code -eq $Global:ToolkitLanguage) {
+ $LanguageComboBox.SelectedItem = $item
+ }
+ }
+
+ if (-not $LanguageComboBox.SelectedItem -and $LanguageComboBox.Items.Count -gt 0) {
+ $LanguageComboBox.SelectedIndex = 0
+ }
+}
+
+function Apply-GuiLocalization {
+ Set-TextBlockText $LanguageLabelText (Get-Loc 'gui.languageLabel')
+ Set-TextBlockText $SendErrorLogsText (Get-Loc 'gui.sendErrorLogs')
+ Set-TextBlockText $SysInfoTitleText "▬▬ $(Get-Loc 'gui.systemInfo') ▬▬"
+ Set-TextBlockText $SysInfoEditionLabel (Get-Loc 'gui.windowsEdition')
+ Set-TextBlockText $SysInfoVersionLabel (Get-Loc 'gui.version')
+ Set-TextBlockText $SysInfoArchitectureLabel (Get-Loc 'gui.architecture')
+ Set-TextBlockText $SysInfoScriptCompatibilityLabel (Get-Loc 'gui.scriptFeatures')
+ Set-TextBlockText $SysInfoBitlockerLabel (Get-Loc 'gui.bitlockerStatus')
+ Set-TextBlockText $SysInfoComputerNameLabel (Get-Loc 'gui.pcName')
+ Set-TextBlockText $SysInfoRAMLabel (Get-Loc 'gui.ram')
+ Set-TextBlockText $SysInfoDiskLabel (Get-Loc 'gui.disk')
+ Set-TextBlockText $AvailableFunctionsText (Get-Loc 'gui.availableFunctions')
+ Set-TextBlockText $OutputLogsText (Get-Loc 'gui.outputLogs')
+ Set-TextBlockText $ExecuteButtonText (Get-Loc 'gui.executeScripts')
+
+ if ($SysInfoScriptCompatibility -and $SysInfoScriptCompatibility.Text -match '^(Complete|Completa)$') {
+ $SysInfoScriptCompatibility.Text = Get-Loc 'gui.complete'
+ }
+ elseif ($SysInfoScriptCompatibility -and $SysInfoScriptCompatibility.Text -match '^(Limited|Limitata)$') {
+ $SysInfoScriptCompatibility.Text = Get-Loc 'gui.limited'
+ }
+ elseif ($SysInfoScriptCompatibility -and $SysInfoScriptCompatibility.Text -match '^(Unsupported|Non supportata)$') {
+ $SysInfoScriptCompatibility.Text = Get-Loc 'gui.unsupported'
+ }
+}
+
+Initialize-LanguageComboBox
+Apply-GuiLocalization
+
+if ($LanguageComboBox) {
+ $LanguageComboBox.Add_SelectionChanged({
+ if (-not $LanguageComboBox.SelectedItem) { return }
+ $selectedLanguage = [string]$LanguageComboBox.SelectedItem.Tag
+ if ([string]::IsNullOrWhiteSpace($selectedLanguage) -or $selectedLanguage -eq $Global:ToolkitLanguage) { return }
+
+ Set-ToolkitLanguage -LanguageCode $selectedLanguage
+ Apply-GuiLocalization
+ Update-SystemInformationPanel
+ Update-ActionsPanel
+ })
+}
+
# Setup ExecuteButton con nuovo stile e inizializza icone
try {
# Inizializza l'icona del pulsante Esegui
@@ -1324,13 +1827,13 @@ try {
# Inizializza l'icona Tool (WinToolkit logo header) - Remote Fallback
if ($ToolIconImage) {
try {
- $localImgDir = Join-Path $env:LOCALAPPDATA "WinToolkit\img"
+ $localImgDir = Join-Path $env:LOCALAPPDATA "WinToolkit\images"
if (-not (Test-Path $localImgDir)) { New-Item -Path $localImgDir -ItemType Directory -Force | Out-Null }
# Qui usiamo la stessa icona scaricata prima, o ne scarichiamo un'altra se serve.
# In base alla richiesta utente carichiamo WinToolkit.ico
$toolLogoPath = Join-Path $localImgDir "WinToolkit.ico"
- $toolLogoUrl = "https://raw.githubusercontent.com/Magnetarman/WinToolkit/refs/heads/main/img/WinToolkit.ico"
+ $toolLogoUrl = "https://raw.githubusercontent.com/Magnetarman/WinToolkit/refs/heads/main/images/WinToolkit.ico"
if (-not (Test-Path $toolLogoPath)) {
Invoke-WebRequest -Uri $toolLogoUrl -OutFile $toolLogoPath -UseBasicParsing -ErrorAction Stop
@@ -1379,7 +1882,7 @@ function Update-SystemInformationPanel {
$SysInfoArchitecture.Text = $sysInfo.Architecture
$SysInfoComputerName.Text = $sysInfo.ComputerName
$SysInfoRAM.Text = "$($sysInfo.TotalRAM) GB"
- $SysInfoDisk.Text = "$($sysInfo.FreePercentage)% Libero ($($sysInfo.FreeDisk) GB / $($sysInfo.TotalDisk) GB)"
+ $SysInfoDisk.Text = Get-Loc 'gui.diskFreeFormat' -Args @($sysInfo.FreePercentage, $sysInfo.FreeDisk, $sysInfo.TotalDisk)
# Set image sources
try {
@@ -1400,36 +1903,48 @@ function Update-SystemInformationPanel {
# Task 2: Compatibility indicator con status text colorato
$statusText = ""
+ $statusIconKey = "LEDStatusRed"
if ($sysInfo.BuildNumber -ge 22000) {
- $statusText = "Completa"
+ $statusText = Get-Loc 'gui.complete'
+ $statusIconKey = "LEDStatusGreen"
$SysInfoScriptCompatibility.Foreground = New-Object System.Windows.Media.SolidColorBrush([System.Windows.Media.Colors]::LimeGreen)
}
elseif ($sysInfo.BuildNumber -ge 17763) {
- $statusText = "Completa"
+ $statusText = Get-Loc 'gui.complete'
+ $statusIconKey = "LEDStatusGreen"
$SysInfoScriptCompatibility.Foreground = New-Object System.Windows.Media.SolidColorBrush([System.Windows.Media.Colors]::LimeGreen)
}
elseif ($sysInfo.BuildNumber -ge 10240) {
- $statusText = "Limitata"
+ $statusText = Get-Loc 'gui.limited'
+ $statusIconKey = "LEDStatusYellow"
$SysInfoScriptCompatibility.Foreground = New-Object System.Windows.Media.SolidColorBrush([System.Windows.Media.Colors]::Orange)
}
else {
- $statusText = "Non supportata"
+ $statusText = Get-Loc 'gui.unsupported'
+ $statusIconKey = "LEDStatusRed"
$SysInfoScriptCompatibility.Foreground = New-Object System.Windows.Media.SolidColorBrush([System.Windows.Media.Colors]::Red)
}
$SysInfoScriptCompatibility.Text = $statusText
+ if ($SysInfoScriptCompatibilityImage) {
+ $statusIconPath = Get-EmojiIconPath -EmojiCharacter $emojiMappings[$statusIconKey]
+ if ($statusIconPath -and (Test-Path $statusIconPath)) {
+ $SysInfoScriptCompatibilityImage.Source = New-Object System.Windows.Media.Imaging.BitmapImage([uri]$statusIconPath)
+ }
+ }
# Aggiorna stato Bitlocker
try {
- $blStatus = Get-BitlockerStatus
+ $blStatusKey = Get-GuiBitlockerStatusKey
+ $blStatus = Get-Loc $blStatusKey
$SysInfoBitlocker.Text = $blStatus
- # Colorazione status Bitlocker (verde/giallo/rosso) basato sulla stringa returned
- if ($blStatus -match '(?i)(attiv|protezione|crittograf|completa)') {
+ # Colorazione status Bitlocker basata su chiave stabile e non sul testo localizzato.
+ if ($blStatusKey -eq 'bitlocker.status.on' -or $blStatusKey -eq 'bitlocker.status.encrypting') {
$SysInfoBitlocker.Foreground = New-Object System.Windows.Media.SolidColorBrush([System.Windows.Media.Colors]::LimeGreen)
}
- elseif ($blStatus -match '(?i)(sospesa|parzial|in corso)') {
+ elseif ($blStatusKey -eq 'bitlocker.status.suspended' -or $blStatusKey -eq 'bitlocker.status.decrypting') {
$SysInfoBitlocker.Foreground = New-Object System.Windows.Media.SolidColorBrush([System.Windows.Media.Colors]::Orange)
}
else {
@@ -1512,7 +2027,7 @@ function Update-ActionsPanel {
# Category Name (Bold, Cyan)
$categoryHeader = New-Object System.Windows.Controls.TextBlock
- $categoryHeader.Text = $category.Name
+ $categoryHeader.Text = Get-ToolkitMenuText $category
$categoryHeader.FontSize = $FontSize.Small
$categoryHeader.FontWeight = 'Bold'
$categoryHeader.Foreground = New-Object System.Windows.Media.SolidColorBrush([System.Windows.Media.Colors]::Cyan)
@@ -1557,7 +2072,7 @@ function Update-ActionsPanel {
# Bold Script Name (White)
$titleRun = New-Object System.Windows.Documents.Run
- $titleRun.Text = $script.Description
+ $titleRun.Text = Get-ToolkitMenuText $script
$titleRun.FontWeight = [System.Windows.FontWeights]::Bold
$titleRun.Foreground = New-Object System.Windows.Media.SolidColorBrush([System.Windows.Media.Colors]::White)
@@ -1933,7 +2448,7 @@ function Start-NextScriptJob {
return @{ Success = $false; TimedOut = $true; ExitCode = -1 }
}
- Write-Warning "[WINTOOLKIT_PROGRESS_TAG] Activity: $Activity | Status: Completato | Percent: 100%."
+ Write-Warning "[WINTOOLKIT_PROGRESS_TAG] Activity: $Activity | Status: Completed | Percent: 100%."
return @{ Success = $true; TimedOut = $false; ExitCode = $result.ExitCode }
}
elseif ($Job -and $result -and $result.GetType().Name -eq 'Job') {
@@ -1990,7 +2505,7 @@ function Start-NextScriptJob {
[switch]$Completed
)
if ($Completed) {
- Write-Warning "[WINTOOLKIT_PROGRESS_TAG] Activity: $Activity | Status: Completato | Percent: 100%."
+ Write-Warning "[WINTOOLKIT_PROGRESS_TAG] Activity: $Activity | Status: Completed | Percent: 100%."
}
elseif ($PercentComplete -ge 0) {
Write-Warning "[WINTOOLKIT_PROGRESS_TAG] Activity: $Activity | Status: $Status | Percent: $($PercentComplete)%."
@@ -2168,11 +2683,11 @@ function Invoke-JobCompletion {
$Global:JobMonitorTimer = $null
}
$executeButton.IsEnabled = $true
- Write-UnifiedLog -Type 'Success' -Message "🎉 Tutti gli script sono stati eseguiti." -GuiColor "#00FF00"
+ Write-UnifiedLog -Type 'Success' -Message "🎉 $(Get-Loc 'gui.allExecuted')" -GuiColor "#00FF00"
if ($progressBar) { $progressBar.Value = 100 }
if ($Global:RebootRequired) {
- $result = [System.Windows.MessageBox]::Show("Il sistema richiede un riavvio per completare le operazioni. Riavviare ora?", "Riavvio Richiesto", [System.Windows.MessageBoxButton]::YesNo, [System.Windows.MessageBoxImage]::Question)
+ $result = [System.Windows.MessageBox]::Show((Get-Loc 'gui.rebootPrompt'), (Get-Loc 'gui.rebootTitle'), [System.Windows.MessageBoxButton]::YesNo, [System.Windows.MessageBoxImage]::Question)
if ($result -eq [System.Windows.MessageBoxResult]::Yes) {
Restart-Computer -Force
}
@@ -2244,7 +2759,7 @@ $executeButton.Add_Click({
}
if ($selectedScripts.Count -eq 0) {
- Write-UnifiedLog -Type 'Warning' -Message "⚠️ Nessuno script selezionato." -GuiColor "#FFA500"
+ Write-UnifiedLog -Type 'Warning' -Message "⚠️ $(Get-Loc 'gui.noneSelected')" -GuiColor "#FFA500"
$window.Dispatcher.Invoke([Action] { $executeButton.IsEnabled = $true })
return
}
@@ -2329,9 +2844,9 @@ $window.Add_Loaded({
Update-ActionsPanel
# Show initial log message
- Write-UnifiedLog -Type 'Success' -Message "🎉 WinToolkit GUI v2.0 inizializzato correttamente." -GuiColor "#00FF00"
+ Write-UnifiedLog -Type 'Success' -Message "🎉 $(Get-Loc 'gui.initialized')" -GuiColor "#00FF00"
Write-UnifiedLog -Type 'Info' -Message "📌 Core Version: $Global:CoreScriptVersion." -GuiColor "#00CED1"
- Write-UnifiedLog -Type 'Info' -Message "💡 Seleziona uno o più script e premi 'Esegui'." -GuiColor "#00CED1"
+ Write-UnifiedLog -Type 'Info' -Message "💡 $(Get-Loc 'gui.instructions')" -GuiColor "#00CED1"
# Minimize console - DISABLED to prevent handle exhaustion crash (Win32Exception 1816)
# Set-ConsoleWindowMinimized
diff --git a/asset/7zr.exe b/assets/7zr.exe
similarity index 100%
rename from asset/7zr.exe
rename to assets/7zr.exe
diff --git a/asset/Basic.xml b/assets/Basic.xml
similarity index 100%
rename from asset/Basic.xml
rename to assets/Basic.xml
diff --git a/asset/DDU.zip b/assets/DDU.zip
similarity index 100%
rename from asset/DDU.zip
rename to assets/DDU.zip
diff --git a/asset/DriverOverrides.json b/assets/DriverOverrides.json
similarity index 100%
rename from asset/DriverOverrides.json
rename to assets/DriverOverrides.json
diff --git a/asset/Microsoft.PowerShell_profile.ps1 b/assets/Microsoft.PowerShell_profile.ps1
similarity index 83%
rename from asset/Microsoft.PowerShell_profile.ps1
rename to assets/Microsoft.PowerShell_profile.ps1
index 7b900e15..8a91cf5b 100644
--- a/asset/Microsoft.PowerShell_profile.ps1
+++ b/assets/Microsoft.PowerShell_profile.ps1
@@ -15,17 +15,17 @@
$ProfileVersion = "2.5.4.5"
-$URL_SPEEDTEST = "https://github.com/Magnetarman/WinToolkit/raw/refs/heads/Dev/asset/speedtest.exe"
+$URL_SPEEDTEST = "https://github.com/Magnetarman/WinToolkit/raw/refs/heads/Dev/assets/speedtest.exe"
$URL_WINTOOLKIT_STABLE = "https://magnetarman.com/WinToolkit"
$URL_WINTOOLKIT_DEV = "https://magnetarman.com/WinToolkit-Dev"
$URL_WINREG = "https://get.activated.win"
$URL_RustDesk_Setup = "https://raw.githubusercontent.com/Magnetarman/WinStarter/refs/heads/main/Asset/RustDesk/SetRustDesk.ps1"
$URL_OHMYPOSH_THEME = "https://raw.githubusercontent.com/JanDeDobbeleer/oh-my-posh/main/themes/atomic.omp.json"
-$URL_PROFILE_DEV = "https://github.com/Magnetarman/WinToolkit/raw/refs/heads/Dev/asset/Microsoft.PowerShell_profile.ps1"
+$URL_PROFILE_DEV = "https://github.com/Magnetarman/WinToolkit/raw/refs/heads/Dev/assets/Microsoft.PowerShell_profile.ps1"
$URL_IP_API = "https://am.i.mullvad.net/ip"
-$URL_WINTOOLKIT_ICO_MAIN = "https://raw.githubusercontent.com/Magnetarman/WinToolkit/refs/heads/main/img/WinToolkit.ico"
-$URL_WINTOOLKIT_ICO_DEV = "https://raw.githubusercontent.com/Magnetarman/WinToolkit/refs/heads/Dev/img/WinToolkit-Dev.ico"
-$URL_PROFILE_MAIN = "https://raw.githubusercontent.com/Magnetarman/WinToolkit/main/asset/Microsoft.PowerShell_profile.ps1"
+$URL_WINTOOLKIT_ICO_MAIN = "https://raw.githubusercontent.com/Magnetarman/WinToolkit/refs/heads/main/images/WinToolkit.ico"
+$URL_WINTOOLKIT_ICO_DEV = "https://raw.githubusercontent.com/Magnetarman/WinToolkit/refs/heads/Dev/images/WinToolkit-Dev.ico"
+$URL_PROFILE_MAIN = "https://raw.githubusercontent.com/Magnetarman/WinToolkit/main/assets/Microsoft.PowerShell_profile.ps1"
$URL_PWSH_RELEASE_API = "https://api.github.com/repos/PowerShell/PowerShell/releases/latest"
# ============================================================================
@@ -82,7 +82,7 @@ function Expand-ZipFile {
Write-Host "✅ Estrazione completata" -ForegroundColor Green
}
catch {
- Write-Host "❌ Errore durante l'estrazione: $($_.Exception.Message)" -ForegroundColor Red
+ Write-Host "❌ Error during extraction: $($_.Exception.Message)" -ForegroundColor Red
}
}
@@ -146,14 +146,14 @@ function Speedtest {
[CmdletBinding()]
param()
- $assetDir = Join-Path $env:LOCALAPPDATA "WinToolkit\asset"
+ $assetDir = Join-Path $env:LOCALAPPDATA "WinToolkit\assets"
$speedtestExePath = Join-Path $assetDir "speedtest.exe"
$desktopPath = [Environment]::GetFolderPath("Desktop")
$timestamp = Get-Date -Format "dd_MM_yyyy_HH_mm_ss"
$outputPath = Join-Path $desktopPath "Speedtest_$timestamp.txt"
if (-not (Test-Path $assetDir)) {
- Write-Host "📦 Creazione directory asset: $assetDir" -ForegroundColor Cyan
+ Write-Host "📦 Creazione directory assets: $assetDir" -ForegroundColor Cyan
New-Item -ItemType Directory -Path $assetDir -Force | Out-Null
}
@@ -171,20 +171,20 @@ function Speedtest {
Write-Host "✅ speedtest.exe scaricato con successo." -ForegroundColor Green
}
catch {
- Write-Host "❌ Errore durante il download di speedtest.exe: $($_.Exception.Message)" -ForegroundColor Red
+ Write-Host "❌ Error downloading speedtest.exe: $($_.Exception.Message)" -ForegroundColor Red
return
}
}
- Write-Host "🚀 Avvio Speedtest..." -ForegroundColor Yellow
+ Write-Host "🚀 Starting Speedtest..." -ForegroundColor Yellow
Write-Host "📝 I risultati (inclusi i progressi) verranno salvati in '$outputPath'." -ForegroundColor Yellow
try {
& $speedtestExePath --accept-license --accept-gdpr -p *>&1 | Tee-Object -FilePath $outputPath
- Write-Host "✅ Speedtest completato e risultati salvati." -ForegroundColor Green
+ Write-Host "✅ Speedtest completed and results saved." -ForegroundColor Green
}
catch {
- Write-Host "❌ Errore durante l'esecuzione di speedtest.exe: $($_.Exception.Message)" -ForegroundColor Red
+ Write-Host "❌ Error running speedtest.exe: $($_.Exception.Message)" -ForegroundColor Red
}
}
@@ -200,7 +200,7 @@ function Reset-Network {
return
}
- Write-Host "⚠️ Attenzione: Questa operazione ripristinerà tutte le impostazioni di rete" -ForegroundColor Yellow
+ Write-Host "⚠️ Warning: This operation will reset all network settings" -ForegroundColor Yellow
Write-Host "ℹ️ Questo include catalogo Winsock, proxy WinHTTP e configurazioni IP" -ForegroundColor Cyan
Write-Host "⚠️ La connessione di rete potrebbe essere interrotta" -ForegroundColor Yellow
@@ -210,7 +210,7 @@ function Reset-Network {
return
}
- Write-Host "`n🚀 Avvio ripristino impostazioni di rete..." -ForegroundColor Cyan
+ Write-Host "`n🚀 Starting network settings reset..." -ForegroundColor Cyan
# Ripristina lo stato pulito del catalogo WinSock
try {
@@ -220,7 +220,7 @@ function Reset-Network {
Write-Host "✅ Catalogo Winsock ripristinato" -ForegroundColor Green
}
catch {
- Write-Host "❌ Errore ripristino Winsock: $($_.Exception.Message)" -ForegroundColor Red
+ Write-Host "❌ Winsock reset error: $($_.Exception.Message)" -ForegroundColor Red
}
# Reimposta le impostazioni proxy WinHTTP su DIRECT
@@ -231,7 +231,7 @@ function Reset-Network {
Write-Host "✅ Impostazioni proxy WinHTTP ripristinate" -ForegroundColor Green
}
catch {
- Write-Host "❌ Errore ripristino proxy WinHTTP: $($_.Exception.Message)" -ForegroundColor Red
+ Write-Host "❌ WinHTTP proxy reset error: $($_.Exception.Message)" -ForegroundColor Red
}
# Rimuove tutte le configurazioni IP definite dall'utente
@@ -250,10 +250,10 @@ function Reset-Network {
}
}
catch {
- Write-Host "❌ Errore ripristino configurazioni IP: $($_.Exception.Message)" -ForegroundColor Red
+ Write-Host "❌ IP configuration reset error: $($_.Exception.Message)" -ForegroundColor Red
}
- Write-Host "`n✅ Ripristino rete completato" -ForegroundColor Green
+ Write-Host "`n✅ Network reset completed" -ForegroundColor Green
Write-Host "⚠️ Riavvia il computer per applicare le modifiche" -ForegroundColor Yellow
}
@@ -268,7 +268,7 @@ function PSProfileUpdate {
$localProfilePath = $PROFILE
$remoteProfileUrl = $URL_PROFILE_MAIN
- Write-Host "🔍 Verifica aggiornamenti per il profilo PowerShell..." -ForegroundColor Cyan
+ Write-Host "🔍 Checking PowerShell profile updates..." -ForegroundColor Cyan
try {
# Controlla la versione locale dalla variabile caricata in sessione
@@ -290,12 +290,12 @@ function PSProfileUpdate {
$remoteVersion = [version]$match.Groups[1].Value
if ($localVersion -ge $remoteVersion) {
- Write-Host "✅ Il profilo è aggiornato all'ultima versione: $localVersion" -ForegroundColor Green
+ Write-Host "✅ The profile is updated to the latest version: $localVersion" -ForegroundColor Green
return
}
- Write-Host "⚠️ È disponibile una versione aggiornata! (Locale: $localVersion -> Remota: $remoteVersion)" -ForegroundColor Yellow
- Write-Host "🔄 Aggiornamento in corso..." -ForegroundColor Cyan
+ Write-Host "⚠️ An updated version is available! (Local: $localVersion -> Remote: $remoteVersion)" -ForegroundColor Yellow
+ Write-Host "🔄 Aggiornamento in progress..." -ForegroundColor Cyan
Invoke-WebRequest -Uri $remoteProfileUrl -OutFile $localProfilePath -UseBasicParsing -ErrorAction Stop
Write-Host "✅ Profilo scaricato e sostituito con successo. Riavvia la sessione per applicare le modifiche." -ForegroundColor Green
@@ -310,7 +310,7 @@ function PSProfileUpdate {
Write-Host "✅ Profilo forzatamente ripristinato dalla versione remota. Riavvia PowerShell." -ForegroundColor Green
}
catch {
- Write-Host "❌ Errore critico: Impossibile scaricare il profilo dal link remoto. Controlla la rete." -ForegroundColor Red
+ Write-Host "❌ Critical error: Unable to download the profile from the remote link. Check the network." -ForegroundColor Red
}
}
}
@@ -329,7 +329,7 @@ function SetRustDesk {
Start-Process -FilePath "wt.exe" -ArgumentList "new-tab -p `"PowerShell`" pwsh.exe -NoExit -ExecutionPolicy Bypass -Command `"irm $URL_RustDesk_Setup | iex`"" -Verb RunAs
- Write-Host "🔍 Avvio configurazione RustDesk..." -ForegroundColor Cyan
+ Write-Host "🔍 Starting RustDesk configuration..." -ForegroundColor Cyan
}
@@ -352,7 +352,7 @@ function SetBranch-Dev {
[CmdletBinding()]
param()
- Write-Host "`n🔄 Avvio procedura di switch di WinToolkit al ramo Dev..." -ForegroundColor Cyan
+ Write-Host "`n🔄 Starting WinToolkit switch procedure to the Dev branch..." -ForegroundColor Cyan
# 1. Ricreazione Scorciatoia Desktop
try {
@@ -386,7 +386,7 @@ function SetBranch-Dev {
Write-Host "✅ Scorciatoia desktop aggiornata al ramo dev." -ForegroundColor Green
}
catch {
- Write-Host "❌ Errore creazione scorciatoia: $($_.Exception.Message)" -ForegroundColor Red
+ Write-Host "❌ Shortcut creation error: $($_.Exception.Message)" -ForegroundColor Red
}
# 2. Sostituzione Profilo PowerShell
@@ -398,14 +398,14 @@ function SetBranch-Dev {
Write-Host "✅ Profilo PowerShell sovrascritto con la versione dev." -ForegroundColor Green
}
catch {
- Write-Host "❌ Errore aggiornamento profilo: $($_.Exception.Message)" -ForegroundColor Red
+ Write-Host "❌ Profile update error: $($_.Exception.Message)" -ForegroundColor Red
}
# 3. Avviso all'utente
- Write-Host "`n🎉 Switch al ramo Dev completato con successo! Modifiche effettuate:" -ForegroundColor Green
+ Write-Host "`n🎉 Switch to Dev branch completed successfully! Changes applied:" -ForegroundColor Green
Write-Host " - Icona desktop 'Win Toolkit' rigenerata e puntata al ramo dev." -ForegroundColor Yellow
Write-Host " - Profilo PowerShell sostituito con la versione del ramo dev." -ForegroundColor Yellow
- Write-Host "`n⚠️ ATTENZIONE: Riavvia il terminale per applicare le modifiche del nuovo profilo." -ForegroundColor Magenta
+ Write-Host "`n⚠️ WARNING: Restart the terminal to apply the new profile changes." -ForegroundColor Magenta
}
function doReboot {
@@ -424,7 +424,7 @@ function SetBranch-Main {
[CmdletBinding()]
param()
- Write-Host "`n🔄 Avvio procedura di switch di WinToolkit al ramo Main..." -ForegroundColor Cyan
+ Write-Host "`n🔄 Starting WinToolkit switch procedure to the Main branch..." -ForegroundColor Cyan
# 1. Ricreazione Scorciatoia Desktop
try {
@@ -458,7 +458,7 @@ function SetBranch-Main {
Write-Host "✅ Scorciatoia desktop aggiornata al ramo main." -ForegroundColor Green
}
catch {
- Write-Host "❌ Errore creazione scorciatoia: $($_.Exception.Message)" -ForegroundColor Red
+ Write-Host "❌ Shortcut creation error: $($_.Exception.Message)" -ForegroundColor Red
}
# 2. Sostituzione Profilo PowerShell
@@ -470,14 +470,14 @@ function SetBranch-Main {
Write-Host "✅ Profilo PowerShell sovrascritto con la versione main." -ForegroundColor Green
}
catch {
- Write-Host "❌ Errore aggiornamento profilo: $($_.Exception.Message)" -ForegroundColor Red
+ Write-Host "❌ Profile update error: $($_.Exception.Message)" -ForegroundColor Red
}
# 3. Avviso all'utente
- Write-Host "`n🎉 Switch al ramo Main completato con successo! Modifiche effettuate:" -ForegroundColor Green
+ Write-Host "`n🎉 Switch to Main branch completed successfully! Changes applied:" -ForegroundColor Green
Write-Host " - Icona desktop 'Win Toolkit' rigenerata e puntata al ramo main." -ForegroundColor Yellow
Write-Host " - Profilo PowerShell sostituito con la versione del ramo main." -ForegroundColor Yellow
- Write-Host "`n⚠️ ATTENZIONE: Riavvia il terminale per applicare le modifiche del nuovo profilo." -ForegroundColor Magenta
+ Write-Host "`n⚠️ WARNING: Restart the terminal to apply the new profile changes." -ForegroundColor Magenta
}
function PS-Reset {
@@ -491,11 +491,11 @@ function PS-Reset {
return
}
- Write-Host "⚠️ ATTENZIONE: Questa operazione eseguirà un ROLLBACK COMPLETO:" -ForegroundColor Yellow
- Write-Host " - Disinstallerà OhMyPosh, Zoxide, Btop, Fastfetch e i font Nerd." -ForegroundColor DarkYellow
- Write-Host " - Eliminerà le cartelle WinToolkit, i log e i file temporanei." -ForegroundColor DarkYellow
- Write-Host " - Resetterà Windows Terminal e il profilo PowerShell alle impostazioni di fabbrica." -ForegroundColor DarkYellow
- Write-Host " - RIAVVIERÀ automaticamente il sistema al termine." -ForegroundColor Red
+ Write-Host "⚠️ WARNING: This operation will perform a FULL ROLLBACK:" -ForegroundColor Yellow
+ Write-Host " - It will uninstall OhMyPosh, Zoxide, Btop, Fastfetch, and Nerd Fonts." -ForegroundColor DarkYellow
+ Write-Host " - It will delete WinToolkit folders, logs, and temporary files." -ForegroundColor DarkYellow
+ Write-Host " - It will reset Windows Terminal and the PowerShell profile to factory settings." -ForegroundColor DarkYellow
+ Write-Host " - It will automatically RESTART the system when finished." -ForegroundColor Red
$confirmation = Read-Host "`n❓ Vuoi procedere in modo irreversibile? (S/N)"
@@ -504,10 +504,10 @@ function PS-Reset {
return
}
- Write-Host "`n🔄 Avvio procedura di reset profondo..." -ForegroundColor Cyan
+ Write-Host "`n🔄 Starting deep reset procedure..." -ForegroundColor Cyan
# 2. Rimozione Scorciatoia Desktop
- Write-Host "`n🗑️ Rimozione scorciatoia Desktop..." -ForegroundColor Cyan
+ Write-Host "`n🗑️ Removing desktop shortcut..." -ForegroundColor Cyan
$desktopPath = [Environment]::GetFolderPath('Desktop')
$shortcut = Join-Path $desktopPath "Win Toolkit.lnk"
if (Test-Path $shortcut) {
@@ -516,7 +516,7 @@ function PS-Reset {
}
# 3. Pulizia cartelle di sistema e temporanee
- Write-Host "`n🧹 Pulizia file temporanei e directory WinToolkit..." -ForegroundColor Cyan
+ Write-Host "`n🧹 Cleaning temporary files and WinToolkit directories..." -ForegroundColor Cyan
$directoriesToRemove = @(
(Join-Path $env:LOCALAPPDATA "WinToolkit"),
(Join-Path $env:TEMP "WinToolkitSetup"),
@@ -529,7 +529,7 @@ function PS-Reset {
Write-Host " -> Rimossa directory: $dir" -ForegroundColor DarkGray
}
}
- Write-Host "✅ Pulizia cartelle completata." -ForegroundColor Green
+ Write-Host "✅ Folder cleanup completed." -ForegroundColor Green
# 4. Reset Windows Terminal
Write-Host "`n🔄 Reset impostazioni Windows Terminal..." -ForegroundColor Cyan
@@ -560,24 +560,24 @@ function PS-Reset {
Write-Host "`n📦 Disinstallazione tool da riga di comando via Winget..." -ForegroundColor Cyan
foreach ($pkg in $wingetPackages) {
- Write-Host " -> Rimozione di $pkg..." -ForegroundColor DarkGray
+ Write-Host " -> Removing $pkg..." -ForegroundColor DarkGray
# Utilizzo di Start-Process per attendere la fine dell'operazione silenziosa
Start-Process -FilePath "winget" -ArgumentList "uninstall --id $pkg --silent --accept-source-agreements" -Wait -NoNewWindow
}
Write-Host "✅ Disinstallazioni Winget completate." -ForegroundColor Green
# 7. Conclusione e Riavvio temporizzato
- Write-Host "`n🎉 RESET COMPLETATO CON SUCCESSO!" -ForegroundColor Green
- Write-Host "L'ambiente è stato riportato alle impostazioni di fabbrica." -ForegroundColor Magenta
- Write-Host "Il sistema verrà riavviato per pulire i processi in sospeso e finalizzare le modifiche.`n" -ForegroundColor Yellow
+ Write-Host "`n🎉 RESET COMPLETED SUCCESSFULLY!" -ForegroundColor Green
+ Write-Host "The environment has been restored to factory settings." -ForegroundColor Magenta
+ Write-Host "The system will restart to clear pending processes and finalize the changes.`n" -ForegroundColor Yellow
- # Countdown di 10 secondi
+ # Countdown di 10 seconds
for ($i = 10; $i -gt 0; $i--) {
- Write-Host "`r⏳ Riavvio automatico tra $i secondi... " -NoNewline -ForegroundColor Red
+ Write-Host "`r⏳ Automatic restart in $i seconds... " -NoNewline -ForegroundColor Red
Start-Sleep -Seconds 1
}
- Write-Host "`n`n🚀 Avvio riavvio del sistema in corso..." -ForegroundColor Cyan
+ Write-Host "`n`n🚀 Starting system restart..." -ForegroundColor Cyan
shutdown /r /f /t 0
}
@@ -585,7 +585,7 @@ function ReadyToGo {
[CmdletBinding()]
param()
- Write-Host "`n🚀 Avvio esecuzione ReadyToGo..." -ForegroundColor Cyan
+ Write-Host "`n🚀 Starting ReadyToGo execution..." -ForegroundColor Cyan
# 1. Elimina i log di PSReadLine
try {
@@ -595,7 +595,7 @@ function ReadyToGo {
Write-Host "✅ Cronologia PSReadLine eliminata." -ForegroundColor Green
}
catch {
- Write-Host "❌ Errore durante l'eliminazione della cronologia PSReadLine: $($_.Exception.Message)" -ForegroundColor Red
+ Write-Host "❌ Error deleting PSReadLine history: $($_.Exception.Message)" -ForegroundColor Red
}
# 2. Reset Microsoft Edge
@@ -615,18 +615,18 @@ function ReadyToGo {
}
}
catch {
- Write-Host "❌ Errore durante il reset di Microsoft Edge: $($_.Exception.Message)" -ForegroundColor Red
+ Write-Host "❌ Error resetting Microsoft Edge: $($_.Exception.Message)" -ForegroundColor Red
}
# 3. Disinstallazione di Revo Uninstaller Pro (se presente)
try {
- Write-Host "📦 Verifica e disinstallazione di Revo Uninstaller Pro..." -ForegroundColor Cyan
+ Write-Host "📦 Checking and uninstalling Revo Uninstaller Pro..." -ForegroundColor Cyan
# Esegui disinstallazione silenziosa ignorando gli errori e accettando gli accordi
Start-Process -FilePath "winget" -ArgumentList "uninstall --id RevoUninstaller.RevoUninstallerPro --silent --accept-source-agreements" -Wait -NoNewWindow
- Write-Host "✅ Verifica Revo Uninstaller Pro completata." -ForegroundColor Green
+ Write-Host "✅ Revo Uninstaller Pro check completed." -ForegroundColor Green
}
catch {
- Write-Host "ℹ️ Revo Uninstaller Pro non trovato o errore durante la disinstallazione." -ForegroundColor Yellow
+ Write-Host "ℹ️ Revo Uninstaller Pro not found or error during uninstall." -ForegroundColor Yellow
}
Write-Host "🎉 Operazione ReadyToGo completata con successo!" -ForegroundColor Green
@@ -713,7 +713,7 @@ function EditPSProfile {
}
}
catch {
- Write-Host "⚠️ Errore nell'apertura con $($EDITOR_INFO.Name): $_" -ForegroundColor Yellow
+ Write-Host "⚠️ Error opening with $($EDITOR_INFO.Name): $_" -ForegroundColor Yellow
Write-Host "📝 Apertura con Notepad come fallback..." -ForegroundColor Cyan
Start-Process notepad $PROFILE
}
@@ -728,15 +728,15 @@ function Show-Help {
$($PSStyle.Foreground.Cyan)Guida al Profilo PowerShell$($PSStyle.Reset) $($PSStyle.Foreground.Red)========================================================$($PSStyle.Reset)
$($PSStyle.Foreground.Green)Verde (Safe):$($PSStyle.Reset) L'utilizzo non comporta rischi o problemi.
-$($PSStyle.Foreground.Yellow)Giallo (Warning):$($PSStyle.Reset) Attenzione! Leggere la descrizione perché questi comandi comportano variazioni rischiose al sistema.
+$($PSStyle.Foreground.Yellow)Yellow (Warning):$($PSStyle.Reset) Warning! Read the description because these commands can make risky system changes.
$($PSStyle.Foreground.Red)Rosso (ALERT!):$($PSStyle.Reset) STOP! Queste funzioni sono state designare per effettuare modifiche profonde e distruttive. Attento!
$($PSStyle.Foreground.Green)====================================================================================$($PSStyle.Reset)
-$($PSStyle.Foreground.Cyan)Informazioni Sistema e Hardware$($PSStyle.Reset) $($PSStyle.Foreground.Yellow)----------------------------------------------------$($PSStyle.Reset)
+$($PSStyle.Foreground.Cyan)System and hardware information$($PSStyle.Reset) $($PSStyle.Foreground.Yellow)----------------------------------------------------$($PSStyle.Reset)
$($PSStyle.Foreground.Green)Get-SystemInfo$($PSStyle.Reset) - Visualizza informazioni di sistema dettagliate.
-$($PSStyle.Foreground.Green)Get-MainboardInfo$($PSStyle.Reset) - Informazioni sulla scheda madre.
-$($PSStyle.Foreground.Green)Get-RAMInfo$($PSStyle.Reset) - Informazioni sui moduli RAM installati.
+$($PSStyle.Foreground.Green)Get-MainboardInfo$($PSStyle.Reset) - Motherboard information.
+$($PSStyle.Foreground.Green)Get-RAMInfo$($PSStyle.Reset) - Information about installed RAM modules.
$($PSStyle.Foreground.Green)Get-PublicIP$($PSStyle.Reset) - Recupera l'indirizzo IP pubblico.
$($PSStyle.Foreground.Cyan)Gestione File e Directory$($PSStyle.Reset) $($PSStyle.Foreground.Yellow)----------------------------------------------------------$($PSStyle.Reset)
@@ -746,7 +746,7 @@ $($PSStyle.Foreground.Green)Find-File$($PSStyle.Reset) - Cerca f
$($PSStyle.Foreground.Green)Expand-ZipFile$($PSStyle.Reset) - Estrae un file ZIP nella directory corrente.
$($PSStyle.Foreground.Cyan)Diagnostica e Strumenti di Rete$($PSStyle.Reset) $($PSStyle.Foreground.Yellow)----------------------------------------------------$($PSStyle.Reset)
-$($PSStyle.Foreground.Green)Speedtest$($PSStyle.Reset) - Esegue un test della velocità di rete.
+$($PSStyle.Foreground.Green)Speedtest$($PSStyle.Reset) - Runs a network speed test.
$($PSStyle.Foreground.Green)FlushDns$($PSStyle.Reset) - Svuota la cache DNS.
$($PSStyle.Foreground.Yellow)Reset-Network$($PSStyle.Reset) - Ripristina le impostazioni di rete a quelle predefinite.
@@ -812,7 +812,7 @@ function Update-Pwsh {
return
}
- Write-Host "🔍 Verifica degli aggiornamenti di PowerShell..." -ForegroundColor Cyan
+ Write-Host "🔍 Checking PowerShell updates..." -ForegroundColor Cyan
try {
[version]$currentPSVersion = $PSVersionTable.PSVersion
@@ -823,7 +823,7 @@ function Update-Pwsh {
Write-Host " Ultima versione : v$latestPSVersion" -ForegroundColor Gray
if ($currentPSVersion -ge $latestPSVersion) {
- Write-Host "✅ PowerShell è già aggiornato (v$currentPSVersion)" -ForegroundColor Green
+ Write-Host "✅ PowerShell is already up to date (v$currentPSVersion)" -ForegroundColor Green
return
}
@@ -837,38 +837,38 @@ function Update-Pwsh {
Write-Host "🔄 Aggiornamento di PowerShell in corso (v$currentPSVersion → v$latestPSVersion)..." -ForegroundColor Yellow
winget upgrade --id Microsoft.PowerShell --source winget --accept-source-agreements --accept-package-agreements
if ($LASTEXITCODE -eq 0) {
- Write-Host "✅ Aggiornamento completato. Chiudi e riapri il terminale per usare PowerShell v$latestPSVersion." -ForegroundColor Green
+ Write-Host "✅ Update completed. Close and reopen the terminal to use PowerShell v$latestPSVersion." -ForegroundColor Green
}
elseif ($LASTEXITCODE -eq -1978335189) {
Write-Host "" -ForegroundColor Yellow
- Write-Host "⚠️ Rilevata incompatibilità tecnologia installazione (codice: $LASTEXITCODE)." -ForegroundColor Yellow
+ Write-Host "⚠️ Detected installation technology incompatibility (code: $LASTEXITCODE)." -ForegroundColor Yellow
Write-Host " Il pacchetto installato utilizza un metodo diverso da quello atteso da winget." -ForegroundColor DarkYellow
- Write-Host "🔄 Avvio procedura di reinstallazione automatica..." -ForegroundColor Cyan
+ Write-Host "🔄 Starting automatic reinstall procedure..." -ForegroundColor Cyan
# Step 1: Disinstallazione
- Write-Host " 1/2 - Disinstallazione di Microsoft.PowerShell in corso..." -ForegroundColor Cyan
+ Write-Host " 1/2 - Disinstallazione di Microsoft.PowerShell in progress..." -ForegroundColor Cyan
winget uninstall --id Microsoft.PowerShell --accept-source-agreements --silent --all-versions
if ($LASTEXITCODE -ne 0) {
- Write-Host "❌ Disinstallazione fallita (codice: $LASTEXITCODE). Operazione interrotta." -ForegroundColor Red
- Write-Host " Prova a disinstallare PowerShell manualmente e poi esegui nuovamente Update-Pwsh." -ForegroundColor DarkYellow
+ Write-Host "❌ Uninstall failed (code: $LASTEXITCODE). Operation interrupted." -ForegroundColor Red
+ Write-Host " Try uninstalling PowerShell manually, then run Update-Pwsh again." -ForegroundColor DarkYellow
return
}
Write-Host " ✅ Disinstallazione completata." -ForegroundColor Green
# Step 2: Reinstallazione
- Write-Host " 2/2 - Installazione di PowerShell v$latestPSVersion in corso..." -ForegroundColor Cyan
+ Write-Host " 2/2 - Installing PowerShell v$latestPSVersion in progress..." -ForegroundColor Cyan
winget install --id Microsoft.PowerShell --source winget --accept-source-agreements --accept-package-agreements
if ($LASTEXITCODE -eq 0) {
Write-Host "✅ Reinstallazione completata con successo." -ForegroundColor Green
Write-Host "⚠️ IMPORTANTE: Devi aprire una nuova sessione del terminale per usare PowerShell v$latestPSVersion." -ForegroundColor Yellow
}
else {
- Write-Host "❌ Reinstallazione fallita (codice: $LASTEXITCODE)." -ForegroundColor Red
- Write-Host " Consulta l'output di winget qui sopra per dettagli sull'errore." -ForegroundColor DarkYellow
+ Write-Host "❌ Reinstall failed (code: $LASTEXITCODE)." -ForegroundColor Red
+ Write-Host " Check the winget output above for error details." -ForegroundColor DarkYellow
}
}
else {
- Write-Host "⚠️ winget ha restituito il codice di uscita $LASTEXITCODE. Verifica l'output qui sopra." -ForegroundColor Yellow
+ Write-Host "⚠️ winget returned exit code $LASTEXITCODE. Check the output above." -ForegroundColor Yellow
}
}
catch {
diff --git a/asset/NVCleanstall_1.19.0.exe b/assets/NVCleanstall_1.19.0.exe
similarity index 100%
rename from asset/NVCleanstall_1.19.0.exe
rename to assets/NVCleanstall_1.19.0.exe
diff --git a/asset/OOSU10.exe b/assets/OOSU10.exe
similarity index 100%
rename from asset/OOSU10.exe
rename to assets/OOSU10.exe
diff --git a/asset/Setup.exe b/assets/Setup.exe
similarity index 100%
rename from asset/Setup.exe
rename to assets/Setup.exe
diff --git a/asset/dxwebsetup.exe b/assets/dxwebsetup.exe
similarity index 100%
rename from asset/dxwebsetup.exe
rename to assets/dxwebsetup.exe
diff --git a/asset/ooshutup10.cfg b/assets/ooshutup10.cfg
similarity index 100%
rename from asset/ooshutup10.cfg
rename to assets/ooshutup10.cfg
diff --git a/asset/png/U+1F194.png b/assets/png/U+1F194.png
similarity index 100%
rename from asset/png/U+1F194.png
rename to assets/png/U+1F194.png
diff --git a/asset/png/U+1F310.png b/assets/png/U+1F310.png
similarity index 100%
rename from asset/png/U+1F310.png
rename to assets/png/U+1F310.png
diff --git a/asset/png/U+1F389.png b/assets/png/U+1F389.png
similarity index 100%
rename from asset/png/U+1F389.png
rename to assets/png/U+1F389.png
diff --git a/asset/png/U+1F393.png b/assets/png/U+1F393.png
similarity index 100%
rename from asset/png/U+1F393.png
rename to assets/png/U+1F393.png
diff --git a/asset/png/U+1F399.png b/assets/png/U+1F399.png
similarity index 100%
rename from asset/png/U+1F399.png
rename to assets/png/U+1F399.png
diff --git a/asset/png/U+1F39A.png b/assets/png/U+1F39A.png
similarity index 100%
rename from asset/png/U+1F39A.png
rename to assets/png/U+1F39A.png
diff --git a/asset/png/U+1F3A4.png b/assets/png/U+1F3A4.png
similarity index 100%
rename from asset/png/U+1F3A4.png
rename to assets/png/U+1F3A4.png
diff --git a/asset/png/U+1F3A7.png b/assets/png/U+1F3A7.png
similarity index 100%
rename from asset/png/U+1F3A7.png
rename to assets/png/U+1F3A7.png
diff --git a/asset/png/U+1F3A8.png b/assets/png/U+1F3A8.png
similarity index 100%
rename from asset/png/U+1F3A8.png
rename to assets/png/U+1F3A8.png
diff --git a/asset/png/U+1F3AE.png b/assets/png/U+1F3AE.png
similarity index 100%
rename from asset/png/U+1F3AE.png
rename to assets/png/U+1F3AE.png
diff --git a/asset/png/U+1F3AF.png b/assets/png/U+1F3AF.png
similarity index 100%
rename from asset/png/U+1F3AF.png
rename to assets/png/U+1F3AF.png
diff --git a/asset/png/U+1F3B5.png b/assets/png/U+1F3B5.png
similarity index 100%
rename from asset/png/U+1F3B5.png
rename to assets/png/U+1F3B5.png
diff --git a/asset/png/U+1F3E0.png b/assets/png/U+1F3E0.png
similarity index 100%
rename from asset/png/U+1F3E0.png
rename to assets/png/U+1F3E0.png
diff --git a/asset/png/U+1F3E2.png b/assets/png/U+1F3E2.png
similarity index 100%
rename from asset/png/U+1F3E2.png
rename to assets/png/U+1F3E2.png
diff --git a/asset/png/U+1F3F7.png b/assets/png/U+1F3F7.png
similarity index 100%
rename from asset/png/U+1F3F7.png
rename to assets/png/U+1F3F7.png
diff --git a/asset/png/U+1F44C.png b/assets/png/U+1F44C.png
similarity index 100%
rename from asset/png/U+1F44C.png
rename to assets/png/U+1F44C.png
diff --git a/asset/png/U+1F44D.png b/assets/png/U+1F44D.png
similarity index 100%
rename from asset/png/U+1F44D.png
rename to assets/png/U+1F44D.png
diff --git a/asset/png/U+1F451.png b/assets/png/U+1F451.png
similarity index 100%
rename from asset/png/U+1F451.png
rename to assets/png/U+1F451.png
diff --git a/asset/png/U+1F48A.png b/assets/png/U+1F48A.png
similarity index 100%
rename from asset/png/U+1F48A.png
rename to assets/png/U+1F48A.png
diff --git a/asset/png/U+1F48E.png b/assets/png/U+1F48E.png
similarity index 100%
rename from asset/png/U+1F48E.png
rename to assets/png/U+1F48E.png
diff --git a/asset/png/U+1F4A1.png b/assets/png/U+1F4A1.png
similarity index 100%
rename from asset/png/U+1F4A1.png
rename to assets/png/U+1F4A1.png
diff --git a/asset/png/U+1F4AD.png b/assets/png/U+1F4AD.png
similarity index 100%
rename from asset/png/U+1F4AD.png
rename to assets/png/U+1F4AD.png
diff --git a/asset/png/U+1F4BB.png b/assets/png/U+1F4BB.png
similarity index 100%
rename from asset/png/U+1F4BB.png
rename to assets/png/U+1F4BB.png
diff --git a/asset/png/U+1F4BC.png b/assets/png/U+1F4BC.png
similarity index 100%
rename from asset/png/U+1F4BC.png
rename to assets/png/U+1F4BC.png
diff --git a/asset/png/U+1F4BD.png b/assets/png/U+1F4BD.png
similarity index 100%
rename from asset/png/U+1F4BD.png
rename to assets/png/U+1F4BD.png
diff --git a/asset/png/U+1F4BE.png b/assets/png/U+1F4BE.png
similarity index 100%
rename from asset/png/U+1F4BE.png
rename to assets/png/U+1F4BE.png
diff --git a/asset/png/U+1F4BF.png b/assets/png/U+1F4BF.png
similarity index 100%
rename from asset/png/U+1F4BF.png
rename to assets/png/U+1F4BF.png
diff --git a/asset/png/U+1F4C1.png b/assets/png/U+1F4C1.png
similarity index 100%
rename from asset/png/U+1F4C1.png
rename to assets/png/U+1F4C1.png
diff --git a/asset/png/U+1F4C2.png b/assets/png/U+1F4C2.png
similarity index 100%
rename from asset/png/U+1F4C2.png
rename to assets/png/U+1F4C2.png
diff --git a/asset/png/U+1F4C4.png b/assets/png/U+1F4C4.png
similarity index 100%
rename from asset/png/U+1F4C4.png
rename to assets/png/U+1F4C4.png
diff --git a/asset/png/U+1F4C5.png b/assets/png/U+1F4C5.png
similarity index 100%
rename from asset/png/U+1F4C5.png
rename to assets/png/U+1F4C5.png
diff --git a/asset/png/U+1F4CA.png b/assets/png/U+1F4CA.png
similarity index 100%
rename from asset/png/U+1F4CA.png
rename to assets/png/U+1F4CA.png
diff --git a/asset/png/U+1F4CB.png b/assets/png/U+1F4CB.png
similarity index 100%
rename from asset/png/U+1F4CB.png
rename to assets/png/U+1F4CB.png
diff --git a/asset/png/U+1F4DC.png b/assets/png/U+1F4DC.png
similarity index 100%
rename from asset/png/U+1F4DC.png
rename to assets/png/U+1F4DC.png
diff --git a/asset/png/U+1F4DD.png b/assets/png/U+1F4DD.png
similarity index 100%
rename from asset/png/U+1F4DD.png
rename to assets/png/U+1F4DD.png
diff --git a/asset/png/U+1F4E1.png b/assets/png/U+1F4E1.png
similarity index 100%
rename from asset/png/U+1F4E1.png
rename to assets/png/U+1F4E1.png
diff --git a/asset/png/U+1F4E6.png b/assets/png/U+1F4E6.png
similarity index 100%
rename from asset/png/U+1F4E6.png
rename to assets/png/U+1F4E6.png
diff --git a/asset/png/U+1F504.png b/assets/png/U+1F504.png
similarity index 100%
rename from asset/png/U+1F504.png
rename to assets/png/U+1F504.png
diff --git a/asset/png/U+1F50A.png b/assets/png/U+1F50A.png
similarity index 100%
rename from asset/png/U+1F50A.png
rename to assets/png/U+1F50A.png
diff --git a/asset/png/U+1F50D.png b/assets/png/U+1F50D.png
similarity index 100%
rename from asset/png/U+1F50D.png
rename to assets/png/U+1F50D.png
diff --git a/asset/png/U+1F510.png b/assets/png/U+1F510.png
similarity index 100%
rename from asset/png/U+1F510.png
rename to assets/png/U+1F510.png
diff --git a/asset/png/U+1F511.png b/assets/png/U+1F511.png
similarity index 100%
rename from asset/png/U+1F511.png
rename to assets/png/U+1F511.png
diff --git a/asset/png/U+1F512.png b/assets/png/U+1F512.png
similarity index 100%
rename from asset/png/U+1F512.png
rename to assets/png/U+1F512.png
diff --git a/asset/png/U+1F525.png b/assets/png/U+1F525.png
similarity index 100%
rename from asset/png/U+1F525.png
rename to assets/png/U+1F525.png
diff --git a/asset/png/U+1F527.png b/assets/png/U+1F527.png
similarity index 100%
rename from asset/png/U+1F527.png
rename to assets/png/U+1F527.png
diff --git a/asset/png/U+1F579.png b/assets/png/U+1F579.png
similarity index 100%
rename from asset/png/U+1F579.png
rename to assets/png/U+1F579.png
diff --git a/asset/png/U+1F5A8.png b/assets/png/U+1F5A8.png
similarity index 100%
rename from asset/png/U+1F5A8.png
rename to assets/png/U+1F5A8.png
diff --git a/asset/png/U+1F5B3.png b/assets/png/U+1F5B3.png
similarity index 100%
rename from asset/png/U+1F5B3.png
rename to assets/png/U+1F5B3.png
diff --git a/asset/png/U+1F5BC.png b/assets/png/U+1F5BC.png
similarity index 100%
rename from asset/png/U+1F5BC.png
rename to assets/png/U+1F5BC.png
diff --git a/asset/png/U+1F5C2.png b/assets/png/U+1F5C2.png
similarity index 100%
rename from asset/png/U+1F5C2.png
rename to assets/png/U+1F5C2.png
diff --git a/asset/png/U+1F5C3.png b/assets/png/U+1F5C3.png
similarity index 100%
rename from asset/png/U+1F5C3.png
rename to assets/png/U+1F5C3.png
diff --git a/asset/png/U+1F5D1.png b/assets/png/U+1F5D1.png
similarity index 100%
rename from asset/png/U+1F5D1.png
rename to assets/png/U+1F5D1.png
diff --git a/asset/png/U+1F5DD.png b/assets/png/U+1F5DD.png
similarity index 100%
rename from asset/png/U+1F5DD.png
rename to assets/png/U+1F5DD.png
diff --git a/asset/png/U+1F605.png b/assets/png/U+1F605.png
similarity index 100%
rename from asset/png/U+1F605.png
rename to assets/png/U+1F605.png
diff --git a/asset/png/U+1F680.png b/assets/png/U+1F680.png
similarity index 100%
rename from asset/png/U+1F680.png
rename to assets/png/U+1F680.png
diff --git a/asset/png/U+1F6D2.png b/assets/png/U+1F6D2.png
similarity index 100%
rename from asset/png/U+1F6D2.png
rename to assets/png/U+1F6D2.png
diff --git a/asset/png/U+1F6E0.png b/assets/png/U+1F6E0.png
similarity index 100%
rename from asset/png/U+1F6E0.png
rename to assets/png/U+1F6E0.png
diff --git a/asset/png/U+1F6E1.png b/assets/png/U+1F6E1.png
similarity index 100%
rename from asset/png/U+1F6E1.png
rename to assets/png/U+1F6E1.png
diff --git a/asset/png/U+1F7E1.png b/assets/png/U+1F7E1.png
similarity index 100%
rename from asset/png/U+1F7E1.png
rename to assets/png/U+1F7E1.png
diff --git a/asset/png/U+1F7E2.png b/assets/png/U+1F7E2.png
similarity index 100%
rename from asset/png/U+1F7E2.png
rename to assets/png/U+1F7E2.png
diff --git a/asset/png/U+1F921.png b/assets/png/U+1F921.png
similarity index 100%
rename from asset/png/U+1F921.png
rename to assets/png/U+1F921.png
diff --git a/asset/png/U+1F923.png b/assets/png/U+1F923.png
similarity index 100%
rename from asset/png/U+1F923.png
rename to assets/png/U+1F923.png
diff --git a/asset/png/U+1F928.png b/assets/png/U+1F928.png
similarity index 100%
rename from asset/png/U+1F928.png
rename to assets/png/U+1F928.png
diff --git a/asset/png/U+1F9E0.png b/assets/png/U+1F9E0.png
similarity index 100%
rename from asset/png/U+1F9E0.png
rename to assets/png/U+1F9E0.png
diff --git a/asset/png/U+1F9EA.png b/assets/png/U+1F9EA.png
similarity index 100%
rename from asset/png/U+1F9EA.png
rename to assets/png/U+1F9EA.png
diff --git a/asset/png/U+1F9F0.png b/assets/png/U+1F9F0.png
similarity index 100%
rename from asset/png/U+1F9F0.png
rename to assets/png/U+1F9F0.png
diff --git a/asset/png/U+1F9F9.png b/assets/png/U+1F9F9.png
similarity index 100%
rename from asset/png/U+1F9F9.png
rename to assets/png/U+1F9F9.png
diff --git a/asset/png/U+1FA93.png b/assets/png/U+1FA93.png
similarity index 100%
rename from asset/png/U+1FA93.png
rename to assets/png/U+1FA93.png
diff --git a/asset/png/U+1FA9B.png b/assets/png/U+1FA9B.png
similarity index 100%
rename from asset/png/U+1FA9B.png
rename to assets/png/U+1FA9B.png
diff --git a/asset/png/U+1FA9D.png b/assets/png/U+1FA9D.png
similarity index 100%
rename from asset/png/U+1FA9D.png
rename to assets/png/U+1FA9D.png
diff --git a/asset/png/U+1FA9F.png b/assets/png/U+1FA9F.png
similarity index 100%
rename from asset/png/U+1FA9F.png
rename to assets/png/U+1FA9F.png
diff --git a/asset/png/U+2139.png b/assets/png/U+2139.png
similarity index 100%
rename from asset/png/U+2139.png
rename to assets/png/U+2139.png
diff --git a/asset/png/U+23F0.png b/assets/png/U+23F0.png
similarity index 100%
rename from asset/png/U+23F0.png
rename to assets/png/U+23F0.png
diff --git a/asset/png/U+23F3.png b/assets/png/U+23F3.png
similarity index 100%
rename from asset/png/U+23F3.png
rename to assets/png/U+23F3.png
diff --git a/asset/png/U+23F8.png b/assets/png/U+23F8.png
similarity index 100%
rename from asset/png/U+23F8.png
rename to assets/png/U+23F8.png
diff --git a/asset/png/U+25B6.png b/assets/png/U+25B6.png
similarity index 100%
rename from asset/png/U+25B6.png
rename to assets/png/U+25B6.png
diff --git a/asset/png/U+2699.png b/assets/png/U+2699.png
similarity index 100%
rename from asset/png/U+2699.png
rename to assets/png/U+2699.png
diff --git a/asset/png/U+26A0.png b/assets/png/U+26A0.png
similarity index 100%
rename from asset/png/U+26A0.png
rename to assets/png/U+26A0.png
diff --git a/asset/png/U+26A1.png b/assets/png/U+26A1.png
similarity index 100%
rename from asset/png/U+26A1.png
rename to assets/png/U+26A1.png
diff --git a/asset/png/U+2705.png b/assets/png/U+2705.png
similarity index 100%
rename from asset/png/U+2705.png
rename to assets/png/U+2705.png
diff --git a/asset/png/U+2728.png b/assets/png/U+2728.png
similarity index 100%
rename from asset/png/U+2728.png
rename to assets/png/U+2728.png
diff --git a/asset/png/U+274C.png b/assets/png/U+274C.png
similarity index 100%
rename from asset/png/U+274C.png
rename to assets/png/U+274C.png
diff --git a/asset/png/U+2764.png b/assets/png/U+2764.png
similarity index 100%
rename from asset/png/U+2764.png
rename to assets/png/U+2764.png
diff --git a/asset/png/U+2B07.png b/assets/png/U+2B07.png
similarity index 100%
rename from asset/png/U+2B07.png
rename to assets/png/U+2B07.png
diff --git a/asset/settings.json b/assets/settings.json
similarity index 100%
rename from asset/settings.json
rename to assets/settings.json
diff --git a/asset/speedtest.exe b/assets/speedtest.exe
similarity index 100%
rename from asset/speedtest.exe
rename to assets/speedtest.exe
diff --git a/compiler.ps1 b/compiler.ps1
index 4549088e..92067a08 100644
--- a/compiler.ps1
+++ b/compiler.ps1
@@ -12,6 +12,51 @@ $ScriptStartTime = [System.Diagnostics.Stopwatch]::StartNew()
# ============================================================================
# 1. SISTEMA DI LOGGING ENTERPRISE
# ============================================================================
+function Convert-SourceTextToEnglish {
+ param([string]$Text)
+ if ([string]::IsNullOrWhiteSpace($Text)) { return $Text }
+
+ $translated = $Text
+ $replacements = [ordered]@{
+ 'Avvio processo di build WinToolkit.' = 'Starting WinToolkit build process.'
+ 'Errore di inzializzazione' = 'Initialization error'
+ 'Errore I/O durante la lettura dei file sorgente' = 'I/O error while reading source files'
+ 'Errore I/O aggregando il modulo' = 'I/O error while aggregating module'
+ 'Avvio minificazione sicura via tokenizer PowerShell.' = 'Starting safe minification through the PowerShell tokenizer.'
+ 'Il sorgente contiene' = 'The source contains'
+ 'errore/i di parse pre-esistenti. Minificazione applicata comunque.' = 'pre-existing parse error(s). Minification applied anyway.'
+ 'Rilevati' = 'Detected'
+ 'errore/i sintassi post-minificazione - rollback al sorgente originale.' = 'post-minification syntax error(s) - rolling back to original source.'
+ 'Minificazione completata' = 'Minification completed'
+ 'nessun errore di sintassi rilevato' = 'no syntax errors detected'
+ 'Errore imprevisto durante la minificazione' = 'Unexpected error during minification'
+ 'Salvataggio eseguibile stand-alone' = 'Saving standalone executable'
+ 'Lettura template originario' = 'Reading source template'
+ 'Inizio aggregazione' = 'Starting aggregation'
+ ' di ' = ' of '
+ ' moduli' = ' modules'
+ 'Rilevata funzione interna in' = 'Detected internal function in'
+ 'Applicazione de-incapsulamento.' = 'Applying unwrapping.'
+ 'Modulo processato' = 'Module processed'
+ 'BUILD DASHBOARD RIEPILOGATIVA' = 'SUMMARY BUILD DASHBOARD'
+ 'STATISTICHE MODULI' = 'MODULE STATISTICS'
+ 'STORAGE E COMPRESSIONE' = 'STORAGE AND COMPRESSION'
+ 'Sorgenti' = 'Sources'
+ 'File Finale' = 'Final file'
+ 'Riduzione' = 'Reduction'
+ 'Flag -Minify non rilevato' = 'Flag -Minify not detected'
+ 'Esecuzione' = 'Execution'
+ 'Pipeline compiler.ps1 eseguita con codice' = 'compiler.ps1 pipeline executed with code'
+ 'Saltati' = 'Skipped'
+ 'Errori' = 'Errors'
+ 'Processati' = 'Processed'
+ }
+ foreach ($entry in $replacements.GetEnumerator()) {
+ $translated = $translated.Replace($entry.Key, $entry.Value)
+ }
+ return $translated
+}
+
function Write-StyledMessage {
param(
[Parameter(Mandatory = $true)]
@@ -23,6 +68,7 @@ function Write-StyledMessage {
)
$timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
+ $Message = Convert-SourceTextToEnglish -Text $Message
switch ($Type) {
'Success' {
@@ -54,13 +100,18 @@ Write-StyledMessage 'Info' "Avvio processo di build WinToolkit."
# 2. INIZIALIZZAZIONE E VERIFICA PERCORSI
# ============================================================================
$scriptPath = Split-Path -Parent $MyInvocation.MyCommand.Path
-$toolFolder = Join-Path $scriptPath "tool"
+$toolFolder = Join-Path $scriptPath "tools"
$sourceFile = Join-Path $scriptPath "WinToolkit-template.ps1"
$outputFile = Join-Path $scriptPath "WinToolkit.ps1"
try {
- if (-not (Test-Path $sourceFile)) { throw "File template non trovato in: $sourceFile" }
- if (-not (Test-Path $toolFolder)) { throw "Cartella tool non trovata in: $toolFolder" }
+ if (-not (Test-Path $sourceFile)) {
+ throw "File template non trovato in: $sourceFile"
+ }
+
+ if (-not (Test-Path $toolFolder)) {
+ throw "Cartella tools non trovata in: $toolFolder"
+ }
}
catch {
Write-StyledMessage 'Error' "Errore di inzializzazione: $($_.Exception.Message)."
@@ -287,7 +338,7 @@ if ($Minify) {
if ($verifyErrors.Count -gt 0) {
Write-StyledMessage 'Warning' "Rilevati $($verifyErrors.Count) errore/i sintassi post-minificazione - rollback al sorgente originale."
foreach ($e in $verifyErrors) {
- Write-StyledMessage 'Warning' " Riga $($e.Extent.StartLineNumber): $($e.Message)."
+ Write-StyledMessage 'Warning' " Line $($e.Extent.StartLineNumber): $($e.Message)."
}
$templateLines = $backupLines
}
@@ -298,7 +349,7 @@ if ($Minify) {
}
catch {
Write-StyledMessage 'Error' "Errore imprevisto durante la minificazione: $($_.Exception.Message)."
- Write-StyledMessage 'Warning' "Continuazione build senza minificazione."
+ Write-StyledMessage 'Warning' "Continuing build without minification."
}
Write-Host ""
}
@@ -317,7 +368,7 @@ try {
}
catch {
- Write-StyledMessage 'Error' "Fallimento irreversibile nella scrittura finale su disco: $($_.Exception.Message)."
+ Write-StyledMessage 'Error' "Irreversible failure while writing final file to disk: $($_.Exception.Message)."
exit 1
}
@@ -340,30 +391,30 @@ $linesReduction = $stats.TotalSourceLines - $finalLinesCount
Write-Host ""
Write-Host "======================================================================" -ForegroundColor Cyan
-Write-Host " BUILD DASHBOARD RIEPILOGATIVA " -ForegroundColor Cyan
+Write-Host " SUMMARY BUILD DASHBOARD " -ForegroundColor Cyan
Write-Host "======================================================================" -ForegroundColor Cyan
-Write-Host "📊 STATISTICHE MODULI " -ForegroundColor Yellow
-Write-Host " ✅ Processati : $($stats.Processed)" -ForegroundColor Green
-Write-Host " ⚠️ Saltati : $($stats.Skipped)" -ForegroundColor Yellow
+Write-Host "📊 MODULE STATISTICS " -ForegroundColor Yellow
+Write-Host " ✅ Processed : $($stats.Processed)" -ForegroundColor Green
+Write-Host " ⚠️ Skipped : $($stats.Skipped)" -ForegroundColor Yellow
if ($stats.Errors -gt 0) {
- Write-Host "❌ Errori : $($stats.Errors)" -ForegroundColor Red
+ Write-Host "❌ Errors : $($stats.Errors)" -ForegroundColor Red
}
else {
- Write-Host "❌ Errori : 0" -ForegroundColor DarkGray
+ Write-Host "❌ Errors : 0" -ForegroundColor DarkGray
}
Write-Host "======================================================================" -ForegroundColor Cyan
-Write-Host "💾 STORAGE E COMPRESSIONE " -ForegroundColor Yellow
-Write-Host " 📦 Sorgenti : $sourceMB KB ($($stats.TotalSourceLines) righe)" -ForegroundColor White
-Write-Host " 📄 File Finale : $finalMB KB ($finalLinesCount righe)" -ForegroundColor Cyan
+Write-Host "💾 STORAGE AND COMPRESSION " -ForegroundColor Yellow
+Write-Host " 📦 Sources : $sourceMB KB ($($stats.TotalSourceLines) lines)" -ForegroundColor White
+Write-Host " 📄 Final file : $finalMB KB ($finalLinesCount lines)" -ForegroundColor Cyan
if ($Minify) {
- Write-Host " 📉 Riduzione : $compressionPercent % ($linesReduction righe eliminate)" -ForegroundColor Green
+ Write-Host " 📉 Reduction : $compressionPercent % ($linesReduction lines removed)" -ForegroundColor Green
}
else {
- Write-Host " 📉 Riduzione : OFF (Flag -Minify non rilevato)" -ForegroundColor DarkGray
+ Write-Host " 📉 Reduction : OFF (Flag -Minify not detected)" -ForegroundColor DarkGray
}
Write-Host "======================================================================" -ForegroundColor Cyan
Write-Host " ⏱️ TIMEDIFF MEASURE " -ForegroundColor Yellow
-Write-Host " ⏳ Esecuzione : $buildTimeSec sec" -ForegroundColor White
+Write-Host " ⏳ Execution : $buildTimeSec sec" -ForegroundColor White
Write-Host "======================================================================" -ForegroundColor Cyan
Write-Host ""
diff --git a/img/Gui.jpg b/images/Gui.jpg
similarity index 100%
rename from img/Gui.jpg
rename to images/Gui.jpg
diff --git a/img/RepairToolkit-old.jpg b/images/RepairToolkit-old.jpg
similarity index 100%
rename from img/RepairToolkit-old.jpg
rename to images/RepairToolkit-old.jpg
diff --git a/img/Run-old.jpg b/images/Run-old.jpg
similarity index 100%
rename from img/Run-old.jpg
rename to images/Run-old.jpg
diff --git a/img/Run.jpg b/images/Run.jpg
similarity index 100%
rename from img/Run.jpg
rename to images/Run.jpg
diff --git a/img/WinToolkit-Dev.ico b/images/WinToolkit-Dev.ico
similarity index 100%
rename from img/WinToolkit-Dev.ico
rename to images/WinToolkit-Dev.ico
diff --git a/img/WinToolkit-icon.png b/images/WinToolkit-icon.png
similarity index 100%
rename from img/WinToolkit-icon.png
rename to images/WinToolkit-icon.png
diff --git a/img/WinToolkit.ico b/images/WinToolkit.ico
similarity index 100%
rename from img/WinToolkit.ico
rename to images/WinToolkit.ico
diff --git a/img/avatar/zakkos.jpg b/images/avatar/zakkos.jpg
similarity index 100%
rename from img/avatar/zakkos.jpg
rename to images/avatar/zakkos.jpg
diff --git a/languages/en-US.json b/languages/en-US.json
new file mode 100644
index 00000000..8e6bca8f
--- /dev/null
+++ b/languages/en-US.json
@@ -0,0 +1,102 @@
+{
+ "code": "en-US",
+ "name": "English",
+ "nativeName": "English",
+ "strings": {
+ "menu.main": "Main Menu",
+ "menu.changeLanguage": "Change language",
+ "menu.language": "Language",
+ "menu.back": "Back to previous menu",
+ "menu.exitSection": "Exit",
+ "menu.exitToolkit": "Exit Toolkit",
+ "menu.choice": "Selection",
+ "menu.multiPrompt": "Enter one or more numbers (for example: 2 3 4 or 2,3,4) to run the operations in sequence",
+ "menu.multiPromptShort": "Enter one or more numbers",
+ "menu.invalidSelection": "No valid selection. Try again.",
+ "menu.pressEnter": "Press ENTER to return to the menu...",
+ "menu.startedInteractive": "WinToolkit started in interactive mode",
+ "menu.support": "Support: Github.com/Magnetarman",
+ "menu.closing": "Closing...",
+ "menu.languageChanged": "Language changed to {0}.",
+ "menu.chooseLanguage": "Choose a language",
+ "menu.noLanguages": "No language files were found.",
+ "system.infoTitle": "SYSTEM INFORMATION",
+ "system.edition": "Edition",
+ "system.version": "Version",
+ "system.architecture": "Architecture",
+ "system.computerName": "PC name",
+ "system.ram": "RAM",
+ "system.disk": "Disk",
+ "system.free": "Free",
+ "system.bitlockerStatus": "BitLocker status",
+ "bitlocker.status.on": "Enabled",
+ "bitlocker.status.off": "Disabled",
+ "bitlocker.status.suspended": "Suspended",
+ "bitlocker.status.encrypting": "Encryption in progress",
+ "bitlocker.status.decrypting": "Decryption in progress",
+ "bitlocker.status.notConfigured": "Not configured",
+ "bitlocker.status.unknown": "Unknown",
+ "confirm.profile.warn1": "WARNING: this option will delete all Windows user profiles except the current user.",
+ "confirm.profile.warn2": "Data stored in deleted profiles cannot be recovered.",
+ "confirm.profile.yes": "Yes, delete user profiles",
+ "confirm.profile.sure": "Are you absolutely sure?",
+ "confirm.profile.accept": "Yes, I understand what I am doing and accept responsibility if files are deleted",
+ "run.sequence": "Running {0} operations in sequence...",
+ "run.start": "Starting: {0}",
+ "run.error": "Error while running {0}: {1}",
+ "run.cancelled": "Operation cancelled. Returning to the main menu.",
+ "summary.operation": "Operation",
+ "summary.status": "Status",
+ "summary.detail": "Detail",
+ "summary.completed": "Completed",
+ "summary.error": "Error",
+ "summary.title": "Execution Summary",
+ "reboot.required": "A restart is required to complete the operations.",
+ "reboot.countdown": "System restart in",
+ "reboot.reminder": "Remember to restart the system manually to complete the operations.",
+ "gui.languageLabel": "Language",
+ "gui.sendErrorLogs": "Send error logs",
+ "gui.systemInfo": "System information",
+ "gui.windowsEdition": "Windows edition: ",
+ "gui.version": "Version: ",
+ "gui.architecture": "Architecture: ",
+ "gui.scriptFeatures": "Script features: ",
+ "gui.bitlockerStatus": "BitLocker status: ",
+ "gui.pcName": "PC name: ",
+ "gui.ram": "RAM: ",
+ "gui.disk": "Disk: ",
+ "gui.availableFunctions": "Available functions",
+ "gui.outputLogs": "Output and logs",
+ "gui.executeScripts": "Run scripts",
+ "gui.loading": "Loading.",
+ "gui.checking": "Checking.",
+ "gui.complete": "Complete",
+ "gui.limited": "Limited",
+ "gui.unsupported": "Unsupported",
+ "gui.diskFreeFormat": "{0}% free ({1} GB / {2} GB)",
+ "gui.initialized": "WinToolkit GUI initialized successfully.",
+ "gui.instructions": "Select one or more scripts and press 'Run scripts'.",
+ "gui.noneSelected": "No script selected.",
+ "gui.allExecuted": "All scripts have been executed.",
+ "gui.rebootPrompt": "The system requires a restart to complete the operations. Restart now?",
+ "gui.rebootTitle": "Restart Required",
+ "category.windows": "Windows",
+ "category.office": "Office",
+ "category.driverGaming": "Driver & Gaming",
+ "category.support": "Support",
+ "script.WinRepairToolkit": "Windows repair",
+ "script.WinUpdateReset": "Reset Windows Update",
+ "script.WinReinstallStore": "Winget/WinStore reset",
+ "script.WinBackupDriver": "PC driver backup",
+ "script.WinCleaner": "Temporary file cleanup",
+ "script.DisableBitlocker": "Disable BitLocker",
+ "script.WinDeleteUserProfiles": "Delete Windows user profiles",
+ "script.Install-Office": "Install Office Basic",
+ "script.Repair-Office": "Repair Office",
+ "script.Uninstall-Office": "Remove Office",
+ "script.AutoVideoDriverInstall": "Auto install video driver [Nvidia-AMD]",
+ "script.VideoDriverReinstall": "Reinstall video driver",
+ "script.GamingToolkit": "Gaming Toolkit",
+ "script.WinExportLog": "Export WinToolkit logs"
+ }
+}
diff --git a/languages/it-IT.json b/languages/it-IT.json
new file mode 100644
index 00000000..34371fc6
--- /dev/null
+++ b/languages/it-IT.json
@@ -0,0 +1,102 @@
+{
+ "code": "it-IT",
+ "name": "Italian",
+ "nativeName": "Italiano",
+ "strings": {
+ "menu.main": "Menu Principale",
+ "menu.changeLanguage": "Cambia lingua",
+ "menu.language": "Lingua",
+ "menu.back": "Torna al menu precedente",
+ "menu.exitSection": "Uscita",
+ "menu.exitToolkit": "Esci dal Toolkit",
+ "menu.choice": "Selezione",
+ "menu.multiPrompt": "Inserisci uno o più numeri (es: 2 3 4 oppure 2,3,4) per eseguire le operazioni in sequenza",
+ "menu.multiPromptShort": "Inserisci uno o più numeri",
+ "menu.invalidSelection": "Nessuna selezione valida. Riprova.",
+ "menu.pressEnter": "Premi INVIO per tornare al menu...",
+ "menu.startedInteractive": "WinToolkit avviato in modalità interattiva",
+ "menu.support": "Per supporto: Github.com/Magnetarman",
+ "menu.closing": "Chiusura in corso...",
+ "menu.languageChanged": "Lingua cambiata in {0}.",
+ "menu.chooseLanguage": "Scegli una lingua",
+ "menu.noLanguages": "Nessun file lingua trovato.",
+ "system.infoTitle": "INFORMAZIONI DI SISTEMA",
+ "system.edition": "Edizione",
+ "system.version": "Versione",
+ "system.architecture": "Architettura",
+ "system.computerName": "Nome PC",
+ "system.ram": "RAM",
+ "system.disk": "Disco",
+ "system.free": "Libero",
+ "system.bitlockerStatus": "Stato Bitlocker",
+ "bitlocker.status.on": "Attivato",
+ "bitlocker.status.off": "Disattivato",
+ "bitlocker.status.suspended": "Sospeso",
+ "bitlocker.status.encrypting": "Crittografia in corso",
+ "bitlocker.status.decrypting": "Decrittografia in corso",
+ "bitlocker.status.notConfigured": "Non configurato",
+ "bitlocker.status.unknown": "Sconosciuto",
+ "confirm.profile.warn1": "ATTENZIONE: eseguendo questa opzione verranno cancellati tutti i profili utenti di Windows, escluso l'utente attuale.",
+ "confirm.profile.warn2": "I dati contenuti nei profili eliminati saranno irrecuperabili.",
+ "confirm.profile.yes": "Sì, cancella i profili utenti",
+ "confirm.profile.sure": "Sei proprio sicuro?",
+ "confirm.profile.accept": "Sì, sono sicuro di ciò che sto facendo e me ne assumo la responsabilità in caso di cancellazione di file",
+ "run.sequence": "Esecuzione sequenziale di {0} operazioni...",
+ "run.start": "Avvio: {0}",
+ "run.error": "Errore durante {0}: {1}",
+ "run.cancelled": "Operazione annullata. Ritorno al menu principale.",
+ "summary.operation": "Operazione",
+ "summary.status": "Stato",
+ "summary.detail": "Dettaglio",
+ "summary.completed": "Completato",
+ "summary.error": "Errore",
+ "summary.title": "Riepilogo Esecuzione",
+ "reboot.required": "È necessario un riavvio per completare le operazioni.",
+ "reboot.countdown": "Riavvio sistema in",
+ "reboot.reminder": "Ricorda di riavviare il sistema manualmente per completare le operazioni.",
+ "gui.languageLabel": "Lingua",
+ "gui.sendErrorLogs": "Invia log errori",
+ "gui.systemInfo": "Informazioni di sistema",
+ "gui.windowsEdition": "Edizione Windows: ",
+ "gui.version": "Versione: ",
+ "gui.architecture": "Architettura: ",
+ "gui.scriptFeatures": "Funzionalità script: ",
+ "gui.bitlockerStatus": "Stato Bitlocker: ",
+ "gui.pcName": "Nome PC: ",
+ "gui.ram": "RAM: ",
+ "gui.disk": "Disco: ",
+ "gui.availableFunctions": "Funzioni disponibili",
+ "gui.outputLogs": "Output e log",
+ "gui.executeScripts": "Esegui script",
+ "gui.loading": "Caricamento.",
+ "gui.checking": "Verifica.",
+ "gui.complete": "Completa",
+ "gui.limited": "Limitata",
+ "gui.unsupported": "Non supportata",
+ "gui.diskFreeFormat": "{0}% libero ({1} GB / {2} GB)",
+ "gui.initialized": "WinToolkit GUI inizializzato correttamente.",
+ "gui.instructions": "Seleziona uno o più script e premi 'Esegui script'.",
+ "gui.noneSelected": "Nessuno script selezionato.",
+ "gui.allExecuted": "Tutti gli script sono stati eseguiti.",
+ "gui.rebootPrompt": "Il sistema richiede un riavvio per completare le operazioni. Riavviare ora?",
+ "gui.rebootTitle": "Riavvio Richiesto",
+ "category.windows": "Windows",
+ "category.office": "Office",
+ "category.driverGaming": "Driver & Gaming",
+ "category.support": "Supporto",
+ "script.WinRepairToolkit": "Riparazione Windows",
+ "script.WinUpdateReset": "Reset Windows Update",
+ "script.WinReinstallStore": "Winget/WinStore Reset",
+ "script.WinBackupDriver": "Backup Driver PC",
+ "script.WinCleaner": "Pulizia File Temporanei",
+ "script.DisableBitlocker": "Disabilita Bitlocker",
+ "script.WinDeleteUserProfiles": "Cancella profili utenti di Windows",
+ "script.Install-Office": "Installa Office Basic",
+ "script.Repair-Office": "Ripara Office",
+ "script.Uninstall-Office": "Rimuovi Office",
+ "script.AutoVideoDriverInstall": "Auto Install Driver Video [Nvidia-AMD]",
+ "script.VideoDriverReinstall": "Reinstalla Driver Video",
+ "script.GamingToolkit": "Gaming Toolkit",
+ "script.WinExportLog": "Esporta Log WinToolkit"
+ }
+}
diff --git a/start-offline.ps1 b/start-offline.ps1
index 437a3ad7..d8775c78 100644
--- a/start-offline.ps1
+++ b/start-offline.ps1
@@ -14,6 +14,41 @@
# Ensure script runs with PowerShell 5.1 or higher for basic compatibility
# This script itself doesn't require PowerShell 7, but the main toolkit might.
+function Convert-SourceTextToEnglish {
+ param([string]$Text)
+ if ([string]::IsNullOrWhiteSpace($Text)) { return $Text }
+
+ $translated = $Text
+ $replacements = [ordered]@{
+ 'Connessione Internet: Non disponibile (modalità offline).' = 'Internet connection: Not available (offline mode).'
+ 'Errore nel recupero informazioni sistema' = 'Error retrieving system information'
+ 'Download di' = 'Download of'
+ 'completato' = 'completed'
+ 'fallito dopo' = 'failed after'
+ 'tentativi' = 'attempts'
+ 'Tentativo' = 'Attempt'
+ 'fallito per' = 'failed for'
+ 'già presente' = 'already present'
+ 'Ricerca installer Windows Terminal più recente su GitHub.' = 'Searching for the latest Windows Terminal installer on GitHub.'
+ 'Errore nel recupero release Windows Terminal da GitHub' = 'Error retrieving the Windows Terminal release from GitHub'
+ 'Avvio preparazione ambiente offline.' = 'Starting offline environment preparation.'
+ 'Verifica presenza script principale' = 'Checking for main script'
+ 'Errore: Lo script' = 'Error: The script'
+ 'modificato non è presente in' = 'is not present in'
+ 'Premi Enter per uscire.' = 'Press Enter to exit.'
+ 'Risorse pronte. Avvio dello script principale in modalità offline.' = 'Resources are ready. Starting the main script in offline mode.'
+ 'La preparazione delle risorse offline è fallita. Impossibile procedere.' = 'Offline resource preparation failed. Cannot continue.'
+ 'Sistema Operativo' = 'Operating system'
+ 'Utente' = 'User'
+ 'su' = 'on'
+ 'Disco C' = 'Disk C'
+ }
+ foreach ($entry in $replacements.GetEnumerator()) {
+ $translated = $translated.Replace($entry.Key, $entry.Value)
+ }
+ return $translated
+}
+
function Write-StyledMessage {
param(
[Parameter(Mandatory = $true)]
@@ -30,7 +65,7 @@ function Write-StyledMessage {
'Success' = 'Green'
'Debug' = 'DarkGray'
}
- Write-Host $text -ForegroundColor $colors[$type]
+ Write-Host (Convert-SourceTextToEnglish -Text $text) -ForegroundColor $colors[$type]
}
function Show-Host {
@@ -235,7 +270,7 @@ function Prepare-OfflineResources {
}
# --- Win Toolkit Icon ---
- $iconUrl = "https://raw.githubusercontent.com/Magnetarman/WinToolkit/refs/heads/main/img/WinToolkit.ico"
+ $iconUrl = "https://raw.githubusercontent.com/Magnetarman/WinToolkit/refs/heads/main/images/WinToolkit.ico"
$iconPath = Join-Path $OfflineResourcesDir "WinToolkit.ico"
if (-not (Test-Path $iconPath)) {
if (-not (Invoke-DownloadFile -Uri $iconUrl -OutputPath $iconPath)) {
@@ -270,7 +305,7 @@ if (Prepare-OfflineResources -OfflineResourcesDir $OfflineResourcesDir) {
if (-not (Test-Path $mainScriptPath)) {
Write-StyledMessage -type 'Error' -text "Errore: Lo script 'start.ps1' modificato non è presente in '$OfflineResourcesDir'."
Write-StyledMessage -type 'Error' -text "Assicurati di aver copiato lo script principale modificato (dopo Step 2) in questa directory."
- Read-Host "Premi Enter per uscire."
+ Read-Host "Press Enter to exit."
exit 1
}
@@ -284,6 +319,6 @@ if (Prepare-OfflineResources -OfflineResourcesDir $OfflineResourcesDir) {
}
else {
Write-StyledMessage -type 'Error' -text "La preparazione delle risorse offline è fallita. Impossibile procedere."
- Read-Host "Premi Enter per uscire."
+ Read-Host "Press Enter to exit."
exit 1
}
diff --git a/start.ps1 b/start.ps1
index 22127e6f..6e7d5a85 100644
--- a/start.ps1
+++ b/start.ps1
@@ -29,9 +29,9 @@ $script:AppConfig = @{
GitRelease = "https://api.github.com/repos/git-for-windows/git/releases/latest"
PowerShellRelease = "https://api.github.com/repos/PowerShell/PowerShell/releases/latest"
OhMyPoshTheme = "https://raw.githubusercontent.com/JanDeDobbeleer/oh-my-posh/main/themes/atomic.omp.json"
- PowerShellProfile = "https://raw.githubusercontent.com/Magnetarman/WinToolkit/Dev/asset/Microsoft.PowerShell_profile.ps1"
- WindowsTerminalSettings = "https://raw.githubusercontent.com/Magnetarman/WinToolkit/Dev/asset/settings.json"
- ToolkitIcon = "https://raw.githubusercontent.com/Magnetarman/WinToolkit/refs/heads/main/img/WinToolkit.ico"
+ PowerShellProfile = "https://raw.githubusercontent.com/Magnetarman/WinToolkit/Dev/assets/Microsoft.PowerShell_profile.ps1"
+ WindowsTerminalSettings = "https://raw.githubusercontent.com/Magnetarman/WinToolkit/Dev/assets/settings.json"
+ ToolkitIcon = "https://raw.githubusercontent.com/Magnetarman/WinToolkit/refs/heads/main/images/WinToolkit.ico"
TerminalRelease = "https://api.github.com/repos/microsoft/terminal/releases/latest"
WebInstaller = "https://magnetarman.com/WinToolkit-Dev"
}
@@ -552,7 +552,7 @@ function Install-WingetCore {
Write-StyledMessage -Type Success -Text "Winget Core installato con successo."
}
else {
- throw "Installazione Winget Core fallita."
+ throw "Winget Core installation failed."
}
}
return $true
@@ -803,6 +803,55 @@ function Show-Header {
Write-Host ''
}
+function Convert-SourceTextToEnglish {
+ param([string]$Text)
+ if ([string]::IsNullOrWhiteSpace($Text)) { return $Text }
+
+ $translated = $Text
+ $replacements = [ordered]@{
+ 'Verifica funzionalità Winget.' = 'Checking Winget functionality.'
+ 'Winget presente ma non risponde correttamente' = 'Winget is present but is not responding correctly'
+ 'Errore durante test Winget' = 'Error while testing Winget'
+ 'Avvio servizio' = 'Starting service'
+ 'Avvio ripristino database Winget.' = 'Starting Winget database restore.'
+ 'Pulizia cache Winget.' = 'Cleaning Winget cache.'
+ 'completato' = 'completed'
+ 'versione superiore già presente' = 'newer version already present'
+ 'Modulo Riparazione fallito' = 'Repair module failed'
+ 'Installazione' = 'Installation'
+ 'fallita' = 'failed'
+ 'fallito' = 'failed'
+ 'riuscita' = 'succeeded'
+ 'già installato' = 'already installed'
+ 'già presente' = 'already present'
+ 'Errore durante' = 'Error during'
+ 'Errore installazione' = 'Installation error'
+ 'Errore creazione scorciatoia' = 'Shortcut creation error'
+ 'Avvio configurazione' = 'Starting configuration'
+ 'Esecuzione controlli di integrità sistema' = 'Running system integrity checks'
+ 'Riavvio con privilegi amministratore.' = 'Restarting with administrator privileges.'
+ 'OBBLIGATORIO: Windows Defender è ATTIVO.' = 'REQUIRED: Windows Defender is ENABLED.'
+ 'PowerShell 7 raccomandato per funzionalità avanzate.' = 'PowerShell 7 is recommended for advanced features.'
+ 'Ripristino veloce fallito. Tentativo metodo avanzato' = 'Quick repair failed. Trying advanced method'
+ 'Lo script proseguirà, ma l''installazione di pacchetti potrebbe fallire.' = 'The script will continue, but package installation may fail.'
+ 'Winget è già operativo.' = 'Winget is already working.'
+ 'Git è già operativo.' = 'Git is already working.'
+ 'Attenzione: Git non è stato installato oppure potrebbe non funzionare correttamente.' = 'Warning: Git was not installed or may not work correctly.'
+ 'WinToolkit è Pronto sul Desktop!' = 'WinToolkit is ready on the desktop!'
+ 'Errore critico durante il setup' = 'Critical error during setup'
+ 'Premi un tasto per uscire.' = 'Press any key to exit.'
+ 'Verifica' = 'Checking'
+ 'Avvio' = 'Starting'
+ 'Pulizia' = 'Cleanup'
+ 'Riparazione' = 'Repair'
+ 'Riavvio' = 'Restart'
+ }
+ foreach ($entry in $replacements.GetEnumerator()) {
+ $translated = $translated.Replace($entry.Key, $entry.Value)
+ }
+ return $translated
+}
+
function Write-StyledMessage {
<#
.SYNOPSIS
@@ -813,6 +862,7 @@ function Write-StyledMessage {
[string]$Type,
[string]$Text
)
+ $Text = Convert-SourceTextToEnglish -Text $Text
# FIX: Windows 11 Indentation Issue
if ([Environment]::OSVersion.Version.Build -ge 22000) { $Text = "`r$Text" }
@@ -1271,7 +1321,7 @@ function Install-WindowsTerminalApp {
Write-StyledMessage -Type Success -Text "Installazione Appx di Windows Terminal riuscita."
}
else {
- throw "Installazione Appx di Windows Terminal fallita."
+ throw "Windows Terminal Appx installation failed."
}
$null = Remove-Item $tempFile -Force -ErrorAction SilentlyContinue
return $true
@@ -1681,7 +1731,7 @@ function Invoke-WinToolkitSetup {
Write-StyledMessage -Type Error -Text "❌ Errore critico durante il setup: $($_.Exception.Message)."
Write-ToolkitLog -Level 'ERROR' -Message "ECCEZIONE UNHANDLED: $($_.Exception.Message) `n $($_.ScriptStackTrace)"
- Write-Host "Premi un tasto per uscire."
+ Write-Host "Press any key to exit."
$null = [Console]::ReadKey($true)
exit 1
}
diff --git a/tool/AutoVideoDriverInstall.ps1 b/tools/AutoVideoDriverInstall.ps1
similarity index 100%
rename from tool/AutoVideoDriverInstall.ps1
rename to tools/AutoVideoDriverInstall.ps1
diff --git a/tool/DisableBitlocker.ps1 b/tools/DisableBitlocker.ps1
similarity index 100%
rename from tool/DisableBitlocker.ps1
rename to tools/DisableBitlocker.ps1
diff --git a/tool/GamingToolkit.ps1 b/tools/GamingToolkit.ps1
similarity index 100%
rename from tool/GamingToolkit.ps1
rename to tools/GamingToolkit.ps1
diff --git a/tool/Install-Office.ps1 b/tools/Install-Office.ps1
similarity index 100%
rename from tool/Install-Office.ps1
rename to tools/Install-Office.ps1
diff --git a/tool/Repair-Office.ps1 b/tools/Repair-Office.ps1
similarity index 100%
rename from tool/Repair-Office.ps1
rename to tools/Repair-Office.ps1
diff --git a/tool/Uninstall-Office.ps1 b/tools/Uninstall-Office.ps1
similarity index 100%
rename from tool/Uninstall-Office.ps1
rename to tools/Uninstall-Office.ps1
diff --git a/tool/VideoDriverReinstall.ps1 b/tools/VideoDriverReinstall.ps1
similarity index 100%
rename from tool/VideoDriverReinstall.ps1
rename to tools/VideoDriverReinstall.ps1
diff --git a/tool/WinBackupDriver.ps1 b/tools/WinBackupDriver.ps1
similarity index 98%
rename from tool/WinBackupDriver.ps1
rename to tools/WinBackupDriver.ps1
index 42889d07..752e7b19 100644
--- a/tool/WinBackupDriver.ps1
+++ b/tools/WinBackupDriver.ps1
@@ -53,7 +53,7 @@ function WinBackupDriver {
-TimeoutSeconds $timeout -LogContextKey "Backup-DISM"
if ($result.TimedOut) { throw "Timeout raggiunto durante l'esportazione DISM" }
- if ($result.ExitCode -ne 0) { throw "Esportazione DISM fallita con ExitCode: $($result.ExitCode)." }
+ if ($result.ExitCode -ne 0) { throw "DISM export failed with ExitCode: $($result.ExitCode)." }
$exportedDrivers = Get-ChildItem -Path $script:BackupConfig.BackupDir -Recurse -File -ErrorAction SilentlyContinue
if (-not $exportedDrivers -or $exportedDrivers.Count -eq 0) {
@@ -179,7 +179,7 @@ function WinBackupDriver {
return $true
}
- throw "Copia archivio fallita."
+ throw "Archive copy failed."
}
catch {
Write-StyledMessage -Type 'Error' -Text "Errore spostamento archivio: $_"
diff --git a/tool/WinCleaner.ps1 b/tools/WinCleaner.ps1
similarity index 100%
rename from tool/WinCleaner.ps1
rename to tools/WinCleaner.ps1
diff --git a/tool/WinDebloat.ps1 b/tools/WinDebloat.ps1
similarity index 100%
rename from tool/WinDebloat.ps1
rename to tools/WinDebloat.ps1
diff --git a/tools/WinDeleteUserProfiles.ps1 b/tools/WinDeleteUserProfiles.ps1
new file mode 100644
index 00000000..272f5499
--- /dev/null
+++ b/tools/WinDeleteUserProfiles.ps1
@@ -0,0 +1,665 @@
+function WinDeleteUserProfiles {
+ <#
+ .SYNOPSIS
+ Rimuove in modo sicuro i profili utente locali non caricati e le cartelle residue in C:\Users.
+
+ .DESCRIPTION
+ Esegue una pulizia controllata dei profili locali presenti in C:\Users utilizzando Win32_UserProfile.
+ Esclude profili speciali, profili caricati, account di sistema, profilo utente corrente e nomi protetti.
+ Dopo la cancellazione dei profili registrati, controlla la cartella utenti e rimuove eventuali directory residue
+ non più associate a profili presenti nel registro/CIM, sempre rispettando le esclusioni protette.
+
+ Lo script non chiede conferme interattive prima delle cancellazioni.
+
+ .PARAMETER MaxThreads
+ Numero massimo di runspace paralleli. Per Win32_UserProfile viene limitato automaticamente a 4.
+
+ .PARAMETER CountdownSeconds
+ Secondi del countdown prima di un eventuale riavvio consigliato.
+
+ .PARAMETER SuppressIndividualReboot
+ Sopprime il riavvio individuale e delega il riavvio finale al toolkit.
+
+ .PARAMETER UsersRoot
+ Percorso radice dei profili utente locali.
+
+ .PARAMETER LogFolder
+ Cartella in cui salvare il file di log.
+
+ .PARAMETER MinimumProfileAgeDays
+ Età minima, in giorni, dell'ultima data di utilizzo del profilo. Default 0 mantiene il comportamento originale.
+
+ .PARAMETER SkipResidualFolderCleanup
+ Salta la pulizia finale delle cartelle residue in C:\Users.
+
+ .PARAMETER SuppressToolkitSession
+ Non richiama Start-ToolkitSession anche se disponibile.
+
+ .EXAMPLE
+ WinDeleteUserProfiles
+
+ .EXAMPLE
+ WinDeleteUserProfiles -MinimumProfileAgeDays 30
+ #>
+ [CmdletBinding()]
+ param(
+ [ValidateRange(1, 16)]
+ [int]$MaxThreads = [Math]::Min(2, [Environment]::ProcessorCount),
+
+ [ValidateRange(0, 3600)]
+ [int]$CountdownSeconds = 30,
+
+ [switch]$SuppressIndividualReboot,
+
+ [ValidateNotNullOrEmpty()]
+ [string]$UsersRoot = 'C:\Users',
+
+ [ValidateNotNullOrEmpty()]
+ [string]$LogFolder = 'C:\Temp',
+
+ [ValidateRange(0, 3650)]
+ [int]$MinimumProfileAgeDays = 0,
+
+ [switch]$SkipResidualFolderCleanup,
+
+ [switch]$SuppressToolkitSession
+ )
+
+ begin {
+ $script:ToolName = 'WinDeleteUserProfiles'
+ $script:ToolVersion = '3.1'
+ $script:SessionStart = Get-Date
+ $script:UsersRoot = [System.IO.Path]::GetFullPath($UsersRoot.TrimEnd('\') + '\')
+ $script:LogFolder = [System.IO.Path]::GetFullPath($LogFolder)
+ $script:LogFile = Join-Path $script:LogFolder ("{0}_{1}.log" -f $script:ToolName, (Get-Date -Format 'yyyyMMdd_HHmmss'))
+ $script:CurrentUser = $env:USERNAME
+ $script:ComputerName = $env:COMPUTERNAME
+ $script:CountdownSeconds = $CountdownSeconds
+ $script:SuppressIndividualReboot = $SuppressIndividualReboot
+ $script:RebootRecommended = $false
+ $script:MinimumLastUseDate = if ($MinimumProfileAgeDays -gt 0) { (Get-Date).AddDays(-$MinimumProfileAgeDays) } else { $null }
+
+ $script:ProtectedProfileNames = @(
+ 'Public',
+ 'Pubblica',
+ 'Default',
+ 'Default User',
+ 'All Users',
+ 'defaultuser0',
+ 'WDAGUtilityAccount',
+ 'Administrator',
+ 'Guest',
+ $script:CurrentUser
+ ) | Where-Object { -not [string]::IsNullOrWhiteSpace($_) }
+
+ $savedErrorActionPreference = $ErrorActionPreference
+ $savedProgressPreference = $ProgressPreference
+ $savedConfirmPreference = $ConfirmPreference
+
+ $ErrorActionPreference = 'Stop'
+ $ProgressPreference = 'Continue'
+ $ConfirmPreference = 'None'
+
+ $script:LogQueue = [System.Collections.Concurrent.ConcurrentQueue[string]]::new()
+ }
+
+ process {
+ if (-not (Get-Command -Name Write-StyledMessage -ErrorAction SilentlyContinue)) {
+ function Write-StyledMessage {
+ param(
+ [ValidateSet('Info', 'Success', 'Warning', 'Error', 'Progress')]
+ [string]$Type = 'Info',
+
+ [Parameter(Mandatory = $true)]
+ [string]$Text
+ )
+
+ $color = switch ($Type) {
+ 'Success' { 'Green' }
+ 'Warning' { 'Yellow' }
+ 'Error' { 'Red' }
+ default { 'Cyan' }
+ }
+
+ Write-Host $Text -ForegroundColor $color
+ }
+ }
+
+
+ function Add-ProfileCleanupLog {
+ param(
+ [Parameter(Mandatory = $true)]
+ [string]$Text,
+
+ [ValidateSet('INFO', 'SUCCESS', 'WARNING', 'ERROR')]
+ [string]$Level = 'INFO'
+ )
+
+ $script:LogQueue.Enqueue(
+ ('{0} [{1}] {2}' -f (Get-Date -Format 'yyyy-MM-dd HH:mm:ss'), $Level, $Text)
+ )
+ }
+
+
+ function Set-ProfileCleanupRebootRecommended {
+ param(
+ [Parameter(Mandatory = $true)]
+ [string]$Reason
+ )
+
+ $script:RebootRecommended = $true
+ Add-ProfileCleanupLog -Level 'WARNING' -Text "Riavvio consigliato: $Reason"
+ }
+
+
+ function Invoke-ProfileCleanupReboot {
+ if (-not $script:RebootRecommended) {
+ return
+ }
+
+ if (Get-Command -Name Invoke-ToolkitReboot -ErrorAction SilentlyContinue) {
+ Invoke-ToolkitReboot -Message 'Riavvio consigliato dopo pulizia profili' -Seconds $script:CountdownSeconds -SuppressIndividualReboot:$script:SuppressIndividualReboot
+ return
+ }
+
+ if ($script:SuppressIndividualReboot) {
+ $Global:NeedsFinalReboot = $true
+ Write-StyledMessage -Type 'Info' -Text '🚫 Riavvio individuale soppresso. Verrà gestito un riavvio finale.'
+ return
+ }
+
+ if (Get-Command -Name Start-InterruptibleCountdown -ErrorAction SilentlyContinue) {
+ if (Start-InterruptibleCountdown -Seconds $script:CountdownSeconds -Message 'Riavvio consigliato dopo pulizia profili') {
+ Restart-Computer -Force
+ }
+ return
+ }
+
+ Write-StyledMessage -Type 'Warning' -Text 'Riavvio consigliato per completare la pulizia dei profili non rimossi.'
+ }
+
+
+ function Test-IsAdministrator {
+ $identity = [Security.Principal.WindowsIdentity]::GetCurrent()
+ $principal = [Security.Principal.WindowsPrincipal]::new($identity)
+ return $principal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)
+ }
+
+
+ function Initialize-ProfileCleanupSession {
+ [System.IO.Directory]::CreateDirectory($script:LogFolder) | Out-Null
+
+ if (-not (Test-IsAdministrator)) {
+ throw 'Lo script deve essere eseguito da una console PowerShell avviata come amministratore.'
+ }
+
+ if (-not (Test-Path -LiteralPath $script:UsersRoot -PathType Container)) {
+ throw "Il percorso profili non esiste: $script:UsersRoot"
+ }
+
+ $os = Get-CimInstance -ClassName Win32_OperatingSystem -ErrorAction SilentlyContinue
+ if ($os -and $os.Caption -notmatch 'Windows 11') {
+ Write-StyledMessage -Type 'Warning' -Text "⚠️ Sistema rilevato: $($os.Caption). Lo script è pensato per Windows 11."
+ }
+
+ if (-not $SuppressToolkitSession -and (Get-Command -Name Start-ToolkitSession -ErrorAction SilentlyContinue)) {
+ Start-ToolkitSession -ToolName $script:ToolName -SubTitle 'Profile Cleanup Toolkit'
+ }
+ else {
+ Write-Host ''
+ Write-Host '====================================================' -ForegroundColor Cyan
+ Write-Host (" {0} v{1}" -f $script:ToolName, $script:ToolVersion)
+ Write-Host '====================================================' -ForegroundColor Cyan
+ Write-Host ''
+ }
+
+ Write-StyledMessage -Type 'Info' -Text ("🖥️ Computer: {0}" -f $script:ComputerName)
+ Write-StyledMessage -Type 'Info' -Text ("👤 Utente corrente protetto: {0}" -f $script:CurrentUser)
+ Write-StyledMessage -Type 'Info' -Text ("📁 Percorso profili: {0}" -f $script:UsersRoot)
+ Write-StyledMessage -Type 'Info' -Text ("🧵 Thread configurati: {0}" -f $MaxThreads)
+ Write-StyledMessage -Type 'Warning' -Text '⚠️ Modalità non interattiva: nessuna conferma verrà richiesta prima delle cancellazioni.'
+
+ if ($script:MinimumLastUseDate) {
+ Write-StyledMessage -Type 'Info' -Text ("📅 Soglia ultima attività: profili non usati da almeno {0} giorni." -f $MinimumProfileAgeDays)
+ }
+
+ Add-ProfileCleanupLog -Text "Sessione avviata su $script:ComputerName."
+ }
+
+
+ function New-ProtectedNameSet {
+ $excluded = [System.Collections.Generic.HashSet[string]]::new([System.StringComparer]::OrdinalIgnoreCase)
+ $script:ProtectedProfileNames | ForEach-Object { [void]$excluded.Add($_) }
+ return ,$excluded
+ }
+
+
+ function Get-RegisteredProfilePathSet {
+ $pathSet = [System.Collections.Generic.HashSet[string]]::new([System.StringComparer]::OrdinalIgnoreCase)
+
+ Get-CimInstance -ClassName Win32_UserProfile -ErrorAction SilentlyContinue |
+ Where-Object {
+ $_.LocalPath -and
+ $_.LocalPath.StartsWith($script:UsersRoot, [System.StringComparison]::OrdinalIgnoreCase)
+ } |
+ ForEach-Object {
+ try {
+ [void]$pathSet.Add([System.IO.Path]::GetFullPath($_.LocalPath).TrimEnd('\'))
+ }
+ catch {
+ Add-ProfileCleanupLog -Level 'WARNING' -Text "Impossibile normalizzare LocalPath profilo registrato: $($_.LocalPath)"
+ }
+ }
+
+ return ,$pathSet
+ }
+
+
+ function Get-RemovableUserProfiles {
+ $excluded = New-ProtectedNameSet
+
+ Write-StyledMessage -Type 'Info' -Text '🔍 Scansione profili locali registrati in corso.'
+
+ $profiles = Get-CimInstance -ClassName Win32_UserProfile | Where-Object {
+ -not $_.Special -and
+ -not $_.Loaded -and
+ $_.LocalPath -and
+ $_.LocalPath.StartsWith($script:UsersRoot, [System.StringComparison]::OrdinalIgnoreCase)
+ }
+
+ foreach ($profile in $profiles) {
+ $profileName = [System.IO.Path]::GetFileName($profile.LocalPath)
+
+ if ($excluded.Contains($profileName)) {
+ Add-ProfileCleanupLog -Text "Profilo escluso: $profileName ($($profile.LocalPath))."
+ continue
+ }
+
+ if ($script:MinimumLastUseDate -and $profile.LastUseTime) {
+ $lastUse = $profile.LastUseTime
+ if ($lastUse -gt $script:MinimumLastUseDate) {
+ Add-ProfileCleanupLog -Text "Profilo escluso per soglia temporale: $profileName, ultimo uso $lastUse."
+ continue
+ }
+ }
+
+ $profile
+ }
+ }
+
+
+ function Show-ProfileCleanupPreview {
+ param(
+ [Parameter(Mandatory = $true)]
+ [array]$Profiles
+ )
+
+ Write-Host ''
+ Write-StyledMessage -Type 'Warning' -Text 'Profili registrati selezionati per la rimozione automatica:'
+ Write-Host ''
+
+ $Profiles |
+ Select-Object @{Name='User'; Expression={ [System.IO.Path]::GetFileName($_.LocalPath) }},
+ @{Name='Loaded'; Expression={ $_.Loaded }},
+ @{Name='LastUseTime'; Expression={ $_.LastUseTime }},
+ @{Name='Path'; Expression={ $_.LocalPath }} |
+ Format-Table -AutoSize
+
+ Write-Host ''
+ }
+
+
+ function Invoke-ProfileRemovalBatch {
+ param(
+ [Parameter(Mandatory = $true)]
+ [array]$Profiles
+ )
+
+ $pool = [RunspaceFactory]::CreateRunspacePool(1, $MaxThreads)
+ $pool.Open()
+
+ $jobs = [System.Collections.Generic.List[object]]::new()
+
+ $scriptBlock = {
+ param($Profile, $LogQueue)
+
+ $ConfirmPreference = 'None'
+ $ErrorActionPreference = 'Stop'
+
+ $userPath = $Profile.LocalPath
+ $userName = [System.IO.Path]::GetFileName($userPath)
+ $start = Get-Date
+
+ $LogQueue.Enqueue(('{0} [INFO] START PROFILE - {1} - {2}' -f $start.ToString('yyyy-MM-dd HH:mm:ss'), $userName, $userPath))
+
+ try {
+ Remove-CimInstance -InputObject $Profile -ErrorAction Stop -Confirm:$false
+ $LogQueue.Enqueue(('{0} [SUCCESS] CIM profile removed - {1}' -f (Get-Date -Format 'yyyy-MM-dd HH:mm:ss'), $userName))
+ }
+ catch {
+ $LogQueue.Enqueue(('{0} [WARNING] CIM remove failed - {1} - {2}' -f (Get-Date -Format 'yyyy-MM-dd HH:mm:ss'), $userName, $_.Exception.Message))
+ }
+
+ if ([System.IO.Directory]::Exists($userPath)) {
+ try {
+ $tempEmpty = Join-Path $env:TEMP "EmptyFolder"
+
+ if (-not (Test-Path $tempEmpty)) {
+ New-Item -ItemType Directory -Path $tempEmpty | Out-Null
+ }
+ robocopy $tempEmpty $userPath /MIR /R:1 /W:1 /NFL /NDL /NJH /NJS /NP | Out-Null
+ Remove-Item -LiteralPath $userPath -Force -Recurse -ErrorAction SilentlyContinue
+
+ $LogQueue.Enqueue(('{0} [SUCCESS] Folder removed - {1}' -f (Get-Date -Format 'yyyy-MM-dd HH:mm:ss'), $userName))
+ }
+ catch {
+ $LogQueue.Enqueue(('{0} [WARNING] Standard folder cleanup failed - {1} - {2}' -f (Get-Date -Format 'yyyy-MM-dd HH:mm:ss'), $userName, $_.Exception.Message))
+
+ try {
+ try {
+ & takeown.exe /F $userPath /R /D S | Out-Null
+ }
+ catch {
+ try {
+ & takeown.exe /F $userPath /R /D Y | Out-Null
+ }
+ catch {
+ $LogQueue.Enqueue(('{0} [ERROR] takeown failed - {1} - {2}' -f (Get-Date -Format 'yyyy-MM-dd HH:mm:ss'), $userName, $_.Exception.Message))
+ }
+ }
+ & icacls.exe $userPath /grant Administrators:F /T /C | Out-Null
+ Remove-Item -LiteralPath $userPath -Force -Recurse -ErrorAction Stop -Confirm:$false
+ $LogQueue.Enqueue(('{0} [SUCCESS] Folder removed after ACL reset - {1}' -f (Get-Date -Format 'yyyy-MM-dd HH:mm:ss'), $userName))
+ }
+ catch {
+ $LogQueue.Enqueue(('{0} [ERROR] Cleanup failed - {1} - {2}' -f (Get-Date -Format 'yyyy-MM-dd HH:mm:ss'), $userName, $_.Exception.Message))
+ }
+ }
+ }
+
+ $success = -not [System.IO.Directory]::Exists($userPath)
+ $duration = New-TimeSpan -Start $start -End (Get-Date)
+
+ if ($success) {
+ $LogQueue.Enqueue(('{0} [SUCCESS] COMPLETED PROFILE - {1} - {2:hh\:mm\:ss}' -f (Get-Date -Format 'yyyy-MM-dd HH:mm:ss'), $userName, $duration))
+ }
+ else {
+ $LogQueue.Enqueue(('{0} [ERROR] FAILED PROFILE - {1}' -f (Get-Date -Format 'yyyy-MM-dd HH:mm:ss'), $userName))
+ }
+
+ return [PSCustomObject]@{
+ Type = 'Profile'
+ UserName = $userName
+ Path = $userPath
+ Success = $success
+ Duration = $duration
+ }
+ }
+
+ try {
+ foreach ($profile in $Profiles) {
+ $ps = [PowerShell]::Create()
+ $ps.RunspacePool = $pool
+
+ [void]$ps.AddScript($scriptBlock, $true).
+ AddArgument($profile).
+ AddArgument($script:LogQueue)
+
+ $handle = $ps.BeginInvoke()
+
+ $jobs.Add([PSCustomObject]@{
+ PowerShell = $ps
+ Handle = $handle
+ })
+ }
+
+ $total = $jobs.Count
+ $lastPercent = -1
+
+ do {
+ $completed = ($jobs | Where-Object { $_.Handle.IsCompleted }).Count
+ $percent = if ($total -gt 0) { [math]::Floor(($completed / $total) * 100) } else { 100 }
+
+ if ($percent -ne $lastPercent) {
+ $lastPercent = $percent
+ Write-Progress -Activity 'Rimozione profili registrati' -Status "$completed / $total completati" -PercentComplete $percent
+ }
+
+ Start-Sleep -Milliseconds 500
+ } while ($completed -lt $total)
+
+ Write-Progress -Activity 'Rimozione profili registrati' -Completed
+
+ $results = foreach ($job in $jobs) {
+ try {
+ $job.PowerShell.EndInvoke($job.Handle)
+ }
+ catch {
+ Add-ProfileCleanupLog -Level 'ERROR' -Text "Errore runspace: $($_.Exception.Message)"
+ }
+ finally {
+ $job.PowerShell.Commands.Clear()
+ $job.PowerShell.Dispose()
+ }
+ }
+
+ return $results
+ }
+ finally {
+ if ($pool) {
+ $pool.Close()
+ $pool.Dispose()
+ }
+ }
+ }
+
+
+ function Get-ResidualUserFolders {
+ $excluded = New-ProtectedNameSet
+ $registeredProfilePaths = Get-RegisteredProfilePathSet
+
+ Write-StyledMessage -Type 'Info' -Text '🔎 Controllo cartelle residue nella directory utenti.'
+
+ $folders = Get-ChildItem -Path $UsersRoot -Directory -Force |
+ Where-Object {
+ -not ($_.Attributes -band [IO.FileAttributes]::ReparsePoint)
+ }
+
+ foreach ($folder in $folders) {
+ $folderName = $folder.Name
+ $folderPath = [System.IO.Path]::GetFullPath($folder.FullName).TrimEnd('\')
+
+ if ($excluded.Contains($folderName)) {
+ Add-ProfileCleanupLog -Text "Cartella residua esclusa per nome protetto: $folderName ($folderPath)."
+ continue
+ }
+
+ if ($registeredProfilePaths.Contains($folderPath)) {
+ Add-ProfileCleanupLog -Text "Cartella residua esclusa perché ancora associata a Win32_UserProfile: $folderName ($folderPath)."
+ continue
+ }
+
+ if ($folder.Attributes -band [System.IO.FileAttributes]::ReparsePoint) {
+ Add-ProfileCleanupLog -Level 'WARNING' -Text "Cartella residua esclusa perché reparse point/symlink: $folderName ($folderPath)."
+ continue
+ }
+
+ [PSCustomObject]@{
+ Name = $folderName
+ Path = $folderPath
+ }
+ }
+ }
+
+
+ function Remove-ResidualUserFolders {
+ param(
+ [Parameter(Mandatory = $true)]
+ [array]$Folders
+ )
+
+ $results = [System.Collections.Generic.List[object]]::new()
+ $total = $Folders.Count
+ $index = 0
+
+ foreach ($folder in $Folders) {
+ $index++
+ $percent = if ($total -gt 0) { [math]::Floor(($index / $total) * 100) } else { 100 }
+
+ Write-Progress `
+ -Activity 'Rimozione cartelle residue in C:\Users' `
+ -Status ("{0} / {1} - {2}" -f $index, $total, $folder.Name) `
+ -PercentComplete $percent
+
+ $start = Get-Date
+ $success = $false
+
+ Add-ProfileCleanupLog -Text "START RESIDUAL FOLDER - $($folder.Name) - $($folder.Path)"
+
+ $folderPath = $folder.Path
+
+ try {
+ Remove-Item -LiteralPath $folderPath -Force -Recurse -ErrorAction Stop -Confirm:$false
+ $success = -not [System.IO.Directory]::Exists($folderPath)
+ }
+ catch {
+ Add-ProfileCleanupLog -Level 'WARNING' -Text "Rimozione standard cartella residua fallita: $folderPath - $($_.Exception.Message)"
+
+ try {
+ & takeown.exe /F $folderPath /R /D Y | Out-Null
+ & icacls.exe $folderPath /grant Administrators:F /T /C | Out-Null
+ Remove-Item -LiteralPath $folderPath -Force -Recurse -ErrorAction Stop -Confirm:$false
+ $success = -not [System.IO.Directory]::Exists($folderPath)
+ }
+ catch {
+ Add-ProfileCleanupLog -Level 'ERROR' -Text "Rimozione cartella residua fallita: $folderPath - $($_.Exception.Message)"
+ $success = $false
+ }
+ }
+
+ $duration = New-TimeSpan -Start $start -End (Get-Date)
+
+ if ($success) {
+ Add-ProfileCleanupLog -Level 'SUCCESS' -Text "COMPLETED RESIDUAL FOLDER - $($folder.Name) - $($duration.ToString())"
+ }
+ else {
+ Add-ProfileCleanupLog -Level 'ERROR' -Text "FAILED RESIDUAL FOLDER - $($folder.Name)"
+ }
+
+ $results.Add([PSCustomObject]@{
+ Type = 'ResidualFolder'
+ UserName = $folder.Name
+ Path = $folder.Path
+ Success = $success
+ Duration = $duration
+ }) | Out-Null
+ }
+
+ Write-Progress -Activity 'Rimozione cartelle residue in C:\Users' -Completed
+
+ return $results
+ }
+
+
+ function Save-ProfileCleanupLog {
+ $logLines = [System.Collections.Generic.List[string]]::new()
+ $line = $null
+
+ while ($script:LogQueue.TryDequeue([ref]$line)) {
+ $logLines.Add($line)
+ }
+
+ $logLines | Set-Content -LiteralPath $script:LogFile -Encoding UTF8
+ }
+
+ try {
+ Initialize-ProfileCleanupSession
+
+ $profileResults = @()
+ $residualResults = @()
+
+ $targets = @(Get-RemovableUserProfiles)
+
+ if ($targets -and $targets.Count -gt 0) {
+ Show-ProfileCleanupPreview -Profiles $targets
+
+ Write-Host ''
+ Write-StyledMessage -Type 'Info' -Text ("🚀 Avvio rimozione automatica di {0} profili registrati." -f $targets.Count)
+ Write-Host ''
+
+ $profileResults = @(Invoke-ProfileRemovalBatch -Profiles $targets)
+ }
+ else {
+ Write-Host ''
+ Write-StyledMessage -Type 'Success' -Text '✅ Nessun profilo registrato rimovibile trovato.'
+ Add-ProfileCleanupLog -Level 'SUCCESS' -Text 'Nessun profilo registrato rimovibile trovato.'
+ }
+
+ if (-not $SkipResidualFolderCleanup) {
+ $residualFolders = @(Get-ResidualUserFolders)
+
+ if ($residualFolders -and $residualFolders.Count -gt 0) {
+ Write-Host ''
+ Write-StyledMessage -Type 'Warning' -Text ("Cartelle residue selezionate per la rimozione automatica: {0}" -f $residualFolders.Count)
+ $residualFolders | Select-Object Name, Path | Format-Table -AutoSize
+
+ Write-Host ''
+ Write-StyledMessage -Type 'Info' -Text '🧹 Avvio rimozione cartelle residue.'
+ Write-Host ''
+
+ $residualResults = @(Remove-ResidualUserFolders -Folders $residualFolders)
+ }
+ else {
+ Write-StyledMessage -Type 'Success' -Text '✅ Nessuna cartella residua rimovibile trovata in C:\Users.'
+ Add-ProfileCleanupLog -Level 'SUCCESS' -Text 'Nessuna cartella residua rimovibile trovata.'
+ }
+ }
+ else {
+ Write-StyledMessage -Type 'Warning' -Text 'Pulizia cartelle residue saltata per parametro SkipResidualFolderCleanup.'
+ Add-ProfileCleanupLog -Level 'WARNING' -Text 'Pulizia cartelle residue saltata.'
+ }
+
+ $allResults = @($profileResults) + @($residualResults)
+ $successCount = @($allResults | Where-Object { $_.Success }).Count
+ $failedCount = @($allResults | Where-Object { -not $_.Success }).Count
+ $profileSuccessCount = @($profileResults | Where-Object { $_.Success }).Count
+ $residualSuccessCount = @($residualResults | Where-Object { $_.Success }).Count
+
+ if ($failedCount -gt 0) {
+ Set-ProfileCleanupRebootRecommended -Reason "$failedCount elementi non rimossi potrebbero essere bloccati da sessioni o handle aperti."
+ }
+
+ $script:SessionEnd = Get-Date
+ $totalDuration = New-TimeSpan -Start $script:SessionStart -End $script:SessionEnd
+
+ Add-ProfileCleanupLog -Level 'INFO' -Text "Sessione completata. Profili rimossi: $profileSuccessCount. Cartelle residue rimosse: $residualSuccessCount. Errori: $failedCount. Durata: $totalDuration."
+ Save-ProfileCleanupLog
+
+ Write-Host ''
+ Write-Host '====================================================' -ForegroundColor Green
+ Write-Host ' COMPLETED'
+ Write-Host '====================================================' -ForegroundColor Green
+ Write-Host ''
+ Write-StyledMessage -Type 'Success' -Text ("✅ Profili registrati rimossi: {0}" -f $profileSuccessCount)
+ Write-StyledMessage -Type 'Success' -Text ("✅ Cartelle residue rimosse: {0}" -f $residualSuccessCount)
+ if ($failedCount -gt 0) {
+ Write-StyledMessage -Type 'Warning' -Text ("⚠️ Elementi non rimossi: {0}" -f $failedCount)
+ }
+ Write-StyledMessage -Type 'Info' -Text ("⏱️ Durata: {0:hh\:mm\:ss}" -f $totalDuration)
+ Write-StyledMessage -Type 'Info' -Text ("📄 Log: {0}" -f $script:LogFile)
+
+ Invoke-ProfileCleanupReboot
+ }
+ catch {
+ Add-ProfileCleanupLog -Level 'ERROR' -Text $_.Exception.Message
+ try { Save-ProfileCleanupLog } catch { }
+ Write-StyledMessage -Type 'Error' -Text ("❌ Errore: {0}" -f $_.Exception.Message)
+ throw
+ }
+ finally {
+ $ErrorActionPreference = $savedErrorActionPreference
+ $ProgressPreference = $savedProgressPreference
+ $ConfirmPreference = $savedConfirmPreference
+ }
+ }
+}
diff --git a/tool/WinExportLog.ps1 b/tools/WinExportLog.ps1
similarity index 100%
rename from tool/WinExportLog.ps1
rename to tools/WinExportLog.ps1
diff --git a/tool/WinReinstallStore.ps1 b/tools/WinReinstallStore.ps1
similarity index 100%
rename from tool/WinReinstallStore.ps1
rename to tools/WinReinstallStore.ps1
diff --git a/tool/WinRepairToolkit.ps1 b/tools/WinRepairToolkit.ps1
similarity index 100%
rename from tool/WinRepairToolkit.ps1
rename to tools/WinRepairToolkit.ps1
diff --git a/tool/WinUpdateReset.ps1 b/tools/WinUpdateReset.ps1
similarity index 100%
rename from tool/WinUpdateReset.ps1
rename to tools/WinUpdateReset.ps1