mirror of
https://github.com/Raphire/Win11Debloat.git
synced 2026-10-07 22:16:43 +00:00
Add localization framework for the GUI (en-US baseline) (#764)
Credit to @jmolinap2 for their work in #643, which was used as inspiration.
This commit is contained in:
+41
-3
@@ -85,7 +85,7 @@ PowerShell 5.1 for pull requests and pushes to `master`.
|
||||
|
||||
## Architecture & Design
|
||||
|
||||
Win11Debloat is a PowerShell script with a WPF (Windows Presentation Foundation) based GUI and command-line interface. The CLI provides an alternative to the GUI, allowing most features to be invoked directly through command-line parameters. The project uses a data-driven architecture, separating feature and application definitions from the logic that applies them.
|
||||
Win11Debloat is a PowerShell script with a WPF (Windows Presentation Foundation)-based GUI and command-line interface. The CLI provides an alternative to the GUI, allowing most features to be invoked directly through command-line parameters. The project uses a data-driven architecture, separating feature and application definitions from the logic that applies them.
|
||||
|
||||
### Entry Points
|
||||
|
||||
@@ -136,7 +136,8 @@ Win11Debloat/
|
||||
│ ├── Apps.json # List of supported apps for removal
|
||||
│ ├── DefaultSettings.json # Default configuration preset
|
||||
│ ├── Features.json # All features with metadata
|
||||
│ └── LastUsedSettings.json # Last used configuration (generated during use)
|
||||
│ ├── LastUsedSettings.json # Last used configuration (generated during use)
|
||||
│ └── Languages/ # GUI translation files, one folder per culture code (e.g. en-US/)
|
||||
├── Regfiles/ # Registry files for all features
|
||||
│ ├── Undo/ # Registry files for reverting features
|
||||
│ └── Sysprep/ # Registry files for Sysprep mode
|
||||
@@ -195,6 +196,8 @@ Avoid these common mistakes when contributing:
|
||||
|
||||
8. **Hardcoded Paths**: When writing PowerShell logic, use `$PSScriptRoot` and script variables instead of hardcoded paths. This ensures the script works regardless of where it's installed.
|
||||
|
||||
9. **Missing Translation Entry**: A new Feature, Category, or UI Group needs a matching entry in `Config/Languages/en-US/` (see [Localizing UI Text](#localizing-ui-text)). Without one, the GUI silently falls back to displaying the raw `FeatureId`/`CategoryId`/`GroupId` instead of your `Label`/`ToolTip` text.
|
||||
|
||||
## Implementing New Features
|
||||
|
||||
### Adding Support for a New App
|
||||
@@ -331,7 +334,11 @@ Add a corresponding parameter to both `Win11Debloat.ps1` AND `Scripts/Get.ps1`,
|
||||
[switch]$YourFeatureId,
|
||||
```
|
||||
|
||||
#### 4. Add or Update Tests
|
||||
#### 4. Add the English Translation Entry
|
||||
|
||||
Add a matching entry for your `FeatureId` to `Config/Languages/en-US/Features.json` (see [Localizing UI Text](#localizing-ui-text)) so the GUI shows your `Label`/`ToolTip`/etc. instead of the raw `FeatureId`.
|
||||
|
||||
#### 5. Add or Update Tests
|
||||
|
||||
Add or update Pester coverage for the new behavior, then run the full suite.
|
||||
|
||||
@@ -406,6 +413,8 @@ To add a new category for organizing features:
|
||||
}
|
||||
```
|
||||
|
||||
`CategoryId` is the stable identifier used for tweak presets, app-removal scope, and localization lookups. `Name` stays as the English display fallback. Skip `CategoryId` and the category falls back to using its `Name` as the ID, which breaks translation lookups, so always set one.
|
||||
|
||||
> [!TIP]
|
||||
> Use [Segoe Fluent Icon Assets](https://learn.microsoft.com/en-us/windows/apps/design/iconography/segoe-fluent-icons-font) for icon codes.
|
||||
|
||||
@@ -438,6 +447,35 @@ UI Groups allow features to be grouped together in the GUI with a combobox
|
||||
>[!IMPORTANT]
|
||||
> Define the referenced features before adding a group. Each `FeatureId` in a group's `Values` list must already be defined in the `"Features"` array in `Features.json`. Features referenced by a group are shown through that group rather than as standalone controls.
|
||||
|
||||
Add a matching entry for your `GroupId` to `Config/Languages/en-US/Features.json` (see [Localizing UI Text](#localizing-ui-text)) so the GUI shows your `Label`/`ToolTip`/option text instead of the raw `GroupId`.
|
||||
|
||||
### Localizing UI Text
|
||||
|
||||
GUI text lives in `Config/Languages/en-US/`, not hardcoded in `Schemas/*.xaml` or `Scripts/GUI/*.ps1`. Adding or changing a Feature, Category, or UI Group means updating the matching English entry, or the GUI falls back to showing the raw ID.
|
||||
|
||||
- `Config/Languages/en-US/Features.json`, under `"Features"`, keyed by `FeatureId`. Add `Label`, `ToolTip`, `ApplyText`, `UndoLabel`, and `ApplyUndoText` to match whatever fields you set in `Config/Features.json`.
|
||||
- `Config/Languages/en-US/Features.json`, under `"UiGroups"`, keyed by `GroupId`. Add `Label`, `ToolTip`, and a `Values` map of `FeatureId: "Option label"` matching the group's `Values` array.
|
||||
- `Config/Languages/en-US/Categories.json`, keyed by `CategoryId`. Add a `Label` for every new category.
|
||||
- `Config/Languages/en-US/Chrome.json`, a flat key/value map for static GUI chrome: buttons, dialog titles, tooltips not tied to a specific Feature/Category/UiGroup. Add a key here only for new static text, and reference it from XAML with a `%LANG:YourKey%` marker or from PowerShell with `Get-Translation -Key 'YourKey'`.
|
||||
|
||||
Check for an existing `Chrome.json` key with the same English text before adding a new one. Two keys holding identical strings is duplication waiting to drift.
|
||||
|
||||
Not everything needs a translation key. A `throw` guarding against a caller passing the wrong argument type, or any other error that signals a bug in the code rather than something the user did, stays as a plain English string. No user will ever see it.
|
||||
|
||||
A new language is its own pull request, not something bundled with unrelated changes.
|
||||
|
||||
### Adding a New Language
|
||||
|
||||
1. **Create the language folder**: copy `Config/Languages/en-US/` to `Config/Languages/<culture-code>/`, using the .NET culture code the GUI should match (`nl-NL`, `es-ES`). This is also the folder matched against the user's Windows display language: exact match first, then a language-only prefix match (`nl-BE` falls back to `nl-NL` if that's the only Dutch folder present), then `en-US` if nothing matches.
|
||||
|
||||
2. **Translate the three files** (`Chrome.json`, `Features.json`, `Categories.json`), keeping every key name exactly as it is in `en-US`. Only the values change. Leave a key untranslated and the GUI shows the `en-US` text for it instead of a blank or a crash, so a translation can land incrementally.
|
||||
|
||||
3. **Add the language's plural rule**. Strings using the `_one`/`_other` suffix convention (see `AppsSelectedCount_one` in `en-US/Chrome.json`) need a matching `case` in `Get-PluralCategory` (`Scripts/FileIO/Import-LanguageFile.ps1`), keyed by language prefix (`'nl'`). Several languages share English's rule, singular at exactly 1, plural otherwise, and can reuse it as-is. Others need more categories: CLDR defines `zero`/`one`/`two`/`few`/`many`/`other`. Match your key suffixes to whatever categories your language's rule actually uses.
|
||||
|
||||
4. **Check your coverage**. Dot-source `Scripts/FileIO/Import-LanguageFile.ps1` and run `Test-LanguageKeyCoverage -LanguageCode '<culture-code>'` to diff your key set against `en-US`. Check `ResolvedLanguageCode` matches the folder you meant to test, `MissingKeys` is the same fallback-eligible gap from step 2, fine to leave for later, and `ExtraKeys` usually means a typo'd key name that nothing will ever look up.
|
||||
|
||||
5. **Test it**. Run `.\Win11Debloat.ps1 -Language '<culture-code>'` to launch the GUI in that language regardless of your Windows display language, and click through every tab, dialog, and tooltip.
|
||||
|
||||
## Submitting a Pull Request
|
||||
|
||||
1. **Commit your changes** with clear, descriptive commit messages:
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
{
|
||||
"PrivacySuggestedContent": {
|
||||
"Label": "Privacy & Suggested Content"
|
||||
},
|
||||
"System": {
|
||||
"Label": "System"
|
||||
},
|
||||
"StartMenuSearch": {
|
||||
"Label": "Start Menu & Search"
|
||||
},
|
||||
"AI": {
|
||||
"Label": "AI"
|
||||
},
|
||||
"WindowsUpdate": {
|
||||
"Label": "Windows Update"
|
||||
},
|
||||
"Taskbar": {
|
||||
"Label": "Taskbar"
|
||||
},
|
||||
"Appearance": {
|
||||
"Label": "Appearance"
|
||||
},
|
||||
"FileExplorer": {
|
||||
"Label": "File Explorer"
|
||||
},
|
||||
"Gaming": {
|
||||
"Label": "Gaming"
|
||||
},
|
||||
"MultiTasking": {
|
||||
"Label": "Multi-tasking"
|
||||
},
|
||||
"OptionalWindowsFeatures": {
|
||||
"Label": "Optional Windows Features"
|
||||
},
|
||||
"Other": {
|
||||
"Label": "Other"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,349 @@
|
||||
{
|
||||
"TitleBarAppName": "Win11Debloat",
|
||||
"MessageBoxOk": "OK",
|
||||
"MessageBoxCancel": "Cancel",
|
||||
"MessageBoxYes": "Yes",
|
||||
"MessageBoxNo": "No",
|
||||
"TitleBarSupportCreator": "Support the creator",
|
||||
"TitleBarOptions": "Options",
|
||||
"TitleBarClose": "Close",
|
||||
|
||||
"MenuImportConfig": "Import config",
|
||||
"MenuImportConfigTooltip": "Import configuration",
|
||||
"MenuExportConfig": "Export config",
|
||||
"MenuExportConfigTooltip": "Export configuration",
|
||||
"MenuRestoreBackup": "Restore backup",
|
||||
"MenuRestoreBackupTooltip": "Restore registry backup",
|
||||
"MenuDocumentation": "Documentation",
|
||||
"MenuReportBug": "Report a bug",
|
||||
"MenuLogs": "Logs",
|
||||
"MenuAbout": "About",
|
||||
|
||||
"HomeTabHeader": "Home",
|
||||
"HomeTitle": "Welcome to Win11Debloat",
|
||||
"HomeSubtitle": "Your clean Windows experience is just a few clicks away!",
|
||||
"HomeUserQuestion": "What user do you want to apply changes to?",
|
||||
"HomeUserSelectionAutomationName": "Apply Changes To",
|
||||
"HomeUserOptionCurrentUser": "Current User",
|
||||
"HomeUserOptionOtherUser": "Other User",
|
||||
"HomeUserOptionSysprep": "Windows Default User (Sysprep)",
|
||||
"HomeUsernamePlaceholder": "Enter username",
|
||||
"HomeDefaultModeBtn": "Default Mode",
|
||||
"HomeDefaultModeBtnTooltip": "Quickly select the recommended settings",
|
||||
"HomeCustomSetupBtn": "Custom Setup",
|
||||
"HomeCustomSetupBtnTooltip": "Manually select your preferred settings",
|
||||
|
||||
"AppRemovalTabHeader": "App Removal",
|
||||
"AppRemovalTitle": "App Removal",
|
||||
"AppRemovalSubtitle": "Select which apps you want to remove",
|
||||
"AppQuickSelect": "Quick Select",
|
||||
"AppQuickSelectAutomationName": "App Presets",
|
||||
"AppClearSelection": "Clear Selection",
|
||||
"AppClearSelectionTooltip": "Clear all selected apps",
|
||||
"AppPresetDefaultApps": "Default apps",
|
||||
"AppPresetDefaultAppsAutomationName": "Default selection",
|
||||
"AppPresetDefaultAppsTooltip": "Select the apps that are safe to remove for most users",
|
||||
"AppPresetLastUsed": "Previously selected apps",
|
||||
"AppPresetLastUsedTooltip": "Select the apps that were removed the last time Win11Debloat was run",
|
||||
"AppOnlyInstalledCheckbox": "Only show installed apps",
|
||||
"AppOnlyInstalledCheckboxTooltip": "Only show applications that are currently installed on this system",
|
||||
"AppSearchPlaceholder": "Search apps...",
|
||||
"AppSearchAutomationName": "Search app",
|
||||
"AppTableHeaderName": "Name",
|
||||
"AppTableHeaderDescription": "Description",
|
||||
"AppTableHeaderAppId": "App ID",
|
||||
"AppLoadingIndicator": "Loading apps...",
|
||||
"AppLegendTitle": "Legend:",
|
||||
"AppLegendRecommendedTooltip": "[Recommended] Safe to remove for most users",
|
||||
"AppLegendRecommended": "Recommended",
|
||||
"AppLegendOptionalTooltip": "[Optional] Can be safely removed if you don't need this app",
|
||||
"AppLegendOptional": "Optional",
|
||||
"AppLegendNotRecommendedTooltip": "[Not Recommended] Only remove if you know what you are doing",
|
||||
"AppLegendNotRecommended": "Not Recommended",
|
||||
|
||||
"TweaksTabHeader": "Tweaks",
|
||||
"TweaksTitle": "System Tweaks",
|
||||
"TweaksSubtitle": "Select what changes you want to make, you can hover over settings for more information",
|
||||
"TweaksQuickSelect": "Quick Select",
|
||||
"TweaksQuickSelectAutomationName": "Tweak Presets",
|
||||
"TweaksClearSelection": "Clear Selection",
|
||||
"TweaksClearSelectionTooltip": "Clear all selected tweaks",
|
||||
"TweaksPresetDefault": "Default settings",
|
||||
"TweaksPresetDefaultTooltip": "Select the settings that are recommended for most people",
|
||||
"TweaksPresetLastUsed": "Previously selected settings",
|
||||
"TweaksPresetLastUsedTooltip": "Select the settings that were applied the last time Win11Debloat was run",
|
||||
"TweaksPresetPrivacy": "Privacy & suggested content",
|
||||
"TweaksPresetPrivacyAutomationName": "All Privacy and Suggested Content",
|
||||
"TweaksPresetPrivacyTooltip": "Select all tweaks related to privacy & suggested content",
|
||||
"TweaksPresetAI": "AI features",
|
||||
"TweaksPresetAIAutomationName": "All AI features",
|
||||
"TweaksPresetAITooltip": "Select all AI related tweaks",
|
||||
"TweaksDetectApplied": "Detect applied tweaks",
|
||||
"TweaksDetectAppliedTooltip": "Detect all tweaks currently applied for the current user",
|
||||
"TweaksSearchPlaceholder": "Search tweaks...",
|
||||
"TweaksSearchAutomationName": "Search tweaks",
|
||||
|
||||
"DeployTabHeader": "Deployment Settings",
|
||||
"DeployTitle": "Deployment Settings",
|
||||
"DeploySubtitle": "Configure how the selected changes will be applied to your system",
|
||||
"DeployApplyToTitle": "Changes will be applied to",
|
||||
"DeployUserSelectionDescription": "The currently logged-in user profile.",
|
||||
"DeployAppRemovalScopeTitle": "Apps will be removed for",
|
||||
"DeployAppRemovalScopeAutomationName": "App Removal Scope",
|
||||
"DeployAppRemovalScopeAllUsers": "All users",
|
||||
"DeployAppRemovalScopeCurrentUser": "Current user only",
|
||||
"DeployAppRemovalScopeTargetUser": "Target user only",
|
||||
"DeployOptionsTitle": "Options",
|
||||
"DeployOptionRegistryBackup": "Create a registry backup (Recommended)",
|
||||
"DeployOptionRestorePoint": "Create a system restore point (Recommended)",
|
||||
"DeployOptionRestartExplorer": "Restart the Windows Explorer process to apply all changes immediately",
|
||||
"DeployReviewChanges": "Review selected changes",
|
||||
"DeployApplyChanges": "Apply Changes",
|
||||
|
||||
"BubbleViewChanges": "View the selected changes here",
|
||||
|
||||
"AboutTitle": "About Win11Debloat",
|
||||
"AboutVersionLabel": "Version:",
|
||||
"AboutAuthorLabel": "Author:",
|
||||
"AboutProjectLabel": "Project:",
|
||||
"AboutDonationMessage": "Win11Debloat is a passion project that I maintain in my free time. If you've found this tool useful, please consider making a small donation to support its development. I really appreciate it!",
|
||||
"AboutKofiLink": "Support me on Ko-fi",
|
||||
|
||||
"AppSelectionWindowTitle": "Win11Debloat Application Selection",
|
||||
"AppSelectionWindowInstructions": "Check apps that you wish to remove, uncheck apps that you wish to keep",
|
||||
"AppSelectionWindowCheckAll": "Check/Uncheck all",
|
||||
"AppSelectionWindowCheckAllAutomationName": "Check or Uncheck all",
|
||||
"AppSelectionWindowConfirm": "Confirm",
|
||||
"AppSelectionWindowCancel": "Cancel",
|
||||
|
||||
"ApplyCancelTooltip": "Cancel",
|
||||
"ApplyInProgressTitle": "Applying Changes",
|
||||
"ApplyPreparing": "Preparing...",
|
||||
"ApplyStepCounter": "Step {0} of {1}",
|
||||
"ApplyCreatingRegistryBackup": "Creating registry backup...",
|
||||
"ApplyCreatingRestorePoint": "Creating system restore point, this may take a moment...",
|
||||
"ApplyCompletionTitleSuccess": "Changes Applied",
|
||||
"ApplyCompletionMessageDefault": "Please note that some changes will only take effect after a reboot. Thanks for using Win11Debloat!",
|
||||
"ApplyRebootRequiredHeader": "A reboot is required for these changes to take effect:",
|
||||
"ApplyCompletionTitleCancelled": "Cancelled",
|
||||
"ApplyCompletionMessageCancelled": "Script execution was cancelled by the user.",
|
||||
"ApplyCompletionTitleErrors": "Changes Applied with Errors",
|
||||
"ApplyCompletionMessageVerificationUnavailable": "All changes were applied without errors, but Win11Debloat could not confirm that all selected apps were successfully uninstalled.",
|
||||
"ApplyCompletionMessageFailures_one": "{0} change failed. See console for details.",
|
||||
"ApplyCompletionMessageFailures_other": "{0} changes failed. See console for details.",
|
||||
"ApplyCompletionMessageReady": "Your system is ready. Thanks for using Win11Debloat!",
|
||||
"ApplyCompletionTitleError": "Error",
|
||||
"ApplyCompletionMessageError": "An error occurred while applying changes: {0}",
|
||||
|
||||
"NavBack": "Back",
|
||||
"NavNext": "Next",
|
||||
"NavProgressAppRemoval": "App Removal",
|
||||
"NavProgressSystemTweaks": "System Tweaks",
|
||||
"NavProgressDeploymentSettings": "Deployment Settings",
|
||||
|
||||
"RestoreWindowTitle": "Restore Backup",
|
||||
"RestoreChooseRegistryTitle": "Restore Registry Backup",
|
||||
"RestoreChooseRegistryDescription": "Restore system registry configuration from a backup",
|
||||
"RestoreChooseStartMenuTitle": "Restore Start Menu Backup",
|
||||
"RestoreChooseStartMenuDescription": "Restore the Start Menu pinned apps layout from a backup",
|
||||
"RestoreSelectTypePrompt": "Choose what changes you want to restore.",
|
||||
"RestoreRegistryStepTitle": "Restore Registry Backup",
|
||||
"RestoreRegistryIntro": "This will restore any system registry changes made by Win11Debloat to the previous state. You can review the changes after selecting a backup file. Removed applications will need to be reinstalled manually.",
|
||||
"RestoreRegistryWarning": "Warning: Only use backup files generated by Win11Debloat.",
|
||||
"RestoreStartMenuStepTitle": "Restore Start Menu Backup",
|
||||
"RestoreStartMenuIntro": "This will restore the Start Menu pinned apps layout for the selected user(s) using a backup that is automatically created by Win11Debloat. Manually created backups can also be used.",
|
||||
"RestoreStartMenuScopeCurrentUser": "Current user",
|
||||
"RestoreStartMenuScopeAllUsers": "All users",
|
||||
"RestoreStartMenuAutoBackupCheckbox": "Automatically find Start Menu backup",
|
||||
"RestoreFileLabel": "File:",
|
||||
"RestoreFileNotSelected": "Not selected",
|
||||
"RestoreCreatedLabel": "Created:",
|
||||
"RestoreCreatedUnavailable": "N/A",
|
||||
"RestoreCreatedUnknown": "Unknown",
|
||||
"RestoreTargetLabel": "Target:",
|
||||
"RestoreRevertibleFeaturesTitle": "The following changes will be reverted:",
|
||||
"RestoreReappliedFeaturesTitle": "The following changes will be re-applied:",
|
||||
"RestoreNonRevertibleFeaturesTitle": "The following changes won't be reverted:",
|
||||
"RestoreWikiLink": "Visit the wiki for more information",
|
||||
"RestoreStartMenuSummary": "This will replace the current Start Menu pinned apps layout for {0} with the selected backup.",
|
||||
"RestoreStartMenuSummaryScopeCurrentUser": "the current user",
|
||||
"RestoreStartMenuSummaryScopeAllUsers": "all users",
|
||||
"RestorePrimaryActionSelectBackupFile": "Select backup file",
|
||||
"RestorePrimaryActionRestoreBackup": "Restore backup",
|
||||
"RestorePrimaryActionRestoreFromBackup": "Restore from backup",
|
||||
"RestoreBackBtnCancel": "Cancel",
|
||||
"RestoreBackBtnBack": "Back",
|
||||
"RestoreInvalidBackupTitle": "Invalid Backup File",
|
||||
"RestoreInvalidBackupMessage": "The selected file could not be loaded:\n{0}",
|
||||
"RestoreNoBackupFoundTitle": "No Backup Found",
|
||||
"RestoreNoBackupFoundMessage": "No Start Menu backup file was found for {0}. Uncheck 'Automatically find Start Menu backup' to select a backup file manually.",
|
||||
"RestoreNoRestorableChangesError": "The selected backup does not contain any changes that can be restored.",
|
||||
"RestoreSelectRegistryFileDialogTitle": "Select Registry Backup File",
|
||||
"RestoreSelectStartMenuFileDialogTitle": "Select Start Menu Backup File",
|
||||
"RestoreUnknownFeature": "Unknown feature",
|
||||
|
||||
"RestoreTargetUnknown": "Unknown",
|
||||
"RestoreTargetDefaultUserProfile": "Default user profile",
|
||||
"RestoreTargetCurrentUser": "Current user",
|
||||
"RestoreTargetAllUsers": "All users",
|
||||
"RestoreTargetCurrentUserNamed": "Current user ({0})",
|
||||
"RestoreTargetUserNamed": "User ({0})",
|
||||
"RestoreTargetUser": "User",
|
||||
|
||||
"RestoreBackupRestoredTitle": "Backup Restored",
|
||||
"RestoreRegistryWhatIfMessage": "[WhatIf] Registry backup would be restored (no changes made).",
|
||||
"RestoreRegistrySuccessMessage": "Registry backup restored successfully. Some changes may require a restart to take effect.",
|
||||
"RestoreStartMenuFailedMessage": "Failed to restore the Start Menu backup.\n{0}",
|
||||
"RestoreStartMenuPartialSuccessMessage": "The Start Menu backup was successfully restored for {0} user(s).\nSome users could not be restored:\n{1}",
|
||||
"RestoreStartMenuWhatIfMessage": "[WhatIf] Start Menu backup would be restored (no changes made).",
|
||||
"RestoreStartMenuAllUsersSuccessMessage": "The Start Menu backup was successfully restored for all users. The changes will apply the next time users sign in.",
|
||||
"RestoreStartMenuCurrentUserSuccessMessage": "The Start Menu backup was successfully restored for the current user. The changes will apply the next time you sign in.",
|
||||
"RestoreOperationFailedMessage": "Restore failed: {0}",
|
||||
"RestoreUnexpectedError": "An unexpected error occurred.",
|
||||
|
||||
"ImportExportCategoryApplications": "Applications",
|
||||
"ImportExportCategorySystemTweaks": "System Tweaks",
|
||||
"ImportExportCategoryDeploymentSettings": "Deployment Settings",
|
||||
"ImportExportCategoryDisabledTooltip": "Nothing selected in this category",
|
||||
"ImportExportSchemaMissingMessage": "Import/Export window schema file could not be found.",
|
||||
"ImportExportExportTitle": "Export Configuration",
|
||||
"ImportExportExportPrompt": "Create a configuration file based on the currently selected settings. You can choose which settings categories you wish to include in the export.",
|
||||
"ImportExportExportActionLabel": "Export Settings",
|
||||
"ImportExportSelectExportFileDialogTitle": "Export Configuration",
|
||||
"ImportExportWhatIfExportMessage": "[WhatIf] Configuration would be exported to this file (no file written).",
|
||||
"ImportExportExportSuccessMessage": "Configuration exported successfully.",
|
||||
"ImportExportExportFailedMessage": "Failed to export configuration",
|
||||
"ImportExportImportTitle": "Import Configuration",
|
||||
"ImportExportImportPrompt": "Choose the settings categories that you wish to import. You can review and modify the imported settings before they are applied.",
|
||||
"ImportExportImportActionLabel": "Import Settings",
|
||||
"ImportExportSelectImportFileDialogTitle": "Select Configuration File",
|
||||
"ImportExportInvalidConfigTitle": "Invalid Config",
|
||||
"ImportExportReadFailedMessage": "Failed to read configuration file",
|
||||
"ImportExportInvalidConfigMessage": "Invalid configuration file: {0}",
|
||||
"ImportExportImportSuccessMessage": "Configuration imported successfully.",
|
||||
"ImportExportDeployUserCurrentUser": "User: Current User",
|
||||
"ImportExportDeployUserOther": "User: {0}",
|
||||
"ImportExportDeployUserOtherFallback": "Other User",
|
||||
"ImportExportDeployUserSysprep": "User: Sysprep",
|
||||
"ImportExportAppRemovalAllUsers": "App Removal: All Users",
|
||||
"ImportExportAppRemovalCurrentUser": "App Removal: Current User",
|
||||
"ImportExportAppRemovalOther": "App Removal: {0}",
|
||||
"ImportExportOptionRestorePoint": "Restore Point",
|
||||
"ImportExportOptionRegistryBackup": "Registry Backup",
|
||||
"ImportExportOptionRestartExplorer": "Restart Explorer",
|
||||
"ImportExportOptionsPrefix": "Options: {0}",
|
||||
"ImportExportDefaultDeploymentSettings": "Default deployment settings",
|
||||
"ImportExportAppsSelected_one": "{0} app selected",
|
||||
"ImportExportAppsSelected_other": "{0} apps selected",
|
||||
"ImportExportNoAppsSelected": "No apps selected",
|
||||
"ImportExportTweaksSelected_one": "{0} tweak selected",
|
||||
"ImportExportTweaksSelected_other": "{0} tweaks selected",
|
||||
"ImportExportNoTweaksSelected": "No tweaks selected",
|
||||
|
||||
"AppSelectionWinGetLoadFailedMessage": "Unable to load list of installed apps via WinGet.",
|
||||
"ErrorTitle": "Error",
|
||||
|
||||
"AppSelectionStatusSelected_one": "{0} app selected for removal",
|
||||
"AppSelectionStatusSelected_other": "{0} apps selected for removal",
|
||||
"AppRemovalScopeDescriptionAllUsers": "Apps will be removed for all users and from the Windows image to prevent reinstallation for new users.",
|
||||
"AppRemovalScopeDescriptionCurrentUser": "Apps will only be removed for the current user.",
|
||||
"AppRemovalScopeDescriptionTargetUser": "Apps will only be removed for the specified target user.",
|
||||
|
||||
"TweaksCategoryHelpTooltip": "Open the wiki for more info on '{0}' tweaks",
|
||||
"TweaksOptionNoChange": "No Change",
|
||||
"TweaksOptionApply": "Apply",
|
||||
"TweaksOptionDisable": "Disable",
|
||||
"TweaksOptionEnable": "Enable",
|
||||
"TweaksAlreadyAppliedTooltip": "This tweak is already applied and cannot be undone automatically. Visit the Win11Debloat wiki for instructions on how to manually revert this change.",
|
||||
|
||||
"DeployChangeRemoveApps_one": "Remove {0} application",
|
||||
"DeployChangeRemoveApps_other": "Remove {0} applications",
|
||||
"DeployChangeUndoPrefix": "Undo: {0}",
|
||||
"DeployNoChangesSelectedMessage": "No changes have been selected.",
|
||||
"DeploySelectedChangesTitle": "Selected Changes",
|
||||
"DeployUserDescriptionCurrentUser": "The currently logged-in user profile",
|
||||
"DeployUserDescriptionCurrentUserNamed": "The currently logged-in user profile: {0}",
|
||||
"DeployUserDescriptionOtherUser": "A different user profile on this system",
|
||||
"DeployUserDescriptionOtherUserNamed": "A different user profile on this system: {0}",
|
||||
"DeployUserDescriptionSysprep": "The default user template, affecting all new users created after this point. Useful for Sysprep deployment.",
|
||||
|
||||
"LogsMenuTitle": "Logs",
|
||||
"LogsFolderNotFoundMessage": "No logs folder found at: {0}",
|
||||
"ExportConfigFailedTitle": "Export Configuration Failed",
|
||||
"ExportConfigFailedMessage": "Unable to open export configuration dialog: {0}",
|
||||
"ImportConfigFailedTitle": "Import Configuration Failed",
|
||||
"ImportConfigFailedMessage": "Unable to open import configuration dialog: {0}",
|
||||
"RestoreBackupFailedTitle": "Restore Backup Failed",
|
||||
"RestoreBackupFailedMessage": "Unable to open restore backup dialog: {0}",
|
||||
"AppPresetSelectTooltip": "Select {0}",
|
||||
"InvalidUsernameFallbackMessage": "Please enter a valid username.",
|
||||
"InvalidUsernameTitle": "Invalid Username",
|
||||
"NoChangesSelectedTitle": "No Changes Selected",
|
||||
"NoChangesSelectedMessage": "No changes have been selected, please select at least one option to proceed.",
|
||||
"HomeCurrentUserWithName": "Current User ({0})",
|
||||
"InitializationErrorTitle": "Initialization Error",
|
||||
"InitializationErrorMessage": "An error occurred during initialization: {0}",
|
||||
"WhatIfModeTitle": "WhatIf Mode",
|
||||
"WhatIfModeMessage": "WhatIf mode is enabled. The script will not make any changes to your system in this mode.\n\nYou can observe the actions that would be taken by the script in the console output.",
|
||||
|
||||
"ConfirmTitle": "Are you sure?",
|
||||
"ConfirmRemoveStoreMessage": "Are you sure that you wish to uninstall the Microsoft Store? This app cannot easily be reinstalled.",
|
||||
"ConfirmRemoveTerminalMessage": "Are you sure that you wish to remove Windows Terminal? Windows Terminal is the default command-line app for Windows. Ensure you are not running Win11Debloat via Windows Terminal before proceeding to avoid a mid-process failure.",
|
||||
|
||||
"ConfigEmptyOrUnreadable": "Configuration is empty or could not be read.",
|
||||
"ConfigMissingVersion": "Configuration is missing a Version field.",
|
||||
"ConfigNoImportableData": "The configuration file contains no importable data.",
|
||||
"ConfigAppsMustBeStrings": "Configuration Apps entries must be strings.",
|
||||
"ConfigEntriesMustHaveNameAndValue": "Configuration {0} entries must contain Name and Value properties.",
|
||||
"ConfigInvalidAppRemovalScopeIndex": "AppRemovalScopeIndex must be a supported numeric value (0, 1, or 2).",
|
||||
"ConfigInvalidUserSelectionIndex": "UserSelectionIndex must be a supported numeric value (0, 1, or 2).",
|
||||
"ConfigScopeRequiresCurrentUserTarget": "App removal scope 'Current user only' (AppRemovalScopeIndex 1) requires the deployment target 'Current User' (UserSelectionIndex 0).",
|
||||
"ConfigScopeRequiresOtherUserTarget": "App removal scope 'Target user only' (AppRemovalScopeIndex 2) requires the deployment target 'Other User' (UserSelectionIndex 1).",
|
||||
"ConfigScopeRequiresOtherUsername": "App removal scope 'Target user only' (AppRemovalScopeIndex 2) requires an 'OtherUsername' value.",
|
||||
|
||||
"UsernameValidationEmpty": "Please enter a username",
|
||||
"UsernameValidationOwnUsername": "Cannot enter your own username, use 'Current User' option instead",
|
||||
"UsernameValidationNotFound": "User not found, please enter a valid username",
|
||||
"UsernameValidationFound": "User found: {0}",
|
||||
|
||||
"RestorePointFailedTitle": "Restore Point Creation Failed",
|
||||
"RestorePointFailedMessage": "Failed to create a system restore point. Do you want to continue without a restore point?",
|
||||
"ForceRemoveEdgeTitle": "Force Uninstall Microsoft Edge?",
|
||||
"ForceRemoveEdgeMessage": "Unable to uninstall Microsoft Edge via WinGet. Would you like to forcefully uninstall it? NOT RECOMMENDED!",
|
||||
"RemovingAppsSubStep": "Removing apps ({0}/{1})",
|
||||
|
||||
"BackupFileNotFound": "Backup file was not found: {0}",
|
||||
"BackupFileNotValidJson": "Failed to read backup file '{0}'. The file is not valid JSON.",
|
||||
"BackupValidationSingleError": "Validation failed: {0}",
|
||||
"BackupValidationMultipleErrors": "Validation failed with {0} errors. See console output for details.",
|
||||
"BackupMissingProperty": "Missing property: {0}",
|
||||
"BackupUnsupportedVersion": "Unsupported backup version '{0}'.",
|
||||
"BackupUnsupportedType": "Unsupported BackupType '{0}'.",
|
||||
"BackupInvalidUser": "Invalid user '{0}'",
|
||||
"BackupUserScopedWrongContext": "Backup was made for '{0}' and is user-scoped. Re-run as that user; SYSTEM cannot restore a CurrentUser backup.",
|
||||
"BackupUserMismatch": "Backup was made for '{0}', this does not match current user '{1}'.",
|
||||
"BackupUnsupportedTarget": "Unsupported Target '{0}'.",
|
||||
"BackupSelectedFeaturesMustBeStrings": "SelectedFeatures must contain non-empty string feature IDs.",
|
||||
"BackupSelectedUndoFeaturesMustBeStrings": "SelectedUndoFeatures must contain non-empty string feature IDs.",
|
||||
"BackupMissingSelectedFeatures": "Backup must contain at least one feature ID in SelectedFeatures or SelectedUndoFeatures.",
|
||||
"BackupAllowlistValidationFailed": "Failed to validate backup: {0}",
|
||||
"BackupSnapshotMissingPath": "Backup validation failed: Registry key snapshot is missing Path.",
|
||||
"BackupFeatureCatalogNotLoaded": "Unable to validate registry backup allowlist because feature definitions are not loaded.",
|
||||
"BackupNoAllowedRegistryPaths": "Backup contains registry snapshots but no allowed registry paths were derived from the selected features.",
|
||||
"BackupFeatureNotInCatalog": "Selected feature '{0}' was not found in the current feature catalog.",
|
||||
"BackupUnsupportedRegistryPath": "Backup contains unsupported registry path '{0}'.",
|
||||
"BackupUnexpectedRegistryPath": "Backup contains unexpected registry path '{0}' that is not allowed by SelectedFeatures.",
|
||||
"BackupUnexpectedValue": "Backup contains unexpected value '{0}' under '{1}'.",
|
||||
"BackupUnsupportedValueKind": "Backup contains unsupported registry value kind '{0}' for '{1}'.",
|
||||
"BackupInvalidValueData": "Backup contains invalid registry data for kind '{0}' at '{1}'.",
|
||||
"BackupValueMustNotDefineKind": "Backup value '{0}' must not define Kind when Exists is false.",
|
||||
|
||||
"StartMenuNoBackupFoundForUser": "No start menu backup file found for user {0}.",
|
||||
"StartMenuWhatIfRestoredForUser": "[WhatIf] Restored start menu for user {0}.",
|
||||
"StartMenuRestoredForUser": "Restored start menu for user {0}.",
|
||||
"StartMenuRestoreFailedForUser": "Failed to restore start menu for user {0}. {1}",
|
||||
"StartMenuWhatIfRemovedDefaultProfile": "[WhatIf] Removed start2.bin for the default user profile.",
|
||||
"StartMenuRemovedDefaultProfile": "Removed start2.bin for the default user profile.",
|
||||
"StartMenuRemoveFailedDefaultProfile": "Failed to remove start2.bin for the default user profile. {0}",
|
||||
"StartMenuNoUserLocationsFound": "No user start menu locations were found."
|
||||
}
|
||||
@@ -0,0 +1,744 @@
|
||||
{
|
||||
"Features": {
|
||||
"RemoveApps": {
|
||||
"Label": "Remove the apps specified with the 'Apps' parameter",
|
||||
"ApplyText": "Removing selected apps"
|
||||
},
|
||||
"Apps": {
|
||||
"Label": "The selection of apps to remove, specified as a comma separated list. Use 'Default' (or omit) to use the default apps list",
|
||||
"ApplyText": "Removing selected apps"
|
||||
},
|
||||
"RemoveGamingApps": {
|
||||
"Label": "Remove the Xbox App and Xbox Gamebar",
|
||||
"ApplyText": "Removing gaming related apps"
|
||||
},
|
||||
"RemoveHPApps": {
|
||||
"Label": "Remove HP OEM applications",
|
||||
"ApplyText": "Removing HP apps"
|
||||
},
|
||||
"CreateRestorePoint": {
|
||||
"Label": "Create a system restore point",
|
||||
"ApplyText": "Creating system restore point"
|
||||
},
|
||||
"DisableTelemetry": {
|
||||
"Label": "Disable telemetry, tracking & targeted ads",
|
||||
"ToolTip": "This setting disables telemetry, diagnostic data collection, activity history, app-launch tracking, targeted ads and more. It limits the data that is sent to Microsoft about your device and usage. If you are a Windows Insider, updates may be blocked until optional diagnostic data collection is turned back on.",
|
||||
"ApplyText": "Disabling telemetry and diagnostic data collection",
|
||||
"UndoLabel": "Enable telemetry, tracking & targeted ads",
|
||||
"ApplyUndoText": "Enabling telemetry and diagnostic data collection"
|
||||
},
|
||||
"DisableSuggestions": {
|
||||
"Label": "Disable tips, tricks & suggested content throughout Windows",
|
||||
"ToolTip": "This setting removes many annoying distractions from Windows. This includes things like notifications, reminders and sync provider ads. It also prevents automated installation of suggested apps.",
|
||||
"ApplyText": "Disabling tips, tricks, suggestions and ads throughout Windows",
|
||||
"UndoLabel": "Enable tips, tricks & suggested content throughout Windows",
|
||||
"ApplyUndoText": "Enabling tips, tricks, suggestions and ads throughout Windows"
|
||||
},
|
||||
"DisableNotifications": {
|
||||
"Label": "Disable Windows Notifications (From apps and other senders)",
|
||||
"ToolTip": "Disables native Windows notifications from apps and other senders, this includes notifications from apps like Discord, WhatsApp, Teams, and Slack.",
|
||||
"ApplyText": "Disabling Windows notifications and reminders",
|
||||
"UndoLabel": "Enable Windows notifications and reminders",
|
||||
"ApplyUndoText": "Enabling Windows notifications and reminders"
|
||||
},
|
||||
"DisableLocationServices": {
|
||||
"Label": "Disable Windows location services & app location access",
|
||||
"ToolTip": "This will turn off Windows Location Services and deny apps access to your location. This feature uses policies, which will lock down certain settings.",
|
||||
"ApplyText": "Disabling Windows location services and app location access",
|
||||
"UndoLabel": "Enable Windows location services & app location access",
|
||||
"ApplyUndoText": "Enabling Windows location services and app location access"
|
||||
},
|
||||
"DisableFindMyDevice": {
|
||||
"Label": "Disable Find My Device location tracking",
|
||||
"ToolTip": "This will turn off the 'Find My Device' feature, which periodically sends your device's location to Microsoft. This feature uses policies, which will lock down certain settings.",
|
||||
"ApplyText": "Disabling Find My Device location tracking",
|
||||
"UndoLabel": "Enable Find My Device location tracking",
|
||||
"ApplyUndoText": "Enabling Find My Device location tracking"
|
||||
},
|
||||
"DisableLockscreenTips": {
|
||||
"Label": "Disable tips & tricks on the lock screen",
|
||||
"ToolTip": "This will turn off the lockscreen spotlight option and disable the tips, tricks and fun facts that appear on the lock screen.",
|
||||
"ApplyText": "Disabling tips & tricks on the lock screen",
|
||||
"UndoLabel": "Enable tips & tricks on the lock screen",
|
||||
"ApplyUndoText": "Enabling tips & tricks on the lock screen"
|
||||
},
|
||||
"EnableDesktopSpotlight": {
|
||||
"Label": "Enable Windows spotlight desktop background and shortcut",
|
||||
"ToolTip": "Restores the Windows spotlight desktop background and the 'Learn about this picture' shortcut.",
|
||||
"ApplyText": "Restoring the Windows spotlight desktop background and shortcut"
|
||||
},
|
||||
"DisableDesktopSpotlight": {
|
||||
"Label": "Disable Windows spotlight desktop background",
|
||||
"ToolTip": "Turns off Windows spotlight for the desktop background, which displays rotating images and may show tips and fun facts. This feature uses policies, which will lock down certain settings.",
|
||||
"ApplyText": "Disabling the Windows spotlight desktop background",
|
||||
"UndoLabel": "Enable Windows spotlight desktop background",
|
||||
"ApplyUndoText": "Restoring the Windows spotlight desktop background and shortcut"
|
||||
},
|
||||
"HideDesktopSpotlightIcon": {
|
||||
"Label": "Hide the 'Learn about this picture' desktop shortcut",
|
||||
"ToolTip": "Hides the 'Learn about this picture' desktop shortcut while keeping Windows spotlight wallpaper rotation enabled. It does not disable Windows spotlight.",
|
||||
"ApplyText": "Hiding the Windows spotlight 'Learn about this picture' desktop shortcut",
|
||||
"UndoLabel": "Show the 'Learn about this picture' desktop shortcut",
|
||||
"ApplyUndoText": "Showing the Windows spotlight 'Learn about this picture' desktop shortcut"
|
||||
},
|
||||
"DisableEdgeAds": {
|
||||
"Label": "Disable ads, suggestions and newsfeed in Edge",
|
||||
"ToolTip": "This will turn off various distractions from Microsoft Edge such as ads, suggestions and the MSN news feed. This feature uses policies, which will lock down certain settings.",
|
||||
"ApplyText": "Disabling ads, suggestions and the MSN news feed in Microsoft Edge",
|
||||
"UndoLabel": "Enable ads, suggestions and newsfeed in Edge",
|
||||
"ApplyUndoText": "Enabling ads, suggestions and the MSN news feed in Microsoft Edge"
|
||||
},
|
||||
"DisableCopilot": {
|
||||
"Label": "Disable Microsoft Copilot",
|
||||
"ToolTip": "This will disable and uninstall Microsoft Copilot, Windows' built-in AI assistant.",
|
||||
"ApplyText": "Disabling Microsoft Copilot",
|
||||
"UndoLabel": "Enable Microsoft Copilot",
|
||||
"ApplyUndoText": "Enabling Microsoft Copilot"
|
||||
},
|
||||
"DisableRecall": {
|
||||
"Label": "Disable Windows Recall",
|
||||
"ToolTip": "This will disable Windows Recall, an AI-powered feature that provides quick access to recently used files, apps and activities. This feature uses policies, which will lock down certain settings.",
|
||||
"ApplyText": "Disabling Windows Recall",
|
||||
"UndoLabel": "Enable Windows Recall",
|
||||
"ApplyUndoText": "Enabling Windows Recall"
|
||||
},
|
||||
"DisableClickToDo": {
|
||||
"Label": "Disable Click To Do, AI text & image analysis",
|
||||
"ToolTip": "This will disable Click To Do, which provides AI-powered text and image analysis features in Windows. This feature uses policies, which will lock down certain settings.",
|
||||
"ApplyText": "Disabling Click to Do",
|
||||
"UndoLabel": "Enable Click To Do, AI text & image analysis",
|
||||
"ApplyUndoText": "Enabling Click to Do"
|
||||
},
|
||||
"DisableAISvcAutoStart": {
|
||||
"Label": "Prevent AI service from starting automatically",
|
||||
"ToolTip": "This will set the WSAIFabricSvc service to manual startup, preventing the service from starting automatically with Windows.",
|
||||
"ApplyText": "Preventing AI service from starting automatically",
|
||||
"UndoLabel": "Allow AI service to start automatically",
|
||||
"ApplyUndoText": "Allowing AI service to start automatically"
|
||||
},
|
||||
"DisableDVR": {
|
||||
"Label": "Disable Xbox game/screen recording",
|
||||
"ToolTip": "This will disable the Xbox game/screen recording features included with the Game Bar app. This feature uses policies, which will lock down certain settings.",
|
||||
"ApplyText": "Disabling Xbox game/screen recording",
|
||||
"UndoLabel": "Enable Xbox game/screen recording",
|
||||
"ApplyUndoText": "Enabling Xbox game/screen recording"
|
||||
},
|
||||
"DisableGameBarIntegration": {
|
||||
"Label": "Disable Game Bar integration",
|
||||
"ToolTip": "This will disable the Game Bar integration with games and controllers. This stops annoying ms-gamebar popups when launching games or connecting a controller.",
|
||||
"ApplyText": "Disabling Game Bar integration",
|
||||
"UndoLabel": "Enable Game Bar integration",
|
||||
"ApplyUndoText": "Enabling Game Bar integration"
|
||||
},
|
||||
"ClearStart": {
|
||||
"Label": "Remove all pinned apps from the start menu for this user only",
|
||||
"ApplyText": "Removing all pinned apps from the start menu"
|
||||
},
|
||||
"ClearStartAllUsers": {
|
||||
"Label": "Remove all pinned apps from the start menu for all existing and new users",
|
||||
"ApplyText": "Removing all pinned apps from the start menu for all users"
|
||||
},
|
||||
"ReplaceStart": {
|
||||
"Label": "Replace the start menu layout for this user only with the provided template file",
|
||||
"ApplyText": "Replacing the start menu"
|
||||
},
|
||||
"ReplaceStartAllUsers": {
|
||||
"Label": "Replace the start menu layout for all existing and new users with the provided template file",
|
||||
"ApplyText": "Replacing the start menu for all users"
|
||||
},
|
||||
"DisableStartRecommended": {
|
||||
"Label": "Hide recommended section in the start menu",
|
||||
"ToolTip": "This will hide the recommended section in the start menu, which shows recently added apps, recently opened files and app recommendations. This feature uses policies, which will lock down certain settings.",
|
||||
"ApplyText": "Disabling the start menu recommended section",
|
||||
"UndoLabel": "Show recommended section in the start menu",
|
||||
"ApplyUndoText": "Enabling the start menu recommended section"
|
||||
},
|
||||
"DisableStartAllApps": {
|
||||
"Label": "Hide 'All Apps' section in the start menu",
|
||||
"ToolTip": "This will hide the 'All Apps' section in the start menu, which shows all installed apps. WARNING: Hiding this section may make it harder to find installed apps on your system. This feature uses policies, which will lock down certain settings.",
|
||||
"ApplyText": "Disabling the 'All Apps' section in the start menu",
|
||||
"UndoLabel": "Show 'All Apps' section in the start menu",
|
||||
"ApplyUndoText": "Enabling the 'All Apps' section in the start menu"
|
||||
},
|
||||
"DisableStartPhoneLink": {
|
||||
"Label": "Disable Phone Link integration in the start menu",
|
||||
"ToolTip": "This will remove the Phone Link integration in the start menu when you have a mobile device linked to your PC.",
|
||||
"ApplyText": "Disabling the Phone Link mobile devices integration in the start menu",
|
||||
"UndoLabel": "Enable Phone Link integration in the start menu",
|
||||
"ApplyUndoText": "Enabling the Phone Link mobile devices integration in the start menu"
|
||||
},
|
||||
"DisableBing": {
|
||||
"Label": "Disable Bing web search & Copilot integration in search",
|
||||
"ToolTip": "This will turn off Bing web search results and Copilot integration in the Windows search experience. This feature uses policies, which will lock down certain settings.",
|
||||
"ApplyText": "Disabling Bing web search & Copilot integration in Windows search",
|
||||
"UndoLabel": "Enable Bing web search & Copilot integration in search",
|
||||
"ApplyUndoText": "Enabling Bing web search & Copilot integration in Windows search"
|
||||
},
|
||||
"DisableStoreSearchSuggestions": {
|
||||
"Label": "Disable Microsoft Store app suggestions in search",
|
||||
"ToolTip": "This will disable the Microsoft Store app suggestions in Windows search.",
|
||||
"ApplyText": "Disabling Microsoft Store app suggestions in search",
|
||||
"UndoLabel": "Enable Microsoft Store app suggestions in search",
|
||||
"ApplyUndoText": "Enabling Microsoft Store app suggestions in search"
|
||||
},
|
||||
"DisableSettings365Ads": {
|
||||
"Label": "Hide Microsoft 365 Copilot ads in Settings Home",
|
||||
"ToolTip": "This will turn off the Microsoft 365 Copilot ads that appear in the Settings Home page.",
|
||||
"ApplyText": "Disabling Microsoft 365 Copilot ads in Settings Home",
|
||||
"UndoLabel": "Show Microsoft 365 Copilot ads in Settings Home",
|
||||
"ApplyUndoText": "Enabling Microsoft 365 Copilot ads in Settings Home"
|
||||
},
|
||||
"DisableSettingsHome": {
|
||||
"Label": "Hide Settings 'Home' page",
|
||||
"ToolTip": "Removes the 'Home' page from the Settings app.",
|
||||
"ApplyText": "Disabling the Settings Home page",
|
||||
"UndoLabel": "Show Settings 'Home' page",
|
||||
"ApplyUndoText": "Enabling the Settings Home page"
|
||||
},
|
||||
"DisableEdgeAI": {
|
||||
"Label": "Disable AI features in Microsoft Edge",
|
||||
"ToolTip": "This will turn off AI features in Microsoft Edge, such as the AI-powered sidebar and Copilot features. This feature uses policies, which will lock down certain settings.",
|
||||
"ApplyText": "Disabling AI features in Microsoft Edge",
|
||||
"UndoLabel": "Enable AI features in Microsoft Edge",
|
||||
"ApplyUndoText": "Enabling AI features in Microsoft Edge"
|
||||
},
|
||||
"DisablePaintAI": {
|
||||
"Label": "Disable AI features in Paint",
|
||||
"ToolTip": "This will turn off AI features in Paint, such as the AI-powered image generation and editing tools. This feature uses policies, which will lock down certain settings.",
|
||||
"ApplyText": "Disabling AI features in Paint",
|
||||
"UndoLabel": "Enable AI features in Paint",
|
||||
"ApplyUndoText": "Enabling AI features in Paint"
|
||||
},
|
||||
"DisableNotepadAI": {
|
||||
"Label": "Disable AI features in Notepad",
|
||||
"ToolTip": "This will turn off AI features in Notepad, such as the AI-powered writing suggestions. This feature uses policies, which will lock down certain settings.",
|
||||
"ApplyText": "Disabling AI features in Notepad",
|
||||
"UndoLabel": "Enable AI features in Notepad",
|
||||
"ApplyUndoText": "Enabling AI features in Notepad"
|
||||
},
|
||||
"EnableDarkMode": {
|
||||
"Label": "Enable dark theme for system and apps",
|
||||
"ToolTip": "This will set the app and system theme to dark mode.",
|
||||
"ApplyText": "Enabling dark mode for system and apps",
|
||||
"UndoLabel": "Disable dark theme for system and apps",
|
||||
"ApplyUndoText": "Disabling dark mode for system and apps"
|
||||
},
|
||||
"DisableDragTray": {
|
||||
"Label": "Disable 'Drag Tray' for sharing & moving files",
|
||||
"ToolTip": "The Drag Tray is a new feature for sharing & moving files in Windows 11, it appears at the top of the screen when dragging files.",
|
||||
"ApplyText": "Disabling Drag Tray",
|
||||
"UndoLabel": "Enable 'Drag Tray' for sharing & moving files",
|
||||
"ApplyUndoText": "Enabling Drag Tray"
|
||||
},
|
||||
"RevertContextMenu": {
|
||||
"Label": "Use classic Windows 10 context menu style",
|
||||
"ToolTip": "This will restore the classic Windows 10 style context menu, which is normally hidden behind the 'Show more options' entry in the new Windows 11 context menu.",
|
||||
"ApplyText": "Restoring the classic Windows 10 style context menu",
|
||||
"UndoLabel": "Use Windows 11 context menu style",
|
||||
"ApplyUndoText": "Restoring the Windows 11 style context menu"
|
||||
},
|
||||
"DisableMouseAcceleration": {
|
||||
"Label": "Disable Enhance Pointer Precision (mouse acceleration)",
|
||||
"ToolTip": "This will disable mouse acceleration which is enabled by default in Windows. This makes mouse movement more consistent and predictable.",
|
||||
"ApplyText": "Turning off Enhanced Pointer Precision",
|
||||
"UndoLabel": "Enable Enhance Pointer Precision (mouse acceleration)",
|
||||
"ApplyUndoText": "Turning on Enhanced Pointer Precision"
|
||||
},
|
||||
"DisableStickyKeys": {
|
||||
"Label": "Disable Sticky Keys keyboard shortcut (5x shift)",
|
||||
"ToolTip": "This will prevent the Sticky Keys dialog from appearing when you press the Shift key 5 times in a row.",
|
||||
"ApplyText": "Disabling the Sticky Keys keyboard shortcut",
|
||||
"UndoLabel": "Enable Sticky Keys keyboard shortcut (5x shift)",
|
||||
"ApplyUndoText": "Enabling the Sticky Keys keyboard shortcut"
|
||||
},
|
||||
"DisableWindowSnapping": {
|
||||
"Label": "Disable window snapping",
|
||||
"ToolTip": "This will turn off the ability to snap windows to the sides or corners of the screen.",
|
||||
"ApplyText": "Disabling window snapping",
|
||||
"UndoLabel": "Enable window snapping",
|
||||
"ApplyUndoText": "Enabling window snapping"
|
||||
},
|
||||
"DisableSnapAssist": {
|
||||
"Label": "Disable showing app suggestions when snapping windows",
|
||||
"ToolTip": "This will turn off app suggestions when you snap windows to the sides or corners of the screen.",
|
||||
"ApplyText": "Disabling the Snap Assist suggestions",
|
||||
"UndoLabel": "Enable showing app suggestions when snapping windows",
|
||||
"ApplyUndoText": "Enabling the Snap Assist suggestions"
|
||||
},
|
||||
"DisableSnapLayouts": {
|
||||
"Label": "Hide snap layout flyout at top of screen and on maximize button",
|
||||
"ToolTip": "This will turn off the snap layout flyout that appears when you hover over the maximize button or drag windows to the top of the screen.",
|
||||
"ApplyText": "Hiding snap layouts when dragging windows to top of the screen and on maximize button",
|
||||
"UndoLabel": "Show snap layout flyout at top of screen and on maximize button",
|
||||
"ApplyUndoText": "Showing snap layouts when dragging windows to top of the screen and on maximize button"
|
||||
},
|
||||
"HideTabsInAltTab": {
|
||||
"Label": "Hide tabs from apps when snapping or pressing Alt+Tab",
|
||||
"ApplyText": "Disable showing tabs from apps when snapping or pressing Alt+Tab"
|
||||
},
|
||||
"Show3TabsInAltTab": {
|
||||
"Label": "Show 3 tabs from apps when snapping or pressing Alt+Tab",
|
||||
"ApplyText": "Enable showing 3 tabs from apps when snapping or pressing Alt+Tab"
|
||||
},
|
||||
"Show5TabsInAltTab": {
|
||||
"Label": "Show 5 tabs from apps when snapping or pressing Alt+Tab",
|
||||
"ApplyText": "Enable showing 5 tabs from apps when snapping or pressing Alt+Tab"
|
||||
},
|
||||
"Show20TabsInAltTab": {
|
||||
"Label": "Show 20 tabs from apps when snapping or pressing Alt+Tab",
|
||||
"ApplyText": "Enable showing 20 tabs from apps when snapping or pressing Alt+Tab"
|
||||
},
|
||||
"TaskbarAlignLeft": {
|
||||
"Label": "Align taskbar to the left",
|
||||
"ToolTip": "By default, Windows 11 has the taskbar buttons centered. Enabling this setting will move the taskbar buttons to the left, similar to previous versions of Windows.",
|
||||
"ApplyText": "Aligning taskbar buttons to the left",
|
||||
"UndoLabel": "Align taskbar to the center",
|
||||
"ApplyUndoText": "Aligning taskbar buttons to the center"
|
||||
},
|
||||
"HideSearchTb": {
|
||||
"Label": "Hide search icon from the taskbar",
|
||||
"ApplyText": "Hiding the search icon from the taskbar",
|
||||
"UndoLabel": "Show search box on the taskbar",
|
||||
"ApplyUndoText": "Changing taskbar search to search box"
|
||||
},
|
||||
"ShowSearchIconTb": {
|
||||
"Label": "Show search icon on the taskbar",
|
||||
"ApplyText": "Changing taskbar search to icon only",
|
||||
"UndoLabel": "Show search box on the taskbar",
|
||||
"ApplyUndoText": "Changing taskbar search to search box"
|
||||
},
|
||||
"ShowSearchLabelTb": {
|
||||
"Label": "Show search icon with label on the taskbar",
|
||||
"ApplyText": "Changing taskbar search to icon with label",
|
||||
"UndoLabel": "Show search box on the taskbar",
|
||||
"ApplyUndoText": "Changing taskbar search to search box"
|
||||
},
|
||||
"ShowSearchBoxTb": {
|
||||
"Label": "Show search box on the taskbar",
|
||||
"ApplyText": "Changing taskbar search to search box"
|
||||
},
|
||||
"HideTaskview": {
|
||||
"Label": "Hide 'Task view' button on the taskbar",
|
||||
"ToolTip": "This will disable the 'Task view' button on the taskbar, which allows you to see all your open windows and virtual desktops.",
|
||||
"ApplyText": "Hiding the taskview button from the taskbar",
|
||||
"UndoLabel": "Show 'Task view' button on the taskbar",
|
||||
"ApplyUndoText": "Showing the taskview button from the taskbar"
|
||||
},
|
||||
"DisableWidgets": {
|
||||
"Label": "Disable widgets on the taskbar & lock screen",
|
||||
"ToolTip": "This will disable the widgets features in Windows, including the widgets button on the taskbar and the widgets that can appear on the lock screen.",
|
||||
"ApplyText": "Disabling widgets on the taskbar & lock screen"
|
||||
},
|
||||
"HideChat": {
|
||||
"Label": "Hide Chat (meet now) icon on the taskbar",
|
||||
"ToolTip": "This will disable the Chat (meet now) icon on the taskbar.",
|
||||
"ApplyText": "Hiding the chat icon from the taskbar",
|
||||
"UndoLabel": "Show Chat (meet now) icon on the taskbar",
|
||||
"ApplyUndoText": "Showing the chat icon from the taskbar"
|
||||
},
|
||||
"DisableStorageSense": {
|
||||
"Label": "Disable Storage Sense automatic disk cleanup",
|
||||
"ToolTip": "This will disable Storage Sense, which automatically frees up disk space by deleting temporary files, emptying the recycle bin and cleaning up files in the Downloads folder.",
|
||||
"ApplyText": "Disabling Storage Sense automatic disk cleanup",
|
||||
"UndoLabel": "Enable Storage Sense automatic disk cleanup",
|
||||
"ApplyUndoText": "Enabling Storage Sense automatic disk cleanup"
|
||||
},
|
||||
"DisableFastStartup": {
|
||||
"Label": "Disable fast start-up",
|
||||
"ToolTip": "Fast Start-up helps your PC start faster after shutdown by saving a system image to disk. Disabling Fast Start-up can help with certain issues, but may result in slightly longer boot times.",
|
||||
"ApplyText": "Disabling Fast Start-up",
|
||||
"UndoLabel": "Enable fast start-up",
|
||||
"ApplyUndoText": "Enabling Fast Start-up"
|
||||
},
|
||||
"DisableBitlockerAutoEncryption": {
|
||||
"Label": "Disable BitLocker automatic device encryption",
|
||||
"ToolTip": "For devices that support it, Windows 11 automatically enables BitLocker device encryption. Disabling this will turn off automatic encryption of the device, but you can still manually enable BitLocker encryption if desired. Drives that are already encrypted with BitLocker will remain encrypted when this setting is applied.",
|
||||
"ApplyText": "Disabling BitLocker automatic device encryption",
|
||||
"UndoLabel": "Enable BitLocker automatic device encryption",
|
||||
"ApplyUndoText": "Enabling BitLocker automatic device encryption"
|
||||
},
|
||||
"DisableModernStandbyNetworking": {
|
||||
"Label": "Disable Modern Standby network connectivity",
|
||||
"ToolTip": "By default, devices that support Modern Standby maintain network connectivity while in sleep mode. Disabling network connectivity during Modern Standby can help save battery life.",
|
||||
"ApplyText": "Disabling network connectivity during Modern Standby",
|
||||
"UndoLabel": "Enable Modern Standby network connectivity",
|
||||
"ApplyUndoText": "Enabling network connectivity during Modern Standby"
|
||||
},
|
||||
"EnableEndTask": {
|
||||
"Label": "Show 'End Task' option in taskbar context menu",
|
||||
"ToolTip": "When enabled, adds an 'End Task' option to the right-click context menu for apps in the taskbar, allowing you to quickly force close apps.",
|
||||
"ApplyText": "Enabling the 'End Task' option in the taskbar right click menu",
|
||||
"UndoLabel": "Hide 'End Task' option in taskbar context menu",
|
||||
"ApplyUndoText": "Disabling the 'End Task' option in the taskbar right click menu"
|
||||
},
|
||||
"EnableLastActiveClick": {
|
||||
"Label": "Enable 'Last Active Click' behavior for taskbar apps",
|
||||
"ToolTip": "When enabled, clicking on an app in the taskbar will switch to the last active window of that app, instead of only showing the thumbnail preview.",
|
||||
"ApplyText": "Enabling the 'Last Active Click' behavior in the taskbar app area",
|
||||
"UndoLabel": "Disable 'Last Active Click' behavior for taskbar apps",
|
||||
"ApplyUndoText": "Disabling the 'Last Active Click' behavior in the taskbar app area"
|
||||
},
|
||||
"CombineTaskbarAlways": {
|
||||
"Label": "Always combine taskbar buttons and hide labels for the main display",
|
||||
"ApplyText": "Setting the taskbar on the main display to always combine buttons and hide labels"
|
||||
},
|
||||
"CombineMMTaskbarAlways": {
|
||||
"Label": "Always combine taskbar buttons and hide labels for secondary displays",
|
||||
"ApplyText": "Setting the taskbar on secondary displays to always combine buttons and hide labels"
|
||||
},
|
||||
"CombineTaskbarWhenFull": {
|
||||
"Label": "Combine taskbar buttons and hide labels when taskbar is full for the main display",
|
||||
"ApplyText": "Setting the taskbar on the main display to only combine buttons and hide labels when the taskbar is full",
|
||||
"UndoLabel": "Always combine taskbar buttons and hide labels for the main display",
|
||||
"ApplyUndoText": "Setting the taskbar on the main display to always combine buttons and hide labels"
|
||||
},
|
||||
"CombineMMTaskbarWhenFull": {
|
||||
"Label": "Combine taskbar buttons and hide labels when taskbar is full for secondary displays",
|
||||
"ApplyText": "Setting the taskbar on secondary displays to only combine buttons and hide labels when the taskbar is full",
|
||||
"UndoLabel": "Always combine taskbar buttons and hide labels for secondary displays",
|
||||
"ApplyUndoText": "Setting the taskbar on secondary displays to always combine buttons and hide labels"
|
||||
},
|
||||
"CombineTaskbarNever": {
|
||||
"Label": "Never combine taskbar buttons and show labels for the main display",
|
||||
"ApplyText": "Setting the taskbar on the main display to never combine buttons or hide labels",
|
||||
"UndoLabel": "Always combine taskbar buttons and hide labels for the main display",
|
||||
"ApplyUndoText": "Setting the taskbar on the main display to always combine buttons and hide labels"
|
||||
},
|
||||
"CombineMMTaskbarNever": {
|
||||
"Label": "Never combine taskbar buttons and show labels for secondary displays",
|
||||
"ApplyText": "Setting the taskbar on secondary displays to never combine buttons or hide labels",
|
||||
"UndoLabel": "Always combine taskbar buttons and hide labels for secondary displays",
|
||||
"ApplyUndoText": "Setting the taskbar on secondary displays to always combine buttons and hide labels"
|
||||
},
|
||||
"MMTaskbarModeAll": {
|
||||
"Label": "Show app icons on all taskbars",
|
||||
"ApplyText": "Setting the taskbar to show app icons on all taskbars"
|
||||
},
|
||||
"MMTaskbarModeMainActive": {
|
||||
"Label": "Show app icons on main taskbar and on taskbar where the windows is open",
|
||||
"ApplyText": "Setting the taskbar to show app icons on main taskbar and on taskbar where the windows is open",
|
||||
"UndoLabel": "Show app icons on all taskbars",
|
||||
"ApplyUndoText": "Setting the taskbar to show app icons on all taskbars"
|
||||
},
|
||||
"MMTaskbarModeActive": {
|
||||
"Label": "Show app icons only on taskbar where the window is open",
|
||||
"ApplyText": "Setting the taskbar to only show app icons on the taskbar where the window is open",
|
||||
"UndoLabel": "Show app icons on all taskbars",
|
||||
"ApplyUndoText": "Setting the taskbar to show app icons on all taskbars"
|
||||
},
|
||||
"ExplorerToHome": {
|
||||
"Label": "Change the default location that File Explorer opens to 'Home'",
|
||||
"ApplyText": "Changing the default location that File Explorer opens to, to 'Home'"
|
||||
},
|
||||
"ExplorerToThisPC": {
|
||||
"Label": "Change the default location that File Explorer opens to 'This PC'",
|
||||
"ApplyText": "Changing the default location that File Explorer opens to, to 'This PC'",
|
||||
"UndoLabel": "Change the default location that File Explorer opens to 'Home'",
|
||||
"ApplyUndoText": "Changing the default location that File Explorer opens to, to 'Home'"
|
||||
},
|
||||
"ExplorerToDownloads": {
|
||||
"Label": "Change the default location that File Explorer opens to 'Downloads'",
|
||||
"ApplyText": "Changing the default location that File Explorer opens to, to 'Downloads'",
|
||||
"UndoLabel": "Change the default location that File Explorer opens to 'Home'",
|
||||
"ApplyUndoText": "Changing the default location that File Explorer opens to, to 'Home'"
|
||||
},
|
||||
"ExplorerToOneDrive": {
|
||||
"Label": "Change the default location that File Explorer opens to 'OneDrive'",
|
||||
"ApplyText": "Changing the default location that File Explorer opens to, to 'OneDrive'",
|
||||
"UndoLabel": "Change the default location that File Explorer opens to 'Home'",
|
||||
"ApplyUndoText": "Changing the default location that File Explorer opens to, to 'Home'"
|
||||
},
|
||||
"ShowKnownFileExt": {
|
||||
"Label": "Show file extensions for known file types",
|
||||
"ToolTip": "This will show file extensions for known file types. By default, Windows hides file extensions for known file types which can lead to confusion and security risks.",
|
||||
"ApplyText": "Enabling file extensions for known file types",
|
||||
"UndoLabel": "Hide file extensions for known file types",
|
||||
"ApplyUndoText": "Disabling file extensions for known file types"
|
||||
},
|
||||
"ShowHiddenFolders": {
|
||||
"Label": "Show hidden files, folders and drives",
|
||||
"ToolTip": "By default, Windows hides certain files, folders and drives to prevent accidental modification or deletion. Turn this on to show all files in File Explorer.",
|
||||
"ApplyText": "Unhiding hidden files, folders and drives",
|
||||
"UndoLabel": "Hide hidden files, folders and drives",
|
||||
"ApplyUndoText": "Hiding hidden files, folders and drives"
|
||||
},
|
||||
"HideDupliDrive": {
|
||||
"Label": "Hide duplicate removable drive entries",
|
||||
"ToolTip": "By default, Windows shows removable drives both under 'This PC' and in the navigation pane with its own entry. Enable this setting to only show removable drives under 'This PC'.",
|
||||
"ApplyText": "Hiding duplicate removable drive entries from the File Explorer navigation pane",
|
||||
"UndoLabel": "Show duplicate removable drive entries",
|
||||
"ApplyUndoText": "Showing duplicate removable drive entries from the File Explorer navigation pane"
|
||||
},
|
||||
"HideHome": {
|
||||
"Label": "Hide 'Home' from navigation pane",
|
||||
"ToolTip": "Hides the 'Home' section from the File Explorer navigation pane.",
|
||||
"ApplyText": "Hiding the 'Home' section from the File Explorer navigation pane",
|
||||
"UndoLabel": "Show 'Home' from navigation pane",
|
||||
"ApplyUndoText": "Showing the 'Home' section from the File Explorer navigation pane"
|
||||
},
|
||||
"HideGallery": {
|
||||
"Label": "Hide 'Gallery' from navigation pane",
|
||||
"ToolTip": "Hides the 'Gallery' section from the File Explorer navigation pane.",
|
||||
"ApplyText": "Hiding the 'Gallery' section from the File Explorer navigation pane",
|
||||
"UndoLabel": "Show 'Gallery' from navigation pane",
|
||||
"ApplyUndoText": "Showing the 'Gallery' section from the File Explorer navigation pane"
|
||||
},
|
||||
"DisableTransparency": {
|
||||
"Label": "Disable transparency effects",
|
||||
"ToolTip": "This will disable transparency effects on Windows and interfaces. Which can help improve performance on older hardware.",
|
||||
"ApplyText": "Disabling transparency effects",
|
||||
"UndoLabel": "Enable transparency effects",
|
||||
"ApplyUndoText": "Enabling transparency effects"
|
||||
},
|
||||
"DisableAnimations": {
|
||||
"Label": "Disable animations and visual effects",
|
||||
"ToolTip": "This will disable animations and some visual effects in Windows, which can make the interface feel snappier, especially on older hardware.",
|
||||
"ApplyText": "Disabling animations and visual effects",
|
||||
"UndoLabel": "Enable animations and visual effects",
|
||||
"ApplyUndoText": "Enabling animations and visual effects"
|
||||
},
|
||||
"DisableUpdateASAP": {
|
||||
"Label": "Prevent getting updates as soon as they're available",
|
||||
"ToolTip": "This will prevent your PC from being among the first to receive new non-security updates. Your PC will still receive these updates eventually.",
|
||||
"ApplyText": "Preventing Windows from getting updates as soon as they are available",
|
||||
"UndoLabel": "Allow getting updates as soon as they're available",
|
||||
"ApplyUndoText": "Allowing Windows to get updates as soon as they are available"
|
||||
},
|
||||
"PreventUpdateAutoReboot": {
|
||||
"Label": "Prevent automatic restarts after updates while signed in",
|
||||
"ToolTip": "This will prevent your PC from automatically restarting after updates while any user is signed in.",
|
||||
"ApplyText": "Preventing automatic restarts after updates while signed in",
|
||||
"UndoLabel": "Allow automatic restarts after updates while signed in",
|
||||
"ApplyUndoText": "Allowing automatic restarts after updates while signed in"
|
||||
},
|
||||
"DisableDeliveryOptimization": {
|
||||
"Label": "Disable sharing downloaded updates with other PCs",
|
||||
"ToolTip": "This will prevent your PC from sharing downloaded updates with other PCs on the local network or on the internet. This also prevents your PC from downloading updates from other PCs.",
|
||||
"ApplyText": "Disabling sharing of downloaded updates with other PCs",
|
||||
"UndoLabel": "Enable sharing downloaded updates with other PCs",
|
||||
"ApplyUndoText": "Enabling sharing of downloaded updates with other PCs"
|
||||
},
|
||||
"DisableDeviceAutoAppDownload": {
|
||||
"Label": "Prevent Windows from auto-installing device companion apps",
|
||||
"ToolTip": "Stops Windows from silently installing device companion apps via Windows Update when you connect certain devices, like monitors from LG and Alienware. These devices will still work as expected, this only stops Windows Update from automatically installing additional apps without consent.",
|
||||
"ApplyText": "Preventing automatic installation of device companion apps",
|
||||
"UndoLabel": "Allow automatic installation of device companion apps",
|
||||
"ApplyUndoText": "Allowing automatic installation of device companion apps"
|
||||
},
|
||||
"ForceRemoveEdge": {
|
||||
"Label": "Forcefully uninstall Microsoft Edge. NOT RECOMMENDED!",
|
||||
"ApplyText": "Forcefully uninstalling Microsoft Edge"
|
||||
},
|
||||
"HideOnedrive": {
|
||||
"Label": "Hide 'OneDrive' from navigation pane",
|
||||
"ToolTip": "Hides the 'OneDrive' section from the File Explorer navigation pane.",
|
||||
"ApplyText": "Hiding the 'OneDrive' section from the File Explorer navigation pane",
|
||||
"UndoLabel": "Show 'OneDrive' from navigation pane",
|
||||
"ApplyUndoText": "Showing the 'OneDrive' section from the File Explorer navigation pane"
|
||||
},
|
||||
"Hide3dObjects": {
|
||||
"Label": "Hide '3D objects' folder under 'This PC'",
|
||||
"ToolTip": "Hides the '3D objects' folder from the File Explorer navigation pane.",
|
||||
"ApplyText": "Hiding the '3D objects' folder from the File Explorer navigation pane",
|
||||
"UndoLabel": "Show '3D objects' folder under 'This PC'",
|
||||
"ApplyUndoText": "Showing the '3D objects' folder from the File Explorer navigation pane"
|
||||
},
|
||||
"HideMusic": {
|
||||
"Label": "Hide 'Music' folder under 'This PC'",
|
||||
"ToolTip": "Hides the 'Music' folder from the File Explorer navigation pane.",
|
||||
"ApplyText": "Hiding the 'Music' folder from the File Explorer navigation pane",
|
||||
"UndoLabel": "Show 'Music' folder under 'This PC'",
|
||||
"ApplyUndoText": "Showing the 'Music' folder from the File Explorer navigation pane"
|
||||
},
|
||||
"AddFoldersToThisPC": {
|
||||
"Label": "Add common folders back to 'This PC' page",
|
||||
"ToolTip": "This setting will add common folders like Desktop, Documents, Downloads, Music, Pictures and Videos back to the 'This PC' page in File Explorer.",
|
||||
"ApplyText": "Adding all common folders (Desktop, Downloads, etc.) back to 'This PC' in File Explorer",
|
||||
"UndoLabel": "Remove common folders back to 'This PC' page",
|
||||
"ApplyUndoText": "Removing all common folders (Desktop, Downloads, etc.) back to 'This PC' in File Explorer"
|
||||
},
|
||||
"HideIncludeInLibrary": {
|
||||
"Label": "Hide 'Include in library' option in the context menu",
|
||||
"ToolTip": "Hides the 'Include in library' option from the File Explorer context menu.",
|
||||
"ApplyText": "Hiding 'Include in library' in the context menu",
|
||||
"UndoLabel": "Show 'Include in library' option in the context menu",
|
||||
"ApplyUndoText": "Showing 'Include in library' in the context menu"
|
||||
},
|
||||
"HideGiveAccessTo": {
|
||||
"Label": "Hide 'Give access to' option in the context menu",
|
||||
"ToolTip": "Hides the 'Give access to' option from the File Explorer context menu.",
|
||||
"ApplyText": "Hiding 'Give access to' in the context menu",
|
||||
"UndoLabel": "Show 'Give access to' option in the context menu",
|
||||
"ApplyUndoText": "Showing 'Give access to' in the context menu"
|
||||
},
|
||||
"HideShare": {
|
||||
"Label": "Hide 'Share' option in the context menu",
|
||||
"ToolTip": "Hides the 'Share' option from the File Explorer context menu.",
|
||||
"ApplyText": "Hiding 'Share' in the context menu",
|
||||
"UndoLabel": "Show 'Share' option in the context menu",
|
||||
"ApplyUndoText": "Showing 'Share' in the context menu"
|
||||
},
|
||||
"DisableBraveBloat": {
|
||||
"Label": "Disable bloat in Brave browser (AI, Crypto, etc.)",
|
||||
"ToolTip": "This will disable Brave's built-in AI features, Crypto wallet, News, Rewards, Talk and VPN. This feature uses policies, which will lock down certain settings.",
|
||||
"ApplyText": "Disabling Brave AI, Crypto, News, Rewards, Talk and VPN in Brave browser",
|
||||
"UndoLabel": "Enable Brave browser features (AI, Crypto, etc.)",
|
||||
"ApplyUndoText": "Enabling Brave AI, Crypto, News, Rewards, Talk and VPN in Brave browser"
|
||||
},
|
||||
"EnableWindowsSandbox": {
|
||||
"Label": "Enable Windows Sandbox",
|
||||
"ToolTip": "Windows Sandbox is a lightweight desktop environment for safely running applications in isolation. Software installed inside the Windows Sandbox environment remains 'sandboxed' and runs separately from the host machine. Only supported on Windows 11 Pro, Workstation, and Enterprise editions.",
|
||||
"ApplyText": "Enabling Windows Sandbox",
|
||||
"UndoLabel": "Disable Windows Sandbox",
|
||||
"ApplyUndoText": "Disabling Windows Sandbox"
|
||||
},
|
||||
"EnableWindowsSubsystemForLinux": {
|
||||
"Label": "Enable Windows Subsystem for Linux",
|
||||
"ToolTip": "Windows Subsystem for Linux allows you to run a Linux environment directly on Windows without the need for a virtual machine.",
|
||||
"ApplyText": "Enabling Windows Subsystem for Linux",
|
||||
"UndoLabel": "Disable Windows Subsystem for Linux",
|
||||
"ApplyUndoText": "Disabling Windows Subsystem for Linux"
|
||||
},
|
||||
"ShowDriveLettersFirst": {
|
||||
"Label": "Show drive letters before drive label",
|
||||
"ToolTip": "This setting will show drive letters before the drive label in File Explorer.",
|
||||
"ApplyText": "Showing drive letters before drive label",
|
||||
"UndoLabel": "Show drive letters after drive label",
|
||||
"ApplyUndoText": "Showing drive letters after drive label"
|
||||
},
|
||||
"ShowDriveLettersLast": {
|
||||
"Label": "Show drive letters after drive label",
|
||||
"ToolTip": "This setting will show drive letters after the drive label in File Explorer (Default Windows behavior).",
|
||||
"ApplyText": "Showing drive letters after drive label"
|
||||
},
|
||||
"ShowNetworkDriveLettersFirst": {
|
||||
"Label": "Show network drive letters before drive label",
|
||||
"ToolTip": "This setting will show only network drive letters before the drive label in File Explorer.",
|
||||
"ApplyText": "Showing network drive letters before drive label",
|
||||
"UndoLabel": "Show drive letters after drive label",
|
||||
"ApplyUndoText": "Showing drive letters after drive label"
|
||||
},
|
||||
"HideDriveLetters": {
|
||||
"Label": "Hide all drive letters",
|
||||
"ToolTip": "This setting will hide all drive letters from the File Explorer navigation pane and 'This PC'.",
|
||||
"ApplyText": "Hiding all drive letters",
|
||||
"UndoLabel": "Show drive letters after drive label",
|
||||
"ApplyUndoText": "Showing drive letters after drive label"
|
||||
},
|
||||
"StartAllAppsCategory": {
|
||||
"Label": "Show All Apps in Category view (Default)",
|
||||
"ToolTip": "This will set the All Apps section in the start menu to show apps grouped by category.",
|
||||
"ApplyText": "Setting All Apps view to Category"
|
||||
},
|
||||
"StartAllAppsGrid": {
|
||||
"Label": "Show All Apps in Grid view",
|
||||
"ToolTip": "This will set the All Apps section in the start menu to show apps in an alphabetical grid layout.",
|
||||
"ApplyText": "Setting All Apps view to Grid",
|
||||
"UndoLabel": "Show All Apps in Category view",
|
||||
"ApplyUndoText": "Setting All Apps view to Category"
|
||||
},
|
||||
"StartAllAppsList": {
|
||||
"Label": "Show All Apps in List view",
|
||||
"ToolTip": "This will set the All Apps section in the start menu to show apps in an alphabetical list layout.",
|
||||
"ApplyText": "Setting All Apps view to List",
|
||||
"UndoLabel": "Show All Apps in Category view",
|
||||
"ApplyUndoText": "Setting All Apps view to Category"
|
||||
}
|
||||
},
|
||||
"UiGroups": {
|
||||
"DesktopSpotlight": {
|
||||
"Label": "Windows spotlight on the desktop",
|
||||
"ToolTip": "This setting controls the Windows spotlight desktop background option. Keep the rotating wallpaper with or without the 'Learn about this picture' shortcut, or disable Windows spotlight entirely.",
|
||||
"Values": {
|
||||
"EnableDesktopSpotlight": "Show 'Learn about this picture' shortcut (Default)",
|
||||
"HideDesktopSpotlightIcon": "Hide 'Learn about this picture' shortcut",
|
||||
"DisableDesktopSpotlight": "Disable Windows spotlight desktop background"
|
||||
}
|
||||
},
|
||||
"SearchIcon": {
|
||||
"Label": "Taskbar search style",
|
||||
"ToolTip": "This setting allows you to customize the appearance of the search box on the taskbar.",
|
||||
"Values": {
|
||||
"ShowSearchBoxTb": "Show search box (Default)",
|
||||
"ShowSearchLabelTb": "Show search icon and label",
|
||||
"ShowSearchIconTb": "Show search icon only",
|
||||
"HideSearchTb": "Hide"
|
||||
}
|
||||
},
|
||||
"MultiMon": {
|
||||
"Label": "Show taskbar apps on",
|
||||
"ToolTip": "This setting allows you to choose where taskbar app buttons are shown when using multiple monitors.",
|
||||
"Values": {
|
||||
"MMTaskbarModeAll": "All taskbars (Default)",
|
||||
"MMTaskbarModeMainActive": "Main taskbar and taskbar where window is open",
|
||||
"MMTaskbarModeActive": "Taskbar where window is open"
|
||||
}
|
||||
},
|
||||
"CombineButtons": {
|
||||
"Label": "Combine taskbar buttons on the main display",
|
||||
"ToolTip": "This setting allows you to choose how taskbar buttons are combined on the main display.",
|
||||
"Values": {
|
||||
"CombineTaskbarAlways": "Always (Default)",
|
||||
"CombineTaskbarWhenFull": "When taskbar is full",
|
||||
"CombineTaskbarNever": "Never"
|
||||
}
|
||||
},
|
||||
"CombineMMButtons": {
|
||||
"Label": "Combine taskbar buttons on secondary displays",
|
||||
"ToolTip": "This setting allows you to choose how taskbar buttons are combined on secondary displays.",
|
||||
"Values": {
|
||||
"CombineMMTaskbarAlways": "Always (Default)",
|
||||
"CombineMMTaskbarWhenFull": "When taskbar is full",
|
||||
"CombineMMTaskbarNever": "Never"
|
||||
}
|
||||
},
|
||||
"ClearStart": {
|
||||
"Label": "Remove pinned apps from the start menu",
|
||||
"ToolTip": "This setting allows you to quickly remove all pinned apps from the start menu.",
|
||||
"Values": {
|
||||
"ClearStart": "Remove for the selected user",
|
||||
"ClearStartAllUsers": "Remove for all users"
|
||||
}
|
||||
},
|
||||
"ExplorerLocation": {
|
||||
"Label": "Open File Explorer to",
|
||||
"ToolTip": "This setting allows you to choose the default location that File Explorer opens to.",
|
||||
"Values": {
|
||||
"ExplorerToHome": "Home (Default)",
|
||||
"ExplorerToThisPC": "This PC",
|
||||
"ExplorerToDownloads": "Downloads",
|
||||
"ExplorerToOneDrive": "OneDrive"
|
||||
}
|
||||
},
|
||||
"DriveLetterPosition": {
|
||||
"Label": "Drive letter position",
|
||||
"ToolTip": "This setting allows you to choose where drive letters are shown in File Explorer.",
|
||||
"Values": {
|
||||
"ShowDriveLettersLast": "Show drive letters after drive label (Default)",
|
||||
"ShowDriveLettersFirst": "Show drive letters before drive label",
|
||||
"ShowNetworkDriveLettersFirst": "Show network drive letters before drive label",
|
||||
"HideDriveLetters": "Hide all drive letters"
|
||||
}
|
||||
},
|
||||
"ShowTabsInAltTab": {
|
||||
"Label": "Show tabs from apps when snapping or pressing Alt+Tab",
|
||||
"ToolTip": "This setting allows you to choose whether to show tabs from apps (such as Edge browser tabs) when snapping windows or pressing Alt+Tab.",
|
||||
"Values": {
|
||||
"HideTabsInAltTab": "Don't show tabs",
|
||||
"Show3TabsInAltTab": "Show 3 most recent tabs (Default)",
|
||||
"Show5TabsInAltTab": "Show 5 most recent tabs",
|
||||
"Show20TabsInAltTab": "Show 20 most recent tabs"
|
||||
}
|
||||
},
|
||||
"StartAllAppsView": {
|
||||
"Label": "Start menu 'All Apps' view",
|
||||
"ToolTip": "This setting allows you to change the layout of the 'All Apps' section in the start menu, or hide it entirely. Hiding this section may make it harder to find installed apps on your system. This feature uses policies, which will lock down certain settings.",
|
||||
"Values": {
|
||||
"DisableStartAllApps": "Hide",
|
||||
"StartAllAppsCategory": "Category (Default)",
|
||||
"StartAllAppsGrid": "Grid",
|
||||
"StartAllAppsList": "List"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
Title="About Win11Debloat"
|
||||
Title="%LANG:AboutTitle%"
|
||||
Width="500"
|
||||
SizeToContent="Height"
|
||||
ResizeMode="NoResize"
|
||||
@@ -22,7 +22,7 @@
|
||||
|
||||
<!-- Title Bar -->
|
||||
<Grid Grid.Row="0" x:Name="TitleBar" Style="{DynamicResource ModalTitleBarStyle}">
|
||||
<TextBlock Text="About Win11Debloat"
|
||||
<TextBlock Text="%LANG:AboutTitle%"
|
||||
Style="{DynamicResource ModalTitleTextStyle}"/>
|
||||
</Grid>
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
|
||||
<!-- Version -->
|
||||
<TextBlock Grid.Row="0" Grid.Column="0"
|
||||
Text="Version:"
|
||||
Text="%LANG:AboutVersionLabel%"
|
||||
Style="{DynamicResource ModalInfoLabelTextStyle}"/>
|
||||
<TextBlock x:Name="VersionText"
|
||||
Grid.Row="0" Grid.Column="1"
|
||||
@@ -57,7 +57,7 @@
|
||||
|
||||
<!-- Author -->
|
||||
<TextBlock Grid.Row="1" Grid.Column="0"
|
||||
Text="Author:"
|
||||
Text="%LANG:AboutAuthorLabel%"
|
||||
Style="{DynamicResource ModalInfoLabelTextStyle}"/>
|
||||
<TextBlock Grid.Row="1" Grid.Column="1"
|
||||
Text="Raphire"
|
||||
@@ -65,7 +65,7 @@
|
||||
|
||||
<!-- Project Link -->
|
||||
<TextBlock Grid.Row="2" Grid.Column="0"
|
||||
Text="Project:"
|
||||
Text="%LANG:AboutProjectLabel%"
|
||||
Style="{DynamicResource ModalInfoLabelTextStyle}"
|
||||
Margin="0,0,16,0"/>
|
||||
<TextBlock x:Name="ProjectLink"
|
||||
@@ -85,7 +85,7 @@
|
||||
<!-- Content -->
|
||||
<StackPanel Grid.Row="2" Margin="20,16,20,16">
|
||||
<!-- Donation Message -->
|
||||
<TextBlock Text="Win11Debloat is a passion project that I maintain in my free time. If you've found this tool useful, please consider making a small donation to support its development. I really appreciate it!"
|
||||
<TextBlock Text="%LANG:AboutDonationMessage%"
|
||||
FontSize="14"
|
||||
Foreground="{DynamicResource AppFgColor}"
|
||||
TextWrapping="Wrap"
|
||||
@@ -117,7 +117,7 @@
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
FontWeight="SemiBold"
|
||||
Text="Support me on Ko-fi"/>
|
||||
Text="%LANG:AboutKofiLink%"/>
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
@@ -132,7 +132,7 @@
|
||||
|
||||
<Button x:Name="CloseButton"
|
||||
Grid.Column="1"
|
||||
Content="Close"
|
||||
Content="%LANG:TitleBarClose%"
|
||||
Style="{DynamicResource ModalSecondaryStretchedButtonStyle}"/>
|
||||
</Grid>
|
||||
</Border>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
Title="Win11Debloat Application Selection"
|
||||
Title="%LANG:AppSelectionWindowTitle%"
|
||||
Width="450" Height="580"
|
||||
ResizeMode="NoResize"
|
||||
WindowStartupLocation="CenterScreen"
|
||||
@@ -64,14 +64,14 @@
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock Grid.Column="0"
|
||||
Text="Win11Debloat Application Selection"
|
||||
<TextBlock Grid.Column="0"
|
||||
Text="%LANG:AppSelectionWindowTitle%"
|
||||
Foreground="{DynamicResource AppFgColor}"
|
||||
VerticalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
Margin="12,0,0,0"
|
||||
FontSize="12"/>
|
||||
<StackPanel Grid.Column="1" Orientation="Horizontal">
|
||||
<Button x:Name="CloseBtn" Content="" Style="{StaticResource CloseButton}" ToolTip="Close" IsCancel="True" AutomationProperties.Name="Close"/>
|
||||
<Button x:Name="CloseBtn" Content="" Style="{StaticResource CloseButton}" ToolTip="%LANG:TitleBarClose%" IsCancel="True" AutomationProperties.Name="%LANG:TitleBarClose%"/>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Border>
|
||||
@@ -87,14 +87,14 @@
|
||||
<RowDefinition Height="Auto"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<TextBlock Grid.Row="0"
|
||||
Text="Check apps that you wish to remove, uncheck apps that you wish to keep"
|
||||
<TextBlock Grid.Row="0"
|
||||
Text="%LANG:AppSelectionWindowInstructions%"
|
||||
TextWrapping="Wrap"
|
||||
Foreground="{DynamicResource AppFgColor}"
|
||||
Margin="0,0,0,10"/>
|
||||
|
||||
<StackPanel Grid.Row="1" Orientation="Horizontal" Margin="0,0,0,8">
|
||||
<CheckBox x:Name="CheckAllBox" Content="Check/Uncheck all" Margin="8,0,15,0" Foreground="{DynamicResource AppFgColor}" AutomationProperties.Name="Check or Uncheck all"/>
|
||||
<CheckBox x:Name="CheckAllBox" Content="%LANG:AppSelectionWindowCheckAll%" Margin="8,0,15,0" Foreground="{DynamicResource AppFgColor}" AutomationProperties.Name="%LANG:AppSelectionWindowCheckAllAutomationName%"/>
|
||||
</StackPanel>
|
||||
|
||||
<Border Grid.Row="2" BorderBrush="{DynamicResource AppBorderColor}" CornerRadius="4" BorderThickness="1" Margin="0,0,0,10" Background="{DynamicResource CardBgColor}">
|
||||
@@ -103,17 +103,17 @@
|
||||
<StackPanel x:Name="AppsPanel" Margin="5"/>
|
||||
</ScrollViewer>
|
||||
<Border x:Name="LoadingAppsIndicator" CornerRadius="4" Background="{DynamicResource ScrollBarThumbColor}" Opacity="0.8" Visibility="Collapsed">
|
||||
<TextBlock Text="Loading apps..." FontSize="16" FontWeight="SemiBold" Foreground="{DynamicResource AppFgColor}" HorizontalAlignment="Center" VerticalAlignment="Center"/>
|
||||
<TextBlock Text="%LANG:AppLoadingIndicator%" FontSize="16" FontWeight="SemiBold" Foreground="{DynamicResource AppFgColor}" HorizontalAlignment="Center" VerticalAlignment="Center"/>
|
||||
</Border>
|
||||
</Grid>
|
||||
</Border>
|
||||
|
||||
<StackPanel Grid.Row="3" Orientation="Horizontal" HorizontalAlignment="Right">
|
||||
<Grid Margin="0,8,10,10">
|
||||
<CheckBox x:Name="OnlyInstalledBox" Content="Only show installed apps" Foreground="{DynamicResource AppFgColor}" AutomationProperties.Name="Only show installed apps"/>
|
||||
<CheckBox x:Name="OnlyInstalledBox" Content="%LANG:AppOnlyInstalledCheckbox%" Foreground="{DynamicResource AppFgColor}" AutomationProperties.Name="%LANG:AppOnlyInstalledCheckbox%"/>
|
||||
</Grid>
|
||||
<Button x:Name="ConfirmBtn" Width="80" Height="32" Margin="0,0,10,0" Content="Confirm" Style="{DynamicResource PrimaryButtonStyle}" AutomationProperties.Name="Confirm"/>
|
||||
<Button x:Name="CancelBtn" Width="80" Height="32" Content="Cancel" Style="{DynamicResource SecondaryButtonStyle}" IsCancel="True" AutomationProperties.Name="Cancel"/>
|
||||
<Button x:Name="ConfirmBtn" Width="80" Height="32" Margin="0,0,10,0" Content="%LANG:AppSelectionWindowConfirm%" Style="{DynamicResource PrimaryButtonStyle}" AutomationProperties.Name="%LANG:AppSelectionWindowConfirm%"/>
|
||||
<Button x:Name="CancelBtn" Width="80" Height="32" Content="%LANG:AppSelectionWindowCancel%" Style="{DynamicResource SecondaryButtonStyle}" IsCancel="True" AutomationProperties.Name="%LANG:AppSelectionWindowCancel%"/>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
Title="Apply Changes"
|
||||
Title="%LANG:DeployApplyChanges%"
|
||||
Width="500"
|
||||
SizeToContent="Height"
|
||||
ResizeMode="NoResize"
|
||||
@@ -24,8 +24,8 @@
|
||||
Width="36" Height="32"
|
||||
BorderThickness="0"
|
||||
Cursor="Hand"
|
||||
ToolTip="Cancel"
|
||||
AutomationProperties.Name="Cancel">
|
||||
ToolTip="%LANG:ApplyCancelTooltip%"
|
||||
AutomationProperties.Name="%LANG:ApplyCancelTooltip%">
|
||||
<Button.Template>
|
||||
<ControlTemplate TargetType="Button">
|
||||
<Border Background="{TemplateBinding Background}" BorderThickness="0" CornerRadius="0,8,0,0">
|
||||
@@ -78,12 +78,12 @@
|
||||
</TextBlock.Triggers>
|
||||
</TextBlock>
|
||||
|
||||
<TextBlock Text="Applying Changes"
|
||||
<TextBlock Text="%LANG:ApplyInProgressTitle%"
|
||||
Style="{DynamicResource ModalTitleStyle}"/>
|
||||
|
||||
<!-- Current step name -->
|
||||
<TextBlock x:Name="ApplyStepName"
|
||||
Text="Preparing..."
|
||||
<TextBlock x:Name="ApplyStepName"
|
||||
Text="%LANG:ApplyPreparing%"
|
||||
Style="{DynamicResource ModalSubtextStyle}"
|
||||
TextTrimming="CharacterEllipsis"
|
||||
MaxWidth="430"/>
|
||||
@@ -99,9 +99,9 @@
|
||||
Margin="0,2,0,8"/>
|
||||
|
||||
<!-- Step counter -->
|
||||
<TextBlock x:Name="ApplyStepCounter"
|
||||
Text="Step 0 of 0"
|
||||
FontSize="12"
|
||||
<TextBlock x:Name="ApplyStepCounter"
|
||||
Text="%LANG:ApplyPreparing%"
|
||||
FontSize="12"
|
||||
Foreground="{DynamicResource AppFgColor}"
|
||||
HorizontalAlignment="Right"
|
||||
Opacity="0.8"/>
|
||||
@@ -121,12 +121,12 @@
|
||||
HorizontalAlignment="Center"
|
||||
Margin="0,0,0,12"/>
|
||||
|
||||
<TextBlock x:Name="ApplyCompletionTitle"
|
||||
Text="Changes Applied"
|
||||
<TextBlock x:Name="ApplyCompletionTitle"
|
||||
Text="%LANG:ApplyCompletionTitleSuccess%"
|
||||
Style="{DynamicResource ModalTitleStyle}"/>
|
||||
|
||||
<TextBlock x:Name="ApplyCompletionMessage"
|
||||
Text="Please note that some changes will only take effect after a reboot. Thanks for using Win11Debloat!"
|
||||
|
||||
<TextBlock x:Name="ApplyCompletionMessage"
|
||||
Text="%LANG:ApplyCompletionMessageDefault%"
|
||||
TextWrapping="Wrap"
|
||||
Style="{DynamicResource ModalSubtextStyle}"/>
|
||||
</StackPanel>
|
||||
@@ -146,7 +146,7 @@
|
||||
Foreground="#e8912d"
|
||||
VerticalAlignment="Center"
|
||||
Margin="0,0,8,0"/>
|
||||
<TextBlock Text="A reboot is required for these changes to take effect:"
|
||||
<TextBlock Text="%LANG:ApplyRebootRequiredHeader%"
|
||||
FontSize="13"
|
||||
FontWeight="SemiBold"
|
||||
Foreground="{DynamicResource AppFgColor}"
|
||||
@@ -161,16 +161,16 @@
|
||||
<UniformGrid x:Name="ButtonPanel" Rows="1">
|
||||
<Button x:Name="ApplyKofiBtn"
|
||||
Style="{DynamicResource ModalSecondaryStretchedButtonStyle}"
|
||||
AutomationProperties.Name="Support the creator">
|
||||
AutomationProperties.Name="%LANG:TitleBarSupportCreator%">
|
||||
<StackPanel Orientation="Horizontal" VerticalAlignment="Center">
|
||||
<TextBlock Text="" FontFamily="{DynamicResource AppIconFontFamily}" FontSize="14" VerticalAlignment="Center" Margin="0,0,8,-1"/>
|
||||
<TextBlock Text="Support the creator" VerticalAlignment="Center" FontSize="14" Margin="0,0,0,1"/>
|
||||
<TextBlock Text="%LANG:TitleBarSupportCreator%" VerticalAlignment="Center" FontSize="14" Margin="0,0,0,1"/>
|
||||
</StackPanel>
|
||||
</Button>
|
||||
<Button x:Name="ApplyCloseBtn"
|
||||
Style="{DynamicResource ModalPrimaryStretchedButtonStyle}"
|
||||
AutomationProperties.Name="Close">
|
||||
<TextBlock Text="Close" VerticalAlignment="Center" FontSize="14" Margin="0,0,0,1"/>
|
||||
AutomationProperties.Name="%LANG:TitleBarClose%">
|
||||
<TextBlock Text="%LANG:TitleBarClose%" VerticalAlignment="Center" FontSize="14" Margin="0,0,0,1"/>
|
||||
</Button>
|
||||
</UniformGrid>
|
||||
</Border>
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
Color="Black"/>
|
||||
</Border.Effect>
|
||||
<TextBlock Name="BubbleText"
|
||||
Text="View the selected changes here"
|
||||
Text="%LANG:BubbleViewChanges%"
|
||||
TextWrapping="Wrap"
|
||||
MaxWidth="260"
|
||||
Foreground="{DynamicResource AppFgColor}"/>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
Title="Select Settings to Import/Export"
|
||||
Title="%LANG:ImportExportImportTitle%"
|
||||
Width="440"
|
||||
SizeToContent="Height"
|
||||
MaxHeight="501"
|
||||
@@ -45,10 +45,10 @@
|
||||
<Border Grid.Row="2" Style="{DynamicResource ModalFooterBorderStyle}">
|
||||
<UniformGrid Rows="1">
|
||||
<Button x:Name="OkButton"
|
||||
Content="OK"
|
||||
Content="%LANG:MessageBoxOk%"
|
||||
Style="{DynamicResource ModalPrimaryStretchedButtonStyle}"/>
|
||||
<Button x:Name="CancelButton"
|
||||
Content="Cancel"
|
||||
Content="%LANG:MessageBoxCancel%"
|
||||
Style="{DynamicResource ModalSecondaryStretchedButtonStyle}"/>
|
||||
</UniformGrid>
|
||||
</Border>
|
||||
|
||||
+89
-89
@@ -1,7 +1,7 @@
|
||||
<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:shell="clr-namespace:System.Windows.Shell;assembly=PresentationFramework"
|
||||
Title="Win11Debloat"
|
||||
Title="%LANG:TitleBarAppName%"
|
||||
MinWidth="860" MinHeight="640"
|
||||
ResizeMode="CanResize"
|
||||
SnapsToDevicePixels="True"
|
||||
@@ -346,7 +346,7 @@
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock Grid.Column="0"
|
||||
Text="Win11Debloat"
|
||||
Text="%LANG:TitleBarAppName%"
|
||||
VerticalAlignment="Center"
|
||||
Margin="12,0,0,0"
|
||||
FontSize="12">
|
||||
@@ -362,8 +362,8 @@
|
||||
</TextBlock.Style>
|
||||
</TextBlock>
|
||||
<StackPanel Grid.Column="1" Orientation="Horizontal">
|
||||
<Button x:Name="KofiBtn" shell:WindowChrome.IsHitTestVisibleInChrome="True" Content="" FontSize="15" Style="{StaticResource TitlebarButton}" ToolTip="Support the creator" AutomationProperties.Name="Support the creator"/>
|
||||
<Button x:Name="MenuBtn" shell:WindowChrome.IsHitTestVisibleInChrome="True" Content="" FontSize="15" Style="{StaticResource TitlebarButton}" ToolTip="Options" AutomationProperties.Name="Options">
|
||||
<Button x:Name="KofiBtn" shell:WindowChrome.IsHitTestVisibleInChrome="True" Content="" FontSize="15" Style="{StaticResource TitlebarButton}" ToolTip="%LANG:TitleBarSupportCreator%" AutomationProperties.Name="%LANG:TitleBarSupportCreator%"/>
|
||||
<Button x:Name="MenuBtn" shell:WindowChrome.IsHitTestVisibleInChrome="True" Content="" FontSize="15" Style="{StaticResource TitlebarButton}" ToolTip="%LANG:TitleBarOptions%" AutomationProperties.Name="%LANG:TitleBarOptions%">
|
||||
<Button.ContextMenu>
|
||||
<ContextMenu x:Name="MainMenu">
|
||||
<ContextMenu.Resources>
|
||||
@@ -378,38 +378,38 @@
|
||||
</Setter>
|
||||
</Style>
|
||||
</ContextMenu.Resources>
|
||||
<MenuItem x:Name="ImportConfigBtn" Header="Import config" AutomationProperties.Name="Import configuration">
|
||||
<MenuItem x:Name="ImportConfigBtn" Header="%LANG:MenuImportConfig%" AutomationProperties.Name="%LANG:MenuImportConfigTooltip%">
|
||||
<MenuItem.Icon>
|
||||
<TextBlock Text="" FontFamily="{DynamicResource AppIconFontFamily}" FontSize="16" Foreground="{DynamicResource AppFgColor}"/>
|
||||
</MenuItem.Icon>
|
||||
</MenuItem>
|
||||
<MenuItem x:Name="ExportConfigBtn" Header="Export config" AutomationProperties.Name="Export configuration">
|
||||
<MenuItem x:Name="ExportConfigBtn" Header="%LANG:MenuExportConfig%" AutomationProperties.Name="%LANG:MenuExportConfigTooltip%">
|
||||
<MenuItem.Icon>
|
||||
<TextBlock Text="" FontFamily="{DynamicResource AppIconFontFamily}" FontSize="16" Foreground="{DynamicResource AppFgColor}"/>
|
||||
</MenuItem.Icon>
|
||||
</MenuItem>
|
||||
<MenuItem x:Name="RestoreBackupBtn" Header="Restore backup" AutomationProperties.Name="Restore registry backup">
|
||||
<MenuItem x:Name="RestoreBackupBtn" Header="%LANG:MenuRestoreBackup%" AutomationProperties.Name="%LANG:MenuRestoreBackupTooltip%">
|
||||
<MenuItem.Icon>
|
||||
<TextBlock Text="" FontFamily="{DynamicResource AppIconFontFamily}" FontSize="16" Foreground="{DynamicResource AppFgColor}"/>
|
||||
</MenuItem.Icon>
|
||||
</MenuItem>
|
||||
<Separator />
|
||||
<MenuItem x:Name="MenuDocumentation" Header="Documentation" AutomationProperties.Name="Documentation">
|
||||
<MenuItem x:Name="MenuDocumentation" Header="%LANG:MenuDocumentation%" AutomationProperties.Name="%LANG:MenuDocumentation%">
|
||||
<MenuItem.Icon>
|
||||
<TextBlock Text="" FontFamily="{DynamicResource AppIconFontFamily}" FontSize="16" Foreground="{DynamicResource AppFgColor}"/>
|
||||
</MenuItem.Icon>
|
||||
</MenuItem>
|
||||
<MenuItem x:Name="MenuReportBug" Header="Report a bug" AutomationProperties.Name="Report a bug">
|
||||
<MenuItem x:Name="MenuReportBug" Header="%LANG:MenuReportBug%" AutomationProperties.Name="%LANG:MenuReportBug%">
|
||||
<MenuItem.Icon>
|
||||
<TextBlock Text="" FontFamily="{DynamicResource AppIconFontFamily}" FontSize="16" Foreground="{DynamicResource AppFgColor}"/>
|
||||
</MenuItem.Icon>
|
||||
</MenuItem>
|
||||
<MenuItem x:Name="MenuLogs" Header="Logs" AutomationProperties.Name="Logs">
|
||||
<MenuItem x:Name="MenuLogs" Header="%LANG:MenuLogs%" AutomationProperties.Name="%LANG:MenuLogs%">
|
||||
<MenuItem.Icon>
|
||||
<TextBlock Text="" FontFamily="{DynamicResource AppIconFontFamily}" FontSize="16" Foreground="{DynamicResource AppFgColor}"/>
|
||||
</MenuItem.Icon>
|
||||
</MenuItem>
|
||||
<MenuItem x:Name="MenuAbout" Header="About" AutomationProperties.Name="About">
|
||||
<MenuItem x:Name="MenuAbout" Header="%LANG:MenuAbout%" AutomationProperties.Name="%LANG:MenuAbout%">
|
||||
<MenuItem.Icon>
|
||||
<TextBlock Text="" FontFamily="{DynamicResource AppIconFontFamily}" FontSize="16" Foreground="{DynamicResource AppFgColor}"/>
|
||||
</MenuItem.Icon>
|
||||
@@ -417,7 +417,7 @@
|
||||
</ContextMenu>
|
||||
</Button.ContextMenu>
|
||||
</Button>
|
||||
<Button x:Name="CloseBtn" shell:WindowChrome.IsHitTestVisibleInChrome="True" Content="" Style="{StaticResource CloseButton}" ToolTip="Close" AutomationProperties.Name="Close"/>
|
||||
<Button x:Name="CloseBtn" shell:WindowChrome.IsHitTestVisibleInChrome="True" Content="" Style="{StaticResource CloseButton}" ToolTip="%LANG:TitleBarClose%" AutomationProperties.Name="%LANG:TitleBarClose%"/>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Border>
|
||||
@@ -460,7 +460,7 @@
|
||||
</TabControl.ItemContainerStyle>
|
||||
|
||||
<!-- Home Tab -->
|
||||
<TabItem Header="Home" x:Name="HomeTab">
|
||||
<TabItem Header="%LANG:HomeTabHeader%" x:Name="HomeTab">
|
||||
<Grid>
|
||||
<StackPanel x:Name="HomeContentPanel" HorizontalAlignment="Center" VerticalAlignment="Top">
|
||||
<!-- Logo -->
|
||||
@@ -482,18 +482,18 @@
|
||||
</Viewbox>
|
||||
|
||||
<!-- Title -->
|
||||
<TextBlock Text="Welcome to Win11Debloat" FontSize="40" FontWeight="SemiBold" Foreground="{DynamicResource AppFgColor}" HorizontalAlignment="Center"/>
|
||||
<TextBlock Text="%LANG:HomeTitle%" FontSize="40" FontWeight="SemiBold" Foreground="{DynamicResource AppFgColor}" HorizontalAlignment="Center"/>
|
||||
<TextBlock TextWrapping="Wrap" Foreground="{DynamicResource AppFgColor}" FontSize="20" HorizontalAlignment="Center" Margin="0,4,0,48">
|
||||
<Run Text="Your clean Windows experience is just a few clicks away!"/>
|
||||
<Run Text="%LANG:HomeSubtitle%"/>
|
||||
</TextBlock>
|
||||
|
||||
<Border HorizontalAlignment="Center" BorderBrush="{DynamicResource AppBorderColor}" BorderThickness="1" CornerRadius="4" Background="{DynamicResource CardBgColor}" Padding="16,12" Width="500">
|
||||
<StackPanel>
|
||||
<TextBlock Text="What user do you want to apply changes to?" Style="{StaticResource CategoryHeaderTextBlock}"/>
|
||||
<ComboBox x:Name="UserSelectionCombo" Margin="0,0,0,6" AutomationProperties.Name="Apply Changes To" ToolTip="The currently logged-in user profile">
|
||||
<ComboBoxItem Content="Current User" IsSelected="True"/>
|
||||
<ComboBoxItem Content="Other User"/>
|
||||
<ComboBoxItem Content="Windows Default User (Sysprep)"/>
|
||||
<TextBlock Text="%LANG:HomeUserQuestion%" Style="{StaticResource CategoryHeaderTextBlock}"/>
|
||||
<ComboBox x:Name="UserSelectionCombo" Margin="0,0,0,6" AutomationProperties.Name="%LANG:HomeUserSelectionAutomationName%" ToolTip="%LANG:DeployUserDescriptionCurrentUser%">
|
||||
<ComboBoxItem Content="%LANG:HomeUserOptionCurrentUser%" IsSelected="True"/>
|
||||
<ComboBoxItem Content="%LANG:HomeUserOptionOtherUser%"/>
|
||||
<ComboBoxItem Content="%LANG:HomeUserOptionSysprep%"/>
|
||||
</ComboBox>
|
||||
<StackPanel x:Name="OtherUserPanel" Visibility="Collapsed" Margin="0,0,0,6">
|
||||
<TextBlock x:Name="UsernameValidationMessage" Text="" FontStyle="Italic" Foreground="{DynamicResource TitleBarCloseHoverColor}" FontSize="11" Margin="3,0,0,4" TextWrapping="Wrap"/>
|
||||
@@ -504,8 +504,8 @@
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock x:Name="UsernameTextBoxPlaceholder" Grid.Column="0" Text="Enter username" Foreground="{DynamicResource AppFgColor}" Opacity="0.7" FontSize="13" Margin="3,0,0,1" VerticalAlignment="Center" IsHitTestVisible="False"/>
|
||||
<TextBox x:Name="OtherUsernameTextBox" Grid.Column="0" Style="{DynamicResource TextBoxInputStyle}" Text="" AutomationProperties.Name="Enter username"/>
|
||||
<TextBlock x:Name="UsernameTextBoxPlaceholder" Grid.Column="0" Text="%LANG:HomeUsernamePlaceholder%" Foreground="{DynamicResource AppFgColor}" Opacity="0.7" FontSize="13" Margin="3,0,0,1" VerticalAlignment="Center" IsHitTestVisible="False"/>
|
||||
<TextBox x:Name="OtherUsernameTextBox" Grid.Column="0" Style="{DynamicResource TextBoxInputStyle}" Text="" AutomationProperties.Name="%LANG:HomeUsernamePlaceholder%"/>
|
||||
<TextBlock Grid.Column="1" Text="" FontFamily="{DynamicResource AppIconFontFamily}" FontSize="14" VerticalAlignment="Center" Margin="8,0,4,0" Foreground="{DynamicResource AppFgColor}" Opacity="0.7"/>
|
||||
</Grid>
|
||||
</Border>
|
||||
@@ -515,16 +515,16 @@
|
||||
<Separator Margin="0,10,0,8" Background="{DynamicResource AppBorderColor}"/>
|
||||
|
||||
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center" Margin="0,8,0,4">
|
||||
<Button x:Name="HomeDefaultModeBtn" Width="227" Height="50" Style="{DynamicResource PrimaryButtonStyle}" Margin="0,0,12,0" AutomationProperties.Name="Default Mode">
|
||||
<Button x:Name="HomeDefaultModeBtn" Width="227" Height="50" Style="{DynamicResource PrimaryButtonStyle}" Margin="0,0,12,0" AutomationProperties.Name="%LANG:HomeDefaultModeBtn%">
|
||||
<StackPanel Orientation="Horizontal" VerticalAlignment="Center">
|
||||
<TextBlock Text="" FontFamily="{DynamicResource AppIconFontFamily}" FontSize="16" VerticalAlignment="Center" Margin="0,0,8,-1"/>
|
||||
<TextBlock Text="Default Mode" ToolTip="Quickly select the recommended settings" FontWeight="SemiBold" VerticalAlignment="Center" FontSize="17" Margin="0,0,0,1"/>
|
||||
<TextBlock Text="%LANG:HomeDefaultModeBtn%" ToolTip="%LANG:HomeDefaultModeBtnTooltip%" FontWeight="SemiBold" VerticalAlignment="Center" FontSize="17" Margin="0,0,0,1"/>
|
||||
</StackPanel>
|
||||
</Button>
|
||||
<Button x:Name="HomeStartBtn" Width="227" Height="50" Style="{DynamicResource SecondaryButtonStyle}" AutomationProperties.Name="Custom Setup">
|
||||
<Button x:Name="HomeStartBtn" Width="227" Height="50" Style="{DynamicResource SecondaryButtonStyle}" AutomationProperties.Name="%LANG:HomeCustomSetupBtn%">
|
||||
<StackPanel Orientation="Horizontal" VerticalAlignment="Center">
|
||||
<TextBlock Text="" FontFamily="{DynamicResource AppIconFontFamily}" FontSize="14" VerticalAlignment="Center" Margin="0,0,8,-1"/>
|
||||
<TextBlock Text="Custom Setup" ToolTip="Manually select your preferred settings" FontWeight="SemiBold" VerticalAlignment="Center" FontSize="17" Margin="0,0,0,1"/>
|
||||
<TextBlock Text="%LANG:HomeCustomSetupBtn%" ToolTip="%LANG:HomeCustomSetupBtnTooltip%" FontWeight="SemiBold" VerticalAlignment="Center" FontSize="17" Margin="0,0,0,1"/>
|
||||
</StackPanel>
|
||||
</Button>
|
||||
</StackPanel>
|
||||
@@ -535,12 +535,12 @@
|
||||
</TabItem>
|
||||
|
||||
<!-- App Removal Tab -->
|
||||
<TabItem Header="App Removal">
|
||||
<TabItem Header="%LANG:AppRemovalTabHeader%">
|
||||
<DockPanel>
|
||||
<Border DockPanel.Dock="Top" Padding="20,4,20,0">
|
||||
<StackPanel>
|
||||
<TextBlock Text="App Removal" FontWeight="SemiBold" FontSize="24" Foreground="{DynamicResource AppFgColor}"/>
|
||||
<TextBlock Text="Select which apps you want to remove" FontSize="14" Margin="0,5,0,20" Foreground="{DynamicResource AppFgColor}" TextWrapping="Wrap"/>
|
||||
<TextBlock Text="%LANG:AppRemovalTitle%" FontWeight="SemiBold" FontSize="24" Foreground="{DynamicResource AppFgColor}"/>
|
||||
<TextBlock Text="%LANG:AppRemovalSubtitle%" FontSize="14" Margin="0,5,0,20" Foreground="{DynamicResource AppFgColor}" TextWrapping="Wrap"/>
|
||||
|
||||
<!-- Filter Options -->
|
||||
<Grid Margin="0,0,0,12">
|
||||
@@ -553,7 +553,7 @@
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<StackPanel Orientation="Horizontal" Grid.Column="0">
|
||||
<ToggleButton x:Name="PresetsBtn" Height="32" Padding="10,0" Margin="0,0,10,0" AutomationProperties.Name="App Presets">
|
||||
<ToggleButton x:Name="PresetsBtn" Height="32" Padding="10,0" Margin="0,0,10,0" AutomationProperties.Name="%LANG:AppQuickSelectAutomationName%">
|
||||
<ToggleButton.Style>
|
||||
<Style TargetType="ToggleButton">
|
||||
<Setter Property="Background" Value="{DynamicResource SecondaryButtonBgColor}"/>
|
||||
@@ -586,7 +586,7 @@
|
||||
</ToggleButton.Style>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBlock Text="" FontFamily="{DynamicResource AppIconFontFamily}" FontSize="14" VerticalAlignment="Center" Margin="0,1,6,0"/>
|
||||
<TextBlock Text="Quick Select" FontSize="13" VerticalAlignment="Center" Margin="0,0,6,1"/>
|
||||
<TextBlock Text="%LANG:AppQuickSelect%" FontSize="13" VerticalAlignment="Center" Margin="0,0,6,1"/>
|
||||
<TextBlock x:Name="PresetsArrow" Text="" FontFamily="{DynamicResource AppIconFontFamily}" FontSize="10" VerticalAlignment="Center" RenderTransformOrigin="0.5,0.5">
|
||||
<TextBlock.RenderTransform>
|
||||
<RotateTransform x:Name="PresetsArrowRotation" Angle="0"/>
|
||||
@@ -594,10 +594,10 @@
|
||||
</TextBlock>
|
||||
</StackPanel>
|
||||
</ToggleButton>
|
||||
<Button x:Name="ClearAppSelectionBtn" ToolTip="Clear all selected apps" Style="{DynamicResource SecondaryButtonStyle}" Height="32" Padding="10,0" Margin="0,0,10,0" AutomationProperties.Name="Clear Selection">
|
||||
<Button x:Name="ClearAppSelectionBtn" ToolTip="%LANG:AppClearSelectionTooltip%" Style="{DynamicResource SecondaryButtonStyle}" Height="32" Padding="10,0" Margin="0,0,10,0" AutomationProperties.Name="%LANG:AppClearSelection%">
|
||||
<StackPanel Orientation="Horizontal" VerticalAlignment="Center">
|
||||
<TextBlock Text="" FontFamily="{DynamicResource AppIconFontFamily}" FontSize="15" VerticalAlignment="Center" Margin="0,3,6,0"/>
|
||||
<TextBlock Text="Clear Selection" FontSize="13" VerticalAlignment="Center" Margin="0,0,0,1"/>
|
||||
<TextBlock Text="%LANG:AppClearSelection%" FontSize="13" VerticalAlignment="Center" Margin="0,0,0,1"/>
|
||||
</StackPanel>
|
||||
</Button>
|
||||
<Popup x:Name="PresetsPopup" PlacementTarget="{Binding ElementName=PresetsBtn}" Placement="Bottom" StaysOpen="True" AllowsTransparency="True" VerticalOffset="2">
|
||||
@@ -606,8 +606,8 @@
|
||||
<DropShadowEffect BlurRadius="12" Opacity="0.25" ShadowDepth="4"/>
|
||||
</Border.Effect>
|
||||
<StackPanel x:Name="PresetsPanel" MinWidth="220">
|
||||
<CheckBox x:Name="PresetDefaultApps" Content="Default apps" IsThreeState="True" ToolTip="Select the apps that are safe to remove for most users" Foreground="{DynamicResource AppFgColor}" Margin="8,4" AutomationProperties.Name="Default selection"/>
|
||||
<CheckBox x:Name="PresetLastUsed" Content="Previously selected apps" IsThreeState="True" ToolTip="Select the apps that were removed the last time Win11Debloat was run" Foreground="{DynamicResource AppFgColor}" Margin="8,4" AutomationProperties.Name="Previously selected apps"/>
|
||||
<CheckBox x:Name="PresetDefaultApps" Content="%LANG:AppPresetDefaultApps%" IsThreeState="True" ToolTip="%LANG:AppPresetDefaultAppsTooltip%" Foreground="{DynamicResource AppFgColor}" Margin="8,4" AutomationProperties.Name="%LANG:AppPresetDefaultAppsAutomationName%"/>
|
||||
<CheckBox x:Name="PresetLastUsed" Content="%LANG:AppPresetLastUsed%" IsThreeState="True" ToolTip="%LANG:AppPresetLastUsedTooltip%" Foreground="{DynamicResource AppFgColor}" Margin="8,4" AutomationProperties.Name="%LANG:AppPresetLastUsed%"/>
|
||||
<Separator Margin="4,6" Background="{DynamicResource AppBorderColor}"/>
|
||||
<StackPanel x:Name="JsonPresetsPanel"/>
|
||||
</StackPanel>
|
||||
@@ -615,7 +615,7 @@
|
||||
</Popup>
|
||||
</StackPanel>
|
||||
|
||||
<CheckBox x:Name="OnlyInstalledAppsBox" Grid.Column="2" Content="Only show installed apps" ToolTip="Only show applications that are currently installed on this system" IsChecked="False" Foreground="{DynamicResource AppFgColor}" VerticalAlignment="Center" AutomationProperties.Name="Only show installed apps"/>
|
||||
<CheckBox x:Name="OnlyInstalledAppsBox" Grid.Column="2" Content="%LANG:AppOnlyInstalledCheckbox%" ToolTip="%LANG:AppOnlyInstalledCheckboxTooltip%" IsChecked="False" Foreground="{DynamicResource AppFgColor}" VerticalAlignment="Center" AutomationProperties.Name="%LANG:AppOnlyInstalledCheckbox%"/>
|
||||
|
||||
<Border x:Name="AppSearchBorder" Grid.Column="4" Width="300">
|
||||
<Border Style="{DynamicResource TextBoxBorderStyle}">
|
||||
@@ -625,8 +625,8 @@
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock x:Name="AppSearchPlaceholder" Grid.Column="0" Text="Search apps..." Foreground="{DynamicResource AppFgColor}" Opacity="0.7" FontSize="13" Margin="3,0,0,1" VerticalAlignment="Center" IsHitTestVisible="False"/>
|
||||
<TextBox x:Name="AppSearchBox" Grid.Column="0" Style="{DynamicResource TextBoxInputStyle}" Text="" AutomationProperties.Name="Search app"/>
|
||||
<TextBlock x:Name="AppSearchPlaceholder" Grid.Column="0" Text="%LANG:AppSearchPlaceholder%" Foreground="{DynamicResource AppFgColor}" Opacity="0.7" FontSize="13" Margin="3,0,0,1" VerticalAlignment="Center" IsHitTestVisible="False"/>
|
||||
<TextBox x:Name="AppSearchBox" Grid.Column="0" Style="{DynamicResource TextBoxInputStyle}" Text="" AutomationProperties.Name="%LANG:AppSearchAutomationName%"/>
|
||||
<TextBlock Grid.Column="1" Text="" FontFamily="{DynamicResource AppIconFontFamily}" FontSize="14" VerticalAlignment="Center" Margin="8,0,4,0" Foreground="{DynamicResource AppFgColor}" Opacity="0.7"/>
|
||||
</Grid>
|
||||
</Border>
|
||||
@@ -657,19 +657,19 @@
|
||||
<ColumnDefinition Width="{StaticResource AppTableIdColWidth}"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<StackPanel x:Name="HeaderNameBtn" Grid.Column="1" Orientation="Horizontal" Cursor="Hand" VerticalAlignment="Center" Style="{StaticResource SortHeaderBtnStyle}">
|
||||
<TextBlock Text="Name" FontWeight="SemiBold" FontSize="16" Foreground="{DynamicResource AppFgColor}"/>
|
||||
<TextBlock Text="%LANG:AppTableHeaderName%" FontWeight="SemiBold" FontSize="16" Foreground="{DynamicResource AppFgColor}"/>
|
||||
<TextBlock x:Name="SortArrowName" Text="" FontFamily="{DynamicResource AppIconFontFamily}" FontSize="11" Foreground="{DynamicResource AppFgColor}" VerticalAlignment="Center" Margin="5,1,0,0" Opacity="0.3" RenderTransformOrigin="0.5,0.5">
|
||||
<TextBlock.RenderTransform><RotateTransform Angle="0"/></TextBlock.RenderTransform>
|
||||
</TextBlock>
|
||||
</StackPanel>
|
||||
<StackPanel x:Name="HeaderDescriptionBtn" Grid.Column="2" Orientation="Horizontal" Cursor="Hand" VerticalAlignment="Center" Margin="8,0,0,0" Style="{StaticResource SortHeaderBtnStyle}">
|
||||
<TextBlock Text="Description" FontWeight="SemiBold" FontSize="16" Foreground="{DynamicResource AppFgColor}"/>
|
||||
<TextBlock Text="%LANG:AppTableHeaderDescription%" FontWeight="SemiBold" FontSize="16" Foreground="{DynamicResource AppFgColor}"/>
|
||||
<TextBlock x:Name="SortArrowDescription" Text="" FontFamily="{DynamicResource AppIconFontFamily}" FontSize="11" Foreground="{DynamicResource AppFgColor}" VerticalAlignment="Center" Margin="5,1,0,0" Opacity="0.3" RenderTransformOrigin="0.5,0.5">
|
||||
<TextBlock.RenderTransform><RotateTransform Angle="0"/></TextBlock.RenderTransform>
|
||||
</TextBlock>
|
||||
</StackPanel>
|
||||
<StackPanel x:Name="HeaderAppIdBtn" Grid.Column="3" Orientation="Horizontal" Cursor="Hand" VerticalAlignment="Center" Style="{StaticResource SortHeaderBtnStyle}">
|
||||
<TextBlock Text="App ID" FontWeight="SemiBold" FontSize="16" Foreground="{DynamicResource AppFgColor}"/>
|
||||
<TextBlock Text="%LANG:AppTableHeaderAppId%" FontWeight="SemiBold" FontSize="16" Foreground="{DynamicResource AppFgColor}"/>
|
||||
<TextBlock x:Name="SortArrowAppId" Text="" FontFamily="{DynamicResource AppIconFontFamily}" FontSize="11" Foreground="{DynamicResource AppFgColor}" VerticalAlignment="Center" Margin="5,1,0,0" Opacity="0.3" RenderTransformOrigin="0.5,0.5">
|
||||
<TextBlock.RenderTransform><RotateTransform Angle="0"/></TextBlock.RenderTransform>
|
||||
</TextBlock>
|
||||
@@ -701,7 +701,7 @@
|
||||
</EventTrigger>
|
||||
</TextBlock.Triggers>
|
||||
</TextBlock>
|
||||
<TextBlock Text="Loading apps..." FontSize="16" FontWeight="SemiBold" Foreground="{DynamicResource AppFgColor}" HorizontalAlignment="Center"/>
|
||||
<TextBlock Text="%LANG:AppLoadingIndicator%" FontSize="16" FontWeight="SemiBold" Foreground="{DynamicResource AppFgColor}" HorizontalAlignment="Center"/>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
</Grid>
|
||||
@@ -716,21 +716,21 @@
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock x:Name="AppSelectionStatus" Grid.Column="0" Text="" Foreground="{DynamicResource AppFgColor}" Margin="10,0,0,5" HorizontalAlignment="Left" VerticalAlignment="Bottom"/>
|
||||
<StackPanel x:Name="AppColorLegend" Grid.Column="1" Orientation="Horizontal" HorizontalAlignment="Right" VerticalAlignment="Bottom" Margin="0,0,10,5">
|
||||
<TextBlock Text="Legend:" Foreground="{DynamicResource AppFgColor}" FontSize="12" FontWeight="SemiBold" VerticalAlignment="Center" Margin="0,0,10,0" Opacity="0.8"/>
|
||||
<TextBlock Text="%LANG:AppLegendTitle%" Foreground="{DynamicResource AppFgColor}" FontSize="12" FontWeight="SemiBold" VerticalAlignment="Center" Margin="0,0,10,0" Opacity="0.8"/>
|
||||
<!-- Recommended (safe) -->
|
||||
<StackPanel Orientation="Horizontal" Margin="0,0,16,0" ToolTip="[Recommended] Safe to remove for most users">
|
||||
<StackPanel Orientation="Horizontal" Margin="0,0,16,0" ToolTip="%LANG:AppLegendRecommendedTooltip%">
|
||||
<Ellipse Width="9" Height="9" Fill="{DynamicResource AppRecommendationSafeColor}" VerticalAlignment="Center" Margin="0,0,6,0"/>
|
||||
<TextBlock Text="Recommended" Foreground="{DynamicResource AppFgColor}" FontSize="12" VerticalAlignment="Center" Opacity="0.8"/>
|
||||
<TextBlock Text="%LANG:AppLegendRecommended%" Foreground="{DynamicResource AppFgColor}" FontSize="12" VerticalAlignment="Center" Opacity="0.8"/>
|
||||
</StackPanel>
|
||||
<!-- Optional (default) -->
|
||||
<StackPanel Orientation="Horizontal" Margin="0,0,16,0" ToolTip="[Optional] Can be safely removed if you don't need this app">
|
||||
<StackPanel Orientation="Horizontal" Margin="0,0,16,0" ToolTip="%LANG:AppLegendOptionalTooltip%">
|
||||
<Ellipse Width="9" Height="9" Fill="{DynamicResource AppRecommendationOptionalColor}" VerticalAlignment="Center" Margin="0,0,6,0"/>
|
||||
<TextBlock Text="Optional" Foreground="{DynamicResource AppFgColor}" FontSize="12" VerticalAlignment="Center" Opacity="0.8"/>
|
||||
<TextBlock Text="%LANG:AppLegendOptional%" Foreground="{DynamicResource AppFgColor}" FontSize="12" VerticalAlignment="Center" Opacity="0.8"/>
|
||||
</StackPanel>
|
||||
<!-- Not Recommended (unsafe) -->
|
||||
<StackPanel Orientation="Horizontal" ToolTip="[Not Recommended] Only remove if you know what you are doing">
|
||||
<StackPanel Orientation="Horizontal" ToolTip="%LANG:AppLegendNotRecommendedTooltip%">
|
||||
<Ellipse Width="9" Height="9" Fill="{DynamicResource AppRecommendationUnsafeColor}" VerticalAlignment="Center" Margin="0,0,6,0"/>
|
||||
<TextBlock Text="Not Recommended" Foreground="{DynamicResource AppFgColor}" FontSize="12" VerticalAlignment="Center" Opacity="0.8"/>
|
||||
<TextBlock Text="%LANG:AppLegendNotRecommended%" Foreground="{DynamicResource AppFgColor}" FontSize="12" VerticalAlignment="Center" Opacity="0.8"/>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
@@ -739,12 +739,12 @@
|
||||
</TabItem>
|
||||
|
||||
<!-- Tweaks Tab -->
|
||||
<TabItem Header="Tweaks">
|
||||
<TabItem Header="%LANG:TweaksTabHeader%">
|
||||
<DockPanel>
|
||||
<Border DockPanel.Dock="Top" Padding="20,4,20,0" Background="{DynamicResource AppBgColor}">
|
||||
<StackPanel>
|
||||
<TextBlock Text="System Tweaks" FontWeight="SemiBold" FontSize="24" Foreground="{DynamicResource AppFgColor}"/>
|
||||
<TextBlock Text="Select what changes you want to make, you can hover over settings for more information" FontSize="14" Margin="0,5,0,20" Foreground="{DynamicResource AppFgColor}" TextWrapping="Wrap"/>
|
||||
<TextBlock Text="%LANG:TweaksTitle%" FontWeight="SemiBold" FontSize="24" Foreground="{DynamicResource AppFgColor}"/>
|
||||
<TextBlock Text="%LANG:TweaksSubtitle%" FontSize="14" Margin="0,5,0,20" Foreground="{DynamicResource AppFgColor}" TextWrapping="Wrap"/>
|
||||
|
||||
<!-- Filter Options -->
|
||||
<Grid Margin="0,0,0,12">
|
||||
@@ -757,7 +757,7 @@
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<StackPanel Grid.Column="0" Orientation="Horizontal">
|
||||
<ToggleButton x:Name="TweaksPresetsBtn" Height="32" Padding="10,0" Margin="0,0,10,0" AutomationProperties.Name="Tweak Presets">
|
||||
<ToggleButton x:Name="TweaksPresetsBtn" Height="32" Padding="10,0" Margin="0,0,10,0" AutomationProperties.Name="%LANG:TweaksQuickSelectAutomationName%">
|
||||
<ToggleButton.Style>
|
||||
<Style TargetType="ToggleButton">
|
||||
<Setter Property="Background" Value="{DynamicResource SecondaryButtonBgColor}"/>
|
||||
@@ -790,7 +790,7 @@
|
||||
</ToggleButton.Style>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBlock Text="" FontFamily="{DynamicResource AppIconFontFamily}" FontSize="14" VerticalAlignment="Center" Margin="0,1,6,0"/>
|
||||
<TextBlock Text="Quick Select" FontSize="13" VerticalAlignment="Center" Margin="0,0,6,1"/>
|
||||
<TextBlock Text="%LANG:TweaksQuickSelect%" FontSize="13" VerticalAlignment="Center" Margin="0,0,6,1"/>
|
||||
<TextBlock x:Name="TweaksPresetsArrow" Text="" FontFamily="{DynamicResource AppIconFontFamily}" FontSize="10" VerticalAlignment="Center" RenderTransformOrigin="0.5,0.5">
|
||||
<TextBlock.RenderTransform>
|
||||
<RotateTransform Angle="0"/>
|
||||
@@ -798,10 +798,10 @@
|
||||
</TextBlock>
|
||||
</StackPanel>
|
||||
</ToggleButton>
|
||||
<Button x:Name="ClearAllTweaksBtn" ToolTip="Clear all selected tweaks" Style="{DynamicResource SecondaryButtonStyle}" Padding="10,0" Height="32" Margin="0,0,10,0" AutomationProperties.Name="Clear Selection">
|
||||
<Button x:Name="ClearAllTweaksBtn" ToolTip="%LANG:TweaksClearSelectionTooltip%" Style="{DynamicResource SecondaryButtonStyle}" Padding="10,0" Height="32" Margin="0,0,10,0" AutomationProperties.Name="%LANG:TweaksClearSelection%">
|
||||
<StackPanel Orientation="Horizontal" VerticalAlignment="Center">
|
||||
<TextBlock Text="" FontFamily="{DynamicResource AppIconFontFamily}" FontSize="15" VerticalAlignment="Center" Margin="0,3,6,0"/>
|
||||
<TextBlock Text="Clear Selection" FontSize="13" VerticalAlignment="Center" Margin="0,0,0,1"/>
|
||||
<TextBlock Text="%LANG:TweaksClearSelection%" FontSize="13" VerticalAlignment="Center" Margin="0,0,0,1"/>
|
||||
</StackPanel>
|
||||
</Button>
|
||||
<Popup x:Name="TweaksPresetsPopup" PlacementTarget="{Binding ElementName=TweaksPresetsBtn}" Placement="Bottom" StaysOpen="True" AllowsTransparency="True" VerticalOffset="2">
|
||||
@@ -810,17 +810,17 @@
|
||||
<DropShadowEffect BlurRadius="12" Opacity="0.25" ShadowDepth="4"/>
|
||||
</Border.Effect>
|
||||
<StackPanel MinWidth="220">
|
||||
<CheckBox x:Name="PresetDefaultTweaksBtn" Content="Default settings" IsThreeState="True" ToolTip="Select the settings that are recommended for most people" Foreground="{DynamicResource AppFgColor}" Style="{DynamicResource PresetCheckBoxStyle}" AutomationProperties.Name="Default settings"/>
|
||||
<CheckBox x:Name="PresetLastUsedTweaksBtn" Content="Previously selected settings" IsThreeState="True" ToolTip="Select the settings that were applied the last time Win11Debloat was run" Foreground="{DynamicResource AppFgColor}" Style="{DynamicResource PresetCheckBoxStyle}" AutomationProperties.Name="Previously selected settings"/>
|
||||
<CheckBox x:Name="PresetDefaultTweaksBtn" Content="%LANG:TweaksPresetDefault%" IsThreeState="True" ToolTip="%LANG:TweaksPresetDefaultTooltip%" Foreground="{DynamicResource AppFgColor}" Style="{DynamicResource PresetCheckBoxStyle}" AutomationProperties.Name="%LANG:TweaksPresetDefault%"/>
|
||||
<CheckBox x:Name="PresetLastUsedTweaksBtn" Content="%LANG:TweaksPresetLastUsed%" IsThreeState="True" ToolTip="%LANG:TweaksPresetLastUsedTooltip%" Foreground="{DynamicResource AppFgColor}" Style="{DynamicResource PresetCheckBoxStyle}" AutomationProperties.Name="%LANG:TweaksPresetLastUsed%"/>
|
||||
<Separator Margin="4,6" Background="{DynamicResource AppBorderColor}"/>
|
||||
<CheckBox x:Name="PresetPrivacyTweaksBtn" Content="Privacy & suggested content" IsThreeState="True" ToolTip="Select all tweaks related to privacy & suggested content" Foreground="{DynamicResource AppFgColor}" Style="{DynamicResource PresetCheckBoxStyle}" AutomationProperties.Name="All Privacy and Suggested Content"/>
|
||||
<CheckBox x:Name="PresetAITweaksBtn" Content="AI features" IsThreeState="True" ToolTip="Select all AI related tweaks" Foreground="{DynamicResource AppFgColor}" Style="{DynamicResource PresetCheckBoxStyle}" AutomationProperties.Name="All AI features"/>
|
||||
<CheckBox x:Name="PresetPrivacyTweaksBtn" Content="%LANG:TweaksPresetPrivacy%" IsThreeState="True" ToolTip="%LANG:TweaksPresetPrivacyTooltip%" Foreground="{DynamicResource AppFgColor}" Style="{DynamicResource PresetCheckBoxStyle}" AutomationProperties.Name="%LANG:TweaksPresetPrivacyAutomationName%"/>
|
||||
<CheckBox x:Name="PresetAITweaksBtn" Content="%LANG:TweaksPresetAI%" IsThreeState="True" ToolTip="%LANG:TweaksPresetAITooltip%" Foreground="{DynamicResource AppFgColor}" Style="{DynamicResource PresetCheckBoxStyle}" AutomationProperties.Name="%LANG:TweaksPresetAIAutomationName%"/>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
</Popup>
|
||||
</StackPanel>
|
||||
|
||||
<CheckBox x:Name="ShowCurrentlyAppliedTweaksCheckBox" Grid.Column="2" Content="Detect applied tweaks" IsChecked="True" Foreground="{DynamicResource AppFgColor}" VerticalAlignment="Center" AutomationProperties.Name="Detect applied tweaks" ToolTip="Detect all tweaks currently applied for the current user"/>
|
||||
<CheckBox x:Name="ShowCurrentlyAppliedTweaksCheckBox" Grid.Column="2" Content="%LANG:TweaksDetectApplied%" IsChecked="True" Foreground="{DynamicResource AppFgColor}" VerticalAlignment="Center" AutomationProperties.Name="%LANG:TweaksDetectApplied%" ToolTip="%LANG:TweaksDetectAppliedTooltip%"/>
|
||||
|
||||
<Border x:Name="TweakSearchBorder" Grid.Column="4" Width="300">
|
||||
<Border Style="{DynamicResource TextBoxBorderStyle}">
|
||||
@@ -830,8 +830,8 @@
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock x:Name="TweakSearchPlaceholder" Grid.Column="0" Text="Search tweaks..." Foreground="{DynamicResource AppFgColor}" Opacity="0.7" FontSize="13" Margin="3,0,0,1" VerticalAlignment="Center" IsHitTestVisible="False"/>
|
||||
<TextBox x:Name="TweakSearchBox" Grid.Column="0" Style="{DynamicResource TextBoxInputStyle}" Text="" AutomationProperties.Name="Search tweaks"/>
|
||||
<TextBlock x:Name="TweakSearchPlaceholder" Grid.Column="0" Text="%LANG:TweaksSearchPlaceholder%" Foreground="{DynamicResource AppFgColor}" Opacity="0.7" FontSize="13" Margin="3,0,0,1" VerticalAlignment="Center" IsHitTestVisible="False"/>
|
||||
<TextBox x:Name="TweakSearchBox" Grid.Column="0" Style="{DynamicResource TextBoxInputStyle}" Text="" AutomationProperties.Name="%LANG:TweaksSearchAutomationName%"/>
|
||||
<TextBlock Grid.Column="1" Text="" FontFamily="{DynamicResource AppIconFontFamily}" FontSize="14" VerticalAlignment="Center" Margin="8,0,4,0" Foreground="{DynamicResource AppFgColor}" Opacity="0.7"/>
|
||||
</Grid>
|
||||
</Border>
|
||||
@@ -883,12 +883,12 @@
|
||||
</TabItem>
|
||||
|
||||
<!-- Deployment Settings Tab -->
|
||||
<TabItem Header="Deployment Settings" x:Name="DeploymentSettingsTab">
|
||||
<TabItem Header="%LANG:DeployTabHeader%" x:Name="DeploymentSettingsTab">
|
||||
<DockPanel>
|
||||
<Border DockPanel.Dock="Top" Padding="20,4,20,0">
|
||||
<StackPanel>
|
||||
<TextBlock Text="Deployment Settings" FontWeight="SemiBold" FontSize="24" Foreground="{DynamicResource AppFgColor}"/>
|
||||
<TextBlock Text="Configure how the selected changes will be applied to your system" FontSize="14" Margin="0,5,0,20" Foreground="{DynamicResource AppFgColor}" TextWrapping="Wrap"/>
|
||||
<TextBlock Text="%LANG:DeployTitle%" FontWeight="SemiBold" FontSize="24" Foreground="{DynamicResource AppFgColor}"/>
|
||||
<TextBlock Text="%LANG:DeploySubtitle%" FontSize="14" Margin="0,5,0,20" Foreground="{DynamicResource AppFgColor}" TextWrapping="Wrap"/>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
@@ -910,39 +910,39 @@
|
||||
<!-- Apply Changes To -->
|
||||
<Border Grid.Row="0" BorderBrush="{DynamicResource AppBorderColor}" BorderThickness="1" CornerRadius="4" Background="{DynamicResource CardBgColor}" Padding="16,12" Margin="0,0,0,16">
|
||||
<StackPanel>
|
||||
<TextBlock Text="Changes will be applied to" Style="{StaticResource CategoryHeaderTextBlock}"/>
|
||||
<TextBlock x:Name="UserSelectionDescription" Text="The currently logged-in user profile." Foreground="{DynamicResource AppFgColor}" FontSize="13" TextWrapping="Wrap" Margin="0,-2,0,3"/>
|
||||
<TextBlock Text="%LANG:DeployApplyToTitle%" Style="{StaticResource CategoryHeaderTextBlock}"/>
|
||||
<TextBlock x:Name="UserSelectionDescription" Text="%LANG:DeployUserSelectionDescription%" Foreground="{DynamicResource AppFgColor}" FontSize="13" TextWrapping="Wrap" Margin="0,-2,0,3"/>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
<!-- App Removal Scope -->
|
||||
<Border Grid.Row="1" x:Name="AppRemovalScopeSection" BorderBrush="{DynamicResource AppBorderColor}" BorderThickness="1" CornerRadius="4" Background="{DynamicResource CardBgColor}" Padding="16,12" Margin="0,0,0,16">
|
||||
<StackPanel>
|
||||
<TextBlock Text="Apps will be removed for" Style="{StaticResource CategoryHeaderTextBlock}"/>
|
||||
<ComboBox x:Name="AppRemovalScopeCombo" Margin="0,0,0,6" AutomationProperties.Name="App Removal Scope">
|
||||
<ComboBoxItem x:Name="AppRemovalScopeAllUsers" Content="All users" IsSelected="True"/>
|
||||
<ComboBoxItem x:Name="AppRemovalScopeCurrentUser" Content="Current user only"/>
|
||||
<ComboBoxItem x:Name="AppRemovalScopeTargetUser" Content="Target user only" Visibility="Collapsed"/>
|
||||
<TextBlock Text="%LANG:DeployAppRemovalScopeTitle%" Style="{StaticResource CategoryHeaderTextBlock}"/>
|
||||
<ComboBox x:Name="AppRemovalScopeCombo" Margin="0,0,0,6" AutomationProperties.Name="%LANG:DeployAppRemovalScopeAutomationName%">
|
||||
<ComboBoxItem x:Name="AppRemovalScopeAllUsers" Content="%LANG:DeployAppRemovalScopeAllUsers%" IsSelected="True"/>
|
||||
<ComboBoxItem x:Name="AppRemovalScopeCurrentUser" Content="%LANG:DeployAppRemovalScopeCurrentUser%"/>
|
||||
<ComboBoxItem x:Name="AppRemovalScopeTargetUser" Content="%LANG:DeployAppRemovalScopeTargetUser%" Visibility="Collapsed"/>
|
||||
</ComboBox>
|
||||
<TextBlock x:Name="AppRemovalScopeDescription" Text="Apps will be removed for all users and from the Windows image to prevent reinstallation for new users." Foreground="{DynamicResource AppFgColor}" FontSize="13" TextWrapping="Wrap" Margin="0,6,0,3"/>
|
||||
<TextBlock x:Name="AppRemovalScopeDescription" Text="%LANG:AppRemovalScopeDescriptionAllUsers%" Foreground="{DynamicResource AppFgColor}" FontSize="13" TextWrapping="Wrap" Margin="0,6,0,3"/>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
<!-- Options -->
|
||||
<Border Grid.Row="2" BorderBrush="{DynamicResource AppBorderColor}" BorderThickness="1" CornerRadius="4" Background="{DynamicResource CardBgColor}" Padding="16,12,16,3" Margin="0,0,0,16">
|
||||
<StackPanel>
|
||||
<TextBlock Text="Options" Style="{StaticResource CategoryHeaderTextBlock}"/>
|
||||
<TextBlock Text="%LANG:DeployOptionsTitle%" Style="{StaticResource CategoryHeaderTextBlock}"/>
|
||||
|
||||
<StackPanel>
|
||||
<CheckBox x:Name="RegistryBackupCheckBox" Style="{DynamicResource FeatureCheckboxStyle}" IsChecked="True" Content="Create a registry backup (Recommended)" AutomationProperties.Name="Create a registry backup (Recommended)"/>
|
||||
<CheckBox x:Name="RegistryBackupCheckBox" Style="{DynamicResource FeatureCheckboxStyle}" IsChecked="True" Content="%LANG:DeployOptionRegistryBackup%" AutomationProperties.Name="%LANG:DeployOptionRegistryBackup%"/>
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel>
|
||||
<CheckBox x:Name="RestorePointCheckBox" Style="{DynamicResource FeatureCheckboxStyle}" IsChecked="True" Content="Create a system restore point (Recommended)" AutomationProperties.Name="Create a system restore point (Recommended)"/>
|
||||
<CheckBox x:Name="RestorePointCheckBox" Style="{DynamicResource FeatureCheckboxStyle}" IsChecked="True" Content="%LANG:DeployOptionRestorePoint%" AutomationProperties.Name="%LANG:DeployOptionRestorePoint%"/>
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel>
|
||||
<CheckBox x:Name="RestartExplorerCheckBox" Style="{DynamicResource FeatureCheckboxStyle}" Content="Restart the Windows Explorer process to apply all changes immediately" AutomationProperties.Name="Restart the Windows Explorer process to apply all changes immediately"/>
|
||||
<CheckBox x:Name="RestartExplorerCheckBox" Style="{DynamicResource FeatureCheckboxStyle}" Content="%LANG:DeployOptionRestartExplorer%" AutomationProperties.Name="%LANG:DeployOptionRestartExplorer%"/>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
@@ -971,10 +971,10 @@
|
||||
|
||||
<!-- Review & Apply Section -->
|
||||
<StackPanel Grid.Row="1" HorizontalAlignment="Stretch" Background="{DynamicResource AppBgColor}">
|
||||
<Button x:Name="ReviewChangesBtn" Background="Transparent" BorderThickness="0" Cursor="Hand" HorizontalAlignment="Center" Margin="0,4,0,10" AutomationProperties.Name="Review selected changes">
|
||||
<Button x:Name="ReviewChangesBtn" Background="Transparent" BorderThickness="0" Cursor="Hand" HorizontalAlignment="Center" Margin="0,4,0,10" AutomationProperties.Name="%LANG:DeployReviewChanges%">
|
||||
<Button.Template>
|
||||
<ControlTemplate TargetType="Button">
|
||||
<TextBlock x:Name="LinkText" Text="Review selected changes" FontSize="16" Foreground="{DynamicResource ButtonBgColor}" FontWeight="SemiBold" HorizontalAlignment="Center"/>
|
||||
<TextBlock x:Name="LinkText" Text="%LANG:DeployReviewChanges%" FontSize="16" Foreground="{DynamicResource ButtonBgColor}" FontWeight="SemiBold" HorizontalAlignment="Center"/>
|
||||
<ControlTemplate.Triggers>
|
||||
<Trigger Property="IsMouseOver" Value="True">
|
||||
<Setter TargetName="LinkText" Property="Foreground" Value="{DynamicResource ButtonHoverColor}"/>
|
||||
@@ -986,10 +986,10 @@
|
||||
</ControlTemplate>
|
||||
</Button.Template>
|
||||
</Button>
|
||||
<Button x:Name="DeploymentApplyBtn" Style="{DynamicResource PrimaryButtonStyle}" Width="200" Height="44" HorizontalAlignment="Center" AutomationProperties.Name="Apply Changes">
|
||||
<Button x:Name="DeploymentApplyBtn" Style="{DynamicResource PrimaryButtonStyle}" Width="200" Height="44" HorizontalAlignment="Center" AutomationProperties.Name="%LANG:DeployApplyChanges%">
|
||||
<StackPanel Orientation="Horizontal" VerticalAlignment="Center">
|
||||
<TextBlock Text="" FontFamily="{DynamicResource AppIconFontFamily}" FontSize="20" FontWeight="SemiBold" VerticalAlignment="Center"/>
|
||||
<TextBlock Text="Apply Changes" VerticalAlignment="Center" FontSize="18" FontWeight="SemiBold" Margin="8,0,0,4"/>
|
||||
<TextBlock Text="%LANG:DeployApplyChanges%" VerticalAlignment="Center" FontSize="18" FontWeight="SemiBold" Margin="8,0,0,4"/>
|
||||
</StackPanel>
|
||||
</Button>
|
||||
</StackPanel>
|
||||
@@ -1006,17 +1006,17 @@
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Button x:Name="PreviousBtn" Grid.Column="0" Width="120" Height="36" Style="{DynamicResource SecondaryButtonStyle}" Visibility="Collapsed" AutomationProperties.Name="Back">
|
||||
<Button x:Name="PreviousBtn" Grid.Column="0" Width="120" Height="36" Style="{DynamicResource SecondaryButtonStyle}" Visibility="Collapsed" AutomationProperties.Name="%LANG:NavBack%">
|
||||
<StackPanel Orientation="Horizontal" VerticalAlignment="Center">
|
||||
<TextBlock Text="" FontFamily="{DynamicResource AppIconFontFamily}" FontSize="12" Margin="0,0,8,0" VerticalAlignment="Center"/>
|
||||
<TextBlock Text="Back" VerticalAlignment="Center" FontSize="14" Margin="0,0,0,3"/>
|
||||
<TextBlock Text="%LANG:NavBack%" VerticalAlignment="Center" FontSize="14" Margin="0,0,0,3"/>
|
||||
</StackPanel>
|
||||
</Button>
|
||||
|
||||
<StackPanel Grid.Column="2" Orientation="Horizontal" HorizontalAlignment="Right">
|
||||
<Button x:Name="NextBtn" Width="120" Height="36" Style="{DynamicResource PrimaryButtonStyle}" AutomationProperties.Name="Next">
|
||||
<Button x:Name="NextBtn" Width="120" Height="36" Style="{DynamicResource PrimaryButtonStyle}" AutomationProperties.Name="%LANG:NavNext%">
|
||||
<StackPanel Orientation="Horizontal" VerticalAlignment="Center">
|
||||
<TextBlock Text="Next" VerticalAlignment="Center" FontSize="14" Margin="0,0,0,3"/>
|
||||
<TextBlock Text="%LANG:NavNext%" VerticalAlignment="Center" FontSize="14" Margin="0,0,0,3"/>
|
||||
<TextBlock Text="" FontFamily="{DynamicResource AppIconFontFamily}" FontSize="12" Margin="8,0,0,0" VerticalAlignment="Center"/>
|
||||
</StackPanel>
|
||||
</Button>
|
||||
@@ -1025,9 +1025,9 @@
|
||||
|
||||
<!-- Progress Indicators - Centered independently -->
|
||||
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center" VerticalAlignment="Center">
|
||||
<Ellipse x:Name="ProgressIndicator1" Width="10" Height="10" Margin="4,0" Fill="#808080" ToolTip="App Removal"/>
|
||||
<Ellipse x:Name="ProgressIndicator2" Width="10" Height="10" Margin="4,0" Fill="#808080" ToolTip="System Tweaks"/>
|
||||
<Ellipse x:Name="ProgressIndicator3" Width="10" Height="10" Margin="4,0" Fill="#808080" ToolTip="Deployment Settings"/>
|
||||
<Ellipse x:Name="ProgressIndicator1" Width="10" Height="10" Margin="4,0" Fill="#808080" ToolTip="%LANG:NavProgressAppRemoval%"/>
|
||||
<Ellipse x:Name="ProgressIndicator2" Width="10" Height="10" Margin="4,0" Fill="#808080" ToolTip="%LANG:NavProgressSystemTweaks%"/>
|
||||
<Ellipse x:Name="ProgressIndicator3" Width="10" Height="10" Margin="4,0" Fill="#808080" ToolTip="%LANG:NavProgressDeploymentSettings%"/>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
Title="MessageBox"
|
||||
Title="%LANG:TitleBarAppName%"
|
||||
Width="440"
|
||||
SizeToContent="Height"
|
||||
MaxHeight="501"
|
||||
@@ -34,8 +34,8 @@
|
||||
|
||||
<!-- Title Bar -->
|
||||
<Grid Grid.Row="0" x:Name="TitleBar" Style="{DynamicResource ModalTitleBarStyle}">
|
||||
<TextBlock x:Name="TitleText"
|
||||
Text="Message"
|
||||
<TextBlock x:Name="TitleText"
|
||||
Text="%LANG:TitleBarAppName%"
|
||||
Style="{DynamicResource ModalTitleTextStyle}"/>
|
||||
</Grid>
|
||||
|
||||
@@ -84,11 +84,11 @@
|
||||
|
||||
<Button x:Name="Button1"
|
||||
Grid.Column="0"
|
||||
Content="OK"
|
||||
Content="%LANG:MessageBoxOk%"
|
||||
Style="{DynamicResource ModalPrimaryStretchedButtonStyle}"/>
|
||||
<Button x:Name="Button2"
|
||||
Grid.Column="1"
|
||||
Content="Cancel"
|
||||
Content="%LANG:MessageBoxCancel%"
|
||||
Style="{DynamicResource ModalSecondaryStretchedButtonStyle}"
|
||||
Visibility="Collapsed"/>
|
||||
</Grid>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
Title="Restore Backup"
|
||||
Title="%LANG:RestoreWindowTitle%"
|
||||
Width="500"
|
||||
SizeToContent="Height"
|
||||
MaxHeight="560"
|
||||
@@ -63,7 +63,7 @@
|
||||
|
||||
<Grid Grid.Row="0" x:Name="TitleBar" Style="{DynamicResource ModalTitleBarStyle}">
|
||||
<TextBlock x:Name="TitleText"
|
||||
Text="Restore Backup"
|
||||
Text="%LANG:RestoreWindowTitle%"
|
||||
Style="{DynamicResource ModalTitleTextStyle}"/>
|
||||
</Grid>
|
||||
|
||||
@@ -72,8 +72,8 @@
|
||||
Width="36" Height="32"
|
||||
BorderThickness="0"
|
||||
Cursor="Hand"
|
||||
ToolTip="Close"
|
||||
AutomationProperties.Name="Close">
|
||||
ToolTip="%LANG:TitleBarClose%"
|
||||
AutomationProperties.Name="%LANG:TitleBarClose%">
|
||||
<Button.Template>
|
||||
<ControlTemplate TargetType="Button">
|
||||
<Border Background="{TemplateBinding Background}" BorderThickness="0" CornerRadius="0,8,0,0">
|
||||
@@ -122,7 +122,7 @@
|
||||
FontSize="14"
|
||||
LineHeight="20"
|
||||
Foreground="{DynamicResource AppFgColor}"
|
||||
Text="Choose what changes you want to restore."/>
|
||||
Text="%LANG:RestoreSelectTypePrompt%"/>
|
||||
|
||||
<Button x:Name="ChooseRegistryBtn"
|
||||
Grid.Row="1"
|
||||
@@ -142,10 +142,10 @@
|
||||
<StackPanel Grid.Column="1"
|
||||
VerticalAlignment="Center"
|
||||
Margin="0,0,12,0">
|
||||
<TextBlock Text="Restore Registry Backup"
|
||||
<TextBlock Text="%LANG:RestoreChooseRegistryTitle%"
|
||||
FontSize="16"
|
||||
FontWeight="SemiBold"/>
|
||||
<TextBlock Text="Restore system registry configuration from a backup"
|
||||
<TextBlock Text="%LANG:RestoreChooseRegistryDescription%"
|
||||
FontSize="12"
|
||||
Opacity="0.75"
|
||||
TextWrapping="Wrap"
|
||||
@@ -172,10 +172,10 @@
|
||||
<StackPanel Grid.Column="1"
|
||||
VerticalAlignment="Center"
|
||||
Margin="0,0,12,0">
|
||||
<TextBlock Text="Restore Start Menu Backup"
|
||||
<TextBlock Text="%LANG:RestoreChooseStartMenuTitle%"
|
||||
FontSize="16"
|
||||
FontWeight="SemiBold"/>
|
||||
<TextBlock Text="Restore the Start Menu pinned apps layout from a backup"
|
||||
<TextBlock Text="%LANG:RestoreChooseStartMenuDescription%"
|
||||
FontSize="12"
|
||||
Opacity="0.75"
|
||||
TextWrapping="Wrap"
|
||||
@@ -204,7 +204,7 @@
|
||||
FontSize="14"
|
||||
LineHeight="20"
|
||||
Foreground="{DynamicResource AppFgColor}"
|
||||
Text="This will restore any system registry changes made by Win11Debloat to the previous state. You can review the changes after selecting a backup file. Removed applications will need to be reinstalled manually."/>
|
||||
Text="%LANG:RestoreRegistryIntro%"/>
|
||||
|
||||
<TextBlock Grid.Row="1"
|
||||
Margin="0,12,0,2"
|
||||
@@ -212,7 +212,7 @@
|
||||
FontSize="13"
|
||||
Foreground="{DynamicResource AppFgColor}"
|
||||
Opacity="0.75"
|
||||
Text="Warning: Only use backup files generated by Win11Debloat."/>
|
||||
Text="%LANG:RestoreRegistryWarning%"/>
|
||||
</Grid>
|
||||
|
||||
<Grid x:Name="OverviewPanel" Grid.Row="1" Margin="0" Visibility="Collapsed">
|
||||
@@ -237,14 +237,14 @@
|
||||
<RowDefinition Height="Auto"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<TextBlock Grid.Row="0" Grid.Column="0" Text="File:" Style="{DynamicResource ModalInfoLabelTextStyle}"/>
|
||||
<TextBlock x:Name="BackupFileText" Grid.Row="0" Grid.Column="1" Text="Not selected" Style="{DynamicResource ModalInfoValueTextStyle}" TextWrapping="Wrap" Margin="0,0,0,8"/>
|
||||
<TextBlock Grid.Row="0" Grid.Column="0" Text="%LANG:RestoreFileLabel%" Style="{DynamicResource ModalInfoLabelTextStyle}"/>
|
||||
<TextBlock x:Name="BackupFileText" Grid.Row="0" Grid.Column="1" Text="%LANG:RestoreFileNotSelected%" Style="{DynamicResource ModalInfoValueTextStyle}" TextWrapping="Wrap" Margin="0,0,0,8"/>
|
||||
|
||||
<TextBlock Grid.Row="1" Grid.Column="0" Text="Created:" Style="{DynamicResource ModalInfoLabelTextStyle}"/>
|
||||
<TextBlock x:Name="BackupCreatedText" Grid.Row="1" Grid.Column="1" Text="N/A" Style="{DynamicResource ModalInfoValueTextStyle}"/>
|
||||
<TextBlock Grid.Row="1" Grid.Column="0" Text="%LANG:RestoreCreatedLabel%" Style="{DynamicResource ModalInfoLabelTextStyle}"/>
|
||||
<TextBlock x:Name="BackupCreatedText" Grid.Row="1" Grid.Column="1" Text="%LANG:RestoreCreatedUnavailable%" Style="{DynamicResource ModalInfoValueTextStyle}"/>
|
||||
|
||||
<TextBlock Grid.Row="2" Grid.Column="0" Text="Target:" Style="{DynamicResource ModalInfoLabelTextStyle}" Margin="0,0,16,0"/>
|
||||
<TextBlock x:Name="BackupTargetText" Grid.Row="2" Grid.Column="1" Text="N/A" Style="{DynamicResource ModalInfoValueTextStyle}" Margin="0"/>
|
||||
<TextBlock Grid.Row="2" Grid.Column="0" Text="%LANG:RestoreTargetLabel%" Style="{DynamicResource ModalInfoLabelTextStyle}" Margin="0,0,16,0"/>
|
||||
<TextBlock x:Name="BackupTargetText" Grid.Row="2" Grid.Column="1" Text="%LANG:RestoreCreatedUnavailable%" Style="{DynamicResource ModalInfoValueTextStyle}" Margin="0"/>
|
||||
</Grid>
|
||||
|
||||
<Border Grid.Row="1" Height="1" Background="{DynamicResource AppBorderColor}" Margin="0,0,0,12"/>
|
||||
@@ -256,7 +256,7 @@
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<TextBlock Grid.Row="0"
|
||||
Text="The following changes will be reverted:"
|
||||
Text="%LANG:RestoreRevertibleFeaturesTitle%"
|
||||
FontSize="13"
|
||||
Foreground="{DynamicResource AppFgColor}"
|
||||
FontWeight="SemiBold"
|
||||
@@ -282,7 +282,7 @@
|
||||
Foreground="{DynamicResource AppFgColor}"
|
||||
TextWrapping="Wrap"
|
||||
Visibility="Collapsed"
|
||||
Text="This will restore the Start Menu pinned apps layout for the current user."/>
|
||||
Text="%LANG:RestoreStartMenuSummary%"/>
|
||||
|
||||
<Border x:Name="ReappliedSeparator" Grid.Row="3" Height="1" Background="{DynamicResource AppBorderColor}" Margin="0,12,0,12" Visibility="Collapsed"/>
|
||||
|
||||
@@ -293,7 +293,7 @@
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<TextBlock Grid.Row="0"
|
||||
Text="The following changes will be re-applied:"
|
||||
Text="%LANG:RestoreReappliedFeaturesTitle%"
|
||||
FontSize="13"
|
||||
Foreground="{DynamicResource AppFgColor}"
|
||||
FontWeight="SemiBold"
|
||||
@@ -323,7 +323,7 @@
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<TextBlock Grid.Row="0"
|
||||
Text="The following changes won't be reverted:"
|
||||
Text="%LANG:RestoreNonRevertibleFeaturesTitle%"
|
||||
FontSize="13"
|
||||
Foreground="{DynamicResource AppFgColor}"
|
||||
FontWeight="SemiBold"
|
||||
@@ -345,7 +345,7 @@
|
||||
<TextBlock x:Name="NonRevertibleWikiLink"
|
||||
Grid.Row="2"
|
||||
Margin="0,8,0,0"
|
||||
Text="Visit the wiki for more information"
|
||||
Text="%LANG:RestoreWikiLink%"
|
||||
FontSize="13"
|
||||
FontWeight="SemiBold"
|
||||
Style="{DynamicResource HyperlinkStyle}"
|
||||
@@ -374,7 +374,7 @@
|
||||
LineHeight="20"
|
||||
Margin="0,0,0,12"
|
||||
Foreground="{DynamicResource AppFgColor}"
|
||||
Text="This will restore the Start Menu pinned apps layout for the selected user(s) using a backup that is automatically created by Win11Debloat. Manually created backups can also be used."/>
|
||||
Text="%LANG:RestoreStartMenuIntro%"/>
|
||||
|
||||
<ComboBox x:Name="StartMenuScopeCombo"
|
||||
Grid.Row="1"
|
||||
@@ -382,13 +382,13 @@
|
||||
Margin="0,0,0,2"
|
||||
MinWidth="408"
|
||||
HorizontalAlignment="Left">
|
||||
<ComboBoxItem Content="Current user" Tag="CurrentUser"/>
|
||||
<ComboBoxItem Content="All users" Tag="AllUsers"/>
|
||||
<ComboBoxItem Content="%LANG:RestoreStartMenuScopeCurrentUser%" Tag="CurrentUser"/>
|
||||
<ComboBoxItem Content="%LANG:RestoreStartMenuScopeAllUsers%" Tag="AllUsers"/>
|
||||
</ComboBox>
|
||||
|
||||
<CheckBox x:Name="StartMenuAutoBackupCheck"
|
||||
Grid.Row="2"
|
||||
Content="Automatically find Start Menu backup"
|
||||
Content="%LANG:RestoreStartMenuAutoBackupCheckbox%"
|
||||
IsChecked="True"
|
||||
FontSize="12"
|
||||
Margin="0,10,0,0"
|
||||
@@ -411,13 +411,13 @@
|
||||
|
||||
<Button x:Name="PrimaryActionBtn"
|
||||
Grid.Column="0"
|
||||
Content="Next"
|
||||
Content="%LANG:NavNext%"
|
||||
Visibility="Collapsed"
|
||||
Style="{DynamicResource ModalPrimaryStretchedButtonStyle}"/>
|
||||
|
||||
<Button x:Name="BackBtn"
|
||||
Grid.Column="1"
|
||||
Content="Back"
|
||||
Content="%LANG:RestoreBackBtnBack%"
|
||||
Visibility="Collapsed"
|
||||
Style="{DynamicResource ModalSecondaryStretchedButtonStyle}"/>
|
||||
</Grid>
|
||||
|
||||
@@ -50,7 +50,7 @@ function Remove-SelectedApps {
|
||||
$appIndex++
|
||||
|
||||
if ($script:ApplySubStepCallback -and $appCount -gt 1) {
|
||||
& $script:ApplySubStepCallback "Removing apps ($appIndex/$appCount)" $appIndex $appCount
|
||||
& $script:ApplySubStepCallback (Get-Translation -Key 'RemovingAppsSubStep' -FormatArgs @($appIndex, $appCount)) $appIndex $appCount
|
||||
}
|
||||
|
||||
Write-Host "Removing $app"
|
||||
@@ -329,7 +329,7 @@ function Get-AppRemovalMethod {
|
||||
#>
|
||||
function Request-EdgeForceRemove {
|
||||
if ($script:GuiWindow) {
|
||||
$result = Show-MessageBox -Message 'Unable to uninstall Microsoft Edge via WinGet. Would you like to forcefully uninstall it? NOT RECOMMENDED!' -Title 'Force Uninstall Microsoft Edge?' -Button 'YesNo' -Icon 'Warning'
|
||||
$result = Show-MessageBox -Message (Get-Translation -Key 'ForceRemoveEdgeMessage') -Title (Get-Translation -Key 'ForceRemoveEdgeTitle') -Button 'YesNo' -Icon 'Warning'
|
||||
if ($result -eq 'Yes') {
|
||||
Write-Host ""
|
||||
return (Invoke-ForceRemoveEdge)
|
||||
|
||||
@@ -265,7 +265,7 @@ function Invoke-ApplyFeatures {
|
||||
|
||||
# Resolve display name for the progress indicator
|
||||
$f = $script:Features[$featureId]
|
||||
$displayName = $f.ApplyText
|
||||
$displayName = Get-Translation -Key $featureId -Field 'ApplyText' -Section 'Features'
|
||||
|
||||
if ($script:ApplyProgressCallback) {
|
||||
& $script:ApplyProgressCallback $step $TotalSteps $displayName
|
||||
@@ -309,8 +309,8 @@ function Invoke-UndoFeatures {
|
||||
if ($script:CancelRequested) { return }
|
||||
|
||||
$f = if ($script:Features.ContainsKey($featureId)) { $script:Features[$featureId] } else { $null }
|
||||
$undoLabel = if ($f -and $f.UndoLabel) { $f.UndoLabel } else { $featureId }
|
||||
$undoText = if ($f -and $f.ApplyUndoText) { $f.ApplyUndoText } else { $undoLabel }
|
||||
$undoLabel = if ($f -and $f.UndoLabel) { Get-Translation -Key $featureId -Field 'UndoLabel' -Section 'Features' } else { $featureId }
|
||||
$undoText = if ($f -and $f.ApplyUndoText) { Get-Translation -Key $featureId -Field 'ApplyUndoText' -Section 'Features' } else { $undoLabel }
|
||||
|
||||
if ($script:ApplyProgressCallback) {
|
||||
& $script:ApplyProgressCallback $step $TotalSteps $undoText
|
||||
@@ -391,7 +391,7 @@ function Invoke-AllChanges {
|
||||
if ($script:CancelRequested) { return }
|
||||
$step++
|
||||
if ($script:ApplyProgressCallback) {
|
||||
& $script:ApplyProgressCallback $step $totalSteps "Creating registry backup..."
|
||||
& $script:ApplyProgressCallback $step $totalSteps (Get-Translation -Key 'ApplyCreatingRegistryBackup')
|
||||
}
|
||||
|
||||
if ($script:Params.ContainsKey("WhatIf")) {
|
||||
@@ -421,7 +421,7 @@ function Invoke-AllChanges {
|
||||
if ($script:CancelRequested) { return }
|
||||
$step++
|
||||
if ($script:ApplyProgressCallback) {
|
||||
& $script:ApplyProgressCallback $step $totalSteps "Creating system restore point, this may take a moment..."
|
||||
& $script:ApplyProgressCallback $step $totalSteps (Get-Translation -Key 'ApplyCreatingRestorePoint')
|
||||
}
|
||||
if ($script:Params.ContainsKey("WhatIf")) {
|
||||
Write-Host "[WhatIf] Create system restore point" -ForegroundColor Cyan
|
||||
|
||||
@@ -89,7 +89,7 @@ function Invoke-SystemRestorePoint {
|
||||
# Ensure that the user is aware if creating a restore point failed, and give them the option to continue without a restore point or cancel the script
|
||||
if ($failed) {
|
||||
if ($script:GuiWindow) {
|
||||
$result = Show-MessageBox "Failed to create a system restore point. Do you want to continue without a restore point?" "Restore Point Creation Failed" "YesNo" "Warning"
|
||||
$result = Show-MessageBox (Get-Translation -Key 'RestorePointFailedMessage') (Get-Translation -Key 'RestorePointFailedTitle') "YesNo" "Warning"
|
||||
|
||||
if ($result -ne "Yes") {
|
||||
$script:CancelRequested = $true
|
||||
|
||||
@@ -1,3 +1,11 @@
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Extracts and deduplicates the SelectedFeatures list from a backup, validating each entry.
|
||||
|
||||
.DESCRIPTION
|
||||
Returns an empty SelectedFeatures array with an error when the property is missing
|
||||
entirely, and flags (without stopping) any entry that isn't a non-empty string.
|
||||
#>
|
||||
function Get-NormalizedSelectedFeatureIdsFromBackup {
|
||||
param(
|
||||
[Parameter(Mandatory)]
|
||||
@@ -10,7 +18,7 @@ function Get-NormalizedSelectedFeatureIdsFromBackup {
|
||||
$hasInvalidSelectedFeatureId = $false
|
||||
|
||||
if (-not $Backup.PSObject.Properties['SelectedFeatures']) {
|
||||
$errors.Add('Missing property: SelectedFeatures')
|
||||
$errors.Add((Get-Translation -Key 'BackupMissingProperty' -FormatArgs @('SelectedFeatures')))
|
||||
return [PSCustomObject]@{
|
||||
SelectedFeatures = $selectedFeatures.ToArray()
|
||||
Errors = $errors.ToArray()
|
||||
@@ -30,7 +38,7 @@ function Get-NormalizedSelectedFeatureIdsFromBackup {
|
||||
}
|
||||
|
||||
if ($hasInvalidSelectedFeatureId) {
|
||||
$errors.Add('SelectedFeatures must contain non-empty string feature IDs.')
|
||||
$errors.Add((Get-Translation -Key 'BackupSelectedFeaturesMustBeStrings'))
|
||||
}
|
||||
|
||||
return [PSCustomObject]@{
|
||||
@@ -39,6 +47,14 @@ function Get-NormalizedSelectedFeatureIdsFromBackup {
|
||||
}
|
||||
}
|
||||
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Extracts and deduplicates the SelectedUndoFeatures list from a backup, validating each entry.
|
||||
|
||||
.DESCRIPTION
|
||||
Unlike SelectedFeatures, this property is optional: a missing property returns an
|
||||
empty array with no error, since not every backup has undone features to restore.
|
||||
#>
|
||||
function Get-NormalizedSelectedUndoFeatureIdsFromBackup {
|
||||
param(
|
||||
[Parameter(Mandatory)]
|
||||
@@ -71,7 +87,7 @@ function Get-NormalizedSelectedUndoFeatureIdsFromBackup {
|
||||
}
|
||||
|
||||
if ($hasInvalidSelectedUndoFeatureId) {
|
||||
$errors.Add('SelectedUndoFeatures must contain non-empty string feature IDs.')
|
||||
$errors.Add((Get-Translation -Key 'BackupSelectedUndoFeaturesMustBeStrings'))
|
||||
}
|
||||
|
||||
return [PSCustomObject]@{
|
||||
@@ -80,6 +96,15 @@ function Get-NormalizedSelectedUndoFeatureIdsFromBackup {
|
||||
}
|
||||
}
|
||||
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Normalizes a raw registry key snapshot into a consistent shape, recursing into sub-keys.
|
||||
|
||||
.DESCRIPTION
|
||||
Fills in defaults for optional properties (Exists, Values, SubKeys) so downstream
|
||||
validation can read a predictable shape regardless of what the backup file omitted.
|
||||
Throws if Path is missing, since every snapshot must be anchored to a real key.
|
||||
#>
|
||||
function Normalize-RegistryKeySnapshot {
|
||||
param(
|
||||
[Parameter(Mandatory)]
|
||||
@@ -87,7 +112,7 @@ function Normalize-RegistryKeySnapshot {
|
||||
)
|
||||
|
||||
if (-not $Snapshot.PSObject.Properties['Path'] -or [string]::IsNullOrWhiteSpace([string]$Snapshot.Path)) {
|
||||
throw 'Backup validation failed: Registry key snapshot is missing Path.'
|
||||
throw (Get-Translation -Key 'BackupSnapshotMissingPath')
|
||||
}
|
||||
|
||||
$exists = $false
|
||||
@@ -127,6 +152,16 @@ function Normalize-RegistryKeySnapshot {
|
||||
}
|
||||
}
|
||||
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Validates that a backup's registry snapshots only contain paths and values the
|
||||
selected (and undo) features are actually allowed to touch.
|
||||
|
||||
.DESCRIPTION
|
||||
Builds an allow-list from the capture plans of the selected features, then checks
|
||||
every snapshot against it. Returns an array of translated error strings; an empty
|
||||
array means the backup is valid.
|
||||
#>
|
||||
function Test-RegistryBackupMatchesSelectedFeatures {
|
||||
param(
|
||||
[Parameter(Mandatory)]
|
||||
@@ -145,7 +180,7 @@ function Test-RegistryBackupMatchesSelectedFeatures {
|
||||
$errors = New-Object System.Collections.Generic.List[string]
|
||||
|
||||
if (-not $script:Features -or $script:Features.Count -eq 0) {
|
||||
$errors.Add('Unable to validate registry backup allowlist because feature definitions are not loaded.')
|
||||
$errors.Add((Get-Translation -Key 'BackupFeatureCatalogNotLoaded'))
|
||||
return $errors.ToArray()
|
||||
}
|
||||
|
||||
@@ -161,7 +196,7 @@ function Test-RegistryBackupMatchesSelectedFeatures {
|
||||
$planMap = New-RegistryBackupAllowListPlanMap -CapturePlans @($capturePlans)
|
||||
|
||||
if ($planMap.Count -eq 0 -and @($RegistryKeys).Count -gt 0) {
|
||||
$errors.Add('Backup contains registry snapshots but no allowed registry paths were derived from the selected features.')
|
||||
$errors.Add((Get-Translation -Key 'BackupNoAllowedRegistryPaths'))
|
||||
}
|
||||
|
||||
foreach ($rootSnapshot in @($RegistryKeys)) {
|
||||
@@ -171,6 +206,15 @@ function Test-RegistryBackupMatchesSelectedFeatures {
|
||||
return $errors.ToArray()
|
||||
}
|
||||
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Resolves selected feature IDs to their catalog entries, keeping only the ones with a
|
||||
registry-backed apply or undo key relevant to the requested direction.
|
||||
|
||||
.DESCRIPTION
|
||||
Appends a translated error to the caller-supplied Errors list for any feature ID that
|
||||
isn't in the current catalog, rather than failing the whole validation outright.
|
||||
#>
|
||||
function Get-SelectedRegistryFeaturesForBackupValidation {
|
||||
param(
|
||||
[Parameter(Mandatory)]
|
||||
@@ -186,11 +230,13 @@ function Get-SelectedRegistryFeaturesForBackupValidation {
|
||||
if ($null -eq $Errors -or -not ($Errors -is [System.Collections.IList])) {
|
||||
throw 'Get-SelectedRegistryFeaturesForBackupValidation requires Errors to be a mutable list collection.'
|
||||
}
|
||||
# Intentionally not localized: this throw signals a programming error (a caller passing the
|
||||
# wrong collection type), not a condition a user's backup file can trigger.
|
||||
|
||||
$selectedRegistryFeatures = New-Object System.Collections.Generic.List[object]
|
||||
foreach ($featureId in @($SelectedFeatureIds)) {
|
||||
if (-not $script:Features.ContainsKey($featureId)) {
|
||||
$Errors.Add("Selected feature '$featureId' was not found in the current feature catalog.")
|
||||
$Errors.Add((Get-Translation -Key 'BackupFeatureNotInCatalog' -FormatArgs @($featureId)))
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -293,13 +339,13 @@ function Test-RegistrySnapshotAgainstAllowList {
|
||||
$snapshotPath = [string]$Snapshot.Path
|
||||
$normalizedPath = Get-NormalizedRegistryPathKey -Path $snapshotPath
|
||||
if ([string]::IsNullOrWhiteSpace($normalizedPath)) {
|
||||
$Errors.Add("Backup contains unsupported registry path '$snapshotPath'.")
|
||||
$Errors.Add((Get-Translation -Key 'BackupUnsupportedRegistryPath' -FormatArgs @($snapshotPath)))
|
||||
return
|
||||
}
|
||||
|
||||
$planMatch = Find-RegistryAllowListPlanMatch -NormalizedPath $normalizedPath -PlanMap $PlanMap
|
||||
if ($null -eq $planMatch) {
|
||||
$Errors.Add("Backup contains unexpected registry path '$snapshotPath' that is not allowed by SelectedFeatures.")
|
||||
$Errors.Add((Get-Translation -Key 'BackupUnexpectedRegistryPath' -FormatArgs @($snapshotPath)))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -308,21 +354,21 @@ function Test-RegistrySnapshotAgainstAllowList {
|
||||
$valueExists = [bool]$valueSnapshot.Exists
|
||||
|
||||
if (-not (Test-RegistryValueAllowedByPlan -PlanMatch $planMatch -ValueName $valueName)) {
|
||||
$Errors.Add("Backup contains unexpected value '$valueName' under '$snapshotPath'.")
|
||||
$Errors.Add((Get-Translation -Key 'BackupUnexpectedValue' -FormatArgs @($valueName, $snapshotPath)))
|
||||
}
|
||||
|
||||
$kindName = if ($valueSnapshot.PSObject.Properties['Kind']) { [string]$valueSnapshot.Kind } else { '' }
|
||||
$valueReference = Get-RegistryValueReferenceForError -SnapshotPath $snapshotPath -ValueName $valueName
|
||||
if ($valueExists) {
|
||||
if (-not (Test-RegistryValueKindNameSupported -KindName $kindName)) {
|
||||
$Errors.Add("Backup contains unsupported registry value kind '$kindName' for '$valueReference'.")
|
||||
$Errors.Add((Get-Translation -Key 'BackupUnsupportedValueKind' -FormatArgs @($kindName, $valueReference)))
|
||||
}
|
||||
elseif (-not (Test-RegistryValueDataMatchesKind -KindName $kindName -Data $valueSnapshot.Data)) {
|
||||
$Errors.Add("Backup contains invalid registry data for kind '$kindName' at '$valueReference'.")
|
||||
$Errors.Add((Get-Translation -Key 'BackupInvalidValueData' -FormatArgs @($kindName, $valueReference)))
|
||||
}
|
||||
}
|
||||
elseif (-not [string]::IsNullOrWhiteSpace($kindName)) {
|
||||
$Errors.Add("Backup value '$valueReference' must not define Kind when Exists is false.")
|
||||
$Errors.Add((Get-Translation -Key 'BackupValueMustNotDefineKind' -FormatArgs @($valueReference)))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -319,7 +319,7 @@ function Restore-StartMenuFromBackup {
|
||||
return [PSCustomObject]@{
|
||||
UserName = $userName
|
||||
Result = $false
|
||||
Message = "No start menu backup file found for user $userName."
|
||||
Message = Get-Translation -Key 'StartMenuNoBackupFoundForUser' -FormatArgs @($userName)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -328,7 +328,7 @@ function Restore-StartMenuFromBackup {
|
||||
return [PSCustomObject]@{
|
||||
UserName = $userName
|
||||
Result = $true
|
||||
Message = "[WhatIf] Restored start menu for user $userName."
|
||||
Message = Get-Translation -Key 'StartMenuWhatIfRestoredForUser' -FormatArgs @($userName)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -336,7 +336,7 @@ function Restore-StartMenuFromBackup {
|
||||
return [PSCustomObject]@{
|
||||
UserName = $userName
|
||||
Result = $false
|
||||
Message = "No start menu backup file found for user $userName."
|
||||
Message = Get-Translation -Key 'StartMenuNoBackupFoundForUser' -FormatArgs @($userName)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -349,14 +349,14 @@ function Restore-StartMenuFromBackup {
|
||||
return [PSCustomObject]@{
|
||||
UserName = $userName
|
||||
Result = $true
|
||||
Message = "Restored start menu for user $userName."
|
||||
Message = Get-Translation -Key 'StartMenuRestoredForUser' -FormatArgs @($userName)
|
||||
}
|
||||
}
|
||||
catch {
|
||||
return [PSCustomObject]@{
|
||||
UserName = $userName
|
||||
Result = $false
|
||||
Message = "Failed to restore start menu for user $userName. $($_.Exception.Message)"
|
||||
Message = Get-Translation -Key 'StartMenuRestoreFailedForUser' -FormatArgs @($userName, $_.Exception.Message)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -440,7 +440,7 @@ function Restore-StartMenuForAllUsers {
|
||||
$results += [PSCustomObject]@{
|
||||
UserName = 'Default'
|
||||
Result = $true
|
||||
Message = '[WhatIf] Removed start2.bin for the default user profile.'
|
||||
Message = Get-Translation -Key 'StartMenuWhatIfRemovedDefaultProfile'
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -449,14 +449,14 @@ function Restore-StartMenuForAllUsers {
|
||||
$results += [PSCustomObject]@{
|
||||
UserName = 'Default'
|
||||
Result = $true
|
||||
Message = 'Removed start2.bin for the default user profile.'
|
||||
Message = Get-Translation -Key 'StartMenuRemovedDefaultProfile'
|
||||
}
|
||||
}
|
||||
catch {
|
||||
$results += [PSCustomObject]@{
|
||||
UserName = 'Default'
|
||||
Result = $false
|
||||
Message = "Failed to remove start2.bin for the default user profile. $($_.Exception.Message)"
|
||||
Message = Get-Translation -Key 'StartMenuRemoveFailedDefaultProfile' -FormatArgs @($_.Exception.Message)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -467,7 +467,7 @@ function Restore-StartMenuForAllUsers {
|
||||
$results += [PSCustomObject]@{
|
||||
UserName = 'unknown'
|
||||
Result = $false
|
||||
Message = 'No user start menu locations were found.'
|
||||
Message = Get-Translation -Key 'StartMenuNoUserLocationsFound'
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -21,14 +21,14 @@ function Import-RegistryBackup {
|
||||
)
|
||||
|
||||
if (-not (Test-Path -LiteralPath $FilePath)) {
|
||||
throw "Backup file was not found: $FilePath"
|
||||
throw (Get-Translation -Key 'BackupFileNotFound' -FormatArgs @($FilePath))
|
||||
}
|
||||
|
||||
try {
|
||||
$rawBackup = Get-Content -LiteralPath $FilePath -Raw -ErrorAction Stop | ConvertFrom-Json -ErrorAction Stop
|
||||
}
|
||||
catch {
|
||||
throw "Failed to read backup file '$FilePath'. The file is not valid JSON."
|
||||
throw (Get-Translation -Key 'BackupFileNotValidJson' -FormatArgs @($FilePath))
|
||||
}
|
||||
|
||||
return ConvertTo-NormalizedRegistryBackup -Backup $rawBackup
|
||||
@@ -61,22 +61,22 @@ function ConvertTo-NormalizedRegistryBackup {
|
||||
$errors = New-Object System.Collections.Generic.List[string]
|
||||
|
||||
if (-not $Backup.PSObject.Properties['Version']) {
|
||||
$errors.Add('Missing property: Version')
|
||||
$errors.Add((Get-Translation -Key 'BackupMissingProperty' -FormatArgs @('Version')))
|
||||
}
|
||||
elseif ([string]$Backup.Version -ne '1.0') {
|
||||
$errors.Add("Unsupported backup version '$($Backup.Version)'.")
|
||||
$errors.Add((Get-Translation -Key 'BackupUnsupportedVersion' -FormatArgs @($Backup.Version)))
|
||||
}
|
||||
|
||||
if (-not $Backup.PSObject.Properties['BackupType']) {
|
||||
$errors.Add('Missing property: BackupType')
|
||||
$errors.Add((Get-Translation -Key 'BackupMissingProperty' -FormatArgs @('BackupType')))
|
||||
}
|
||||
elseif ([string]$Backup.BackupType -ne 'RegistryState') {
|
||||
$errors.Add("Unsupported BackupType '$($Backup.BackupType)'.")
|
||||
$errors.Add((Get-Translation -Key 'BackupUnsupportedType' -FormatArgs @($Backup.BackupType)))
|
||||
}
|
||||
|
||||
$normalizedTarget = ''
|
||||
if (-not $Backup.PSObject.Properties['Target'] -or [string]::IsNullOrWhiteSpace([string]$Backup.Target)) {
|
||||
$errors.Add('Missing property: Target')
|
||||
$errors.Add((Get-Translation -Key 'BackupMissingProperty' -FormatArgs @('Target')))
|
||||
}
|
||||
else {
|
||||
$normalizedTarget = [string]$Backup.Target
|
||||
@@ -88,27 +88,27 @@ function ConvertTo-NormalizedRegistryBackup {
|
||||
$targetUserName = $normalizedTarget.Substring(5)
|
||||
$targetValidation = Test-TargetUserName -UserName $targetUserName
|
||||
if (-not $targetValidation.IsValid) {
|
||||
$errors.Add("Invalid user '$normalizedTarget'")
|
||||
$errors.Add((Get-Translation -Key 'BackupInvalidUser' -FormatArgs @($normalizedTarget)))
|
||||
}
|
||||
}
|
||||
elseif ($normalizedTarget -like 'CurrentUser:*') {
|
||||
$targetCurrentUserName = $normalizedTarget.Substring(12)
|
||||
if (Test-RunningAsSystem) {
|
||||
$errors.Add("Backup was made for '$targetCurrentUserName' and is user-scoped. Re-run as that user; SYSTEM cannot restore a CurrentUser backup.")
|
||||
$errors.Add((Get-Translation -Key 'BackupUserScopedWrongContext' -FormatArgs @($targetCurrentUserName)))
|
||||
}
|
||||
elseif ([string]::IsNullOrWhiteSpace($targetCurrentUserName) -or
|
||||
-not (Test-UserNameMatch -UserNameA $targetCurrentUserName -UserNameB $env:USERNAME)) {
|
||||
$errors.Add("Backup was made for '$targetCurrentUserName', this does not match current user '$env:USERNAME'.")
|
||||
$errors.Add((Get-Translation -Key 'BackupUserMismatch' -FormatArgs @($targetCurrentUserName, $env:USERNAME)))
|
||||
}
|
||||
}
|
||||
else {
|
||||
$errors.Add("Unsupported Target '$normalizedTarget'.")
|
||||
$errors.Add((Get-Translation -Key 'BackupUnsupportedTarget' -FormatArgs @($normalizedTarget)))
|
||||
}
|
||||
}
|
||||
|
||||
$registryKeys = @()
|
||||
if (-not $Backup.PSObject.Properties['RegistryKeys']) {
|
||||
$errors.Add('Missing property: RegistryKeys')
|
||||
$errors.Add((Get-Translation -Key 'BackupMissingProperty' -FormatArgs @('RegistryKeys')))
|
||||
}
|
||||
else {
|
||||
$registryKeys = @($Backup.RegistryKeys)
|
||||
@@ -133,7 +133,7 @@ function ConvertTo-NormalizedRegistryBackup {
|
||||
|
||||
$allSelectedFeatures = @($selectedFeatures) + @($selectedUndoFeatures)
|
||||
if ($allSelectedFeatures.Count -eq 0) {
|
||||
$errors.Add('Backup must contain at least one feature ID in SelectedFeatures or SelectedUndoFeatures.')
|
||||
$errors.Add((Get-Translation -Key 'BackupMissingSelectedFeatures'))
|
||||
}
|
||||
else {
|
||||
try {
|
||||
@@ -143,17 +143,17 @@ function ConvertTo-NormalizedRegistryBackup {
|
||||
}
|
||||
}
|
||||
catch {
|
||||
$errors.Add("Failed to validate backup: $($_.Exception.Message)")
|
||||
$errors.Add((Get-Translation -Key 'BackupAllowlistValidationFailed' -FormatArgs @($_.Exception.Message)))
|
||||
}
|
||||
}
|
||||
|
||||
if ($errors.Count -gt 0) {
|
||||
Write-Error "Backup validation failed: $($errors -join ' ')"
|
||||
if ($errors.Count -eq 1) {
|
||||
throw ("Validation failed: $($errors[0])")
|
||||
throw (Get-Translation -Key 'BackupValidationSingleError' -FormatArgs @($errors[0]))
|
||||
}
|
||||
else {
|
||||
throw ("Validation failed with $($errors.Count) errors. See console output for details.")
|
||||
throw (Get-Translation -Key 'BackupValidationMultipleErrors' -FormatArgs @($errors.Count))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ function Import-JsonFile {
|
||||
}
|
||||
|
||||
try {
|
||||
$jsonContent = Get-Content -Path $filePath -Raw | ConvertFrom-Json
|
||||
$jsonContent = Get-Content -Path $filePath -Raw -Encoding UTF8 | ConvertFrom-Json
|
||||
|
||||
# Validate version if specified
|
||||
if ($expectedVersion -and $jsonContent.Version -and $jsonContent.Version -ne $expectedVersion) {
|
||||
|
||||
@@ -0,0 +1,424 @@
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Resolves a language code to an available Config/Languages folder, or falls back to en-US.
|
||||
#>
|
||||
function Resolve-LanguageFolder {
|
||||
param(
|
||||
[Parameter(Mandatory)]
|
||||
[string]$LanguageCode,
|
||||
[string]$LanguagesPath = $script:LanguagesPath
|
||||
)
|
||||
|
||||
$exactPath = Join-Path $LanguagesPath $LanguageCode
|
||||
if (Test-Path $exactPath -PathType Container) {
|
||||
return $LanguageCode
|
||||
}
|
||||
|
||||
$languagePrefix = ($LanguageCode -split '-')[0]
|
||||
$prefixMatch = Get-ChildItem -Path $LanguagesPath -Directory -Filter "$languagePrefix-*" -ErrorAction SilentlyContinue | Select-Object -First 1
|
||||
if ($prefixMatch) {
|
||||
return $prefixMatch.Name
|
||||
}
|
||||
|
||||
return 'en-US'
|
||||
}
|
||||
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Loads Chrome/Features/Categories JSON for a language folder, or returns $null on failure.
|
||||
#>
|
||||
function Import-LanguageContent {
|
||||
param(
|
||||
[Parameter(Mandatory)]
|
||||
[string]$LanguageFolder,
|
||||
[string]$LanguagesPath = $script:LanguagesPath
|
||||
)
|
||||
|
||||
$folderPath = Join-Path $LanguagesPath $LanguageFolder
|
||||
$chrome = Import-JsonFile -filePath (Join-Path $folderPath 'Chrome.json')
|
||||
$features = Import-JsonFile -filePath (Join-Path $folderPath 'Features.json')
|
||||
$categories = Import-JsonFile -filePath (Join-Path $folderPath 'Categories.json')
|
||||
|
||||
if (-not $chrome -or -not $features -or -not $categories) {
|
||||
return $null
|
||||
}
|
||||
|
||||
return [PSCustomObject]@{
|
||||
LanguageCode = $LanguageFolder
|
||||
Chrome = $chrome
|
||||
Features = $features.Features
|
||||
UiGroups = $features.UiGroups
|
||||
Categories = $categories
|
||||
}
|
||||
}
|
||||
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Loads the active language content, falling back to en-US when the requested language fails to load.
|
||||
#>
|
||||
function Import-LanguageFile {
|
||||
param(
|
||||
[string]$LanguageCode = ([System.Globalization.CultureInfo]::CurrentUICulture.Name),
|
||||
[string]$LanguagesPath = $script:LanguagesPath
|
||||
)
|
||||
|
||||
$resolvedFolder = Resolve-LanguageFolder -LanguageCode $LanguageCode -LanguagesPath $LanguagesPath
|
||||
$content = Import-LanguageContent -LanguageFolder $resolvedFolder -LanguagesPath $LanguagesPath
|
||||
|
||||
if (-not $content -and $resolvedFolder -ne 'en-US') {
|
||||
Write-Warning "Failed to load language '$resolvedFolder', falling back to en-US."
|
||||
$content = Import-LanguageContent -LanguageFolder 'en-US' -LanguagesPath $LanguagesPath
|
||||
}
|
||||
|
||||
if (-not $content) {
|
||||
Write-Error "Unable to load the en-US language files. The GUI cannot continue without them."
|
||||
return $null
|
||||
}
|
||||
|
||||
if ($content.LanguageCode -ne 'en-US') {
|
||||
$content | Add-Member -MemberType NoteProperty -Name 'Fallback' -Value (Import-LanguageContent -LanguageFolder 'en-US' -LanguagesPath $LanguagesPath)
|
||||
}
|
||||
|
||||
return $content
|
||||
}
|
||||
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Returns the CLDR plural category ('one', 'other', etc.) for a count in the given language.
|
||||
|
||||
.DESCRIPTION
|
||||
Each language's CLDR plural rule is its own case, keyed by language prefix ('en' matches
|
||||
both 'en-US' and 'en-GB'). A language without a case falls back to the English rule, which
|
||||
is also correct for Dutch and German but wrong for languages with 3+ plural categories or
|
||||
a different singular/plural split.
|
||||
#>
|
||||
function Get-PluralCategory {
|
||||
param(
|
||||
[Parameter(Mandatory)]
|
||||
[string]$LanguageCode,
|
||||
[Parameter(Mandatory)]
|
||||
[int]$Count
|
||||
)
|
||||
|
||||
$languagePrefix = ($LanguageCode -split '-')[0].ToLowerInvariant()
|
||||
|
||||
switch ($languagePrefix) {
|
||||
# English CLDR rule: singular only for exactly 1, plural otherwise. Also correct for Dutch/German.
|
||||
default {
|
||||
if ($Count -eq 1) {
|
||||
return 'one'
|
||||
}
|
||||
|
||||
return 'other'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Returns a language and its Fallback (if any) as an ordered lookup chain, skipping nulls.
|
||||
#>
|
||||
function Get-LanguageFallbackChain {
|
||||
param(
|
||||
[object]$Lang
|
||||
)
|
||||
|
||||
return @($Lang, $Lang.Fallback) | Where-Object { $_ }
|
||||
}
|
||||
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Finds the named section object (Chrome, Features, UiGroups, or Categories) that owns a key.
|
||||
|
||||
.DESCRIPTION
|
||||
A FeatureId, GroupId, and CategoryId aren't guaranteed to be disjoint (Config/Features.json
|
||||
can and does reuse the same string as both a GroupId and a FeatureId), so the default search
|
||||
order below is only a fallback for callers who don't know which section their key belongs to.
|
||||
Pass -Section to look in exactly one section instead of guessing from the order.
|
||||
#>
|
||||
function Find-TranslationSection {
|
||||
param(
|
||||
[Parameter(Mandatory)]
|
||||
[object]$Lang,
|
||||
[Parameter(Mandatory)]
|
||||
[string]$Key,
|
||||
[ValidateSet('', 'Chrome', 'Features', 'UiGroups', 'Categories')]
|
||||
[string]$Section = ''
|
||||
)
|
||||
|
||||
if ($Section) {
|
||||
$sectionObject = $Lang.$Section
|
||||
if ($sectionObject -and $sectionObject.PSObject.Properties[$Key]) {
|
||||
return $sectionObject
|
||||
}
|
||||
return $null
|
||||
}
|
||||
|
||||
foreach ($sectionName in 'Chrome', 'Features', 'UiGroups', 'Categories') {
|
||||
$sectionObject = $Lang.$sectionName
|
||||
if ($sectionObject -and $sectionObject.PSObject.Properties[$Key]) {
|
||||
return $sectionObject
|
||||
}
|
||||
}
|
||||
|
||||
return $null
|
||||
}
|
||||
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Looks up a translated value from the active language, falling back to en-US, then to the key itself.
|
||||
|
||||
.DESCRIPTION
|
||||
Chrome.json keys are flat strings, so -Field is omitted for those. Features/UiGroups/Categories
|
||||
keys (FeatureId/GroupId/CategoryId) resolve to an object, so -Field picks the property on it
|
||||
(Label, ToolTip, ApplyText, UndoLabel, ApplyUndoText). One generic lookup covers every section
|
||||
instead of a separate function per section.
|
||||
|
||||
A FeatureId and a GroupId aren't guaranteed to be distinct strings, so pass -Section
|
||||
('Features', 'UiGroups', or 'Categories') whenever the caller knows which one it means,
|
||||
rather than relying on Find-TranslationSection's search order to guess correctly.
|
||||
|
||||
When -Count is supplied, tries the plural-suffixed key ("$Key`_$category") before the bare key,
|
||||
so callers don't need to add a plural variant for every string, only the ones that need one.
|
||||
.OUTPUTS
|
||||
System.String. The translated string, or the original Key if no language has it.
|
||||
#>
|
||||
function Get-Translation {
|
||||
param(
|
||||
[Parameter(Mandatory)]
|
||||
[string]$Key,
|
||||
[string]$Field = $null,
|
||||
[object]$Lang = $script:Lang,
|
||||
[Nullable[int]]$Count = $null,
|
||||
[object[]]$FormatArgs = $null,
|
||||
[ValidateSet('', 'Chrome', 'Features', 'UiGroups', 'Categories')]
|
||||
[string]$Section = ''
|
||||
)
|
||||
|
||||
if (-not $Lang) {
|
||||
return $Key
|
||||
}
|
||||
|
||||
$lookupKeys = @($Key)
|
||||
if ($null -ne $Count) {
|
||||
$category = Get-PluralCategory -LanguageCode $Lang.LanguageCode -Count $Count
|
||||
$lookupKeys = @("${Key}_$category", $Key)
|
||||
}
|
||||
|
||||
$resolved = $Key
|
||||
:langLoop foreach ($candidateLang in (Get-LanguageFallbackChain -Lang $Lang)) {
|
||||
foreach ($lookupKey in $lookupKeys) {
|
||||
$sectionObject = Find-TranslationSection -Lang $candidateLang -Key $lookupKey -Section $Section
|
||||
if (-not $sectionObject) { continue }
|
||||
|
||||
$entry = $sectionObject.$lookupKey
|
||||
if ($Field) {
|
||||
if ($entry.PSObject.Properties[$Field]) {
|
||||
$resolved = [string]$entry.$Field
|
||||
break langLoop
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
$resolved = [string]$entry
|
||||
break langLoop
|
||||
}
|
||||
}
|
||||
|
||||
if ($null -ne $FormatArgs -and $FormatArgs.Count -gt 0) {
|
||||
try {
|
||||
return $resolved -f $FormatArgs
|
||||
}
|
||||
catch {
|
||||
Write-Warning "Translation '$Key' has a placeholder mismatch with its format arguments: $($_.Exception.Message)"
|
||||
return $resolved
|
||||
}
|
||||
}
|
||||
|
||||
return $resolved
|
||||
}
|
||||
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Looks up a translated label for one value of a UiGroup, falling back to en-US, then to the raw label.
|
||||
|
||||
.DESCRIPTION
|
||||
UiGroups[GroupId].Values in the language files is a FeatureId -> Label map, one level deeper
|
||||
than Get-Translation's -Field lookup handles, so this has its own small fallback walk instead
|
||||
of overloading Get-Translation's flat-field shape.
|
||||
#>
|
||||
function Get-GroupValueTranslation {
|
||||
param(
|
||||
[Parameter(Mandatory)]
|
||||
[string]$GroupId,
|
||||
[Parameter(Mandatory)]
|
||||
[string]$FeatureId,
|
||||
[Parameter(Mandatory)]
|
||||
[string]$FallbackLabel,
|
||||
[object]$Lang = $script:Lang
|
||||
)
|
||||
|
||||
if (-not $Lang) {
|
||||
return $FallbackLabel
|
||||
}
|
||||
|
||||
foreach ($candidateLang in (Get-LanguageFallbackChain -Lang $Lang)) {
|
||||
if (-not $candidateLang.UiGroups) { continue }
|
||||
|
||||
$group = $candidateLang.UiGroups.$GroupId
|
||||
if (-not $group -or -not $group.Values) { continue }
|
||||
|
||||
$valueEntry = $group.Values.PSObject.Properties[$FeatureId]
|
||||
if ($valueEntry) {
|
||||
return [string]$valueEntry.Value
|
||||
}
|
||||
}
|
||||
|
||||
return $FallbackLabel
|
||||
}
|
||||
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Flattens a loaded language object into a sorted set of dotted key paths.
|
||||
|
||||
.DESCRIPTION
|
||||
Chrome.json keys are already flat and are used as-is. Features/UiGroups/Categories
|
||||
keys are one level deeper (EntryId -> {Field: value}), so each field becomes its own
|
||||
"Section.EntryId.Field" path (e.g. "Features.DisableTelemetry.Label"). UiGroups' nested
|
||||
Values map becomes "UiGroups.GroupId.Values.FeatureId". Used by Test-LanguageKeyCoverage
|
||||
to diff one language's key set against another's.
|
||||
#>
|
||||
function Get-LanguageKeyPaths {
|
||||
param(
|
||||
[Parameter(Mandatory)]
|
||||
[object]$Lang
|
||||
)
|
||||
|
||||
$paths = New-Object System.Collections.Generic.List[string]
|
||||
|
||||
foreach ($chromeKey in @($Lang.Chrome.PSObject.Properties.Name)) {
|
||||
if ([string]::IsNullOrEmpty($chromeKey)) { continue }
|
||||
$paths.Add($chromeKey)
|
||||
}
|
||||
|
||||
foreach ($sectionName in 'Features', 'Categories') {
|
||||
$section = $Lang.$sectionName
|
||||
if (-not $section) { continue }
|
||||
|
||||
foreach ($entryId in @($section.PSObject.Properties.Name)) {
|
||||
if ([string]::IsNullOrEmpty($entryId)) { continue }
|
||||
foreach ($field in @($section.$entryId.PSObject.Properties.Name)) {
|
||||
if ([string]::IsNullOrEmpty($field)) { continue }
|
||||
$paths.Add("$sectionName.$entryId.$field")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($groupId in @($Lang.UiGroups.PSObject.Properties.Name)) {
|
||||
if ([string]::IsNullOrEmpty($groupId)) { continue }
|
||||
$group = $Lang.UiGroups.$groupId
|
||||
foreach ($field in @($group.PSObject.Properties.Name)) {
|
||||
if ([string]::IsNullOrEmpty($field)) { continue }
|
||||
if ($field -eq 'Values') {
|
||||
foreach ($featureId in @($group.Values.PSObject.Properties.Name)) {
|
||||
if ([string]::IsNullOrEmpty($featureId)) { continue }
|
||||
$paths.Add("UiGroups.$groupId.Values.$featureId")
|
||||
}
|
||||
continue
|
||||
}
|
||||
$paths.Add("UiGroups.$groupId.$field")
|
||||
}
|
||||
}
|
||||
|
||||
return @($paths | Sort-Object -Unique)
|
||||
}
|
||||
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Diffs a language's key set against a baseline language, reporting missing and extra keys.
|
||||
|
||||
.DESCRIPTION
|
||||
Loads both languages fresh from disk (independent of any already-cached $script:Lang),
|
||||
flattens each with Get-LanguageKeyPaths, and compares the two sets. Intended to run
|
||||
against en-US as the baseline; useful today (catches a key present in one en-US file but
|
||||
not another) and becomes the real translation-completeness check once non-English
|
||||
language folders are contributed.
|
||||
|
||||
.OUTPUTS
|
||||
PSCustomObject with ResolvedLanguageCode (the folder actually compared, after the same
|
||||
exact/prefix/en-US resolution Import-LanguageFile applies), MissingKeys (present in
|
||||
Baseline, absent from LanguageCode), and ExtraKeys (present in LanguageCode, absent from
|
||||
Baseline) properties, the latter two sorted arrays.
|
||||
#>
|
||||
function Test-LanguageKeyCoverage {
|
||||
param(
|
||||
[Parameter(Mandatory)]
|
||||
[string]$LanguageCode,
|
||||
[string]$BaselineLanguageCode = 'en-US',
|
||||
[string]$LanguagesPath = $script:LanguagesPath
|
||||
)
|
||||
|
||||
$resolvedLanguageCode = Resolve-LanguageFolder -LanguageCode $LanguageCode -LanguagesPath $LanguagesPath
|
||||
$baseline = Import-LanguageContent -LanguageFolder $BaselineLanguageCode -LanguagesPath $LanguagesPath
|
||||
$target = Import-LanguageContent -LanguageFolder $resolvedLanguageCode -LanguagesPath $LanguagesPath
|
||||
|
||||
if (-not $baseline -or -not $target) {
|
||||
Write-Error "Unable to load language content for coverage comparison ('$BaselineLanguageCode' vs '$resolvedLanguageCode')."
|
||||
return $null
|
||||
}
|
||||
|
||||
$baselineKeys = [System.Collections.Generic.HashSet[string]]::new([string[]](Get-LanguageKeyPaths -Lang $baseline))
|
||||
$targetKeys = [System.Collections.Generic.HashSet[string]]::new([string[]](Get-LanguageKeyPaths -Lang $target))
|
||||
|
||||
$missingKeys = @($baselineKeys | Where-Object { -not $targetKeys.Contains($_) } | Sort-Object)
|
||||
$extraKeys = @($targetKeys | Where-Object { -not $baselineKeys.Contains($_) } | Sort-Object)
|
||||
|
||||
return [PSCustomObject]@{
|
||||
ResolvedLanguageCode = $resolvedLanguageCode
|
||||
MissingKeys = $missingKeys
|
||||
ExtraKeys = $extraKeys
|
||||
}
|
||||
}
|
||||
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Substitutes %LANG:Key% markers in XAML text with translated, XML-escaped values.
|
||||
|
||||
.DESCRIPTION
|
||||
Only resolves flat Chrome.json keys, since XAML markers never reference a Feature/Category/
|
||||
UiGroup field directly (those get their text from Get-Translation calls in the GUI scripts
|
||||
that build dynamic controls). Runs a single pass over every marker rather than sequential
|
||||
.Replace() calls, so an already-substituted value can't be re-matched by a later key.
|
||||
|
||||
After substitution, scans for any %LANG:...% text that survived unresolved and throws,
|
||||
since Get-Translation's key-as-fallback behavior means a missing key would otherwise render
|
||||
as plain, un-marked text (e.g. "TitleBarClose" instead of a visible error) rather than being
|
||||
caught here. Checks both the active language and its Fallback before flagging a key missing,
|
||||
so a partially-translated language degrades to en-US text instead of failing to load.
|
||||
#>
|
||||
function ConvertTo-LocalizedXaml {
|
||||
param(
|
||||
[Parameter(Mandatory)]
|
||||
[string]$Xaml,
|
||||
[object]$Lang = $script:Lang
|
||||
)
|
||||
|
||||
$missingKeys = New-Object System.Collections.Generic.List[string]
|
||||
|
||||
$evaluator = [System.Text.RegularExpressions.MatchEvaluator]{
|
||||
param($match)
|
||||
$key = $match.Groups[1].Value
|
||||
$resolvesSomewhere = @(Get-LanguageFallbackChain -Lang $Lang) | Where-Object { $_.Chrome -and $_.Chrome.PSObject.Properties[$key] }
|
||||
if (-not $resolvesSomewhere) { $missingKeys.Add($key) }
|
||||
$value = if ($resolvesSomewhere) { Get-Translation -Key $key -Lang $Lang } else { $key }
|
||||
return [System.Security.SecurityElement]::Escape($value)
|
||||
}
|
||||
|
||||
$result = [System.Text.RegularExpressions.Regex]::Replace($Xaml, '%LANG:([A-Za-z0-9_]+)%', $evaluator)
|
||||
|
||||
if ($missingKeys.Count -gt 0) {
|
||||
throw "Unresolved localization marker(s) in XAML, key(s) not found in any language: $($missingKeys -join ', ')"
|
||||
}
|
||||
|
||||
return $result
|
||||
}
|
||||
@@ -142,6 +142,15 @@ function Update-AppsPanelSort {
|
||||
}
|
||||
}
|
||||
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Updates the "N apps selected" status text and shows or hides the app-removal scope section.
|
||||
|
||||
.DESCRIPTION
|
||||
The scope section only makes sense once at least one app is selected, so it's hidden
|
||||
entirely at zero and its description text is refreshed via Update-AppRemovalScopeDescription
|
||||
whenever the selection count changes.
|
||||
#>
|
||||
function Update-AppSelectionStatus {
|
||||
param(
|
||||
[System.Windows.Controls.Panel]$AppsPanel,
|
||||
@@ -158,7 +167,7 @@ function Update-AppSelectionStatus {
|
||||
$selectedCount++
|
||||
}
|
||||
}
|
||||
$AppSelectionStatus.Text = "$selectedCount app(s) selected for removal"
|
||||
$AppSelectionStatus.Text = Get-Translation -Key 'AppSelectionStatusSelected' -Count $selectedCount -FormatArgs @($selectedCount)
|
||||
|
||||
if ($AppRemovalScopeCombo -and $AppRemovalScopeSection -and $AppRemovalScopeDescription) {
|
||||
if ($selectedCount -gt 0) {
|
||||
@@ -189,13 +198,13 @@ function Update-AppRemovalScopeDescription {
|
||||
# Content is the display text and will change once translated; Name is stable.
|
||||
switch ($selectedItem.Name) {
|
||||
"AppRemovalScopeAllUsers" {
|
||||
$AppRemovalScopeDescription.Text = "Apps will be removed for all users and from the Windows image to prevent reinstallation for new users."
|
||||
$AppRemovalScopeDescription.Text = Get-Translation -Key 'AppRemovalScopeDescriptionAllUsers'
|
||||
}
|
||||
"AppRemovalScopeCurrentUser" {
|
||||
$AppRemovalScopeDescription.Text = "Apps will only be removed for the current user."
|
||||
$AppRemovalScopeDescription.Text = Get-Translation -Key 'AppRemovalScopeDescriptionCurrentUser'
|
||||
}
|
||||
"AppRemovalScopeTargetUser" {
|
||||
$AppRemovalScopeDescription.Text = "Apps will only be removed for the specified target user."
|
||||
$AppRemovalScopeDescription.Text = Get-Translation -Key 'AppRemovalScopeDescriptionTargetUser'
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -448,9 +457,9 @@ function Add-AppsToMainWindow {
|
||||
$dot.Style = $Window.Resources['AppRecommendationDotStyle']
|
||||
$dot.Fill = switch ($app.Recommendation) { 'safe' { $brushSafe } 'unsafe' { $brushUnsafe } default { $brushDefault } }
|
||||
$dot.ToolTip = switch ($app.Recommendation) {
|
||||
'safe' { '[Recommended] Safe to remove for most users' }
|
||||
'unsafe' { '[Not Recommended] Only remove if you know what you are doing' }
|
||||
default { "[Optional] Can be safely removed if you don't need this app" }
|
||||
'safe' { Get-Translation -Key 'AppLegendRecommendedTooltip' }
|
||||
'unsafe' { Get-Translation -Key 'AppLegendNotRecommendedTooltip' }
|
||||
default { Get-Translation -Key 'AppLegendOptionalTooltip' }
|
||||
}
|
||||
[System.Windows.Controls.Grid]::SetColumn($dot, 0)
|
||||
|
||||
@@ -608,7 +617,7 @@ function Initialize-MainWindowApps {
|
||||
|
||||
if ($null -eq $listOfApps) {
|
||||
Write-Warning "WinGet returned no data (command timed out or failed)"
|
||||
Show-MessageBox -Message 'Unable to load list of installed apps via WinGet.' -Title 'Error' -Button 'OK' -Icon 'Error' | Out-Null
|
||||
Show-MessageBox -Message (Get-Translation -Key 'AppSelectionWinGetLoadFailedMessage') -Title (Get-Translation -Key 'ErrorTitle') -Button 'OK' -Icon 'Error' | Out-Null
|
||||
$OnlyInstalledAppsBox.IsChecked = $false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -96,6 +96,17 @@ function Get-PendingTweakActions {
|
||||
return @($actions.ToArray())
|
||||
}
|
||||
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Builds the plain-text list of pending changes: apps to remove and tweaks to apply or undo.
|
||||
|
||||
.PARAMETER ShowCurrentlyAppliedTweaksCheckBox
|
||||
When checked, pending tweak actions are computed against the system's live applied
|
||||
state instead of the controls' initial values.
|
||||
|
||||
.OUTPUTS
|
||||
System.String[]. One line per pending change, or an empty array if nothing is selected.
|
||||
#>
|
||||
function New-Overview {
|
||||
param(
|
||||
[System.Windows.Window]$Window,
|
||||
@@ -114,12 +125,12 @@ function New-Overview {
|
||||
}
|
||||
}
|
||||
if ($selectedAppsCount -gt 0) {
|
||||
$changesList += "Remove $selectedAppsCount application(s)"
|
||||
$changesList += Get-Translation -Key 'DeployChangeRemoveApps' -Count $selectedAppsCount -FormatArgs @($selectedAppsCount)
|
||||
}
|
||||
|
||||
foreach ($tweakAction in @(Get-PendingTweakActions -Window $Window -ShowAppliedTweaksMode:$showAppliedTweaksMode)) {
|
||||
if ($tweakAction.Action -eq 'Undo') {
|
||||
$changesList += "Undo: $($tweakAction.Label)"
|
||||
$changesList += Get-Translation -Key 'DeployChangeUndoPrefix' -FormatArgs @($tweakAction.Label)
|
||||
}
|
||||
else {
|
||||
$changesList += $tweakAction.Label
|
||||
@@ -129,6 +140,11 @@ function New-Overview {
|
||||
return $changesList
|
||||
}
|
||||
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Shows the pending-changes list from New-Overview in a message box, or a
|
||||
"no changes selected" message when there's nothing to show.
|
||||
#>
|
||||
function Invoke-ShowChangesOverview {
|
||||
param(
|
||||
[System.Windows.Window]$Window,
|
||||
@@ -139,12 +155,12 @@ function Invoke-ShowChangesOverview {
|
||||
$changesList = New-Overview -Window $Window -AppsPanel $AppsPanel -ShowCurrentlyAppliedTweaksCheckBox $ShowCurrentlyAppliedTweaksCheckBox
|
||||
|
||||
if ($changesList.Count -eq 0) {
|
||||
Show-MessageBox -Message 'No changes have been selected.' -Title 'Selected Changes' -Button 'OK' -Icon 'Information'
|
||||
Show-MessageBox -Message (Get-Translation -Key 'DeployNoChangesSelectedMessage') -Title (Get-Translation -Key 'DeploySelectedChangesTitle') -Button 'OK' -Icon 'Information'
|
||||
return
|
||||
}
|
||||
|
||||
$message = ($changesList | ForEach-Object { "$([char]0x2022) $_" }) -join "`n"
|
||||
Show-MessageBox -Message $message -Title 'Selected Changes' -Button 'OK' -Icon 'None' -Width 600
|
||||
Show-MessageBox -Message $message -Title (Get-Translation -Key 'DeploySelectedChangesTitle') -Button 'OK' -Icon 'None' -Width 600
|
||||
}
|
||||
|
||||
<#
|
||||
@@ -437,6 +453,14 @@ function Update-AppliedTweaksUserModeState {
|
||||
}
|
||||
}
|
||||
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Updates the deployment-target description text to match the selected user option.
|
||||
|
||||
.DESCRIPTION
|
||||
Falls back to a generic description (current user, other user, or Sysprep default
|
||||
profile) when a username can't be resolved yet, and to a specific one once it can.
|
||||
#>
|
||||
function Update-UserSelectionDescription {
|
||||
param(
|
||||
[System.Windows.Window]$Window,
|
||||
@@ -449,23 +473,23 @@ function Update-UserSelectionDescription {
|
||||
0 {
|
||||
$currentUserName = Get-UserName
|
||||
if ([string]::IsNullOrWhiteSpace($currentUserName)) {
|
||||
$UserSelectionDescription.Text = "The currently logged-in user profile"
|
||||
$UserSelectionDescription.Text = Get-Translation -Key 'DeployUserDescriptionCurrentUser'
|
||||
}
|
||||
else {
|
||||
$UserSelectionDescription.Text = "The currently logged-in user profile: $currentUserName"
|
||||
$UserSelectionDescription.Text = Get-Translation -Key 'DeployUserDescriptionCurrentUserNamed' -FormatArgs @($currentUserName)
|
||||
}
|
||||
}
|
||||
1 {
|
||||
$targetUserName = $OtherUsernameTextBox.Text.Trim()
|
||||
if ([string]::IsNullOrWhiteSpace($targetUserName)) {
|
||||
$UserSelectionDescription.Text = "A different user profile on this system"
|
||||
$UserSelectionDescription.Text = Get-Translation -Key 'DeployUserDescriptionOtherUser'
|
||||
}
|
||||
else {
|
||||
$UserSelectionDescription.Text = "A different user profile on this system: $targetUserName"
|
||||
$UserSelectionDescription.Text = Get-Translation -Key 'DeployUserDescriptionOtherUserNamed' -FormatArgs @($targetUserName)
|
||||
}
|
||||
}
|
||||
default {
|
||||
$UserSelectionDescription.Text = "The default user template, affecting all new users created after this point. Useful for Sysprep deployment."
|
||||
$UserSelectionDescription.Text = Get-Translation -Key 'DeployUserDescriptionSysprep'
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -154,7 +154,8 @@ function New-DynamicTweakControls {
|
||||
Returns the existing category panel or creates and registers a new one.
|
||||
|
||||
.PARAMETER CategoryObj
|
||||
The category definition containing Name and Icon properties.
|
||||
The category definition, with Name and Icon for internal use (the map key, the
|
||||
wiki-link slug) and CategoryId for the translated header shown to the user.
|
||||
|
||||
.OUTPUTS
|
||||
System.Windows.Controls.StackPanel. The category's content panel.
|
||||
@@ -162,6 +163,7 @@ function New-DynamicTweakControls {
|
||||
function Get-OrCreateCategoryCard($categoryObj) {
|
||||
$categoryName = $categoryObj.Name
|
||||
$categoryIcon = $categoryObj.Icon
|
||||
$categoryLabel = Get-Translation -Key $categoryObj.CategoryId -Field 'Label' -Section 'Categories'
|
||||
|
||||
if ($script:CategoryCardMap.ContainsKey($categoryName)) { return $script:CategoryCardMap[$categoryName] }
|
||||
|
||||
@@ -193,7 +195,7 @@ function New-DynamicTweakControls {
|
||||
$headerRow.Children.Add($icon) | Out-Null
|
||||
|
||||
$header = New-Object System.Windows.Controls.TextBlock
|
||||
$header.Text = $categoryName
|
||||
$header.Text = $categoryLabel
|
||||
$header.Style = $Window.Resources['CategoryHeaderTextBlock']
|
||||
$headerRow.Children.Add($header) | Out-Null
|
||||
|
||||
@@ -203,7 +205,7 @@ function New-DynamicTweakControls {
|
||||
|
||||
$helpBtn = New-Object System.Windows.Controls.Button
|
||||
$helpBtn.Content = $helpIcon
|
||||
$helpBtn.ToolTip = "Open the wiki for more info on '$categoryName' tweaks"
|
||||
$helpBtn.ToolTip = Get-Translation -Key 'TweaksCategoryHelpTooltip' -FormatArgs @($categoryLabel)
|
||||
$helpBtn.Tag = (Get-WikiUrlForCategory -category $categoryName)
|
||||
$helpBtn.Style = $Window.Resources['CategoryHelpLinkButtonStyle']
|
||||
$helpBtn.Add_Click({
|
||||
@@ -347,17 +349,18 @@ function New-DynamicTweakControls {
|
||||
|
||||
if ($featureMap.ContainsKey($soleFid)) {
|
||||
$soleFeature = $featureMap[$soleFid]
|
||||
$opt = 'Apply'
|
||||
if ($soleFeature.FeatureId -match '^Disable') { $opt = 'Disable' } elseif ($soleFeature.FeatureId -match '^Enable') { $opt = 'Enable' }
|
||||
$items = @('No Change', $opt)
|
||||
$soleFeatureLabel = Get-Translation -Key $soleFeature.FeatureId -Field 'Label' -Section 'Features'
|
||||
$opt = Get-Translation -Key 'TweaksOptionApply'
|
||||
if ($soleFeature.FeatureId -match '^Disable') { $opt = Get-Translation -Key 'TweaksOptionDisable' } elseif ($soleFeature.FeatureId -match '^Enable') { $opt = Get-Translation -Key 'TweaksOptionEnable' }
|
||||
$items = @((Get-Translation -Key 'TweaksOptionNoChange'), $opt)
|
||||
$comboName = ("Feature_{0}_Combo" -f $soleFeature.FeatureId) -replace '[^a-zA-Z0-9_]', ''
|
||||
if (-not $panel) { $panel = Get-OrCreateCategoryCard -categoryObj $categoryObj }
|
||||
$combo = New-LabeledCombo -parent $panel -labelText $soleFeature.Label -comboName $comboName -items $items
|
||||
$combo = New-LabeledCombo -parent $panel -labelText $soleFeatureLabel -comboName $comboName -items $items
|
||||
# attach tooltip from Features.json if present
|
||||
if ($soleFeature.ToolTip -or $soleFeature.DisableWhenApplied -eq $true) {
|
||||
$tooltipText = $soleFeature.ToolTip
|
||||
$tooltipText = Get-Translation -Key $soleFeature.FeatureId -Field 'ToolTip' -Section 'Features'
|
||||
if ($soleFeature.DisableWhenApplied -eq $true) {
|
||||
$tooltipText = "This tweak is already applied and cannot be undone automatically. Visit the Win11Debloat wiki for instructions on how to manually revert this change."
|
||||
$tooltipText = Get-Translation -Key 'TweaksAlreadyAppliedTooltip'
|
||||
}
|
||||
$tipBlock = New-Object System.Windows.Controls.TextBlock
|
||||
$tipBlock.Text = $tooltipText
|
||||
@@ -369,19 +372,20 @@ function New-DynamicTweakControls {
|
||||
try { $lblBorderObj = $Window.FindName("$comboName`_LabelBorder") } catch {}
|
||||
if ($lblBorderObj) { $lblBorderObj.ToolTip = $tipBlock }
|
||||
}
|
||||
$script:UiControlMappings[$comboName] = @{ Type = 'feature'; FeatureId = $soleFeature.FeatureId; Label = $soleFeature.Label; CategoryId = $categoryId }
|
||||
$script:UiControlMappings[$comboName] = @{ Type = 'feature'; FeatureId = $soleFeature.FeatureId; Label = $soleFeatureLabel; CategoryId = $categoryId }
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
$items = @('No Change') + ($filteredValues | ForEach-Object { $_.Label })
|
||||
$groupLabel = Get-Translation -Key $group.GroupId -Field 'Label' -Section 'UiGroups'
|
||||
$items = @((Get-Translation -Key 'TweaksOptionNoChange')) + ($filteredValues | ForEach-Object { Get-GroupValueTranslation -GroupId $group.GroupId -FeatureId $_.FeatureIds[0] -FallbackLabel $_.Label })
|
||||
$comboName = 'Group_{0}Combo' -f $group.GroupId
|
||||
if (-not $panel) { $panel = Get-OrCreateCategoryCard -categoryObj $categoryObj }
|
||||
$combo = New-LabeledCombo -parent $panel -labelText $group.Label -comboName $comboName -items $items
|
||||
$combo = New-LabeledCombo -parent $panel -labelText $groupLabel -comboName $comboName -items $items
|
||||
# attach tooltip from UiGroups if present
|
||||
if ($group.ToolTip) {
|
||||
$tipBlock = New-Object System.Windows.Controls.TextBlock
|
||||
$tipBlock.Text = $group.ToolTip
|
||||
$tipBlock.Text = Get-Translation -Key $group.GroupId -Field 'ToolTip' -Section 'UiGroups'
|
||||
$tipBlock.TextWrapping = 'Wrap'
|
||||
$tipBlock.MaxWidth = 420
|
||||
$combo.ToolTip = $tipBlock
|
||||
@@ -389,21 +393,22 @@ function New-DynamicTweakControls {
|
||||
try { $lblBorderObj = $Window.FindName("$comboName`_LabelBorder") } catch {}
|
||||
if ($lblBorderObj) { $lblBorderObj.ToolTip = $tipBlock }
|
||||
}
|
||||
$script:UiControlMappings[$comboName] = @{ Type = 'group'; Values = $filteredValues; Label = $group.Label; CategoryId = $categoryId }
|
||||
$script:UiControlMappings[$comboName] = @{ Type = 'group'; Values = $filteredValues; Label = $groupLabel; CategoryId = $categoryId }
|
||||
}
|
||||
elseif ($item.Type -eq 'feature') {
|
||||
$feature = $item.Data
|
||||
$opt = 'Apply'
|
||||
if ($feature.FeatureId -match '^Disable') { $opt = 'Disable' } elseif ($feature.FeatureId -match '^Enable') { $opt = 'Enable' }
|
||||
$items = @('No Change', $opt)
|
||||
$featureLabel = Get-Translation -Key $feature.FeatureId -Field 'Label' -Section 'Features'
|
||||
$opt = Get-Translation -Key 'TweaksOptionApply'
|
||||
if ($feature.FeatureId -match '^Disable') { $opt = Get-Translation -Key 'TweaksOptionDisable' } elseif ($feature.FeatureId -match '^Enable') { $opt = Get-Translation -Key 'TweaksOptionEnable' }
|
||||
$items = @((Get-Translation -Key 'TweaksOptionNoChange'), $opt)
|
||||
$comboName = ("Feature_{0}_Combo" -f $feature.FeatureId) -replace '[^a-zA-Z0-9_]', ''
|
||||
if (-not $panel) { $panel = Get-OrCreateCategoryCard -categoryObj $categoryObj }
|
||||
$combo = New-LabeledCombo -parent $panel -labelText $feature.Label -comboName $comboName -items $items
|
||||
$combo = New-LabeledCombo -parent $panel -labelText $featureLabel -comboName $comboName -items $items
|
||||
# attach tooltip from Features.json if present, and include the disabled-state reason
|
||||
if ($feature.ToolTip -or $feature.DisableWhenApplied -eq $true) {
|
||||
$tooltipText = $feature.ToolTip
|
||||
$tooltipText = Get-Translation -Key $feature.FeatureId -Field 'ToolTip' -Section 'Features'
|
||||
if ($feature.DisableWhenApplied -eq $true) {
|
||||
$tooltipText = "This tweak is already applied and cannot be undone automatically. Visit the Win11Debloat wiki for instructions on how to manually revert this change."
|
||||
$tooltipText = Get-Translation -Key 'TweaksAlreadyAppliedTooltip'
|
||||
}
|
||||
|
||||
$tipBlock = New-Object System.Windows.Controls.TextBlock
|
||||
@@ -425,8 +430,10 @@ function New-DynamicTweakControls {
|
||||
$script:FeatureLabelLookup = @{}
|
||||
$script:UndoFeatureLabelLookup = @{}
|
||||
foreach ($f in $featuresJson.Features) {
|
||||
$script:FeatureLabelLookup[$f.FeatureId] = $f.Label
|
||||
$script:UndoFeatureLabelLookup[$f.FeatureId] = $f.UndoLabel
|
||||
$script:FeatureLabelLookup[$f.FeatureId] = Get-Translation -Key $f.FeatureId -Field 'Label' -Section 'Features'
|
||||
if ($f.UndoLabel) {
|
||||
$script:UndoFeatureLabelLookup[$f.FeatureId] = Get-Translation -Key $f.FeatureId -Field 'UndoLabel' -Section 'Features'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -81,8 +81,9 @@ function Test-RestoreDialogFeatureCanAutoRevert {
|
||||
Resolves a human-readable label for a feature shown in the restore dialog.
|
||||
|
||||
.DESCRIPTION
|
||||
Returns the feature's Label from Features.json when found, falling back
|
||||
to the raw FeatureId string. For null/empty FeatureIds returns 'Unknown feature'.
|
||||
Returns the feature's translated Label when found, falling back to the
|
||||
raw FeatureId string. For null/empty FeatureIds returns the translated
|
||||
"unknown feature" placeholder.
|
||||
|
||||
.PARAMETER FeatureId
|
||||
The feature identifier to resolve a label for.
|
||||
@@ -97,12 +98,12 @@ function Get-RestoreDialogFeatureDisplayLabel {
|
||||
)
|
||||
|
||||
if ([string]::IsNullOrWhiteSpace($FeatureId)) {
|
||||
return 'Unknown feature'
|
||||
return Get-Translation -Key 'RestoreUnknownFeature'
|
||||
}
|
||||
|
||||
$featureDefinition = Get-RestoreDialogFeatureDefinition -FeatureId $FeatureId -Features $Features
|
||||
if ($featureDefinition) {
|
||||
return [string]$featureDefinition.Label
|
||||
return Get-Translation -Key $FeatureId -Field 'Label' -Section 'Features'
|
||||
}
|
||||
|
||||
return $FeatureId
|
||||
|
||||
@@ -32,6 +32,7 @@ function Show-AboutDialog {
|
||||
|
||||
# Load XAML from file
|
||||
$xaml = Get-Content -Path $script:AboutWindowSchema -Raw
|
||||
$xaml = ConvertTo-LocalizedXaml -Xaml $xaml
|
||||
$reader = [System.Xml.XmlReader]::Create([System.IO.StringReader]::new($xaml))
|
||||
try {
|
||||
$aboutWindow = [System.Windows.Markup.XamlReader]::Load($reader)
|
||||
|
||||
@@ -24,6 +24,7 @@ function Show-AppSelectionWindow {
|
||||
|
||||
# Load XAML from file
|
||||
$xaml = Get-Content -Path $script:AppSelectionSchema -Raw
|
||||
$xaml = ConvertTo-LocalizedXaml -Xaml $xaml
|
||||
$reader = [System.Xml.XmlReader]::Create([System.IO.StringReader]::new($xaml))
|
||||
try {
|
||||
$window = [System.Windows.Markup.XamlReader]::Load($reader)
|
||||
@@ -72,7 +73,7 @@ function Show-AppSelectionWindow {
|
||||
$listOfApps = Get-WingetInstalledApps -TimeOut 10 -NonBlocking
|
||||
if ($null -eq $listOfApps) {
|
||||
# Show error that the script was unable to get list of apps from WinGet
|
||||
Show-MessageBox -Message 'Unable to load list of installed apps via WinGet.' -Title 'Error' -Button 'OK' -Icon 'Error' -Owner $window | Out-Null
|
||||
Show-MessageBox -Message (Get-Translation -Key 'AppSelectionWinGetLoadFailedMessage') -Title (Get-Translation -Key 'ErrorTitle') -Button 'OK' -Icon 'Error' -Owner $window | Out-Null
|
||||
$onlyInstalledBox.IsChecked = $false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,6 +37,7 @@ function Show-ApplyModal {
|
||||
|
||||
# Load XAML from file
|
||||
$xaml = Get-Content -Path $script:ApplyChangesWindowSchema -Raw
|
||||
$xaml = ConvertTo-LocalizedXaml -Xaml $xaml
|
||||
$reader = [System.Xml.XmlReader]::Create([System.IO.StringReader]::new($xaml))
|
||||
try {
|
||||
$applyWindow = [System.Windows.Markup.XamlReader]::Load($reader)
|
||||
@@ -74,8 +75,8 @@ function Show-ApplyModal {
|
||||
# Initialize in-progress state
|
||||
$script:ApplyInProgressPanel.Visibility = 'Visible'
|
||||
$script:ApplyCompletionPanel.Visibility = 'Collapsed'
|
||||
$script:ApplyStepNameEl.Text = "Preparing..."
|
||||
$script:ApplyStepCounterEl.Text = "Preparing..."
|
||||
$script:ApplyStepNameEl.Text = Get-Translation -Key 'ApplyPreparing'
|
||||
$script:ApplyStepCounterEl.Text = Get-Translation -Key 'ApplyStepCounter' -FormatArgs @(0, 0)
|
||||
$script:ApplyProgressBarEl.Value = 0
|
||||
$script:ApplyModalInErrorState = $false
|
||||
|
||||
@@ -83,7 +84,7 @@ function Show-ApplyModal {
|
||||
$script:ApplyProgressCallback = {
|
||||
param($currentStep, $totalSteps, $stepName)
|
||||
$script:ApplyStepNameEl.Text = $stepName
|
||||
$script:ApplyStepCounterEl.Text = "Step $currentStep of $totalSteps"
|
||||
$script:ApplyStepCounterEl.Text = Get-Translation -Key 'ApplyStepCounter' -FormatArgs @($currentStep, $totalSteps)
|
||||
# Store current step/total in Tag properties for sub-step interpolation
|
||||
$script:ApplyStepCounterEl.Tag = $currentStep
|
||||
$script:ApplyProgressBarEl.Tag = $totalSteps
|
||||
@@ -140,8 +141,8 @@ function Show-ApplyModal {
|
||||
|
||||
$script:ApplyCompletionIconEl.Text = [char]0xE7BA
|
||||
$script:ApplyCompletionIconEl.Foreground = [System.Windows.Media.SolidColorBrush]::new([System.Windows.Media.ColorConverter]::ConvertFromString("#e8912d"))
|
||||
$script:ApplyCompletionTitleEl.Text = "Cancelled"
|
||||
$script:ApplyCompletionMessageEl.Text = "Script execution was cancelled by the user."
|
||||
$script:ApplyCompletionTitleEl.Text = Get-Translation -Key 'ApplyCompletionTitleCancelled'
|
||||
$script:ApplyCompletionMessageEl.Text = Get-Translation -Key 'ApplyCompletionMessageCancelled'
|
||||
} elseif ($failureCount -gt 0 -or $appRemovalVerificationUnavailable) {
|
||||
if ($failureCount -gt 0) {
|
||||
Write-Host "Script completed with $failureCount error(s)."
|
||||
@@ -150,17 +151,17 @@ function Show-ApplyModal {
|
||||
$script:ApplyCompletionIconEl.Text = [char]0xE7BA
|
||||
$script:ApplyCompletionIconEl.Foreground = [System.Windows.Media.SolidColorBrush]::new([System.Windows.Media.ColorConverter]::ConvertFromString("#e8912d"))
|
||||
if ($failureCount -eq 0 -and $appRemovalVerificationUnavailable) {
|
||||
$script:ApplyCompletionTitleEl.Text = "Changes Applied"
|
||||
$script:ApplyCompletionMessageEl.Text = "All changes were applied without errors, but Win11Debloat could not confirm that all selected apps were successfully uninstalled."
|
||||
$script:ApplyCompletionTitleEl.Text = Get-Translation -Key 'ApplyCompletionTitleSuccess'
|
||||
$script:ApplyCompletionMessageEl.Text = Get-Translation -Key 'ApplyCompletionMessageVerificationUnavailable'
|
||||
}
|
||||
else {
|
||||
$script:ApplyCompletionTitleEl.Text = "Changes Applied with Errors"
|
||||
$script:ApplyCompletionMessageEl.Text = "$failureCount change(s) failed. See console for details."
|
||||
$script:ApplyCompletionTitleEl.Text = Get-Translation -Key 'ApplyCompletionTitleErrors'
|
||||
$script:ApplyCompletionMessageEl.Text = Get-Translation -Key 'ApplyCompletionMessageFailures' -Count $failureCount -FormatArgs @($failureCount)
|
||||
}
|
||||
} else {
|
||||
Write-Host "All changes have been applied successfully!"
|
||||
|
||||
$script:ApplyCompletionTitleEl.Text = "Changes Applied"
|
||||
$script:ApplyCompletionTitleEl.Text = Get-Translation -Key 'ApplyCompletionTitleSuccess'
|
||||
|
||||
# Show completion message with reboot instructions if any applied features require reboot
|
||||
if ($InvokeRestartExplorer) {
|
||||
@@ -179,7 +180,7 @@ function Show-ApplyModal {
|
||||
$applyRebootPanel.Visibility = 'Visible'
|
||||
}
|
||||
else {
|
||||
$script:ApplyCompletionMessageEl.Text = "Your system is ready. Thanks for using Win11Debloat!"
|
||||
$script:ApplyCompletionMessageEl.Text = Get-Translation -Key 'ApplyCompletionMessageReady'
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -191,24 +192,24 @@ function Show-ApplyModal {
|
||||
$script:ApplyCompletionPanel.Visibility = 'Visible'
|
||||
$script:ApplyCompletionIconEl.Text = [char]0xEA39
|
||||
$script:ApplyCompletionIconEl.Foreground = [System.Windows.Media.SolidColorBrush]::new([System.Windows.Media.ColorConverter]::ConvertFromString("#c42b1c"))
|
||||
$script:ApplyCompletionTitleEl.Text = "Error"
|
||||
$script:ApplyCompletionMessageEl.Text = "An error occurred while applying changes: $($_.Exception.Message)"
|
||||
|
||||
$script:ApplyCompletionTitleEl.Text = Get-Translation -Key 'ApplyCompletionTitleError'
|
||||
$script:ApplyCompletionMessageEl.Text = Get-Translation -Key 'ApplyCompletionMessageError' -FormatArgs @($_.Exception.Message)
|
||||
|
||||
# Set error state to change Kofi button to report link
|
||||
$script:ApplyModalInErrorState = $true
|
||||
|
||||
# Update Kofi button to be a report issue button
|
||||
$applyKofiBtn.Content = $null
|
||||
|
||||
|
||||
$reportText = [System.Windows.Controls.TextBlock]::new()
|
||||
$reportText.Text = 'Report a bug'
|
||||
$reportText.Text = Get-Translation -Key 'MenuReportBug'
|
||||
$reportText.VerticalAlignment = 'Center'
|
||||
$reportText.FontSize = 14
|
||||
$reportText.Margin = [System.Windows.Thickness]::new(0, 0, 0, 1)
|
||||
|
||||
$applyKofiBtn.Content = $reportText
|
||||
|
||||
[System.Windows.Automation.AutomationProperties]::SetName($applyKofiBtn, 'Report a bug')
|
||||
|
||||
[System.Windows.Automation.AutomationProperties]::SetName($applyKofiBtn, (Get-Translation -Key 'MenuReportBug'))
|
||||
|
||||
$applyWindow.Dispatcher.Invoke([System.Windows.Threading.DispatcherPriority]::Render, [action]{})
|
||||
}
|
||||
|
||||
@@ -72,8 +72,8 @@ function Hide-Bubble {
|
||||
parameter is mandatory.
|
||||
|
||||
.PARAMETER Message
|
||||
The text message to display inside the bubble. Defaults to
|
||||
'View the selected changes here'.
|
||||
The text message to display inside the bubble. Defaults to the
|
||||
translated "view the selected changes" prompt.
|
||||
|
||||
.PARAMETER DurationSeconds
|
||||
The number of seconds the bubble remains visible before auto-hiding.
|
||||
@@ -91,7 +91,7 @@ function Show-Bubble {
|
||||
[System.Windows.Controls.Control]$TargetControl,
|
||||
|
||||
[Parameter(Mandatory=$false)]
|
||||
[string]$Message = 'View the selected changes here',
|
||||
[string]$Message = (Get-Translation -Key 'BubbleViewChanges'),
|
||||
|
||||
[Parameter(Mandatory=$false)]
|
||||
[int]$DurationSeconds = 5
|
||||
@@ -104,6 +104,7 @@ function Show-Bubble {
|
||||
Hide-Bubble -Immediate
|
||||
|
||||
$xaml = Get-Content -Path $script:BubbleHintSchema -Raw
|
||||
$xaml = ConvertTo-LocalizedXaml -Xaml $xaml
|
||||
$reader = [System.Xml.XmlReader]::Create([System.IO.StringReader]::new($xaml))
|
||||
try {
|
||||
$bubblePanel = [System.Windows.Markup.XamlReader]::Load($reader)
|
||||
|
||||
@@ -1,3 +1,26 @@
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Maps an internal import/export category ID ('Applications', 'System Tweaks',
|
||||
'Deployment Settings') to its Chrome.json translation key.
|
||||
|
||||
.DESCRIPTION
|
||||
The category strings stay as the stable internal IDs used for -contains checks and
|
||||
hashtable lookups throughout this file; only the label shown to the user is translated.
|
||||
#>
|
||||
function Get-ImportExportCategoryLabelKey {
|
||||
param(
|
||||
[Parameter(Mandatory)]
|
||||
[string]$Category
|
||||
)
|
||||
|
||||
switch ($Category) {
|
||||
'Applications' { return 'ImportExportCategoryApplications' }
|
||||
'System Tweaks' { return 'ImportExportCategorySystemTweaks' }
|
||||
'Deployment Settings' { return 'ImportExportCategoryDeploymentSettings' }
|
||||
default { return $Category }
|
||||
}
|
||||
}
|
||||
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Shows a modal category-selection dialog for importing or exporting configuration.
|
||||
@@ -11,7 +34,7 @@ function Show-ImportExportConfigWindow {
|
||||
[string[]]$Categories = @('Applications', 'System Tweaks', 'Deployment Settings'),
|
||||
[string[]]$DisabledCategories = @(),
|
||||
[hashtable]$CategoryDetails = @(),
|
||||
[string]$ActionLabel = 'OK'
|
||||
[string]$ActionLabel = (Get-Translation -Key 'MessageBoxOk')
|
||||
)
|
||||
|
||||
# Show overlay on owner window
|
||||
@@ -32,7 +55,7 @@ function Show-ImportExportConfigWindow {
|
||||
$schemaPath = $script:ImportExportConfigSchema
|
||||
|
||||
if (-not $schemaPath -or -not (Test-Path $schemaPath)) {
|
||||
Show-MessageBox -Message 'Import/Export window schema file could not be found.' -Title 'Error' -Button 'OK' -Icon 'Error' -Owner $Owner | Out-Null
|
||||
Show-MessageBox -Message (Get-Translation -Key 'ImportExportSchemaMissingMessage') -Title (Get-Translation -Key 'ErrorTitle') -Button 'OK' -Icon 'Error' -Owner $Owner | Out-Null
|
||||
if ($overlay -and -not $overlayWasAlreadyVisible) {
|
||||
try { $Owner.Dispatcher.Invoke([action]{ $overlay.Visibility = 'Collapsed' }) } catch { }
|
||||
}
|
||||
@@ -40,6 +63,7 @@ function Show-ImportExportConfigWindow {
|
||||
}
|
||||
|
||||
$xaml = Get-Content -Path $schemaPath -Raw
|
||||
$xaml = ConvertTo-LocalizedXaml -Xaml $xaml
|
||||
$reader = [System.Xml.XmlReader]::Create([System.IO.StringReader]::new($xaml))
|
||||
try {
|
||||
$dlg = [System.Windows.Markup.XamlReader]::Load($reader)
|
||||
@@ -79,7 +103,7 @@ function Show-ImportExportConfigWindow {
|
||||
|
||||
# Create checkbox
|
||||
$cb = New-Object System.Windows.Controls.CheckBox
|
||||
$cb.Content = $cat
|
||||
$cb.Content = Get-Translation -Key (Get-ImportExportCategoryLabelKey -Category $cat)
|
||||
$cb.IsChecked = $true
|
||||
$cb.Margin = [System.Windows.Thickness]::new(0,0,0,4)
|
||||
$cb.FontSize = 14
|
||||
@@ -89,7 +113,7 @@ function Show-ImportExportConfigWindow {
|
||||
$cb.IsChecked = $false
|
||||
$cb.IsEnabled = $false
|
||||
$cb.Opacity = 0.65
|
||||
$cb.ToolTip = 'No selected settings available in this category.'
|
||||
$cb.ToolTip = Get-Translation -Key 'ImportExportCategoryDisabledTooltip'
|
||||
}
|
||||
|
||||
$container.Children.Add($cb) | Out-Null
|
||||
@@ -245,6 +269,11 @@ function Get-AvailableImportExportCategories {
|
||||
return $availableCategories
|
||||
}
|
||||
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Summarizes a deployment settings array into the short description shown under the
|
||||
"Deployment Settings" checkbox in the import/export category picker.
|
||||
#>
|
||||
function Get-DeploymentCategoryDetailString {
|
||||
param (
|
||||
[array]$DeploymentSettings
|
||||
@@ -261,33 +290,38 @@ function Get-DeploymentCategoryDetailString {
|
||||
|
||||
if ($lookup.ContainsKey('UserSelectionIndex')) {
|
||||
switch ([int]$lookup['UserSelectionIndex']) {
|
||||
0 { $line1 += 'User: Current User' }
|
||||
1 { $line1 += "User: $(if ($lookup['OtherUsername']) { $lookup['OtherUsername'] } else { 'Other User' })" }
|
||||
2 { $line1 += 'User: Sysprep' }
|
||||
0 { $line1 += Get-Translation -Key 'ImportExportDeployUserCurrentUser' }
|
||||
1 { $line1 += Get-Translation -Key 'ImportExportDeployUserOther' -FormatArgs @($(if ($lookup['OtherUsername']) { $lookup['OtherUsername'] } else { Get-Translation -Key 'ImportExportDeployUserOtherFallback' })) }
|
||||
2 { $line1 += Get-Translation -Key 'ImportExportDeployUserSysprep' }
|
||||
}
|
||||
}
|
||||
|
||||
if ($lookup.ContainsKey('AppRemovalScopeIndex')) {
|
||||
switch ([int]$lookup['AppRemovalScopeIndex']) {
|
||||
0 { $line1 += 'App Removal: All Users' }
|
||||
1 { $line1 += 'App Removal: Current User' }
|
||||
2 { $line1 += "App Removal: $(if ($lookup['OtherUsername']) { $lookup['OtherUsername'] } else { 'Other User' })" }
|
||||
0 { $line1 += Get-Translation -Key 'ImportExportAppRemovalAllUsers' }
|
||||
1 { $line1 += Get-Translation -Key 'ImportExportAppRemovalCurrentUser' }
|
||||
2 { $line1 += Get-Translation -Key 'ImportExportAppRemovalOther' -FormatArgs @($(if ($lookup['OtherUsername']) { $lookup['OtherUsername'] } else { Get-Translation -Key 'ImportExportDeployUserOtherFallback' })) }
|
||||
}
|
||||
}
|
||||
|
||||
$options = @()
|
||||
if ($lookup.ContainsKey('CreateRestorePoint') -and [bool]$lookup['CreateRestorePoint']) { $options += 'Restore Point' }
|
||||
if (-not ($lookup.ContainsKey('SkipRegistryBackup') -and [bool]$lookup['SkipRegistryBackup'])) { $options += 'Registry Backup' }
|
||||
if ($lookup.ContainsKey('RestartExplorer') -and [bool]$lookup['RestartExplorer']) { $options += 'Restart Explorer' }
|
||||
if ($lookup.ContainsKey('CreateRestorePoint') -and [bool]$lookup['CreateRestorePoint']) { $options += Get-Translation -Key 'ImportExportOptionRestorePoint' }
|
||||
if (-not ($lookup.ContainsKey('SkipRegistryBackup') -and [bool]$lookup['SkipRegistryBackup'])) { $options += Get-Translation -Key 'ImportExportOptionRegistryBackup' }
|
||||
if ($lookup.ContainsKey('RestartExplorer') -and [bool]$lookup['RestartExplorer']) { $options += Get-Translation -Key 'ImportExportOptionRestartExplorer' }
|
||||
|
||||
$lines = @()
|
||||
if ($line1.Count -gt 0) { $lines += $line1 -join ', ' }
|
||||
if ($options.Count -gt 0) { $lines += "Options: $($options -join ', ')" }
|
||||
if ($options.Count -gt 0) { $lines += Get-Translation -Key 'ImportExportOptionsPrefix' -FormatArgs @($options -join ', ') }
|
||||
|
||||
if ($lines.Count -gt 0) { return $lines -join "`n" }
|
||||
return 'Default deployment settings'
|
||||
return Get-Translation -Key 'ImportExportDefaultDeploymentSettings'
|
||||
}
|
||||
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Builds the per-category description text shown under each checkbox in the
|
||||
import/export category picker.
|
||||
#>
|
||||
function Build-CategoryDetails {
|
||||
param (
|
||||
[int]$AppCount = 0,
|
||||
@@ -298,17 +332,17 @@ function Build-CategoryDetails {
|
||||
$details = @{}
|
||||
|
||||
if ($AppCount -gt 0) {
|
||||
$details['Applications'] = "$AppCount app$(if ($AppCount -ne 1) { 's' }) selected"
|
||||
$details['Applications'] = Get-Translation -Key 'ImportExportAppsSelected' -Count $AppCount -FormatArgs @($AppCount)
|
||||
}
|
||||
else {
|
||||
$details['Applications'] = 'No apps selected'
|
||||
$details['Applications'] = Get-Translation -Key 'ImportExportNoAppsSelected'
|
||||
}
|
||||
|
||||
if ($TweakCount -gt 0) {
|
||||
$details['System Tweaks'] = "$TweakCount tweak$(if ($TweakCount -ne 1) { 's' }) selected"
|
||||
$details['System Tweaks'] = Get-Translation -Key 'ImportExportTweaksSelected' -Count $TweakCount -FormatArgs @($TweakCount)
|
||||
}
|
||||
else {
|
||||
$details['System Tweaks'] = 'No tweaks selected'
|
||||
$details['System Tweaks'] = Get-Translation -Key 'ImportExportNoTweaksSelected'
|
||||
}
|
||||
|
||||
if ($DeploymentSettings) {
|
||||
@@ -422,7 +456,7 @@ function Export-Configuration {
|
||||
$deploymentSettings = Get-DeploymentSettings -Owner $Owner -UserSelectionCombo $UserSelectionCombo -OtherUsernameTextBox $OtherUsernameTextBox
|
||||
$categoryDetails = Build-CategoryDetails -AppCount $selectedApps.Count -TweakCount $tweakSettings.Count -DeploymentSettings $deploymentSettings
|
||||
|
||||
$categories = Show-ImportExportConfigWindow -Owner $Owner -UsesDarkMode $UsesDarkMode -Title 'Export Configuration' -Prompt 'Create a configuration file based on the currently selected settings. You can choose which settings categories you wish to include in the export.' -DisabledCategories $disabledCategories -CategoryDetails $categoryDetails -ActionLabel 'Export Settings'
|
||||
$categories = Show-ImportExportConfigWindow -Owner $Owner -UsesDarkMode $UsesDarkMode -Title (Get-Translation -Key 'ImportExportExportTitle') -Prompt (Get-Translation -Key 'ImportExportExportPrompt') -DisabledCategories $disabledCategories -CategoryDetails $categoryDetails -ActionLabel (Get-Translation -Key 'ImportExportExportActionLabel')
|
||||
if (-not $categories) {
|
||||
Write-Host 'Export canceled.'
|
||||
return
|
||||
@@ -442,7 +476,7 @@ function Export-Configuration {
|
||||
|
||||
# Show native save-file dialog
|
||||
$saveDialog = New-Object Microsoft.Win32.SaveFileDialog
|
||||
$saveDialog.Title = 'Export Configuration'
|
||||
$saveDialog.Title = Get-Translation -Key 'ImportExportSelectExportFileDialogTitle'
|
||||
$saveDialog.Filter = 'JSON files (*.json)|*.json|All files (*.*)|*.*'
|
||||
$saveDialog.DefaultExt = '.json'
|
||||
$saveDialog.FileName = "Win11Debloat-Config-$(Get-Date -Format 'yyyyMMdd').json"
|
||||
@@ -456,17 +490,17 @@ function Export-Configuration {
|
||||
|
||||
if ($script:Params.ContainsKey("WhatIf")) {
|
||||
Write-Host "[WhatIf] Export configuration to '$($saveDialog.FileName)'" -ForegroundColor Cyan
|
||||
Show-MessageBox -Message "[WhatIf] Configuration would be exported to this file (no file written)." -Title 'Export Configuration' -Button 'OK' -Icon 'Information' | Out-Null
|
||||
Show-MessageBox -Message (Get-Translation -Key 'ImportExportWhatIfExportMessage') -Title (Get-Translation -Key 'ImportExportExportTitle') -Button 'OK' -Icon 'Information' | Out-Null
|
||||
return
|
||||
}
|
||||
|
||||
if (Save-ToFile -Config $config -FilePath $saveDialog.FileName) {
|
||||
Write-Host "Configuration exported successfully: $($saveDialog.FileName)"
|
||||
Show-MessageBox -Message "Configuration exported successfully." -Title 'Export Configuration' -Button 'OK' -Icon 'Information' | Out-Null
|
||||
Show-MessageBox -Message (Get-Translation -Key 'ImportExportExportSuccessMessage') -Title (Get-Translation -Key 'ImportExportExportTitle') -Button 'OK' -Icon 'Information' | Out-Null
|
||||
}
|
||||
else {
|
||||
Write-Error "Failed to export configuration to '$($saveDialog.FileName)'"
|
||||
Show-MessageBox -Message "Failed to export configuration" -Title 'Error' -Button 'OK' -Icon 'Error' | Out-Null
|
||||
Show-MessageBox -Message (Get-Translation -Key 'ImportExportExportFailedMessage') -Title (Get-Translation -Key 'ErrorTitle') -Button 'OK' -Icon 'Error' | Out-Null
|
||||
}
|
||||
}
|
||||
|
||||
@@ -488,7 +522,7 @@ function Import-Configuration {
|
||||
|
||||
# Show native open-file dialog
|
||||
$openDialog = New-Object Microsoft.Win32.OpenFileDialog
|
||||
$openDialog.Title = 'Select Configuration File'
|
||||
$openDialog.Title = Get-Translation -Key 'ImportExportSelectImportFileDialogTitle'
|
||||
$openDialog.Filter = 'JSON files (*.json)|*.json|All files (*.*)|*.*'
|
||||
$openDialog.DefaultExt = '.json'
|
||||
|
||||
@@ -502,14 +536,14 @@ function Import-Configuration {
|
||||
$config = Import-JsonFile -filePath $openDialog.FileName -expectedVersion '1.0'
|
||||
if (-not $config) {
|
||||
Write-Error "Failed to read configuration file '$($openDialog.FileName)'"
|
||||
Show-MessageBox -Message "Failed to read configuration file" -Title 'Invalid Config' -Button 'OK' -Icon 'Error' | Out-Null
|
||||
Show-MessageBox -Message (Get-Translation -Key 'ImportExportReadFailedMessage') -Title (Get-Translation -Key 'ImportExportInvalidConfigTitle') -Button 'OK' -Icon 'Error' | Out-Null
|
||||
return
|
||||
}
|
||||
|
||||
$consistencyError = Test-ConfigConsistency -Config $config
|
||||
if ($consistencyError) {
|
||||
Write-Error "Invalid configuration file '$($openDialog.FileName)': $consistencyError"
|
||||
Show-MessageBox -Message "Invalid configuration file: $consistencyError" -Title 'Invalid Config' -Button 'OK' -Icon 'Error' | Out-Null
|
||||
Show-MessageBox -Message (Get-Translation -Key 'ImportExportInvalidConfigMessage' -FormatArgs @($consistencyError)) -Title (Get-Translation -Key 'ImportExportInvalidConfigTitle') -Button 'OK' -Icon 'Error' | Out-Null
|
||||
return
|
||||
}
|
||||
|
||||
@@ -521,7 +555,7 @@ function Import-Configuration {
|
||||
$tweakCount = @($config.Tweaks | Where-Object { $_ -and $_.Name -and $_.Value -eq $true }).Count
|
||||
$categoryDetails = Build-CategoryDetails -AppCount $appCount -TweakCount $tweakCount -DeploymentSettings @($config.Deployment)
|
||||
|
||||
$categories = Show-ImportExportConfigWindow -Owner $Owner -UsesDarkMode $UsesDarkMode -Title 'Import Configuration' -Prompt 'Choose the settings categories that you wish to import. You can review and modify the imported settings before they are applied.' -Categories $availableCategories -CategoryDetails $categoryDetails -ActionLabel 'Import Settings'
|
||||
$categories = Show-ImportExportConfigWindow -Owner $Owner -UsesDarkMode $UsesDarkMode -Title (Get-Translation -Key 'ImportExportImportTitle') -Prompt (Get-Translation -Key 'ImportExportImportPrompt') -Categories $availableCategories -CategoryDetails $categoryDetails -ActionLabel (Get-Translation -Key 'ImportExportImportActionLabel')
|
||||
if (-not $categories) {
|
||||
Write-Host 'Import canceled.'
|
||||
return
|
||||
@@ -553,7 +587,7 @@ function Import-Configuration {
|
||||
}
|
||||
|
||||
Write-Host 'Configuration imported successfully.'
|
||||
Show-MessageBox -Message "Configuration imported successfully." -Title 'Import Configuration' -Button 'OK' -Icon 'Information' | Out-Null
|
||||
Show-MessageBox -Message (Get-Translation -Key 'ImportExportImportSuccessMessage') -Title (Get-Translation -Key 'ImportExportImportTitle') -Button 'OK' -Icon 'Information' | Out-Null
|
||||
|
||||
if ($OnImportCompleted) {
|
||||
& $OnImportCompleted $categories
|
||||
|
||||
@@ -10,6 +10,7 @@ function Show-MainWindow {
|
||||
|
||||
# ---- Load XAML ----
|
||||
$xaml = Get-Content -Path $script:MainWindowSchema -Raw
|
||||
$xaml = ConvertTo-LocalizedXaml -Xaml $xaml
|
||||
$reader = [System.Xml.XmlReader]::Create([System.IO.StringReader]::new($xaml))
|
||||
try {
|
||||
$window = [System.Windows.Markup.XamlReader]::Load($reader)
|
||||
@@ -97,7 +98,7 @@ function Show-MainWindow {
|
||||
Start-Process "explorer.exe" -ArgumentList $logsFolder
|
||||
}
|
||||
else {
|
||||
Show-MessageBox -Message "No logs folder found at: $logsFolder" -Title "Logs" -Button 'OK' -Icon 'Information'
|
||||
Show-MessageBox -Message (Get-Translation -Key 'LogsFolderNotFoundMessage' -FormatArgs @($logsFolder)) -Title (Get-Translation -Key 'LogsMenuTitle') -Button 'OK' -Icon 'Information'
|
||||
}
|
||||
})
|
||||
|
||||
@@ -170,7 +171,7 @@ function Show-MainWindow {
|
||||
}
|
||||
catch {
|
||||
Write-Warning "Export configuration failed: $($_.Exception.Message)"
|
||||
Show-MessageBox -Owner $window -Message "Unable to open export configuration dialog: $($_.Exception.Message)" -Title 'Export Configuration Failed' -Button 'OK' -Icon 'Error' | Out-Null
|
||||
Show-MessageBox -Owner $window -Message (Get-Translation -Key 'ExportConfigFailedMessage' -FormatArgs @($_.Exception.Message)) -Title (Get-Translation -Key 'ExportConfigFailedTitle') -Button 'OK' -Icon 'Error' | Out-Null
|
||||
}
|
||||
})
|
||||
|
||||
@@ -180,13 +181,13 @@ function Show-MainWindow {
|
||||
$tabControl.SelectedIndex = 3
|
||||
Update-NavigationButtons -Window $window -TabControl $tabControl
|
||||
$window.Dispatcher.BeginInvoke([System.Windows.Threading.DispatcherPriority]::Loaded, [action]{
|
||||
Show-Bubble -TargetControl $reviewChangesBtn -Message 'View the selected changes here'
|
||||
Show-Bubble -TargetControl $reviewChangesBtn -Message (Get-Translation -Key 'BubbleViewChanges')
|
||||
}) | Out-Null
|
||||
}
|
||||
}
|
||||
catch {
|
||||
Write-Warning "Import configuration failed: $($_.Exception.Message)"
|
||||
Show-MessageBox -Owner $window -Message "Unable to open import configuration dialog: $($_.Exception.Message)" -Title 'Import Configuration Failed' -Button 'OK' -Icon 'Error' | Out-Null
|
||||
Show-MessageBox -Owner $window -Message (Get-Translation -Key 'ImportConfigFailedMessage' -FormatArgs @($_.Exception.Message)) -Title (Get-Translation -Key 'ImportConfigFailedTitle') -Button 'OK' -Icon 'Error' | Out-Null
|
||||
}
|
||||
})
|
||||
|
||||
@@ -206,7 +207,7 @@ function Show-MainWindow {
|
||||
}
|
||||
catch {
|
||||
Write-Warning "Restore backup action failed: $($_.Exception.Message)"
|
||||
Show-MessageBox -Owner $window -Message "Unable to open restore backup dialog: $($_.Exception.Message)" -Title 'Restore Backup Failed' -Button 'OK' -Icon 'Error' | Out-Null
|
||||
Show-MessageBox -Owner $window -Message (Get-Translation -Key 'RestoreBackupFailedMessage' -FormatArgs @($_.Exception.Message)) -Title (Get-Translation -Key 'RestoreBackupFailedTitle') -Button 'OK' -Icon 'Error' | Out-Null
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -232,7 +233,7 @@ function Show-MainWindow {
|
||||
$checkbox.Content = $preset.Name
|
||||
$checkbox.IsThreeState = $true
|
||||
$checkbox.Style = $window.Resources['PresetCheckBoxStyle']
|
||||
$checkbox.ToolTip = "Select $($preset.Name)"
|
||||
$checkbox.ToolTip = Get-Translation -Key 'AppPresetSelectTooltip' -FormatArgs @($preset.Name)
|
||||
$checkbox.SetValue([System.Windows.Automation.AutomationProperties]::NameProperty, $preset.Name)
|
||||
Add-TriStateClickBehavior -CheckBox $checkbox
|
||||
Add-Member -InputObject $checkbox -MemberType NoteProperty -Name 'PresetAppIds' -Value $preset.AppIds
|
||||
@@ -501,12 +502,11 @@ function Show-MainWindow {
|
||||
param($sourceControl, $e)
|
||||
if ($e.Key -eq [System.Windows.Input.Key]::F -and
|
||||
([System.Windows.Input.Keyboard]::Modifiers -band [System.Windows.Input.ModifierKeys]::Control)) {
|
||||
$currentTab = $tabControl.SelectedItem
|
||||
if ($currentTab.Header -eq "App Removal" -and $appSearchBox) {
|
||||
if ($tabControl.SelectedIndex -eq 1 -and $appSearchBox) {
|
||||
$appSearchBox.Focus()
|
||||
$e.Handled = $true
|
||||
}
|
||||
elseif ($currentTab.Header -eq "Tweaks" -and $tweakSearchBox) {
|
||||
elseif ($tabControl.SelectedIndex -eq 2 -and $tweakSearchBox) {
|
||||
$tweakSearchBox.Focus()
|
||||
$e.Handled = $true
|
||||
}
|
||||
@@ -599,9 +599,9 @@ function Show-MainWindow {
|
||||
$usernameValidationMessage.Text
|
||||
}
|
||||
else {
|
||||
"Please enter a valid username."
|
||||
Get-Translation -Key 'InvalidUsernameFallbackMessage'
|
||||
}
|
||||
Show-MessageBox -Message $validationMessage -Title "Invalid Username" -Button 'OK' -Icon 'Warning' | Out-Null
|
||||
Show-MessageBox -Message $validationMessage -Title (Get-Translation -Key 'InvalidUsernameTitle') -Button 'OK' -Icon 'Warning' | Out-Null
|
||||
return $false
|
||||
}
|
||||
return $true
|
||||
@@ -638,7 +638,7 @@ function Show-MainWindow {
|
||||
Invoke-NavigationUpdate
|
||||
|
||||
$window.Dispatcher.BeginInvoke([System.Windows.Threading.DispatcherPriority]::Loaded, [action]{
|
||||
Show-Bubble -TargetControl $reviewChangesBtn -Message 'View the selected changes here'
|
||||
Show-Bubble -TargetControl $reviewChangesBtn -Message (Get-Translation -Key 'BubbleViewChanges')
|
||||
}) | Out-Null
|
||||
})
|
||||
|
||||
@@ -692,7 +692,7 @@ function Show-MainWindow {
|
||||
}
|
||||
|
||||
if (-not $hasAppSelection -and $selectedForwardFeatureIds.Count -eq 0 -and $script:UndoParams.Count -eq 0) {
|
||||
Show-MessageBox -Message 'No changes have been selected, please select at least one option to proceed.' -Title 'No Changes Selected' -Button 'OK' -Icon 'Information'
|
||||
Show-MessageBox -Message (Get-Translation -Key 'NoChangesSelectedMessage') -Title (Get-Translation -Key 'NoChangesSelectedTitle') -Button 'OK' -Icon 'Information'
|
||||
return
|
||||
}
|
||||
|
||||
@@ -764,7 +764,7 @@ function Show-MainWindow {
|
||||
if ($userSelectionCombo -and $userSelectionCombo.Items.Count -gt 0) {
|
||||
$currentUserItem = $userSelectionCombo.Items[0]
|
||||
if ($currentUserItem -is [System.Windows.Controls.ComboBoxItem]) {
|
||||
$currentUserItem.Content = "Current User ($(Get-UserName))"
|
||||
$currentUserItem.Content = Get-Translation -Key 'HomeCurrentUserWithName' -FormatArgs @(Get-UserName)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -810,7 +810,7 @@ function Show-MainWindow {
|
||||
catch {
|
||||
Write-Warning "Error during GUI initialization: $($_.Exception.Message)"
|
||||
Write-Warning "Stack trace: $($_.Exception.StackTrace)"
|
||||
Show-MessageBox -Message "An error occurred during initialization: $($_.Exception.Message)" -Title "Initialization Error" -Button 'OK' -Icon 'Error' | Out-Null
|
||||
Show-MessageBox -Message (Get-Translation -Key 'InitializationErrorMessage' -FormatArgs @($_.Exception.Message)) -Title (Get-Translation -Key 'InitializationErrorTitle') -Button 'OK' -Icon 'Error' | Out-Null
|
||||
}
|
||||
})
|
||||
|
||||
@@ -900,7 +900,7 @@ function Show-MainWindow {
|
||||
# If WhatIf mode is enabled, notify the user that no changes will be made
|
||||
if ($script:Params.ContainsKey("WhatIf")) {
|
||||
$window.Dispatcher.BeginInvoke([System.Windows.Threading.DispatcherPriority]::Loaded, [action]{
|
||||
Show-MessageBox -Message "WhatIf mode is enabled. The script will not make any changes to your system in this mode.`n`nYou can observe the actions that would be taken by the script in the console output." -Title 'WhatIf Mode' -Button 'OK' -Icon 'Information' -Owner $window
|
||||
Show-MessageBox -Message (Get-Translation -Key 'WhatIfModeMessage') -Title (Get-Translation -Key 'WhatIfModeTitle') -Button 'OK' -Icon 'Information' -Owner $window
|
||||
}) | Out-Null
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,15 @@
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Shows a themed Windows 11-style message box.
|
||||
|
||||
.DESCRIPTION
|
||||
Falls back to a plain native MessageBox if the themed dialog's own schema fails to
|
||||
localize (a bad %LANG:% marker in MessageBox.xaml, not a missing app-wide translation),
|
||||
so a broken schema can't take down every error dialog in the app at once.
|
||||
|
||||
.OUTPUTS
|
||||
System.String. One of 'OK', 'Cancel', 'Yes', or 'No', matching the pressed button
|
||||
in both the themed dialog and the native fallback.
|
||||
#>
|
||||
function Show-MessageBox {
|
||||
param (
|
||||
@@ -50,6 +59,41 @@ function Show-MessageBox {
|
||||
|
||||
# Load XAML from file
|
||||
$xaml = Get-Content -Path $script:MessageBoxSchema -Raw
|
||||
try {
|
||||
$xaml = ConvertTo-LocalizedXaml -Xaml $xaml
|
||||
}
|
||||
catch {
|
||||
# The themed dialog depends on the schema's own %LANG:% markers resolving. If that ever
|
||||
# breaks (a bad marker added to MessageBoxSchema, not a missing app-wide translation - that
|
||||
# never reaches this function), fall back to a plain native MessageBox rather than losing
|
||||
# the ability to show any error dialog at all.
|
||||
Write-Warning "Falling back to a native message box, themed dialog failed to localize: $($_.Exception.Message)"
|
||||
|
||||
# Hide overlay before showing the fallback (only if this dialog was the one that showed it)
|
||||
if ($overlay -and -not $overlayWasAlreadyVisible) {
|
||||
try {
|
||||
$ownerWindow.Dispatcher.Invoke([action]{ $overlay.Visibility = 'Collapsed' })
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
$nativeButton = switch ($Button) {
|
||||
'OKCancel' { [System.Windows.MessageBoxButton]::OKCancel }
|
||||
'YesNo' { [System.Windows.MessageBoxButton]::YesNo }
|
||||
default { [System.Windows.MessageBoxButton]::OK }
|
||||
}
|
||||
$nativeIcon = switch ($Icon) {
|
||||
'Warning' { [System.Windows.MessageBoxImage]::Warning }
|
||||
'Error' { [System.Windows.MessageBoxImage]::Error }
|
||||
'Question' { [System.Windows.MessageBoxImage]::Question }
|
||||
'Information' { [System.Windows.MessageBoxImage]::Information }
|
||||
'Success' { [System.Windows.MessageBoxImage]::Information }
|
||||
default { [System.Windows.MessageBoxImage]::None }
|
||||
}
|
||||
|
||||
$nativeResult = [System.Windows.MessageBox]::Show($Message, $Title, $nativeButton, $nativeIcon)
|
||||
return [string]$nativeResult
|
||||
}
|
||||
$reader = [System.Xml.XmlReader]::Create([System.IO.StringReader]::new($xaml))
|
||||
try {
|
||||
$msgWindow = [System.Windows.Markup.XamlReader]::Load($reader)
|
||||
@@ -121,22 +165,22 @@ function Show-MessageBox {
|
||||
# Configure buttons - store result in window's Tag property
|
||||
switch ($Button) {
|
||||
'OK' {
|
||||
$button1.Content = 'OK'
|
||||
$button1.Content = Get-Translation -Key 'MessageBoxOk'
|
||||
$button1.Add_Click({ $msgWindow.Tag = 'OK'; $msgWindow.Close() })
|
||||
$button2.Visibility = 'Collapsed'
|
||||
# Right-align sole button by moving it to column 1
|
||||
[System.Windows.Controls.Grid]::SetColumn($button1, 1)
|
||||
}
|
||||
'OKCancel' {
|
||||
$button1.Content = 'OK'
|
||||
$button2.Content = 'Cancel'
|
||||
$button1.Content = Get-Translation -Key 'MessageBoxOk'
|
||||
$button2.Content = Get-Translation -Key 'MessageBoxCancel'
|
||||
$button1.Add_Click({ $msgWindow.Tag = 'OK'; $msgWindow.Close() })
|
||||
$button2.Add_Click({ $msgWindow.Tag = 'Cancel'; $msgWindow.Close() })
|
||||
$button2.Visibility = 'Visible'
|
||||
}
|
||||
'YesNo' {
|
||||
$button1.Content = 'Yes'
|
||||
$button2.Content = 'No'
|
||||
$button1.Content = Get-Translation -Key 'MessageBoxYes'
|
||||
$button2.Content = Get-Translation -Key 'MessageBoxNo'
|
||||
$button1.Add_Click({ $msgWindow.Tag = 'Yes'; $msgWindow.Close() })
|
||||
$button2.Add_Click({ $msgWindow.Tag = 'No'; $msgWindow.Close() })
|
||||
$button2.Visibility = 'Visible'
|
||||
|
||||
@@ -44,422 +44,429 @@ function Show-RestoreBackupDialog {
|
||||
catch { }
|
||||
}
|
||||
|
||||
$schemaPath = $script:RestoreBackupWindowSchema
|
||||
if (-not $schemaPath -or -not (Test-Path $schemaPath)) {
|
||||
throw 'Restore backup window schema file could not be found.'
|
||||
}
|
||||
|
||||
$xaml = Get-Content -Path $schemaPath -Raw
|
||||
|
||||
$reader = [System.Xml.XmlReader]::Create([System.IO.StringReader]::new($xaml))
|
||||
# Everything from here through ShowDialog() sits inside one outer try/finally. The overlay
|
||||
# was already made visible above, and nothing hides it again until ShowDialog() returns, so
|
||||
# a failure anywhere in schema loading, control wiring, or event registration would otherwise
|
||||
# leave the owner window permanently dimmed.
|
||||
try {
|
||||
$window = [System.Windows.Markup.XamlReader]::Load($reader)
|
||||
}
|
||||
finally {
|
||||
$reader.Close()
|
||||
}
|
||||
$schemaPath = $script:RestoreBackupWindowSchema
|
||||
if (-not $schemaPath -or -not (Test-Path $schemaPath)) {
|
||||
throw 'Restore backup window schema file could not be found.'
|
||||
}
|
||||
|
||||
if ($ownerWindow) {
|
||||
$xaml = Get-Content -Path $schemaPath -Raw
|
||||
$xaml = ConvertTo-LocalizedXaml -Xaml $xaml
|
||||
|
||||
$reader = [System.Xml.XmlReader]::Create([System.IO.StringReader]::new($xaml))
|
||||
try {
|
||||
$window.Owner = $ownerWindow
|
||||
$window = [System.Windows.Markup.XamlReader]::Load($reader)
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
try {
|
||||
Set-WindowThemeResources -window $window -usesDarkMode $usesDarkMode
|
||||
}
|
||||
catch { }
|
||||
|
||||
$titleBar = $window.FindName('TitleBar')
|
||||
$titleText = $window.FindName('TitleText')
|
||||
$closeBtn = $window.FindName('CloseBtn')
|
||||
$backBtn = $window.FindName('BackBtn')
|
||||
$primaryActionBtn = $window.FindName('PrimaryActionBtn')
|
||||
$chooseRegistryBtn = $window.FindName('ChooseRegistryBtn')
|
||||
$chooseStartMenuBtn = $window.FindName('ChooseStartMenuBtn')
|
||||
$restoreModeTabs = $window.FindName('RestoreModeTabs')
|
||||
$startMenuIntroPanel = $window.FindName('StartMenuIntroPanel')
|
||||
$startMenuScopeCombo = $window.FindName('StartMenuScopeCombo')
|
||||
$startMenuAutoBackupCheck = $window.FindName('StartMenuAutoBackupCheck')
|
||||
$introInfoPanel = $window.FindName('IntroInfoPanel')
|
||||
$overviewPanel = $window.FindName('OverviewPanel')
|
||||
$overviewFeaturesSection = $window.FindName('OverviewFeaturesSection')
|
||||
$overviewSummaryText = $window.FindName('OverviewSummaryText')
|
||||
$backupFileText = $window.FindName('BackupFileText')
|
||||
$backupCreatedText = $window.FindName('BackupCreatedText')
|
||||
$backupTargetText = $window.FindName('BackupTargetText')
|
||||
$featuresItemsControl = $window.FindName('FeaturesItemsControl')
|
||||
$reappliedSeparator = $window.FindName('ReappliedSeparator')
|
||||
$reappliedPanel = $window.FindName('ReappliedPanel')
|
||||
$reappliedFeaturesItemsControl = $window.FindName('ReappliedFeaturesItemsControl')
|
||||
$nonRevertibleSeparator = $window.FindName('NonRevertibleSeparator')
|
||||
$nonRevertiblePanel = $window.FindName('NonRevertiblePanel')
|
||||
$nonRevertibleFeaturesItemsControl = $window.FindName('NonRevertibleFeaturesItemsControl')
|
||||
$nonRevertibleWikiLink = $window.FindName('NonRevertibleWikiLink')
|
||||
|
||||
$titleBar.Add_MouseLeftButtonDown({ $window.DragMove() })
|
||||
$window.Tag = New-RestoreDialogState
|
||||
$chooseRegistryBtn.IsDefault = $true
|
||||
|
||||
$state = @{ WizardStep = 'SelectType'; SelectedRegistryBackup = $null; SelectedStartMenuBackupFilePath = $null }
|
||||
|
||||
$getStartMenuScopeInfo = {
|
||||
$isAllUsersScope = ($startMenuScopeCombo.SelectedItem.Tag -eq 'AllUsers')
|
||||
$scopeValue = if ($isAllUsersScope) { 'AllUsers' } else { 'CurrentUser' }
|
||||
$summaryScopeText = if ($isAllUsersScope) { 'all users' } else { 'the current user' }
|
||||
|
||||
return [PSCustomObject]@{
|
||||
Scope = $scopeValue
|
||||
Target = $scopeValue
|
||||
SummaryText = $summaryScopeText
|
||||
}
|
||||
}
|
||||
|
||||
$showStartMenuIntroState = {
|
||||
$backupFileText.Text = 'Not selected'
|
||||
$backupCreatedText.Text = 'N/A'
|
||||
$overviewSummaryText.Visibility = 'Collapsed'
|
||||
$overviewPanel.Visibility = 'Collapsed'
|
||||
$startMenuIntroPanel.Visibility = 'Visible'
|
||||
$restoreModeTabs.SelectedIndex = 2
|
||||
}
|
||||
|
||||
$showStartMenuOverviewState = {
|
||||
param([string]$BackupFilePath)
|
||||
|
||||
$scopeInfo = & $getStartMenuScopeInfo
|
||||
$backupTargetText.Text = Get-FriendlyRegistryBackupTarget -Target $scopeInfo.Target
|
||||
$overviewSummaryText.Text = "This will replace the current Start Menu pinned apps layout for $($scopeInfo.SummaryText) with the selected backup."
|
||||
$backupFileText.Text = Split-Path -Path $BackupFilePath -Leaf
|
||||
|
||||
$createdText = 'Unknown'
|
||||
try {
|
||||
$createdText = (Get-Item -LiteralPath $BackupFilePath -ErrorAction Stop).LastWriteTime.ToString('yyyy-MM-dd HH:mm')
|
||||
}
|
||||
catch { }
|
||||
$backupCreatedText.Text = $createdText
|
||||
|
||||
$overviewFeaturesSection.Visibility = 'Collapsed'
|
||||
$overviewSummaryText.Visibility = 'Visible'
|
||||
$reappliedSeparator.Visibility = 'Collapsed'
|
||||
$reappliedPanel.Visibility = 'Collapsed'
|
||||
$nonRevertibleSeparator.Visibility = 'Collapsed'
|
||||
$nonRevertiblePanel.Visibility = 'Collapsed'
|
||||
$introInfoPanel.Visibility = 'Collapsed'
|
||||
$overviewPanel.Visibility = 'Visible'
|
||||
$restoreModeTabs.SelectedIndex = 1
|
||||
}
|
||||
|
||||
$updateStartMenuOverviewPanel = {
|
||||
if ($state.WizardStep -ne 'StartMenu') {
|
||||
return
|
||||
finally {
|
||||
$reader.Close()
|
||||
}
|
||||
|
||||
if ([string]::IsNullOrWhiteSpace($state.SelectedStartMenuBackupFilePath)) {
|
||||
& $showStartMenuIntroState
|
||||
return
|
||||
}
|
||||
|
||||
& $showStartMenuOverviewState $state.SelectedStartMenuBackupFilePath
|
||||
}
|
||||
|
||||
$updateStartMenuPrimaryActionText = {
|
||||
if ($state.WizardStep -ne 'StartMenu') {
|
||||
return
|
||||
}
|
||||
|
||||
$isAutoBackupEnabled = ($startMenuAutoBackupCheck.IsChecked -eq $true)
|
||||
$hasSelectedManualFile = -not [string]::IsNullOrWhiteSpace($state.SelectedStartMenuBackupFilePath)
|
||||
if ($isAutoBackupEnabled -or $hasSelectedManualFile) {
|
||||
$primaryActionBtn.Content = 'Restore backup'
|
||||
}
|
||||
else {
|
||||
$primaryActionBtn.Content = 'Select backup file'
|
||||
}
|
||||
}
|
||||
|
||||
$refreshStartMenuUi = {
|
||||
& $updateStartMenuOverviewPanel
|
||||
& $updateStartMenuPrimaryActionText
|
||||
}
|
||||
|
||||
$enterSelectTypeStep = {
|
||||
$titleText.Text = 'Restore Backup'
|
||||
$restoreModeTabs.SelectedIndex = 0
|
||||
$backBtn.Visibility = 'Visible'
|
||||
$backBtn.Content = 'Cancel'
|
||||
$primaryActionBtn.Visibility = 'Collapsed'
|
||||
$chooseRegistryBtn.IsDefault = $true
|
||||
$primaryActionBtn.IsDefault = $false
|
||||
}
|
||||
|
||||
$enterRegistryStep = {
|
||||
$titleText.Text = 'Restore Registry Backup'
|
||||
$restoreModeTabs.SelectedIndex = 1
|
||||
$introInfoPanel.Visibility = 'Visible'
|
||||
$overviewPanel.Visibility = 'Collapsed'
|
||||
$overviewFeaturesSection.Visibility = 'Visible'
|
||||
$overviewSummaryText.Visibility = 'Collapsed'
|
||||
$backBtn.Visibility = 'Visible'
|
||||
$backBtn.Content = 'Back'
|
||||
$primaryActionBtn.Visibility = 'Visible'
|
||||
$primaryActionBtn.Content = 'Select backup file'
|
||||
$primaryActionBtn.IsDefault = $true
|
||||
$chooseRegistryBtn.IsDefault = $false
|
||||
}
|
||||
|
||||
$enterStartMenuStep = {
|
||||
$titleText.Text = 'Restore Start Menu Backup'
|
||||
$restoreModeTabs.SelectedIndex = 2
|
||||
$backBtn.Visibility = 'Visible'
|
||||
$backBtn.Content = 'Back'
|
||||
$primaryActionBtn.Visibility = 'Visible'
|
||||
$primaryActionBtn.IsDefault = $true
|
||||
$chooseRegistryBtn.IsDefault = $false
|
||||
|
||||
# Show intro panel so user can configure scope & auto-detect
|
||||
$startMenuAutoBackupCheck.IsChecked = $true
|
||||
$state.SelectedStartMenuBackupFilePath = $null
|
||||
& $refreshStartMenuUi
|
||||
}
|
||||
|
||||
$showRegistryOverview = {
|
||||
param(
|
||||
[Parameter(Mandatory = $true)]
|
||||
$SelectedBackup,
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string]$SelectedBackupFilePath
|
||||
)
|
||||
|
||||
$createdText = if ([string]::IsNullOrWhiteSpace($SelectedBackup.CreatedAt)) {
|
||||
'Unknown'
|
||||
}
|
||||
else {
|
||||
if ($ownerWindow) {
|
||||
try {
|
||||
[DateTime]::Parse($SelectedBackup.CreatedAt).ToString('yyyy-MM-dd HH:mm')
|
||||
}
|
||||
catch {
|
||||
$SelectedBackup.CreatedAt
|
||||
$window.Owner = $ownerWindow
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
$selectedForwardFeatureIds = @(Get-SelectedForwardFeatureIdsFromBackup -SelectedBackup $SelectedBackup)
|
||||
$selectedUndoFeatureIds = @(Get-SelectedUndoFeatureIdsFromBackup -SelectedBackup $SelectedBackup)
|
||||
|
||||
$seenForwardFeatureIds = New-Object 'System.Collections.Generic.HashSet[string]' ([System.StringComparer]::OrdinalIgnoreCase)
|
||||
foreach ($featureId in $selectedForwardFeatureIds) {
|
||||
[void]$seenForwardFeatureIds.Add([string]$featureId)
|
||||
}
|
||||
|
||||
$filteredUndoFeatureIds = New-Object System.Collections.Generic.List[string]
|
||||
foreach ($featureId in $selectedUndoFeatureIds) {
|
||||
if ($seenForwardFeatureIds.Contains([string]$featureId)) {
|
||||
continue
|
||||
}
|
||||
|
||||
$filteredUndoFeatureIds.Add([string]$featureId)
|
||||
}
|
||||
|
||||
$forwardFeatureLists = Get-RestoreBackupFeatureLists -SelectedFeatureIds $selectedForwardFeatureIds -Features $script:Features
|
||||
$undoFeatureLists = Get-RestoreBackupFeatureLists -SelectedFeatureIds @($filteredUndoFeatureIds.ToArray()) -Features $script:Features
|
||||
$combinedFeatureLists = Get-RestoreBackupFeatureLists -SelectedFeatureIds (Get-SelectedFeatureIdsFromBackup -SelectedBackup $SelectedBackup) -Features $script:Features
|
||||
|
||||
$revertibleFeaturesList = @($forwardFeatureLists.Revertible)
|
||||
$reappliedFeaturesList = @($undoFeatureLists.Revertible)
|
||||
$nonRevertibleFeaturesList = @($combinedFeatureLists.NonRevertible)
|
||||
Write-Host "Backup overview prepared. Reverted=$($revertibleFeaturesList.Count), ReApplied=$($reappliedFeaturesList.Count), NonRevertible=$($nonRevertibleFeaturesList.Count)"
|
||||
|
||||
if ($revertibleFeaturesList.Count -eq 0 -and $reappliedFeaturesList.Count -eq 0) {
|
||||
throw 'The selected backup does not contain any changes that can be restored.'
|
||||
}
|
||||
|
||||
$backupFileText.Text = Split-Path $SelectedBackupFilePath -Leaf
|
||||
$backupCreatedText.Text = $createdText
|
||||
$backupTargetText.Text = Get-FriendlyRegistryBackupTarget -Target ([string]$SelectedBackup.Target)
|
||||
$featuresItemsControl.ItemsSource = $revertibleFeaturesList
|
||||
$overviewFeaturesSection.Visibility = if ($revertibleFeaturesList.Count -gt 0) { 'Visible' } else { 'Collapsed' }
|
||||
$reappliedFeaturesItemsControl.ItemsSource = $reappliedFeaturesList
|
||||
if ($reappliedFeaturesList.Count -gt 0) { $reappliedPanel.Visibility = 'Visible' } else { $reappliedPanel.Visibility = 'Collapsed' }
|
||||
if ($revertibleFeaturesList.Count -gt 0 -and $reappliedFeaturesList.Count -gt 0) { $reappliedSeparator.Visibility = 'Visible' } else { $reappliedSeparator.Visibility = 'Collapsed' }
|
||||
$overviewSummaryText.Visibility = 'Collapsed'
|
||||
$nonRevertibleFeaturesItemsControl.ItemsSource = $nonRevertibleFeaturesList
|
||||
|
||||
$hasNonRevertibleItems = ($nonRevertibleFeaturesList.Count -gt 0)
|
||||
if ($hasNonRevertibleItems) { $nonRevertiblePanel.Visibility = 'Visible' } else { $nonRevertiblePanel.Visibility = 'Collapsed' }
|
||||
if ($hasNonRevertibleItems -and ($revertibleFeaturesList.Count -gt 0 -or $reappliedFeaturesList.Count -gt 0)) { $nonRevertibleSeparator.Visibility = 'Visible' } else { $nonRevertibleSeparator.Visibility = 'Collapsed' }
|
||||
$introInfoPanel.Visibility = 'Collapsed'
|
||||
$overviewPanel.Visibility = 'Visible'
|
||||
|
||||
return $true
|
||||
}
|
||||
|
||||
$handleRegistryPrimaryAction = {
|
||||
if ($state.SelectedRegistryBackup) {
|
||||
$window.Tag = @{
|
||||
Result = 'RestoreRegistry'
|
||||
Backup = $state.SelectedRegistryBackup
|
||||
}
|
||||
$window.DialogResult = $true
|
||||
$window.Close()
|
||||
return
|
||||
}
|
||||
|
||||
$openDialog = New-Object Microsoft.Win32.OpenFileDialog
|
||||
$openDialog.Title = 'Select Registry Backup File'
|
||||
$openDialog.Filter = 'Registry backup (*.json)|*.json'
|
||||
$openDialog.DefaultExt = '.json'
|
||||
$openDialog.InitialDirectory = $script:RegistryBackupsPath
|
||||
|
||||
if ($openDialog.ShowDialog($window) -ne $true) {
|
||||
return
|
||||
}
|
||||
|
||||
Write-Host "Backup file selected: $($openDialog.FileName)"
|
||||
|
||||
try {
|
||||
$selectedBackup = Import-RegistryBackup -FilePath $openDialog.FileName
|
||||
Set-WindowThemeResources -window $window -usesDarkMode $usesDarkMode
|
||||
}
|
||||
catch { }
|
||||
|
||||
if (-not (& $showRegistryOverview -SelectedBackup $selectedBackup -SelectedBackupFilePath $openDialog.FileName)) {
|
||||
$titleBar = $window.FindName('TitleBar')
|
||||
$titleText = $window.FindName('TitleText')
|
||||
$closeBtn = $window.FindName('CloseBtn')
|
||||
$backBtn = $window.FindName('BackBtn')
|
||||
$primaryActionBtn = $window.FindName('PrimaryActionBtn')
|
||||
$chooseRegistryBtn = $window.FindName('ChooseRegistryBtn')
|
||||
$chooseStartMenuBtn = $window.FindName('ChooseStartMenuBtn')
|
||||
$restoreModeTabs = $window.FindName('RestoreModeTabs')
|
||||
$startMenuIntroPanel = $window.FindName('StartMenuIntroPanel')
|
||||
$startMenuScopeCombo = $window.FindName('StartMenuScopeCombo')
|
||||
$startMenuAutoBackupCheck = $window.FindName('StartMenuAutoBackupCheck')
|
||||
$introInfoPanel = $window.FindName('IntroInfoPanel')
|
||||
$overviewPanel = $window.FindName('OverviewPanel')
|
||||
$overviewFeaturesSection = $window.FindName('OverviewFeaturesSection')
|
||||
$overviewSummaryText = $window.FindName('OverviewSummaryText')
|
||||
$backupFileText = $window.FindName('BackupFileText')
|
||||
$backupCreatedText = $window.FindName('BackupCreatedText')
|
||||
$backupTargetText = $window.FindName('BackupTargetText')
|
||||
$featuresItemsControl = $window.FindName('FeaturesItemsControl')
|
||||
$reappliedSeparator = $window.FindName('ReappliedSeparator')
|
||||
$reappliedPanel = $window.FindName('ReappliedPanel')
|
||||
$reappliedFeaturesItemsControl = $window.FindName('ReappliedFeaturesItemsControl')
|
||||
$nonRevertibleSeparator = $window.FindName('NonRevertibleSeparator')
|
||||
$nonRevertiblePanel = $window.FindName('NonRevertiblePanel')
|
||||
$nonRevertibleFeaturesItemsControl = $window.FindName('NonRevertibleFeaturesItemsControl')
|
||||
$nonRevertibleWikiLink = $window.FindName('NonRevertibleWikiLink')
|
||||
|
||||
$titleBar.Add_MouseLeftButtonDown({ $window.DragMove() })
|
||||
$window.Tag = New-RestoreDialogState
|
||||
$chooseRegistryBtn.IsDefault = $true
|
||||
|
||||
$state = @{ WizardStep = 'SelectType'; SelectedRegistryBackup = $null; SelectedStartMenuBackupFilePath = $null }
|
||||
|
||||
$getStartMenuScopeInfo = {
|
||||
$isAllUsersScope = ($startMenuScopeCombo.SelectedItem.Tag -eq 'AllUsers')
|
||||
$scopeValue = if ($isAllUsersScope) { 'AllUsers' } else { 'CurrentUser' }
|
||||
$summaryScopeText = if ($isAllUsersScope) { Get-Translation -Key 'RestoreStartMenuSummaryScopeAllUsers' } else { Get-Translation -Key 'RestoreStartMenuSummaryScopeCurrentUser' }
|
||||
|
||||
return [PSCustomObject]@{
|
||||
Scope = $scopeValue
|
||||
Target = $scopeValue
|
||||
SummaryText = $summaryScopeText
|
||||
}
|
||||
}
|
||||
|
||||
$showStartMenuIntroState = {
|
||||
$backupFileText.Text = Get-Translation -Key 'RestoreFileNotSelected'
|
||||
$backupCreatedText.Text = Get-Translation -Key 'RestoreCreatedUnavailable'
|
||||
$overviewSummaryText.Visibility = 'Collapsed'
|
||||
$overviewPanel.Visibility = 'Collapsed'
|
||||
$startMenuIntroPanel.Visibility = 'Visible'
|
||||
$restoreModeTabs.SelectedIndex = 2
|
||||
}
|
||||
|
||||
$showStartMenuOverviewState = {
|
||||
param([string]$BackupFilePath)
|
||||
|
||||
$scopeInfo = & $getStartMenuScopeInfo
|
||||
$backupTargetText.Text = Get-FriendlyRegistryBackupTarget -Target $scopeInfo.Target
|
||||
$overviewSummaryText.Text = Get-Translation -Key 'RestoreStartMenuSummary' -FormatArgs @($scopeInfo.SummaryText)
|
||||
$backupFileText.Text = Split-Path -Path $BackupFilePath -Leaf
|
||||
|
||||
$createdText = Get-Translation -Key 'RestoreCreatedUnknown'
|
||||
try {
|
||||
$createdText = (Get-Item -LiteralPath $BackupFilePath -ErrorAction Stop).LastWriteTime.ToString('yyyy-MM-dd HH:mm')
|
||||
}
|
||||
catch { }
|
||||
$backupCreatedText.Text = $createdText
|
||||
|
||||
$overviewFeaturesSection.Visibility = 'Collapsed'
|
||||
$overviewSummaryText.Visibility = 'Visible'
|
||||
$reappliedSeparator.Visibility = 'Collapsed'
|
||||
$reappliedPanel.Visibility = 'Collapsed'
|
||||
$nonRevertibleSeparator.Visibility = 'Collapsed'
|
||||
$nonRevertiblePanel.Visibility = 'Collapsed'
|
||||
$introInfoPanel.Visibility = 'Collapsed'
|
||||
$overviewPanel.Visibility = 'Visible'
|
||||
$restoreModeTabs.SelectedIndex = 1
|
||||
}
|
||||
|
||||
$updateStartMenuOverviewPanel = {
|
||||
if ($state.WizardStep -ne 'StartMenu') {
|
||||
return
|
||||
}
|
||||
}
|
||||
catch {
|
||||
Show-MessageBox -Owner $window -Title 'Invalid Backup File' -Message "The selected file could not be loaded:`n$($_.Exception.Message)" -Button 'OK' -Icon 'Error' | Out-Null
|
||||
return
|
||||
|
||||
if ([string]::IsNullOrWhiteSpace($state.SelectedStartMenuBackupFilePath)) {
|
||||
& $showStartMenuIntroState
|
||||
return
|
||||
}
|
||||
|
||||
& $showStartMenuOverviewState $state.SelectedStartMenuBackupFilePath
|
||||
}
|
||||
|
||||
$state.SelectedRegistryBackup = $selectedBackup
|
||||
$primaryActionBtn.Content = 'Restore from backup'
|
||||
}
|
||||
$updateStartMenuPrimaryActionText = {
|
||||
if ($state.WizardStep -ne 'StartMenu') {
|
||||
return
|
||||
}
|
||||
|
||||
$handleStartMenuPrimaryAction = {
|
||||
$scope = (& $getStartMenuScopeInfo).Scope
|
||||
$useManualBackupFile = -not ($startMenuAutoBackupCheck.IsChecked -eq $true)
|
||||
$isAutoBackupEnabled = ($startMenuAutoBackupCheck.IsChecked -eq $true)
|
||||
$hasSelectedManualFile = -not [string]::IsNullOrWhiteSpace($state.SelectedStartMenuBackupFilePath)
|
||||
if ($isAutoBackupEnabled -or $hasSelectedManualFile) {
|
||||
$primaryActionBtn.Content = Get-Translation -Key 'RestorePrimaryActionRestoreBackup'
|
||||
}
|
||||
else {
|
||||
$primaryActionBtn.Content = Get-Translation -Key 'RestorePrimaryActionSelectBackupFile'
|
||||
}
|
||||
}
|
||||
|
||||
$refreshStartMenuUi = {
|
||||
& $updateStartMenuOverviewPanel
|
||||
& $updateStartMenuPrimaryActionText
|
||||
}
|
||||
|
||||
$enterSelectTypeStep = {
|
||||
$titleText.Text = Get-Translation -Key 'RestoreWindowTitle'
|
||||
$restoreModeTabs.SelectedIndex = 0
|
||||
$backBtn.Visibility = 'Visible'
|
||||
$backBtn.Content = Get-Translation -Key 'RestoreBackBtnCancel'
|
||||
$primaryActionBtn.Visibility = 'Collapsed'
|
||||
$chooseRegistryBtn.IsDefault = $true
|
||||
$primaryActionBtn.IsDefault = $false
|
||||
}
|
||||
|
||||
$enterRegistryStep = {
|
||||
$titleText.Text = Get-Translation -Key 'RestoreRegistryStepTitle'
|
||||
$restoreModeTabs.SelectedIndex = 1
|
||||
$introInfoPanel.Visibility = 'Visible'
|
||||
$overviewPanel.Visibility = 'Collapsed'
|
||||
$overviewFeaturesSection.Visibility = 'Visible'
|
||||
$overviewSummaryText.Visibility = 'Collapsed'
|
||||
$backBtn.Visibility = 'Visible'
|
||||
$backBtn.Content = Get-Translation -Key 'RestoreBackBtnBack'
|
||||
$primaryActionBtn.Visibility = 'Visible'
|
||||
$primaryActionBtn.Content = Get-Translation -Key 'RestorePrimaryActionSelectBackupFile'
|
||||
$primaryActionBtn.IsDefault = $true
|
||||
$chooseRegistryBtn.IsDefault = $false
|
||||
}
|
||||
|
||||
$enterStartMenuStep = {
|
||||
$titleText.Text = Get-Translation -Key 'RestoreStartMenuStepTitle'
|
||||
$restoreModeTabs.SelectedIndex = 2
|
||||
$backBtn.Visibility = 'Visible'
|
||||
$backBtn.Content = Get-Translation -Key 'RestoreBackBtnBack'
|
||||
$primaryActionBtn.Visibility = 'Visible'
|
||||
$primaryActionBtn.IsDefault = $true
|
||||
$chooseRegistryBtn.IsDefault = $false
|
||||
|
||||
# Show intro panel so user can configure scope & auto-detect
|
||||
$startMenuAutoBackupCheck.IsChecked = $true
|
||||
$state.SelectedStartMenuBackupFilePath = $null
|
||||
& $refreshStartMenuUi
|
||||
}
|
||||
|
||||
$showRegistryOverview = {
|
||||
param(
|
||||
[Parameter(Mandatory = $true)]
|
||||
$SelectedBackup,
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string]$SelectedBackupFilePath
|
||||
)
|
||||
|
||||
$createdText = if ([string]::IsNullOrWhiteSpace($SelectedBackup.CreatedAt)) {
|
||||
Get-Translation -Key 'RestoreCreatedUnknown'
|
||||
}
|
||||
else {
|
||||
try {
|
||||
[DateTime]::Parse($SelectedBackup.CreatedAt).ToString('yyyy-MM-dd HH:mm')
|
||||
}
|
||||
catch {
|
||||
$SelectedBackup.CreatedAt
|
||||
}
|
||||
}
|
||||
|
||||
$selectedForwardFeatureIds = @(Get-SelectedForwardFeatureIdsFromBackup -SelectedBackup $SelectedBackup)
|
||||
$selectedUndoFeatureIds = @(Get-SelectedUndoFeatureIdsFromBackup -SelectedBackup $SelectedBackup)
|
||||
|
||||
$seenForwardFeatureIds = New-Object 'System.Collections.Generic.HashSet[string]' ([System.StringComparer]::OrdinalIgnoreCase)
|
||||
foreach ($featureId in $selectedForwardFeatureIds) {
|
||||
[void]$seenForwardFeatureIds.Add([string]$featureId)
|
||||
}
|
||||
|
||||
$filteredUndoFeatureIds = New-Object System.Collections.Generic.List[string]
|
||||
foreach ($featureId in $selectedUndoFeatureIds) {
|
||||
if ($seenForwardFeatureIds.Contains([string]$featureId)) {
|
||||
continue
|
||||
}
|
||||
|
||||
$filteredUndoFeatureIds.Add([string]$featureId)
|
||||
}
|
||||
|
||||
$forwardFeatureLists = Get-RestoreBackupFeatureLists -SelectedFeatureIds $selectedForwardFeatureIds -Features $script:Features
|
||||
$undoFeatureLists = Get-RestoreBackupFeatureLists -SelectedFeatureIds @($filteredUndoFeatureIds.ToArray()) -Features $script:Features
|
||||
$combinedFeatureLists = Get-RestoreBackupFeatureLists -SelectedFeatureIds (Get-SelectedFeatureIdsFromBackup -SelectedBackup $SelectedBackup) -Features $script:Features
|
||||
|
||||
$revertibleFeaturesList = @($forwardFeatureLists.Revertible)
|
||||
$reappliedFeaturesList = @($undoFeatureLists.Revertible)
|
||||
$nonRevertibleFeaturesList = @($combinedFeatureLists.NonRevertible)
|
||||
Write-Host "Backup overview prepared. Reverted=$($revertibleFeaturesList.Count), ReApplied=$($reappliedFeaturesList.Count), NonRevertible=$($nonRevertibleFeaturesList.Count)"
|
||||
|
||||
if ($revertibleFeaturesList.Count -eq 0 -and $reappliedFeaturesList.Count -eq 0) {
|
||||
throw (Get-Translation -Key 'RestoreNoRestorableChangesError')
|
||||
}
|
||||
|
||||
$backupFileText.Text = Split-Path $SelectedBackupFilePath -Leaf
|
||||
$backupCreatedText.Text = $createdText
|
||||
$backupTargetText.Text = Get-FriendlyRegistryBackupTarget -Target ([string]$SelectedBackup.Target)
|
||||
$featuresItemsControl.ItemsSource = $revertibleFeaturesList
|
||||
$overviewFeaturesSection.Visibility = if ($revertibleFeaturesList.Count -gt 0) { 'Visible' } else { 'Collapsed' }
|
||||
$reappliedFeaturesItemsControl.ItemsSource = $reappliedFeaturesList
|
||||
if ($reappliedFeaturesList.Count -gt 0) { $reappliedPanel.Visibility = 'Visible' } else { $reappliedPanel.Visibility = 'Collapsed' }
|
||||
if ($revertibleFeaturesList.Count -gt 0 -and $reappliedFeaturesList.Count -gt 0) { $reappliedSeparator.Visibility = 'Visible' } else { $reappliedSeparator.Visibility = 'Collapsed' }
|
||||
$overviewSummaryText.Visibility = 'Collapsed'
|
||||
$nonRevertibleFeaturesItemsControl.ItemsSource = $nonRevertibleFeaturesList
|
||||
|
||||
$hasNonRevertibleItems = ($nonRevertibleFeaturesList.Count -gt 0)
|
||||
if ($hasNonRevertibleItems) { $nonRevertiblePanel.Visibility = 'Visible' } else { $nonRevertiblePanel.Visibility = 'Collapsed' }
|
||||
if ($hasNonRevertibleItems -and ($revertibleFeaturesList.Count -gt 0 -or $reappliedFeaturesList.Count -gt 0)) { $nonRevertibleSeparator.Visibility = 'Visible' } else { $nonRevertibleSeparator.Visibility = 'Collapsed' }
|
||||
$introInfoPanel.Visibility = 'Collapsed'
|
||||
$overviewPanel.Visibility = 'Visible'
|
||||
|
||||
return $true
|
||||
}
|
||||
|
||||
$handleRegistryPrimaryAction = {
|
||||
if ($state.SelectedRegistryBackup) {
|
||||
$window.Tag = @{
|
||||
Result = 'RestoreRegistry'
|
||||
Backup = $state.SelectedRegistryBackup
|
||||
}
|
||||
$window.DialogResult = $true
|
||||
$window.Close()
|
||||
return
|
||||
}
|
||||
|
||||
if ($useManualBackupFile -and [string]::IsNullOrWhiteSpace($state.SelectedStartMenuBackupFilePath)) {
|
||||
$openDialog = New-Object Microsoft.Win32.OpenFileDialog
|
||||
$openDialog.Title = 'Select Start Menu Backup File'
|
||||
$openDialog.Filter = 'Start Menu backup (*.bak)|*.bak'
|
||||
$openDialog.InitialDirectory = "$env:LOCALAPPDATA\Packages\Microsoft.Windows.StartMenuExperienceHost_cw5n1h2txyewy\LocalState"
|
||||
$openDialog.DefaultExt = '.bak'
|
||||
$openDialog.Title = Get-Translation -Key 'RestoreSelectRegistryFileDialogTitle'
|
||||
$openDialog.Filter = 'Registry backup (*.json)|*.json'
|
||||
$openDialog.DefaultExt = '.json'
|
||||
$openDialog.InitialDirectory = $script:RegistryBackupsPath
|
||||
|
||||
if ($openDialog.ShowDialog($window) -ne $true) {
|
||||
return
|
||||
}
|
||||
|
||||
$state.SelectedStartMenuBackupFilePath = $openDialog.FileName
|
||||
Write-Host "Selected Start Menu backup file: $($state.SelectedStartMenuBackupFilePath)"
|
||||
& $refreshStartMenuUi
|
||||
return
|
||||
}
|
||||
Write-Host "Backup file selected: $($openDialog.FileName)"
|
||||
|
||||
if (-not $useManualBackupFile) {
|
||||
$scopeInfo = & $getStartMenuScopeInfo
|
||||
$autoBackupPath = Get-StartMenuBackupPath -Scope $scopeInfo.Scope
|
||||
if ($null -eq $autoBackupPath) {
|
||||
$scopeText = $scopeInfo.SummaryText
|
||||
Show-MessageBox -Owner $window -Title 'No Backup Found' -Message "No Start Menu backup file was found for $scopeText. Uncheck 'Automatically find Start Menu backup' to select a backup file manually." -Button 'OK' -Icon 'Warning' | Out-Null
|
||||
try {
|
||||
$selectedBackup = Import-RegistryBackup -FilePath $openDialog.FileName
|
||||
|
||||
if (-not (& $showRegistryOverview -SelectedBackup $selectedBackup -SelectedBackupFilePath $openDialog.FileName)) {
|
||||
return
|
||||
}
|
||||
}
|
||||
catch {
|
||||
Show-MessageBox -Owner $window -Title (Get-Translation -Key 'RestoreInvalidBackupTitle') -Message (Get-Translation -Key 'RestoreInvalidBackupMessage' -FormatArgs @($_.Exception.Message)) -Button 'OK' -Icon 'Error' | Out-Null
|
||||
return
|
||||
}
|
||||
$state.SelectedStartMenuBackupFilePath = if ($scopeInfo.Scope -eq 'CurrentUser') { $autoBackupPath } else { $null }
|
||||
|
||||
$state.SelectedRegistryBackup = $selectedBackup
|
||||
$primaryActionBtn.Content = Get-Translation -Key 'RestorePrimaryActionRestoreFromBackup'
|
||||
}
|
||||
|
||||
$window.Tag = @{
|
||||
Result = 'Restore-StartMenu'
|
||||
StartMenuScope = $scope
|
||||
UseManualBackupFile = $useManualBackupFile
|
||||
BackupFilePath = $state.SelectedStartMenuBackupFilePath
|
||||
$handleStartMenuPrimaryAction = {
|
||||
$scope = (& $getStartMenuScopeInfo).Scope
|
||||
$useManualBackupFile = -not ($startMenuAutoBackupCheck.IsChecked -eq $true)
|
||||
|
||||
if ($useManualBackupFile -and [string]::IsNullOrWhiteSpace($state.SelectedStartMenuBackupFilePath)) {
|
||||
$openDialog = New-Object Microsoft.Win32.OpenFileDialog
|
||||
$openDialog.Title = Get-Translation -Key 'RestoreSelectStartMenuFileDialogTitle'
|
||||
$openDialog.Filter = 'Start Menu backup (*.bak)|*.bak'
|
||||
$openDialog.InitialDirectory = "$env:LOCALAPPDATA\Packages\Microsoft.Windows.StartMenuExperienceHost_cw5n1h2txyewy\LocalState"
|
||||
$openDialog.DefaultExt = '.bak'
|
||||
|
||||
if ($openDialog.ShowDialog($window) -ne $true) {
|
||||
return
|
||||
}
|
||||
|
||||
$state.SelectedStartMenuBackupFilePath = $openDialog.FileName
|
||||
Write-Host "Selected Start Menu backup file: $($state.SelectedStartMenuBackupFilePath)"
|
||||
& $refreshStartMenuUi
|
||||
return
|
||||
}
|
||||
|
||||
if (-not $useManualBackupFile) {
|
||||
$scopeInfo = & $getStartMenuScopeInfo
|
||||
$autoBackupPath = Get-StartMenuBackupPath -Scope $scopeInfo.Scope
|
||||
if ($null -eq $autoBackupPath) {
|
||||
$scopeText = $scopeInfo.SummaryText
|
||||
Show-MessageBox -Owner $window -Title (Get-Translation -Key 'RestoreNoBackupFoundTitle') -Message (Get-Translation -Key 'RestoreNoBackupFoundMessage' -FormatArgs @($scopeText)) -Button 'OK' -Icon 'Warning' | Out-Null
|
||||
return
|
||||
}
|
||||
$state.SelectedStartMenuBackupFilePath = if ($scopeInfo.Scope -eq 'CurrentUser') { $autoBackupPath } else { $null }
|
||||
}
|
||||
|
||||
$window.Tag = @{
|
||||
Result = 'Restore-StartMenu'
|
||||
StartMenuScope = $scope
|
||||
UseManualBackupFile = $useManualBackupFile
|
||||
BackupFilePath = $state.SelectedStartMenuBackupFilePath
|
||||
}
|
||||
$window.DialogResult = $true
|
||||
$window.Close()
|
||||
}
|
||||
$window.DialogResult = $true
|
||||
$window.Close()
|
||||
}
|
||||
|
||||
$setWizardStep = {
|
||||
param([string]$step)
|
||||
$setWizardStep = {
|
||||
param([string]$step)
|
||||
|
||||
$state.WizardStep = $step
|
||||
$state.WizardStep = $step
|
||||
|
||||
switch ($step) {
|
||||
'SelectType' { & $enterSelectTypeStep }
|
||||
'Registry' { & $enterRegistryStep }
|
||||
'StartMenu' { & $enterStartMenuStep }
|
||||
switch ($step) {
|
||||
'SelectType' { & $enterSelectTypeStep }
|
||||
'Registry' { & $enterRegistryStep }
|
||||
'StartMenu' { & $enterStartMenuStep }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$startMenuAutoBackupCheck.Add_Checked({
|
||||
$state.SelectedStartMenuBackupFilePath = $null
|
||||
& $refreshStartMenuUi
|
||||
})
|
||||
$startMenuAutoBackupCheck.Add_Unchecked({
|
||||
& $refreshStartMenuUi
|
||||
})
|
||||
$startMenuAutoBackupCheck.Add_Checked({
|
||||
$state.SelectedStartMenuBackupFilePath = $null
|
||||
& $refreshStartMenuUi
|
||||
})
|
||||
$startMenuAutoBackupCheck.Add_Unchecked({
|
||||
& $refreshStartMenuUi
|
||||
})
|
||||
|
||||
$startMenuScopeCombo.Add_SelectionChanged({
|
||||
$state.SelectedStartMenuBackupFilePath = $null
|
||||
& $refreshStartMenuUi
|
||||
})
|
||||
$startMenuScopeCombo.Add_SelectionChanged({
|
||||
$state.SelectedStartMenuBackupFilePath = $null
|
||||
& $refreshStartMenuUi
|
||||
})
|
||||
|
||||
$nonRevertibleWikiLink.Add_MouseLeftButtonUp({
|
||||
try {
|
||||
Start-Process 'https://github.com/Raphire/Win11Debloat/wiki/Reverting-Changes' | Out-Null
|
||||
}
|
||||
catch { }
|
||||
})
|
||||
$nonRevertibleWikiLink.Add_MouseLeftButtonUp({
|
||||
try {
|
||||
Start-Process 'https://github.com/Raphire/Win11Debloat/wiki/Reverting-Changes' | Out-Null
|
||||
}
|
||||
catch { }
|
||||
})
|
||||
|
||||
$closeBtn.Add_Click({
|
||||
$window.Tag = New-RestoreDialogState
|
||||
$window.DialogResult = $false
|
||||
$window.Close()
|
||||
})
|
||||
|
||||
$chooseRegistryBtn.Add_Click({ & $setWizardStep 'Registry' })
|
||||
$chooseStartMenuBtn.Add_Click({ & $setWizardStep 'StartMenu' })
|
||||
|
||||
$backBtn.Add_Click({
|
||||
if ($state.WizardStep -eq 'SelectType') {
|
||||
$closeBtn.Add_Click({
|
||||
$window.Tag = New-RestoreDialogState
|
||||
$window.DialogResult = $false
|
||||
$window.Close()
|
||||
return
|
||||
}
|
||||
})
|
||||
|
||||
if ($state.WizardStep -eq 'Registry') {
|
||||
$state.SelectedRegistryBackup = $null
|
||||
}
|
||||
$chooseRegistryBtn.Add_Click({ & $setWizardStep 'Registry' })
|
||||
$chooseStartMenuBtn.Add_Click({ & $setWizardStep 'StartMenu' })
|
||||
|
||||
if ($state.WizardStep -eq 'StartMenu') {
|
||||
$state.SelectedStartMenuBackupFilePath = $null
|
||||
$startMenuAutoBackupCheck.IsChecked = $true
|
||||
}
|
||||
$backBtn.Add_Click({
|
||||
if ($state.WizardStep -eq 'SelectType') {
|
||||
$window.Tag = New-RestoreDialogState
|
||||
$window.DialogResult = $false
|
||||
$window.Close()
|
||||
return
|
||||
}
|
||||
|
||||
if ($state.WizardStep -eq 'Registry') {
|
||||
$state.SelectedRegistryBackup = $null
|
||||
}
|
||||
|
||||
if ($state.WizardStep -eq 'StartMenu') {
|
||||
$state.SelectedStartMenuBackupFilePath = $null
|
||||
$startMenuAutoBackupCheck.IsChecked = $true
|
||||
}
|
||||
|
||||
& $setWizardStep 'SelectType'
|
||||
})
|
||||
|
||||
$primaryActionBtn.Add_Click({
|
||||
switch ($state.WizardStep) {
|
||||
'Registry' { & $handleRegistryPrimaryAction }
|
||||
'StartMenu' { & $handleStartMenuPrimaryAction }
|
||||
}
|
||||
})
|
||||
|
||||
$window.Add_KeyDown({
|
||||
param($source, $e)
|
||||
if ($e.Key -eq 'Escape') {
|
||||
$window.Tag = New-RestoreDialogState
|
||||
$window.DialogResult = $false
|
||||
$window.Close()
|
||||
}
|
||||
})
|
||||
|
||||
& $setWizardStep 'SelectType'
|
||||
})
|
||||
|
||||
$primaryActionBtn.Add_Click({
|
||||
switch ($state.WizardStep) {
|
||||
'Registry' { & $handleRegistryPrimaryAction }
|
||||
'StartMenu' { & $handleStartMenuPrimaryAction }
|
||||
try {
|
||||
$null = $window.ShowDialog()
|
||||
}
|
||||
})
|
||||
|
||||
$window.Add_KeyDown({
|
||||
param($source, $e)
|
||||
if ($e.Key -eq 'Escape') {
|
||||
$window.Tag = New-RestoreDialogState
|
||||
$window.DialogResult = $false
|
||||
$window.Close()
|
||||
catch {
|
||||
$innerMessage = if ($_.Exception.InnerException) { $_.Exception.InnerException.Message } else { 'None' }
|
||||
throw "Failed to show restore backup dialog. Error: $($_.Exception.Message) Inner: $innerMessage"
|
||||
}
|
||||
})
|
||||
|
||||
& $setWizardStep 'SelectType'
|
||||
|
||||
try {
|
||||
$null = $window.ShowDialog()
|
||||
}
|
||||
catch {
|
||||
$innerMessage = if ($_.Exception.InnerException) { $_.Exception.InnerException.Message } else { 'None' }
|
||||
throw "Failed to show restore backup dialog. Error: $($_.Exception.Message) Inner: $innerMessage"
|
||||
}
|
||||
finally {
|
||||
if ($overlay -and -not $overlayWasAlreadyVisible) {
|
||||
|
||||
@@ -35,10 +35,10 @@ function Show-RestoreBackupWindow {
|
||||
if ($restoreOpResult -and $restoreOpResult.Result) {
|
||||
$restoreResult.RestoredRegistry = $true
|
||||
if ($script:Params.ContainsKey("WhatIf")) {
|
||||
$successMessage = '[WhatIf] Registry backup would be restored (no changes made).'
|
||||
$successMessage = Get-Translation -Key 'RestoreRegistryWhatIfMessage'
|
||||
}
|
||||
else {
|
||||
$successMessage = 'Registry backup restored successfully. Some changes may require a restart to take effect.'
|
||||
$successMessage = Get-Translation -Key 'RestoreRegistrySuccessMessage'
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -70,22 +70,22 @@ function Show-RestoreBackupWindow {
|
||||
|
||||
if ($successCount -eq 0) {
|
||||
$errorSummary = ($resultEntries | ForEach-Object { $_.Message }) -join [Environment]::NewLine
|
||||
throw "Failed to restore the Start Menu backup.`n$errorSummary"
|
||||
throw (Get-Translation -Key 'RestoreStartMenuFailedMessage' -FormatArgs @($errorSummary))
|
||||
}
|
||||
|
||||
if ($failedEntries.Count -gt 0) {
|
||||
$failureSummary = ($failedEntries | ForEach-Object { $_.Message }) -join [Environment]::NewLine
|
||||
$warningMessage = "The Start Menu backup was successfully restored for $successCount user(s).`nSome users could not be restored:`n$failureSummary"
|
||||
$warningMessage = Get-Translation -Key 'RestoreStartMenuPartialSuccessMessage' -FormatArgs @($successCount, $failureSummary)
|
||||
}
|
||||
else {
|
||||
if ($script:Params.ContainsKey("WhatIf")) {
|
||||
$successMessage = '[WhatIf] Start Menu backup would be restored (no changes made).'
|
||||
$successMessage = Get-Translation -Key 'RestoreStartMenuWhatIfMessage'
|
||||
}
|
||||
elseif ($scope -eq 'AllUsers') {
|
||||
$successMessage = "The Start Menu backup was successfully restored for all users. The changes will apply the next time users sign in."
|
||||
$successMessage = Get-Translation -Key 'RestoreStartMenuAllUsersSuccessMessage'
|
||||
}
|
||||
else {
|
||||
$successMessage = "The Start Menu backup was successfully restored for the current user. The changes will apply the next time you sign in."
|
||||
$successMessage = Get-Translation -Key 'RestoreStartMenuCurrentUserSuccessMessage'
|
||||
}
|
||||
}
|
||||
|
||||
@@ -94,19 +94,19 @@ function Show-RestoreBackupWindow {
|
||||
|
||||
if ($warningMessage) {
|
||||
Write-Host "$warningMessage"
|
||||
Show-MessageBox -Title 'Backup Restored' -Message $warningMessage -Icon Warning
|
||||
Show-MessageBox -Title (Get-Translation -Key 'RestoreBackupRestoredTitle') -Message $warningMessage -Icon Warning
|
||||
}
|
||||
elseif ($successMessage) {
|
||||
Write-Host "$successMessage"
|
||||
Show-MessageBox -Title 'Backup Restored' -Message $successMessage -Icon Success
|
||||
Show-MessageBox -Title (Get-Translation -Key 'RestoreBackupRestoredTitle') -Message $successMessage -Icon Success
|
||||
}
|
||||
|
||||
return $restoreResult
|
||||
}
|
||||
catch {
|
||||
$errorMessage = if ($_.Exception.Message) { $_.Exception.Message } else { 'An unexpected error occurred.' }
|
||||
$errorMessage = if ($_.Exception.Message) { $_.Exception.Message } else { Get-Translation -Key 'RestoreUnexpectedError' }
|
||||
Write-Error "Restore operation failed: $errorMessage"
|
||||
Show-MessageBox -Title 'Error' -Message "Restore failed: $errorMessage" -Icon Error
|
||||
Show-MessageBox -Title (Get-Translation -Key 'ErrorTitle') -Message (Get-Translation -Key 'RestoreOperationFailedMessage' -FormatArgs @($errorMessage)) -Icon Error
|
||||
return [PSCustomObject]@{
|
||||
RestoredRegistry = $false
|
||||
RestoredStartMenu = $false
|
||||
|
||||
@@ -6,6 +6,7 @@ param (
|
||||
[switch]$Silent,
|
||||
[switch]$Sysprep,
|
||||
[string]$LogPath,
|
||||
[string]$Language,
|
||||
[string]$User,
|
||||
[Alias('NoRestartExplorer')]
|
||||
[switch]$SkipExplorerRestart,
|
||||
|
||||
@@ -15,7 +15,7 @@ function Confirm-UnsafeAppRemoval {
|
||||
|
||||
# Microsoft Store warning
|
||||
if ($SelectedApps -contains "Microsoft.WindowsStore") {
|
||||
$result = Show-MessageBox -Message 'Are you sure that you wish to uninstall the Microsoft Store? This app cannot easily be reinstalled.' -Title 'Are you sure?' -Button 'YesNo' -Icon 'Warning' -Owner $Owner
|
||||
$result = Show-MessageBox -Message (Get-Translation -Key 'ConfirmRemoveStoreMessage') -Title (Get-Translation -Key 'ConfirmTitle') -Button 'YesNo' -Icon 'Warning' -Owner $Owner
|
||||
|
||||
if ($result -ne 'Yes') {
|
||||
return $false
|
||||
@@ -24,7 +24,7 @@ function Confirm-UnsafeAppRemoval {
|
||||
|
||||
# Windows Terminal warning
|
||||
if ($SelectedApps -contains "Microsoft.WindowsTerminal") {
|
||||
$result = Show-MessageBox -Message 'Are you sure that you wish to remove Windows Terminal? Windows Terminal is the default command-line app for Windows. Ensure you are not running Win11Debloat via Windows Terminal before proceeding to avoid a mid-process failure.' -Title 'Are you sure?' -Button 'YesNo' -Icon 'Warning' -Owner $Owner
|
||||
$result = Show-MessageBox -Message (Get-Translation -Key 'ConfirmRemoveTerminalMessage') -Title (Get-Translation -Key 'ConfirmTitle') -Button 'YesNo' -Icon 'Warning' -Owner $Owner
|
||||
|
||||
if ($result -ne 'Yes') {
|
||||
return $false
|
||||
|
||||
@@ -10,37 +10,37 @@ function Get-FriendlyRegistryBackupTarget {
|
||||
)
|
||||
|
||||
if ([string]::IsNullOrWhiteSpace($Target)) {
|
||||
return 'Unknown'
|
||||
return Get-Translation -Key 'RestoreTargetUnknown'
|
||||
}
|
||||
|
||||
if ($Target -eq 'DefaultUserProfile') {
|
||||
return 'Default user profile'
|
||||
return Get-Translation -Key 'RestoreTargetDefaultUserProfile'
|
||||
}
|
||||
|
||||
if ($Target -eq 'CurrentUser') {
|
||||
return 'Current user'
|
||||
return Get-Translation -Key 'RestoreTargetCurrentUser'
|
||||
}
|
||||
|
||||
if ($Target -eq 'AllUsers') {
|
||||
return 'All users'
|
||||
return Get-Translation -Key 'RestoreTargetAllUsers'
|
||||
}
|
||||
|
||||
if ($Target -like 'CurrentUser:*') {
|
||||
$userName = $Target.Substring(12)
|
||||
if ([string]::IsNullOrWhiteSpace($userName)) {
|
||||
return 'Current user'
|
||||
return Get-Translation -Key 'RestoreTargetCurrentUser'
|
||||
}
|
||||
|
||||
return "Current user ($userName)"
|
||||
return Get-Translation -Key 'RestoreTargetCurrentUserNamed' -FormatArgs @($userName)
|
||||
}
|
||||
|
||||
if ($Target -like 'User:*') {
|
||||
$userName = $Target.Substring(5)
|
||||
if ([string]::IsNullOrWhiteSpace($userName)) {
|
||||
return 'User'
|
||||
return Get-Translation -Key 'RestoreTargetUser'
|
||||
}
|
||||
|
||||
return "User ($userName)"
|
||||
return Get-Translation -Key 'RestoreTargetUserNamed' -FormatArgs @($userName)
|
||||
}
|
||||
|
||||
return $Target
|
||||
|
||||
@@ -15,7 +15,7 @@ function Get-RebootFeatureLabels {
|
||||
if ($script:Features.ContainsKey($paramKey) -and $script:Features[$paramKey].RequiresReboot -eq $true) {
|
||||
$feature = $script:Features[$paramKey]
|
||||
$isUndo = $script:UndoParams.ContainsKey($paramKey)
|
||||
$displayLabel = if ($isUndo -and $feature.UndoLabel) { $feature.UndoLabel } else { $feature.Label }
|
||||
$displayLabel = if ($isUndo -and $feature.UndoLabel) { Get-Translation -Key $paramKey -Field 'UndoLabel' -Section 'Features' } else { Get-Translation -Key $paramKey -Field 'Label' -Section 'Features' }
|
||||
|
||||
if (-not [string]::IsNullOrWhiteSpace([string]$displayLabel)) {
|
||||
[void]$rebootFeatureLabels.Add([string]$displayLabel)
|
||||
|
||||
@@ -14,24 +14,24 @@ function Test-ConfigConsistency {
|
||||
param($Config)
|
||||
|
||||
if (-not $Config) {
|
||||
return 'Configuration is empty or could not be read.'
|
||||
return Get-Translation -Key 'ConfigEmptyOrUnreadable'
|
||||
}
|
||||
|
||||
if (-not $Config.Version) {
|
||||
return 'Configuration is missing a Version field.'
|
||||
return Get-Translation -Key 'ConfigMissingVersion'
|
||||
}
|
||||
|
||||
if (-not $Config.Apps -and -not $Config.Tweaks -and -not $Config.Deployment) {
|
||||
return 'The configuration file contains no importable data.'
|
||||
return Get-Translation -Key 'ConfigNoImportableData'
|
||||
}
|
||||
|
||||
if ($null -ne $Config.Apps) {
|
||||
if ($Config.Apps -isnot [string] -and $Config.Apps -isnot [System.Collections.IEnumerable]) {
|
||||
return 'Configuration Apps entries must be strings.'
|
||||
return Get-Translation -Key 'ConfigAppsMustBeStrings'
|
||||
}
|
||||
foreach ($app in @($Config.Apps)) {
|
||||
if ($app -isnot [string]) {
|
||||
return 'Configuration Apps entries must be strings.'
|
||||
return Get-Translation -Key 'ConfigAppsMustBeStrings'
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -41,13 +41,13 @@ function Test-ConfigConsistency {
|
||||
if ($null -eq $category) { continue }
|
||||
|
||||
if ($category -is [string] -or $category -isnot [System.Collections.IEnumerable]) {
|
||||
return "Configuration $categoryName entries must contain Name and Value properties."
|
||||
return Get-Translation -Key 'ConfigEntriesMustHaveNameAndValue' -FormatArgs @($categoryName)
|
||||
}
|
||||
foreach ($setting in @($category)) {
|
||||
$hasName = if ($setting -is [System.Collections.IDictionary]) { $setting.Contains('Name') } else { $null -ne $setting.PSObject.Properties['Name'] }
|
||||
$hasValue = if ($setting -is [System.Collections.IDictionary]) { $setting.Contains('Value') } else { $null -ne $setting.PSObject.Properties['Value'] }
|
||||
if (-not $setting -or -not $hasName -or -not $hasValue -or $setting.Name -isnot [string] -or [string]::IsNullOrWhiteSpace($setting.Name)) {
|
||||
return "Configuration $categoryName entries must contain Name and Value properties."
|
||||
return Get-Translation -Key 'ConfigEntriesMustHaveNameAndValue' -FormatArgs @($categoryName)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -65,31 +65,31 @@ function Test-ConfigConsistency {
|
||||
$scopeIndex = $null
|
||||
if ($hasScope) {
|
||||
if (-not [int]::TryParse("$($lookup['AppRemovalScopeIndex'])", [ref]$scopeIndex) -or $scopeIndex -notin @(0, 1, 2)) {
|
||||
return 'AppRemovalScopeIndex must be a supported numeric value (0, 1, or 2).'
|
||||
return Get-Translation -Key 'ConfigInvalidAppRemovalScopeIndex'
|
||||
}
|
||||
}
|
||||
|
||||
$userIndex = $null
|
||||
if ($hasUser) {
|
||||
if (-not [int]::TryParse("$($lookup['UserSelectionIndex'])", [ref]$userIndex) -or $userIndex -notin @(0, 1, 2)) {
|
||||
return 'UserSelectionIndex must be a supported numeric value (0, 1, or 2).'
|
||||
return Get-Translation -Key 'ConfigInvalidUserSelectionIndex'
|
||||
}
|
||||
}
|
||||
|
||||
# "Current user only" (index 1) is only valid together with "Current User" (index 0)
|
||||
if ($hasScope -and $scopeIndex -eq 1) {
|
||||
if (-not $hasUser -or $userIndex -ne 0) {
|
||||
return "App removal scope 'Current user only' (AppRemovalScopeIndex 1) requires the deployment target 'Current User' (UserSelectionIndex 0)."
|
||||
return Get-Translation -Key 'ConfigScopeRequiresCurrentUserTarget'
|
||||
}
|
||||
}
|
||||
|
||||
# "Target user only" (index 2) is only valid together with "Other User" (index 1)
|
||||
if ($hasScope -and $scopeIndex -eq 2) {
|
||||
if (-not $hasUser -or $userIndex -ne 1) {
|
||||
return "App removal scope 'Target user only' (AppRemovalScopeIndex 2) requires the deployment target 'Other User' (UserSelectionIndex 1)."
|
||||
return Get-Translation -Key 'ConfigScopeRequiresOtherUserTarget'
|
||||
}
|
||||
if (-not $lookup.ContainsKey('OtherUsername') -or [string]::IsNullOrWhiteSpace("$($lookup['OtherUsername'])")) {
|
||||
return "App removal scope 'Target user only' (AppRemovalScopeIndex 2) requires an 'OtherUsername' value."
|
||||
return Get-Translation -Key 'ConfigScopeRequiresOtherUsername'
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,15 @@
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Validates a username for the "apply changes to another user" deployment target.
|
||||
|
||||
.DESCRIPTION
|
||||
Rejects an empty name, the current user's own name (that's the "Current User" option
|
||||
instead), and a name with no matching local profile, in that order.
|
||||
|
||||
.OUTPUTS
|
||||
PSCustomObject with IsValid (bool), UserName (the trimmed input), and Message (a
|
||||
translated string describing the validation result, success or failure alike).
|
||||
#>
|
||||
function Test-TargetUserName {
|
||||
param(
|
||||
[AllowNull()]
|
||||
@@ -11,7 +23,7 @@ function Test-TargetUserName {
|
||||
return [PSCustomObject]@{
|
||||
IsValid = $false
|
||||
UserName = $normalizedUserName
|
||||
Message = 'Please enter a username'
|
||||
Message = Get-Translation -Key 'UsernameValidationEmpty'
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,7 +31,7 @@ function Test-TargetUserName {
|
||||
return [PSCustomObject]@{
|
||||
IsValid = $false
|
||||
UserName = $normalizedUserName
|
||||
Message = "Cannot enter your own username, use 'Current User' option instead"
|
||||
Message = Get-Translation -Key 'UsernameValidationOwnUsername'
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,13 +39,13 @@ function Test-TargetUserName {
|
||||
return [PSCustomObject]@{
|
||||
IsValid = $false
|
||||
UserName = $normalizedUserName
|
||||
Message = 'User not found, please enter a valid username'
|
||||
Message = Get-Translation -Key 'UsernameValidationNotFound'
|
||||
}
|
||||
}
|
||||
|
||||
return [PSCustomObject]@{
|
||||
IsValid = $true
|
||||
UserName = $normalizedUserName
|
||||
Message = "User found: $normalizedUserName"
|
||||
Message = Get-Translation -Key 'UsernameValidationFound' -FormatArgs @($normalizedUserName)
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,10 @@
|
||||
BeforeAll {
|
||||
function Show-MessageBox { param($Message, $Title, $Button, $Icon, $Owner) 'Yes' }
|
||||
. (Join-Path $PSScriptRoot '..\Scripts\FileIO\Import-JsonFile.ps1')
|
||||
. (Join-Path $PSScriptRoot '..\Scripts\FileIO\Import-LanguageFile.ps1')
|
||||
. (Join-Path $PSScriptRoot '..\Scripts\Helpers\Confirm-UnsafeAppRemoval.ps1')
|
||||
$script:LanguagesPath = Join-Path $PSScriptRoot '..\Config\Languages'
|
||||
$script:Lang = Import-LanguageFile -LanguageCode 'en-US'
|
||||
}
|
||||
|
||||
Describe 'Confirm-UnsafeAppRemoval' {
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
BeforeAll {
|
||||
$friendlyTargetScriptPath = Join-Path $PSScriptRoot '..\Scripts\Helpers\Get-FriendlyRegistryBackupTarget.ps1'
|
||||
. $friendlyTargetScriptPath
|
||||
. (Join-Path $PSScriptRoot '..\Scripts\FileIO\Import-JsonFile.ps1')
|
||||
. (Join-Path $PSScriptRoot '..\Scripts\FileIO\Import-LanguageFile.ps1')
|
||||
$script:LanguagesPath = Join-Path $PSScriptRoot '..\Config\Languages'
|
||||
$script:Lang = Import-LanguageFile -LanguageCode 'en-US'
|
||||
}
|
||||
|
||||
Describe 'Get-FriendlyRegistryBackupTarget' {
|
||||
|
||||
@@ -1,6 +1,29 @@
|
||||
BeforeAll {
|
||||
$rebootFeatureLabelsScriptPath = Join-Path $PSScriptRoot '..\Scripts\Helpers\Get-RebootFeatureLabels.ps1'
|
||||
. $rebootFeatureLabelsScriptPath
|
||||
. (Join-Path $PSScriptRoot '..\Scripts\FileIO\Import-LanguageFile.ps1')
|
||||
|
||||
# Builds a $script:Lang fixture whose Features section mirrors $script:Features exactly,
|
||||
# so Get-Translation resolves the same Label/UndoLabel text the test's fake FeatureIds expect.
|
||||
function Sync-TestLangFeatures {
|
||||
$langFeatures = @{}
|
||||
foreach ($featureId in $script:Features.Keys) {
|
||||
$feature = $script:Features[$featureId]
|
||||
$entry = @{}
|
||||
foreach ($field in 'Label', 'UndoLabel') {
|
||||
if ($null -ne $feature.$field) { $entry[$field] = [string]$feature.$field }
|
||||
}
|
||||
$langFeatures[$featureId] = [PSCustomObject]$entry
|
||||
}
|
||||
|
||||
$script:Lang = [PSCustomObject]@{
|
||||
LanguageCode = 'en-US'
|
||||
Chrome = [PSCustomObject]@{}
|
||||
Features = [PSCustomObject]$langFeatures
|
||||
UiGroups = [PSCustomObject]@{}
|
||||
Categories = [PSCustomObject]@{}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Describe 'Get-RebootFeatureLabels' {
|
||||
@@ -18,6 +41,7 @@ Describe 'Get-RebootFeatureLabels' {
|
||||
UndoFeature = [PSCustomObject]@{ RequiresReboot = $true; Label = 'Undoable feature'; UndoLabel = 'Undo feature' }
|
||||
NoRebootFeature = [PSCustomObject]@{ RequiresReboot = $false; Label = 'No reboot'; UndoLabel = 'Undo no reboot' }
|
||||
}
|
||||
Sync-TestLangFeatures
|
||||
}
|
||||
|
||||
It 'includes reboot-required selections once and uses undo labels for undo operations' {
|
||||
@@ -43,6 +67,7 @@ Describe 'Get-RebootFeatureLabels' {
|
||||
$script:Params = @{}
|
||||
$script:UndoParams = @{ ApplyFeature = $true }
|
||||
$script:Features.ApplyFeature.UndoLabel = $null
|
||||
Sync-TestLangFeatures
|
||||
|
||||
$result = @(Get-RebootFeatureLabels)
|
||||
|
||||
@@ -57,6 +82,7 @@ Describe 'Get-RebootFeatureLabels' {
|
||||
RequiresReboot = $true
|
||||
Label = 'Feature without undo label'
|
||||
}
|
||||
Sync-TestLangFeatures
|
||||
|
||||
$result = @(Get-RebootFeatureLabels)
|
||||
|
||||
@@ -87,6 +113,7 @@ Describe 'Get-RebootFeatureLabels' {
|
||||
Label = 'Shared label'
|
||||
UndoLabel = 'Undo second shared label'
|
||||
}
|
||||
Sync-TestLangFeatures
|
||||
|
||||
$result = @(Get-RebootFeatureLabels)
|
||||
|
||||
@@ -110,6 +137,7 @@ Describe 'Get-RebootFeatureLabels' {
|
||||
Label = 'Numeric reboot'
|
||||
UndoLabel = 'Undo numeric reboot'
|
||||
}
|
||||
Sync-TestLangFeatures
|
||||
|
||||
$result = @(Get-RebootFeatureLabels)
|
||||
|
||||
@@ -130,6 +158,7 @@ Describe 'Get-RebootFeatureLabels' {
|
||||
Label = ' '
|
||||
UndoLabel = $null
|
||||
}
|
||||
Sync-TestLangFeatures
|
||||
|
||||
@(Get-RebootFeatureLabels).Count | Should -Be 0
|
||||
}
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
BeforeAll {
|
||||
. (Join-Path $PSScriptRoot '..\Scripts\FileIO\Import-JsonFile.ps1')
|
||||
. (Join-Path $PSScriptRoot '..\Scripts\FileIO\Import-LanguageFile.ps1')
|
||||
. (Join-Path $PSScriptRoot '..\Scripts\Helpers\Add-Parameter.ps1')
|
||||
. (Join-Path $PSScriptRoot '..\Scripts\Helpers\Import-ConfigToParams.ps1')
|
||||
. (Join-Path $PSScriptRoot '..\Scripts\Helpers\Test-ConfigConsistency.ps1')
|
||||
$script:ConfigFixturePath = Join-Path $PSScriptRoot 'TestData\JsonFileLoading\ExportedConfig.WithSettings.json'
|
||||
$script:SkipRegistryBackupFixturePath = Join-Path $PSScriptRoot 'TestData\JsonFileLoading\ExportedConfig.SkipRegistryBackup.json'
|
||||
$script:LanguagesPath = Join-Path $PSScriptRoot '..\Config\Languages'
|
||||
$script:Lang = Import-LanguageFile -LanguageCode 'en-US'
|
||||
}
|
||||
|
||||
Describe 'Import-ConfigToParams' {
|
||||
|
||||
@@ -42,4 +42,17 @@ Describe 'Import-JsonFile' {
|
||||
$result | Should -BeNullOrEmpty
|
||||
Should -Invoke Write-Error -Times 0 -Exactly
|
||||
}
|
||||
|
||||
It 'reads non-ASCII characters correctly from a UTF-8 file with no BOM, regardless of the system default encoding' {
|
||||
# Unicode escapes here, not literal accented characters: this test file has no BOM, so
|
||||
# PowerShell 5.1 would decode literal non-ASCII source characters using the system default
|
||||
# codepage too, defeating the point of the assertion on a non-UTF-8-default machine.
|
||||
$expectedName = "Espa$([char]0x00F1)ol"
|
||||
$expectedNote = "caf$([char]0x00E9), na$([char]0x00EF)ve, $([char]0x00FC)ber"
|
||||
|
||||
$result = Import-JsonFile -filePath (Join-Path $script:FixturePath 'Config.Utf8NoBom.json')
|
||||
|
||||
$result.Name | Should -Be $expectedName
|
||||
$result.Note | Should -Be $expectedNote
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,254 @@
|
||||
BeforeAll {
|
||||
. (Join-Path $PSScriptRoot '..\Scripts\FileIO\Import-JsonFile.ps1')
|
||||
. (Join-Path $PSScriptRoot '..\Scripts\FileIO\Import-LanguageFile.ps1')
|
||||
$script:LanguagesPath = Join-Path $PSScriptRoot 'TestData\LanguageLoading'
|
||||
}
|
||||
|
||||
Describe 'Resolve-LanguageFolder' {
|
||||
It 'resolves <Case>' -ForEach @(
|
||||
@{ Case = 'an exact match'; LanguageCode = 'en-US'; Expected = 'en-US' }
|
||||
@{ Case = 'a second exact match'; LanguageCode = 'es-ES'; Expected = 'es-ES' }
|
||||
@{ Case = 'a language-prefix match when no exact folder exists'; LanguageCode = 'es-AR'; Expected = 'es-ES' }
|
||||
@{ Case = 'an unrelated locale to the en-US fallback'; LanguageCode = 'fr-FR'; Expected = 'en-US' }
|
||||
) {
|
||||
Resolve-LanguageFolder -LanguageCode $LanguageCode | Should -Be $Expected
|
||||
}
|
||||
}
|
||||
|
||||
Describe 'Import-LanguageFile' {
|
||||
It 'loads all three JSON files for the resolved language' {
|
||||
$lang = Import-LanguageFile -LanguageCode 'en-US'
|
||||
|
||||
$lang.LanguageCode | Should -Be 'en-US'
|
||||
$lang.Chrome.TitleBarClose | Should -Be 'Close'
|
||||
$lang.Features.DisableTelemetry.Label | Should -Be 'Disable telemetry'
|
||||
$lang.Categories.PrivacySuggestedContent.Label | Should -Be 'Privacy & Suggested Content'
|
||||
}
|
||||
|
||||
It 'attaches an en-US Fallback when a non-en-US language is loaded' {
|
||||
$lang = Import-LanguageFile -LanguageCode 'es-ES'
|
||||
|
||||
$lang.LanguageCode | Should -Be 'es-ES'
|
||||
$lang.Fallback.LanguageCode | Should -Be 'en-US'
|
||||
}
|
||||
|
||||
It 'does not attach a Fallback when en-US itself is loaded' {
|
||||
$lang = Import-LanguageFile -LanguageCode 'en-US'
|
||||
|
||||
$lang.PSObject.Properties['Fallback'] | Should -BeNullOrEmpty
|
||||
}
|
||||
|
||||
It 'falls back to en-US without warning when the requested language has no folder at all' {
|
||||
# Resolve-LanguageFolder already resolves a nonexistent language straight to 'en-US',
|
||||
# so there's no real fallback to warn about here, unlike the broken-folder case below.
|
||||
Mock Write-Warning {}
|
||||
|
||||
$lang = Import-LanguageFile -LanguageCode 'fr-FR'
|
||||
|
||||
$lang.LanguageCode | Should -Be 'en-US'
|
||||
Should -Invoke Write-Warning -Times 0 -Exactly
|
||||
}
|
||||
|
||||
It 'warns and falls back to en-US when the requested language folder exists but fails to load' {
|
||||
Mock Write-Warning {}
|
||||
Mock Write-Error {}
|
||||
|
||||
$languagesPath = Join-Path $TestDrive 'WithWorkingFallback'
|
||||
$brokenFolder = Join-Path $languagesPath 'xx-XX'
|
||||
New-Item -ItemType Directory -Path $brokenFolder | Out-Null
|
||||
'not valid json' | Out-File (Join-Path $brokenFolder 'Chrome.json')
|
||||
|
||||
$enUsFolder = Join-Path $languagesPath 'en-US'
|
||||
Copy-Item -Path (Join-Path $PSScriptRoot 'TestData\LanguageLoading\en-US') -Destination $enUsFolder -Recurse
|
||||
|
||||
$lang = Import-LanguageFile -LanguageCode 'xx-XX' -LanguagesPath $languagesPath
|
||||
|
||||
$lang.LanguageCode | Should -Be 'en-US'
|
||||
Should -Invoke Write-Warning -ParameterFilter { $Message -match "Failed to load language 'xx-XX'" } -Times 1 -Exactly
|
||||
# The broken xx-XX folder is still expected to report load errors; the fallback en-US load itself must not.
|
||||
Should -Invoke Write-Error -ParameterFilter { $Message -match 'Unable to load the en-US language files' } -Times 0
|
||||
}
|
||||
|
||||
It 'returns null and reports an error when the requested language fails and en-US is unavailable too' {
|
||||
Mock Write-Warning {}
|
||||
Mock Write-Error {}
|
||||
|
||||
$languagesPath = Join-Path $TestDrive 'WithoutFallback'
|
||||
$brokenFolder = Join-Path $languagesPath 'xx-XX'
|
||||
New-Item -ItemType Directory -Path $brokenFolder | Out-Null
|
||||
'not valid json' | Out-File (Join-Path $brokenFolder 'Chrome.json')
|
||||
|
||||
$lang = Import-LanguageFile -LanguageCode 'xx-XX' -LanguagesPath $languagesPath
|
||||
|
||||
$lang | Should -BeNullOrEmpty
|
||||
Should -Invoke Write-Warning -ParameterFilter { $Message -match "Failed to load language 'xx-XX'" } -Times 1 -Exactly
|
||||
}
|
||||
|
||||
It 'reports an error and returns null when en-US itself cannot be loaded' {
|
||||
Mock Write-Error {}
|
||||
|
||||
$lang = Import-LanguageFile -LanguageCode 'en-US' -LanguagesPath $TestDrive
|
||||
|
||||
$lang | Should -BeNullOrEmpty
|
||||
Should -Invoke Write-Error -ParameterFilter { $Message -match 'Unable to load the en-US language files' } -Times 1 -Exactly
|
||||
}
|
||||
}
|
||||
|
||||
Describe 'Get-PluralCategory' {
|
||||
It 'returns <Expected> for a count of <Count>' -ForEach @(
|
||||
@{ Count = 0; Expected = 'other' }
|
||||
@{ Count = 1; Expected = 'one' }
|
||||
@{ Count = 2; Expected = 'other' }
|
||||
@{ Count = 5; Expected = 'other' }
|
||||
) {
|
||||
Get-PluralCategory -LanguageCode 'en-US' -Count $Count | Should -Be $Expected
|
||||
}
|
||||
}
|
||||
|
||||
Describe 'Get-Translation' {
|
||||
BeforeAll {
|
||||
$script:EnLang = Import-LanguageFile -LanguageCode 'en-US'
|
||||
$script:EsLang = Import-LanguageFile -LanguageCode 'es-ES'
|
||||
}
|
||||
|
||||
It 'looks up a flat Chrome key' {
|
||||
Get-Translation -Key 'TitleBarClose' -Lang $script:EnLang | Should -Be 'Close'
|
||||
}
|
||||
|
||||
It 'looks up a Field on a Features entry' {
|
||||
Get-Translation -Key 'DisableTelemetry' -Field 'Label' -Lang $script:EnLang | Should -Be 'Disable telemetry'
|
||||
Get-Translation -Key 'DisableTelemetry' -Field 'ToolTip' -Lang $script:EnLang | Should -Be 'Disables telemetry collection.'
|
||||
}
|
||||
|
||||
It 'looks up a Field on a Categories entry' {
|
||||
Get-Translation -Key 'PrivacySuggestedContent' -Field 'Label' -Lang $script:EnLang | Should -Be 'Privacy & Suggested Content'
|
||||
}
|
||||
|
||||
It 'looks up a Field on a UiGroups entry' {
|
||||
Get-Translation -Key 'SearchIcon' -Field 'Label' -Lang $script:EnLang | Should -Be 'Taskbar search style'
|
||||
}
|
||||
|
||||
It 'resolves the correct section for a key that exists as both a FeatureId and a GroupId' {
|
||||
# ClearStart is a real collision in Config/Features.json: a FeatureId ("remove for this
|
||||
# user only") and a GroupId (the combo heading above it) that share the same string.
|
||||
# Mirrored in the TestData fixture above so this test doesn't depend on production content.
|
||||
Get-Translation -Key 'ClearStart' -Field 'Label' -Section 'Features' -Lang $script:EnLang | Should -Be 'Remove for the selected user'
|
||||
Get-Translation -Key 'ClearStart' -Field 'Label' -Section 'UiGroups' -Lang $script:EnLang | Should -Be 'Remove pinned apps from the start menu'
|
||||
Get-Translation -Key 'ClearStart' -Field 'ToolTip' -Section 'UiGroups' -Lang $script:EnLang | Should -Be 'This setting allows you to quickly remove all pinned apps from the start menu.'
|
||||
}
|
||||
|
||||
It 'returns the key itself when no language has it' {
|
||||
Get-Translation -Key 'NoSuchKeyAnywhere' -Lang $script:EnLang | Should -Be 'NoSuchKeyAnywhere'
|
||||
}
|
||||
|
||||
It 'returns the key itself when the field is missing on an entry that exists' {
|
||||
Get-Translation -Key 'DisableTelemetry' -Field 'UndoLabel' -Lang $script:EnLang | Should -Be 'DisableTelemetry'
|
||||
}
|
||||
|
||||
It 'falls back to en-US for a key missing from the active language' {
|
||||
Get-Translation -Key 'TitleBarOptions' -Lang $script:EsLang | Should -Be 'Options'
|
||||
}
|
||||
|
||||
It 'prefers the active language over the fallback when both have the key' {
|
||||
Get-Translation -Key 'TitleBarClose' -Lang $script:EsLang | Should -Be 'Cerrar'
|
||||
}
|
||||
|
||||
It 'resolves a plural-suffixed key by count, falling back to the bare key' {
|
||||
Get-Translation -Key 'AppsSelectedCount' -Count 1 -Lang $script:EnLang | Should -Be '{0} app selected'
|
||||
Get-Translation -Key 'AppsSelectedCount' -Count 5 -Lang $script:EnLang | Should -Be '{0} apps selected'
|
||||
}
|
||||
|
||||
It 'substitutes FormatArgs into the resolved string' {
|
||||
Get-Translation -Key 'AppsSelectedCount' -Count 1 -FormatArgs @(1) -Lang $script:EnLang | Should -Be '1 app selected'
|
||||
Get-Translation -Key 'AppsSelectedCount' -Count 5 -FormatArgs @(5) -Lang $script:EnLang | Should -Be '5 apps selected'
|
||||
}
|
||||
|
||||
It 'substitutes a zero FormatArgs value instead of treating the array as falsy' {
|
||||
Get-Translation -Key 'AppsSelectedCount' -Count 0 -FormatArgs @(0) -Lang $script:EnLang | Should -Be '0 apps selected'
|
||||
}
|
||||
|
||||
It 'returns the unformatted string instead of throwing when FormatArgs is empty' {
|
||||
Get-Translation -Key 'AppsSelectedCount' -Count 1 -FormatArgs @() -Lang $script:EnLang | Should -Be '{0} app selected'
|
||||
}
|
||||
|
||||
It 'warns and returns the unformatted string instead of throwing on a placeholder/argument count mismatch' {
|
||||
Mock Write-Warning {}
|
||||
|
||||
$result = Get-Translation -Key 'TooManyPlaceholders' -FormatArgs @('only one arg') -Lang $script:EnLang
|
||||
|
||||
$result | Should -Be 'Value is {0} and {1}'
|
||||
Should -Invoke Write-Warning -ParameterFilter { $Message -match 'TooManyPlaceholders' } -Times 1 -Exactly
|
||||
}
|
||||
}
|
||||
|
||||
Describe 'Test-LanguageKeyCoverage' {
|
||||
It 'reports no missing or extra keys when a language is compared against itself' {
|
||||
$coverage = Test-LanguageKeyCoverage -LanguageCode 'en-US'
|
||||
|
||||
$coverage.MissingKeys | Should -BeNullOrEmpty
|
||||
$coverage.ExtraKeys | Should -BeNullOrEmpty
|
||||
}
|
||||
|
||||
It 'flags every key missing from a partially translated language' {
|
||||
$coverage = Test-LanguageKeyCoverage -LanguageCode 'es-ES'
|
||||
|
||||
$coverage.MissingKeys | Should -Contain 'TitleBarOptions'
|
||||
$coverage.MissingKeys | Should -Contain 'AppsSelectedCount_one'
|
||||
$coverage.MissingKeys | Should -Contain 'AppsSelectedCount_other'
|
||||
$coverage.MissingKeys | Should -Contain 'Features.DisableTelemetry.Label'
|
||||
$coverage.MissingKeys | Should -Contain 'Features.DisableTelemetry.ToolTip'
|
||||
$coverage.MissingKeys | Should -Contain 'UiGroups.SearchIcon.Label'
|
||||
$coverage.MissingKeys | Should -Contain 'Categories.PrivacySuggestedContent.Label'
|
||||
$coverage.ExtraKeys | Should -BeNullOrEmpty
|
||||
}
|
||||
|
||||
It 'does not flag a key the target language already has' {
|
||||
$coverage = Test-LanguageKeyCoverage -LanguageCode 'es-ES'
|
||||
|
||||
$coverage.MissingKeys | Should -Not -Contain 'TitleBarClose'
|
||||
}
|
||||
|
||||
It 'resolves a regional variant to its language-prefix match instead of erroring' {
|
||||
$coverage = Test-LanguageKeyCoverage -LanguageCode 'es-AR'
|
||||
|
||||
$coverage.ResolvedLanguageCode | Should -Be 'es-ES'
|
||||
$coverage.MissingKeys | Should -Contain 'TitleBarOptions'
|
||||
}
|
||||
}
|
||||
|
||||
Describe 'ConvertTo-LocalizedXaml' {
|
||||
BeforeAll {
|
||||
$script:EnLang = Import-LanguageFile -LanguageCode 'en-US'
|
||||
$script:EsLang = Import-LanguageFile -LanguageCode 'es-ES'
|
||||
}
|
||||
|
||||
It 'substitutes every marker and XML-escapes the resolved value' {
|
||||
$xaml = '<Button Content="%LANG:TitleBarClose%"/>'
|
||||
|
||||
ConvertTo-LocalizedXaml -Xaml $xaml -Lang $script:EnLang | Should -Be '<Button Content="Close"/>'
|
||||
}
|
||||
|
||||
It 'does not let one substituted value be re-matched by a later marker' {
|
||||
$xaml = '<a Text="%LANG:TitleBarOptions%"/><b Text="%LANG:TitleBarClose%"/>'
|
||||
|
||||
ConvertTo-LocalizedXaml -Xaml $xaml -Lang $script:EnLang | Should -Be '<a Text="Options"/><b Text="Close"/>'
|
||||
}
|
||||
|
||||
It 'throws when a marker key does not exist in any language' {
|
||||
$xaml = '<Button Content="%LANG:NoSuchKeyAnywhere%"/>'
|
||||
|
||||
{ ConvertTo-LocalizedXaml -Xaml $xaml -Lang $script:EnLang } | Should -Throw '*NoSuchKeyAnywhere*'
|
||||
}
|
||||
|
||||
It 'falls back to en-US instead of throwing for a key missing from a partially translated language' {
|
||||
$xaml = '<Button Content="%LANG:TitleBarOptions%"/>'
|
||||
|
||||
ConvertTo-LocalizedXaml -Xaml $xaml -Lang $script:EsLang | Should -Be '<Button Content="Options"/>'
|
||||
}
|
||||
|
||||
It 'throws rather than stringifying a Features/UiGroups/Categories entry used by mistake' {
|
||||
$xaml = '<TextBlock Text="%LANG:DisableTelemetry%"/>'
|
||||
|
||||
{ ConvertTo-LocalizedXaml -Xaml $xaml -Lang $script:EnLang } | Should -Throw '*DisableTelemetry*'
|
||||
}
|
||||
}
|
||||
@@ -20,7 +20,31 @@ BeforeAll {
|
||||
function Set-StoreSearchSuggestionsDisabledForAllUsers { $true }
|
||||
function Set-StoreSearchSuggestionsDisabled { param($StoreAppsDatabase) $true }
|
||||
|
||||
. (Join-Path $PSScriptRoot '..\Scripts\FileIO\Import-LanguageFile.ps1')
|
||||
. (Join-Path $PSScriptRoot '..\Scripts\Features\Invoke-Changes.ps1')
|
||||
|
||||
# Builds a $script:Lang fixture whose Features section mirrors $script:Features exactly,
|
||||
# so Get-Translation resolves the same ApplyText/UndoLabel/ApplyUndoText text the fake
|
||||
# FeatureIds in this file's fixtures expect, without duplicating per-test lookup data.
|
||||
function Sync-TestLangFeatures {
|
||||
$langFeatures = @{}
|
||||
foreach ($featureId in $script:Features.Keys) {
|
||||
$feature = $script:Features[$featureId]
|
||||
$entry = @{}
|
||||
foreach ($field in 'ApplyText', 'UndoLabel', 'ApplyUndoText') {
|
||||
if ($null -ne $feature.$field) { $entry[$field] = [string]$feature.$field }
|
||||
}
|
||||
$langFeatures[$featureId] = [PSCustomObject]$entry
|
||||
}
|
||||
|
||||
$script:Lang = [PSCustomObject]@{
|
||||
LanguageCode = 'en-US'
|
||||
Chrome = [PSCustomObject]@{}
|
||||
Features = [PSCustomObject]$langFeatures
|
||||
UiGroups = [PSCustomObject]@{}
|
||||
Categories = [PSCustomObject]@{}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Describe 'Resolve-UndoRegFilePath' {
|
||||
@@ -62,6 +86,7 @@ Describe 'Invoke-FeatureApply' {
|
||||
ReplaceStartAllUsers = [PSCustomObject]@{ ApplyText = 'Replace Start all users'; RegistryKey = '' }
|
||||
DisableStoreSearchSuggestions = [PSCustomObject]@{ ApplyText = 'Disable Store suggestions'; RegistryKey = '' }
|
||||
}
|
||||
Sync-TestLangFeatures
|
||||
Mock Import-RegistryFile { $true }
|
||||
Mock Remove-SelectedApps { $true }
|
||||
Mock Invoke-ForceRemoveEdge { $true }
|
||||
@@ -238,6 +263,7 @@ Describe 'Invoke-ApplyFeatures' {
|
||||
One = [PSCustomObject]@{ ApplyText = 'Apply one' }
|
||||
Two = [PSCustomObject]@{ ApplyText = 'Apply two' }
|
||||
}
|
||||
Sync-TestLangFeatures
|
||||
$script:progressCalls = New-Object System.Collections.Generic.List[object]
|
||||
$script:ApplyProgressCallback = { param($Step, $Total, $Text) $script:progressCalls.Add(@($Step, $Total, $Text)) }
|
||||
Mock Invoke-FeatureApply { $true }
|
||||
@@ -283,6 +309,7 @@ Describe 'Invoke-UndoFeatures' {
|
||||
RegistryUndo = [PSCustomObject]@{ UndoLabel = 'Undo registry'; ApplyUndoText = 'Restoring registry'; RegistryUndoKey = 'undo.reg' }
|
||||
CustomUndo = [PSCustomObject]@{ UndoLabel = 'Undo custom'; ApplyUndoText = ''; RegistryUndoKey = '' }
|
||||
}
|
||||
Sync-TestLangFeatures
|
||||
Mock Resolve-UndoRegFilePath { param($FileName) "Undo\$FileName" }
|
||||
Mock Import-RegistryFile { $true }
|
||||
Mock Invoke-FeatureUndo { $true }
|
||||
|
||||
@@ -4,7 +4,11 @@ BeforeAll {
|
||||
function Enable-ComputerRestore { param($Drive) }
|
||||
function Get-ComputerRestorePoint {}
|
||||
function Checkpoint-Computer { param($Description, $RestorePointType) }
|
||||
. (Join-Path $PSScriptRoot '..\Scripts\FileIO\Import-JsonFile.ps1')
|
||||
. (Join-Path $PSScriptRoot '..\Scripts\FileIO\Import-LanguageFile.ps1')
|
||||
. (Join-Path $PSScriptRoot '..\Scripts\Features\Invoke-SystemRestorePoint.ps1')
|
||||
$script:LanguagesPath = Join-Path $PSScriptRoot '..\Config\Languages'
|
||||
$script:Lang = Import-LanguageFile -LanguageCode 'en-US'
|
||||
}
|
||||
|
||||
Describe 'Invoke-SystemRestorePoint' {
|
||||
|
||||
@@ -1,10 +1,14 @@
|
||||
BeforeAll {
|
||||
Add-Type -AssemblyName PresentationFramework
|
||||
. (Join-Path $PSScriptRoot '..\Scripts\FileIO\Import-JsonFile.ps1')
|
||||
. (Join-Path $PSScriptRoot '..\Scripts\FileIO\Import-LanguageFile.ps1')
|
||||
. (Join-Path $PSScriptRoot '..\Scripts\GUI\MainWindow-AppSelection.ps1')
|
||||
. (Join-Path $PSScriptRoot '..\Scripts\GUI\MainWindow-Deployment.ps1')
|
||||
. (Join-Path $PSScriptRoot '..\Scripts\GUI\MainWindow-Navigation.ps1')
|
||||
. (Join-Path $PSScriptRoot '..\Scripts\GUI\MainWindow-TweaksBuilder.ps1')
|
||||
. (Join-Path $PSScriptRoot '..\Scripts\GUI\Set-WindowThemeResources.ps1')
|
||||
$script:LanguagesPath = Join-Path $PSScriptRoot '..\Config\Languages'
|
||||
$script:Lang = Import-LanguageFile -LanguageCode 'en-US'
|
||||
|
||||
function New-TestWindow {
|
||||
$window = New-Object System.Windows.Window
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
BeforeAll {
|
||||
function Get-RegistryBackupCapturePlans {}
|
||||
|
||||
. (Join-Path $PSScriptRoot '..\Scripts\FileIO\Import-JsonFile.ps1')
|
||||
. (Join-Path $PSScriptRoot '..\Scripts\FileIO\Import-LanguageFile.ps1')
|
||||
. (Join-Path $PSScriptRoot '..\Scripts\Helpers\Registry-PathHelpers.ps1')
|
||||
. (Join-Path $PSScriptRoot '..\Scripts\Features\Registry-BackupValidation.ps1')
|
||||
$script:LanguagesPath = Join-Path $PSScriptRoot '..\Config\Languages'
|
||||
$script:Lang = Import-LanguageFile -LanguageCode 'en-US'
|
||||
}
|
||||
|
||||
Describe 'Test-RegistryBackupMatchesSelectedFeatures' {
|
||||
|
||||
@@ -2,8 +2,12 @@ BeforeAll {
|
||||
$registryPathHelperScriptPath = Join-Path $PSScriptRoot '..\Scripts\Helpers\Registry-PathHelpers.ps1'
|
||||
$validationScriptPath = Join-Path $PSScriptRoot '..\Scripts\Features\Registry-BackupValidation.ps1'
|
||||
|
||||
. (Join-Path $PSScriptRoot '..\Scripts\FileIO\Import-JsonFile.ps1')
|
||||
. (Join-Path $PSScriptRoot '..\Scripts\FileIO\Import-LanguageFile.ps1')
|
||||
. $registryPathHelperScriptPath
|
||||
. $validationScriptPath
|
||||
$script:LanguagesPath = Join-Path $PSScriptRoot '..\Config\Languages'
|
||||
$script:Lang = Import-LanguageFile -LanguageCode 'en-US'
|
||||
}
|
||||
|
||||
Describe 'Get-NormalizedSelectedFeatureIdsFromBackup' {
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
BeforeAll {
|
||||
function Get-UserDirectory { param($userName, $fileName, $exitIfPathNotFound) }
|
||||
|
||||
. (Join-Path $PSScriptRoot '..\Scripts\FileIO\Import-JsonFile.ps1')
|
||||
. (Join-Path $PSScriptRoot '..\Scripts\FileIO\Import-LanguageFile.ps1')
|
||||
. (Join-Path $PSScriptRoot '..\Scripts\Features\Replace-StartMenu.ps1')
|
||||
$script:LanguagesPath = Join-Path $PSScriptRoot '..\Config\Languages'
|
||||
$script:Lang = Import-LanguageFile -LanguageCode 'en-US'
|
||||
}
|
||||
|
||||
Describe 'Get-StartMenuUserNameFromPath' {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
BeforeAll {
|
||||
. (Join-Path $PSScriptRoot '..\Scripts\GUI\Restore-BackupDialogFeatureLists.ps1')
|
||||
. (Join-Path $PSScriptRoot '..\Scripts\FileIO\Import-LanguageFile.ps1')
|
||||
}
|
||||
|
||||
Describe 'New-RestoreDialogState' {
|
||||
@@ -9,6 +10,18 @@ Describe 'New-RestoreDialogState' {
|
||||
CustomFeature = [PSCustomObject]@{ Label = 'Custom feature'; Category = 'Privacy'; RegistryKey = '' }
|
||||
HiddenFeature = [PSCustomObject]@{ Label = 'Hidden feature'; Category = ''; RegistryKey = 'HiddenFeature.reg' }
|
||||
}
|
||||
|
||||
$script:Lang = [PSCustomObject]@{
|
||||
LanguageCode = 'en-US'
|
||||
Chrome = [PSCustomObject]@{ RestoreUnknownFeature = 'Unknown feature' }
|
||||
Features = [PSCustomObject]@{
|
||||
RegistryFeature = [PSCustomObject]@{ Label = 'Registry feature' }
|
||||
CustomFeature = [PSCustomObject]@{ Label = 'Custom feature' }
|
||||
HiddenFeature = [PSCustomObject]@{ Label = 'Hidden feature' }
|
||||
}
|
||||
UiGroups = [PSCustomObject]@{}
|
||||
Categories = [PSCustomObject]@{}
|
||||
}
|
||||
}
|
||||
|
||||
It 'creates a dialog state with the supplied values' {
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
BeforeAll {
|
||||
function Test-RunningAsSystem { $false }
|
||||
. (Join-Path $PSScriptRoot '..\Scripts\FileIO\Import-JsonFile.ps1')
|
||||
. (Join-Path $PSScriptRoot '..\Scripts\FileIO\Import-LanguageFile.ps1')
|
||||
. (Join-Path $PSScriptRoot '..\Scripts\Helpers\Registry-PathHelpers.ps1')
|
||||
. (Join-Path $PSScriptRoot '..\Scripts\Helpers\Get-FriendlyRegistryBackupTarget.ps1')
|
||||
. (Join-Path $PSScriptRoot '..\Scripts\Helpers\Resolve-UserProfilePath.ps1')
|
||||
@@ -8,6 +10,8 @@ BeforeAll {
|
||||
. (Join-Path $PSScriptRoot '..\Scripts\Features\Restore-RegistryApplyState.ps1')
|
||||
. (Join-Path $PSScriptRoot '..\Scripts\Features\Restore-RegistryBackup.ps1')
|
||||
$script:JsonFixturePath = Join-Path $PSScriptRoot 'TestData\JsonFileLoading'
|
||||
$script:LanguagesPath = Join-Path $PSScriptRoot '..\Config\Languages'
|
||||
$script:Lang = Import-LanguageFile -LanguageCode 'en-US'
|
||||
}
|
||||
|
||||
Describe 'Import-RegistryBackup' {
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"Name": "Español",
|
||||
"Note": "café, naïve, über"
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"PrivacySuggestedContent": {
|
||||
"Label": "Privacy & Suggested Content"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"TitleBarClose": "Close",
|
||||
"TitleBarOptions": "Options",
|
||||
"AppsSelectedCount_one": "{0} app selected",
|
||||
"AppsSelectedCount_other": "{0} apps selected",
|
||||
"TooManyPlaceholders": "Value is {0} and {1}"
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"Features": {
|
||||
"DisableTelemetry": {
|
||||
"Label": "Disable telemetry",
|
||||
"ToolTip": "Disables telemetry collection."
|
||||
},
|
||||
"ClearStart": {
|
||||
"Label": "Remove for the selected user"
|
||||
}
|
||||
},
|
||||
"UiGroups": {
|
||||
"SearchIcon": {
|
||||
"Label": "Taskbar search style"
|
||||
},
|
||||
"ClearStart": {
|
||||
"Label": "Remove pinned apps from the start menu",
|
||||
"ToolTip": "This setting allows you to quickly remove all pinned apps from the start menu."
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
{}
|
||||
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"TitleBarClose": "Cerrar"
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"Features": {},
|
||||
"UiGroups": {}
|
||||
}
|
||||
@@ -54,9 +54,9 @@ Describe 'XAML UI contracts' {
|
||||
$applyWindow = Get-Content -LiteralPath (Join-Path $script:SchemaPath 'ApplyChangesWindow.xaml') -Raw
|
||||
$appWindow = Get-Content -LiteralPath (Join-Path $script:SchemaPath 'AppSelectionWindow.xaml') -Raw
|
||||
|
||||
$applyWindow | Should -Match 'AutomationProperties.Name="Cancel"'
|
||||
$applyWindow | Should -Match 'AutomationProperties.Name="Close"'
|
||||
$appWindow | Should -Match 'AutomationProperties.Name="Confirm"'
|
||||
$appWindow | Should -Match 'AutomationProperties.Name="Cancel"'
|
||||
$applyWindow | Should -Match 'AutomationProperties.Name="%LANG:ApplyCancelTooltip%"'
|
||||
$applyWindow | Should -Match 'AutomationProperties.Name="%LANG:TitleBarClose%"'
|
||||
$appWindow | Should -Match 'AutomationProperties.Name="%LANG:AppSelectionWindowConfirm%"'
|
||||
$appWindow | Should -Match 'AutomationProperties.Name="%LANG:AppSelectionWindowCancel%"'
|
||||
}
|
||||
}
|
||||
|
||||
+10
-2
@@ -4,6 +4,7 @@ param (
|
||||
[switch]$Silent,
|
||||
[switch]$Sysprep,
|
||||
[string]$LogPath,
|
||||
[string]$Language,
|
||||
[string]$User,
|
||||
[Alias('NoRestartExplorer')]
|
||||
[switch]$SkipExplorerRestart,
|
||||
@@ -182,6 +183,8 @@ $script:AppsListFilePath = Join-Path $configPath 'Apps.json'
|
||||
$script:DefaultSettingsFilePath = Join-Path $configPath 'DefaultSettings.json'
|
||||
$script:FeaturesFilePath = Join-Path $configPath 'Features.json'
|
||||
$script:SavedSettingsFilePath = Join-Path $configPath 'LastUsedSettings.json'
|
||||
$script:LanguagesPath = Join-Path $configPath 'Languages'
|
||||
$script:DefaultLanguagePath = Join-Path $script:LanguagesPath 'en-US'
|
||||
$script:DefaultLogPath = Join-Path $logsPath 'Win11Debloat.log'
|
||||
$script:RegfilesPath = Join-Path $PSScriptRoot 'Regfiles'
|
||||
$script:RegistryBackupsPath = Join-Path $PSScriptRoot 'Backups'
|
||||
@@ -198,7 +201,7 @@ $script:RestoreBackupWindowSchema = Join-Path $schemasPath 'RestoreBackupWindow.
|
||||
$script:LoadAppsDetailsScriptPath = Join-Path (Join-Path $scriptsPath 'FileIO') 'Import-AppDetailsFromJson.ps1'
|
||||
$script:TestAppInWingetListScriptPath = Join-Path (Join-Path $scriptsPath 'AppRemoval') 'Test-AppInWingetList.ps1'
|
||||
|
||||
$script:ControlParams = 'WhatIf', 'Confirm', 'Verbose', 'Debug', 'LogPath', 'Silent', 'Sysprep', 'User', 'SkipExplorerRestart', 'SkipRegistryBackup', 'RunDefaults', 'RunDefaultsLite', 'RunSavedSettings', 'Config', 'CLI', 'AppRemovalTarget'
|
||||
$script:ControlParams = 'WhatIf', 'Confirm', 'Verbose', 'Debug', 'LogPath', 'Language', 'Silent', 'Sysprep', 'User', 'SkipExplorerRestart', 'SkipRegistryBackup', 'RunDefaults', 'RunDefaultsLite', 'RunSavedSettings', 'Config', 'CLI', 'AppRemovalTarget'
|
||||
|
||||
# Script-level variables for GUI elements
|
||||
$script:GuiWindow = $null
|
||||
@@ -298,7 +301,7 @@ try {
|
||||
catch { }
|
||||
|
||||
# Check if script has all required files
|
||||
if (-not ((Test-Path $script:DefaultSettingsFilePath) -and (Test-Path $script:AppsListFilePath) -and (Test-Path $script:RegfilesPath) -and (Test-Path $script:AssetsPath) -and (Test-Path $script:AppSelectionSchema) -and (Test-Path $script:ApplyChangesWindowSchema) -and (Test-Path $script:SharedStylesSchema) -and (Test-Path $script:BubbleHintSchema) -and (Test-Path $script:RestoreBackupWindowSchema) -and (Test-Path $script:FeaturesFilePath))) {
|
||||
if (-not ((Test-Path $script:DefaultSettingsFilePath) -and (Test-Path $script:AppsListFilePath) -and (Test-Path $script:RegfilesPath) -and (Test-Path $script:AssetsPath) -and (Test-Path $script:AppSelectionSchema) -and (Test-Path $script:ApplyChangesWindowSchema) -and (Test-Path $script:SharedStylesSchema) -and (Test-Path $script:BubbleHintSchema) -and (Test-Path $script:RestoreBackupWindowSchema) -and (Test-Path $script:FeaturesFilePath) -and (Test-Path $script:DefaultLanguagePath))) {
|
||||
Write-Error "Win11Debloat is unable to find required files, please ensure all script files are present"
|
||||
Write-Output "Press any key to exit..."
|
||||
$null = [System.Console]::ReadKey()
|
||||
@@ -389,6 +392,7 @@ if (-not $script:WingetInstalled -and -not $Silent) {
|
||||
|
||||
# File I/O functions
|
||||
. "$PSScriptRoot/Scripts/FileIO/Import-JsonFile.ps1"
|
||||
. "$PSScriptRoot/Scripts/FileIO/Import-LanguageFile.ps1"
|
||||
. "$PSScriptRoot/Scripts/FileIO/Save-ToFile.ps1"
|
||||
. "$PSScriptRoot/Scripts/FileIO/Save-Settings.ps1"
|
||||
. "$PSScriptRoot/Scripts/FileIO/Import-Settings.ps1"
|
||||
@@ -459,6 +463,10 @@ $WinVersion = Get-ItemPropertyValue 'HKLM:\SOFTWARE\Microsoft\Windows NT\Current
|
||||
# Check if the machine supports Modern Standby, this is used to determine if the DisableModernStandbyNetworking option can be used
|
||||
$script:ModernStandbySupported = Test-ModernStandbySupport
|
||||
|
||||
# Load the GUI's language content. -Language overrides the current user's system UI culture,
|
||||
# falling back to en-US either way if the requested language isn't available.
|
||||
$script:Lang = if ($Language) { Import-LanguageFile -LanguageCode $Language } else { Import-LanguageFile }
|
||||
|
||||
$script:Params = $PSBoundParameters
|
||||
$script:UndoParams = @{}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user