diff --git a/CFixer/CFixer.csproj b/CFixer/CFixer.csproj
index 4648efc..cfba1fe 100644
--- a/CFixer/CFixer.csproj
+++ b/CFixer/CFixer.csproj
@@ -50,6 +50,7 @@
+
..\..\..\..\..\Program Files (x86)\Windows Kits\8.1\References\CommonConfiguration\Neutral\Annotated\Windows.winmd
@@ -88,7 +89,9 @@
+
+
Form
@@ -207,6 +210,12 @@
+
+ PreserveNewest
+
+
+ PreserveNewest
+
diff --git a/CFixer/Features/FeatureManager.cs b/CFixer/Features/FeatureManager.cs
index 2e9ac18..e187269 100644
--- a/CFixer/Features/FeatureManager.cs
+++ b/CFixer/Features/FeatureManager.cs
@@ -4,6 +4,7 @@
using System.Drawing;
using System.Threading.Tasks;
using System.Windows.Forms;
+using CFixer;
namespace CrapFixer
{
@@ -64,12 +65,17 @@ public static void LoadFeatures(TreeView tree)
///
private static void AddNode(TreeNodeCollection treeNodes, FeatureNode featureNode)
{
+ string localizedName = featureNode.IsCategory
+ ? LocalizationManager.LocalizeFeatureCategory(featureNode.Name)
+ : LocalizationManager.LocalizeFeatureName(featureNode);
+
string text = featureNode.IsCategory
- ? " " + featureNode.Name + " " // add extra space to avoid clipping
- : featureNode.Name;
+ ? " " + localizedName + " " // add extra space to avoid clipping
+ : localizedName;
TreeNode node = new TreeNode(text)
{
+ Name = featureNode.Name,
Tag = featureNode,
Checked = featureNode.DefaultChecked,
};
@@ -93,7 +99,7 @@ public static async Task AnalyzeAll(TreeNodeCollection nodes)
await AnalyzeCheckedRecursive(node);
}
- Logger.Log("🔎 ANALYSIS COMPLETE", LogLevel.Info);
+ Logger.Log("ANALYSIS COMPLETE", LogLevel.Info);
Logger.Log(new string('=', 50), LogLevel.Info);
int ok = totalChecked - issuesFound;
@@ -119,8 +125,8 @@ private static async Task AnalyzeCheckedRecursive(TreeNode node)
issuesFound++;
node.ForeColor = Color.Red; // Mark as misconfigured
string category = node.Parent?.Text ?? "General";
- Logger.Log($"❌ [{category}] {fn.Name} - Not configured as recommended.");
- Logger.Log($" ➤ {fn.Feature.GetFeatureDetails()}");
+ Logger.Log($"[{category}] {fn.Name} - Not configured as recommended.");
+ Logger.Log($" {fn.Feature.GetFeatureDetails()}");
// Log a separator when an issue was found
Logger.Log(new string('-', 50), LogLevel.Info);
}
@@ -147,10 +153,11 @@ public static async Task FixChecked(TreeNode node)
{
if (!fn.IsCategory && node.Checked && fn.Feature != null)
{
+ string displayName = LocalizationManager.LocalizeFeatureName(fn);
bool result = await fn.Feature.DoFeature();
Logger.Log(result
- ? $"🔧 {fn.Name} - Fixed"
- : $"❌ {fn.Name} - ⚠️ Fix failed (This feature may require admin privileges)",
+ ? $"{displayName} - Fixed"
+ : $"{displayName} - Fix failed (This feature may require admin privileges)",
result ? LogLevel.Info : LogLevel.Error);
}
@@ -168,11 +175,12 @@ public static void RestoreChecked(TreeNode node)
{
if (!fn.IsCategory && node.Checked && fn.Feature != null)
{
+ string displayName = LocalizationManager.LocalizeFeatureName(fn);
bool ok = fn.Feature.UndoFeature();
string category = node.Parent?.Text ?? "General";
Logger.Log(ok
- ? $"↩️ [{category}] {fn.Name} - Restored"
- : $"❌ [{category}] {fn.Name} - Restore failed",
+ ? $"[{category}] {displayName} - Restored"
+ : $"[{category}] {displayName} - Restore failed",
ok ? LogLevel.Info : LogLevel.Error);
}
@@ -193,13 +201,13 @@ public static async void AnalyzeFeature(TreeNode node)
if (isOk)
{
- Logger.Log($"✅ Feature: {fn.Name} is properly configured.", LogLevel.Info);
+ Logger.Log($"Feature: {fn.Name} is properly configured.", LogLevel.Info);
}
else
{
string category = node.Parent?.Text ?? "General";
- Logger.Log($"❌ Feature: {fn.Name} requires attention.", LogLevel.Warning);
- Logger.Log($" ➤ {fn.Feature.GetFeatureDetails()}");
+ Logger.Log($"Feature: {fn.Name} requires attention.", LogLevel.Warning);
+ Logger.Log($" {fn.Feature.GetFeatureDetails()}");
Logger.Log(new string('-', 50), LogLevel.Info);
}
}
@@ -223,11 +231,12 @@ public static async Task FixFeature(TreeNode node)
// Try to fix this node if it is NOT a category (i.e., a leaf node)
if (node.Tag is FeatureNode fn && !fn.IsCategory && fn.Feature != null)
{
+ string displayName = LocalizationManager.LocalizeFeatureName(fn);
// Always fix the selected leaf node, regardless of Checked
bool result = await fn.Feature.DoFeature();
Logger.Log(result
- ? $"🔧 {fn.Name} - Fixed"
- : $"❌ {fn.Name} - ⚠️ Fix failed (This feature may require admin privileges)",
+ ? $"{displayName} - Fixed"
+ : $"{displayName} - Fix failed (This feature may require admin privileges)",
result ? LogLevel.Info : LogLevel.Error);
}
else
@@ -251,10 +260,11 @@ public static void RestoreFeature(TreeNode node)
// Restore feature node regardless of Checked state
if (node.Tag is FeatureNode fn && !fn.IsCategory && fn.Feature != null)
{
+ string displayName = LocalizationManager.LocalizeFeatureName(fn);
bool ok = fn.Feature.UndoFeature();
Logger.Log(ok
- ? $"↩️ {fn.Name} - Restored"
- : $"❌ {fn.Name} - Restore failed",
+ ? $"{displayName} - Restored"
+ : $"{displayName} - Restore failed",
ok ? LogLevel.Info : LogLevel.Error);
}
else
@@ -278,10 +288,11 @@ public static void ShowHelp(TreeNode node)
// Show help for features
if (node?.Tag is FeatureNode fn && fn.Feature != null)
{
+ string displayName = LocalizationManager.LocalizeFeatureName(fn);
string info = fn.Feature.Info();
MessageBox.Show(
!string.IsNullOrEmpty(info) ? info : "No additional information available.",
- $"Help: {fn.Name}",
+ $"Help: {displayName}",
MessageBoxButtons.OK,
MessageBoxIcon.Information);
@@ -309,7 +320,7 @@ public static void ShowHelp(TreeNode node)
// Show help for plugins
if (!PluginManager.ShowHelp(node))
{
- MessageBox.Show("⚠️ No feature or plugin selected, or help info unavailable.",
+ MessageBox.Show("No feature or plugin selected, or help info unavailable.",
"Help",
MessageBoxButtons.OK,
MessageBoxIcon.Warning);
diff --git a/CFixer/ILocalizedControl.cs b/CFixer/ILocalizedControl.cs
new file mode 100644
index 0000000..17dbf78
--- /dev/null
+++ b/CFixer/ILocalizedControl.cs
@@ -0,0 +1,7 @@
+namespace CFixer
+{
+ internal interface ILocalizedControl
+ {
+ void RefreshLocalization();
+ }
+}
diff --git a/CFixer/IniStateManager.cs b/CFixer/IniStateManager.cs
index a53f864..8c22456 100644
--- a/CFixer/IniStateManager.cs
+++ b/CFixer/IniStateManager.cs
@@ -58,7 +58,8 @@ private static void AppendNodeStates(List lines, TreeNodeCollection node
{
foreach (TreeNode node in nodes)
{
- lines.Add($"{node.Text.Trim()}={node.Checked}");
+ string key = string.IsNullOrWhiteSpace(node.Name) ? node.Text.Trim() : node.Name.Trim();
+ lines.Add($"{key}={node.Checked}");
if (node.Nodes.Count > 0)
AppendNodeStates(lines, node.Nodes);
}
@@ -175,9 +176,11 @@ private static void ApplyStates(TreeNodeCollection nodes, Dictionary 0)
ApplyStates(node.Nodes, states);
@@ -240,6 +243,65 @@ public static Dictionary LoadViewSettings(string viewName)
return result;
}
+ // Saves an individual string setting in a specific section.
+ public static void SaveViewStringSetting(string viewName, string key, string value)
+ {
+ lock (FileLock)
+ {
+ var lines = File.Exists(IniPath) ? File.ReadAllLines(IniPath).ToList() : new List();
+ var sectionData = LoadViewStringSettingsInternal(lines, viewName);
+ sectionData[key] = value;
+
+ RemoveSection(lines, viewName);
+ lines.Add($"[{viewName}]");
+ foreach (var kvp in sectionData)
+ {
+ lines.Add($"{kvp.Key}={kvp.Value}");
+ }
+
+ File.WriteAllLines(IniPath, lines);
+ }
+ }
+
+ // Loads one individual string setting from a section.
+ public static string LoadViewStringSetting(string viewName, string key, string defaultValue = "")
+ {
+ if (!File.Exists(IniPath)) return defaultValue;
+
+ var lines = File.ReadAllLines(IniPath).ToList();
+ var settings = LoadViewStringSettingsInternal(lines, viewName);
+ return settings.TryGetValue(key, out var value) ? value : defaultValue;
+ }
+
+ private static Dictionary LoadViewStringSettingsInternal(List lines, string viewName)
+ {
+ var result = new Dictionary(StringComparer.OrdinalIgnoreCase);
+ bool inTargetSection = false;
+
+ foreach (var line in lines)
+ {
+ var trimmed = line.Trim();
+ if (string.IsNullOrWhiteSpace(trimmed)) continue;
+
+ if (trimmed.StartsWith("[") && trimmed.EndsWith("]"))
+ {
+ inTargetSection = trimmed.Equals($"[{viewName}]", StringComparison.OrdinalIgnoreCase);
+ continue;
+ }
+
+ if (!inTargetSection) continue;
+
+ var parts = trimmed.Split(new[] { '=' }, 2);
+ if (parts.Length != 2) continue;
+
+ var settingKey = parts[0].Trim();
+ var settingValue = parts[1].Trim();
+ result[settingKey] = settingValue;
+ }
+
+ return result;
+ }
+
// Checks if a setting exists in a specific section
public static bool IsViewSettingEnabled(string sectionName, string settingName)
{
diff --git a/CFixer/LocalizationManager.cs b/CFixer/LocalizationManager.cs
new file mode 100644
index 0000000..d0ee61e
--- /dev/null
+++ b/CFixer/LocalizationManager.cs
@@ -0,0 +1,234 @@
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.IO;
+using System.Linq;
+using System.Text.RegularExpressions;
+using System.Threading;
+using System.Web.Script.Serialization;
+
+namespace CFixer
+{
+ internal static class LocalizationManager
+ {
+ public static event EventHandler LanguageChanged;
+
+ public const string DefaultLanguage = "en-US";
+ public const string ChineseLanguage = "zh-CN";
+
+ private static readonly Dictionary> LocalizedTexts =
+ new Dictionary>(StringComparer.OrdinalIgnoreCase);
+
+ private static readonly Dictionary LanguageDisplayNames =
+ new Dictionary(StringComparer.OrdinalIgnoreCase);
+
+ private static readonly Dictionary FeatureTypeKeys =
+ new Dictionary(StringComparer.OrdinalIgnoreCase)
+ {
+ ["BasicCleanup"] = "issues.disk_cleanup_basic",
+ ["WingetUpgradeAll"] = "issues.upgrade_all_apps_winget",
+
+ ["BSODDetails"] = "system.show_bsod_details",
+ ["VerboseStatus"] = "system.enable_logon_verbose_status",
+ ["SpeedUpShutdown"] = "system.speed_up_shutdown",
+ ["NetworkThrottling"] = "system.disable_network_throttling",
+ ["SystemResponsiveness"] = "system.optimize_responsiveness",
+ ["MenuShowDelay"] = "system.reduce_menu_show_delay",
+ ["TaskbarEndTask"] = "system.enable_taskbar_end_task",
+
+ ["BrowserSignin"] = "edge.disable_browser_signin_sync",
+ ["DefaultTopSites"] = "edge.hide_new_tab_sponsored_links",
+ ["DefautBrowserSetting"] = "edge.disable_default_browser_setting",
+ ["EdgeCollections"] = "edge.disable_collections_access",
+ ["EdgeShoppingAssistant"] = "edge.disable_shopping_assistant",
+ ["FirstRunExperience"] = "edge.hide_first_run_experience",
+ ["GamerMode"] = "edge.disable_gamer_mode",
+ ["HubsSidebar"] = "edge.disable_copilot_symbol",
+ ["ImportOnEachLaunch"] = "edge.disable_cross_browser_import_on_launch",
+ ["StartupBoost"] = "edge.disable_startup_boost",
+ ["TabPageQuickLinks"] = "edge.hide_new_tab_quick_links",
+ ["UserFeedback"] = "edge.disable_user_feedback_submit",
+
+ ["FullContextMenus"] = "ui.show_windows11_full_context_menu",
+ ["LockScreen"] = "ui.disable_personalized_lock_screen",
+ ["SearchboxTaskbarMode"] = "ui.hide_taskbar_search_box",
+ ["ShowOrHideMostUsedApps"] = "ui.hide_start_menu_most_used_apps",
+ ["ShowTaskViewButton"] = "ui.hide_taskbar_task_view_button",
+ ["DisableSearchBoxSuggestions"] = "ui.disable_search_box_suggestions",
+ ["DisableBingSearch"] = "ui.disable_bing_search",
+ ["StartLayout"] = "ui.enable_start_menu_more_pins",
+ ["TaskbarAlignment"] = "ui.align_taskbar_start_left",
+ ["Transparency"] = "ui.disable_transparency_effects",
+ ["AppDarkMode"] = "ui.enable_dark_mode_apps",
+ ["SystemDarkMode"] = "ui.enable_dark_mode_system",
+ ["DisableSnapAssistFlyout"] = "ui.disable_snap_assist_flyout",
+
+ ["GameDVR"] = "gaming.disable_game_dvr",
+ ["PowerThrottling"] = "gaming.disable_power_throttling",
+ ["VisualFX"] = "gaming.disable_visual_effects",
+
+ ["ActivityHistory"] = "privacy.disable_activity_history",
+ ["LocationTracking"] = "privacy.disable_location_tracking",
+ ["PrivacyExperience"] = "privacy.disable_signin_privacy_experience",
+ ["Telemetry"] = "privacy.disable_telemetry",
+
+ ["FileExplorerAds"] = "ads.disable_file_explorer",
+ ["FinishSetupAds"] = "ads.disable_finish_setup",
+ ["LockScreenAds"] = "ads.disable_lock_screen_tips",
+ ["PersonalizedAds"] = "ads.disable_personalized",
+ ["SettingsAds"] = "ads.disable_settings",
+ ["StartmenuAds"] = "ads.disable_start_menu",
+ ["TailoredExperiences"] = "ads.disable_tailored_experiences",
+ ["TipsAndSuggestions"] = "ads.disable_tips_suggestions",
+ ["WelcomeExperienceAds"] = "ads.disable_welcome_experience",
+
+ ["AskCopilot"] = "ai.remove_ask_copilot_context_menu",
+ ["ClickToDo"] = "ai.disable_click_to_do",
+ ["CopilotTaskbar"] = "ai.hide_taskbar_copilot",
+ ["Recall"] = "ai.disable_recall",
+ };
+
+ private static readonly Regex DontWordPattern = new Regex(@"\bdon't\b", RegexOptions.Compiled);
+ private static readonly Regex DontNoApostrophePattern = new Regex(@"\bdont\b", RegexOptions.Compiled);
+ private static readonly Regex NonAlphaNumericPattern = new Regex(@"[^a-z0-9]+", RegexOptions.Compiled);
+
+ static LocalizationManager()
+ {
+ LoadLanguageFiles();
+ }
+
+ public static string CurrentLanguageCode { get; private set; } = DefaultLanguage;
+
+ public static void SetLanguage(string languageCode)
+ {
+ string normalizedLanguage = DefaultLanguage;
+ if (!string.IsNullOrWhiteSpace(languageCode) && LocalizedTexts.ContainsKey(languageCode))
+ {
+ normalizedLanguage = languageCode;
+ }
+
+ bool changed = !string.Equals(CurrentLanguageCode, normalizedLanguage, StringComparison.OrdinalIgnoreCase);
+ CurrentLanguageCode = normalizedLanguage;
+
+ var culture = new CultureInfo(CurrentLanguageCode);
+ Thread.CurrentThread.CurrentCulture = culture;
+ Thread.CurrentThread.CurrentUICulture = culture;
+
+ if (changed)
+ {
+ LanguageChanged?.Invoke(null, EventArgs.Empty);
+ }
+ }
+
+ public static string T(string key)
+ {
+ if (LocalizedTexts.TryGetValue(CurrentLanguageCode, out var languageValues) && languageValues.TryGetValue(key, out var text))
+ {
+ return text;
+ }
+
+ if (LocalizedTexts.TryGetValue(DefaultLanguage, out var fallbackLanguage) && fallbackLanguage.TryGetValue(key, out var fallback))
+ {
+ return fallback;
+ }
+
+ return key;
+ }
+
+ public static string LocalizeFeatureCategory(string categoryName)
+ {
+ return T("feature.category." + categoryName);
+ }
+
+ public static string LocalizeFeatureName(FeatureNode featureNode)
+ {
+ if (featureNode?.Feature != null)
+ {
+ var typeName = featureNode.Feature.GetType().Name;
+ if (FeatureTypeKeys.TryGetValue(typeName, out var semanticKey))
+ {
+ return T("feature.name." + semanticKey);
+ }
+ }
+
+ return T("feature.name." + ToFeatureKey(featureNode?.Name ?? string.Empty));
+ }
+
+ public static IReadOnlyDictionary GetAvailableLanguages()
+ {
+ return new Dictionary(LanguageDisplayNames, StringComparer.OrdinalIgnoreCase);
+ }
+
+ private static void LoadLanguageFiles()
+ {
+ var baseDir = AppDomain.CurrentDomain.BaseDirectory;
+ var langDir = Path.Combine(baseDir, "lang");
+
+ LocalizedTexts.Clear();
+ LanguageDisplayNames.Clear();
+
+ if (Directory.Exists(langDir))
+ {
+ foreach (var file in Directory.GetFiles(langDir, "*.json").OrderBy(f => f))
+ {
+ var languageCode = Path.GetFileNameWithoutExtension(file);
+ LoadLanguageFromFile(file, languageCode);
+ }
+ }
+
+ if (!LocalizedTexts.ContainsKey(DefaultLanguage))
+ {
+ LocalizedTexts[DefaultLanguage] = new Dictionary(StringComparer.OrdinalIgnoreCase);
+ LanguageDisplayNames[DefaultLanguage] = DefaultLanguage;
+ }
+ }
+
+ private static void LoadLanguageFromFile(string filePath, string languageCode)
+ {
+ if (!File.Exists(filePath))
+ {
+ return;
+ }
+
+ try
+ {
+ var json = File.ReadAllText(filePath);
+ var serializer = new JavaScriptSerializer();
+ var dictionary = serializer.Deserialize>(json);
+
+ if (dictionary != null)
+ {
+ LocalizedTexts[languageCode] = new Dictionary(dictionary, StringComparer.OrdinalIgnoreCase);
+
+ if (dictionary.TryGetValue("__meta.languageName", out var languageName) && !string.IsNullOrWhiteSpace(languageName))
+ {
+ LanguageDisplayNames[languageCode] = languageName;
+ }
+ else
+ {
+ LanguageDisplayNames[languageCode] = languageCode;
+ }
+ }
+ }
+ catch
+ {
+ // Keep silent and fallback to existing loaded languages.
+ }
+ }
+
+ private static string ToFeatureKey(string featureName)
+ {
+ if (string.IsNullOrWhiteSpace(featureName))
+ {
+ return string.Empty;
+ }
+
+ var normalized = featureName.Trim().ToLowerInvariant();
+ normalized = DontWordPattern.Replace(normalized, "disable");
+ normalized = DontNoApostrophePattern.Replace(normalized, "disable");
+ normalized = NonAlphaNumericPattern.Replace(normalized, "_");
+ normalized = normalized.Trim('_');
+ return normalized;
+ }
+ }
+}
diff --git a/CFixer/MainForm.cs b/CFixer/MainForm.cs
index 954c3f1..6a6d681 100644
--- a/CFixer/MainForm.cs
+++ b/CFixer/MainForm.cs
@@ -11,7 +11,7 @@
namespace CrapFixer
{
- public partial class MainForm : Form
+ public partial class MainForm : Form, ILocalizedControl
{
private NavigationManager _navigationManager;
private NavigationHandler _navigationHandler;
@@ -23,6 +23,8 @@ public MainForm()
{
InitializeComponent();
IniStateManager.ApplyWindowState(this);
+ ApplyLocalization();
+ LocalizationManager.LanguageChanged += LocalizationManager_LanguageChanged;
// Set up the main navigation manager and logger
_navigationManager = new NavigationManager(panelContainer);
@@ -71,6 +73,105 @@ private async Task InitializeUI()
lblOSInfo.Text = await OSHelper.OSHelper.GetWindowsVersion();
}
+ private void ApplyLocalization()
+ {
+ btnAnalyze.Text = LocalizationManager.T("main.btnAnalyze");
+ Windows.Text = LocalizationManager.T("main.tabWindows");
+ Apps.Text = LocalizationManager.T("main.tabApplications");
+ analyzeMarkedFeatureToolStripMenuItem.Text = LocalizationManager.T("main.ctxAnalyze");
+ fixMarkedFeatureToolStripMenuItem.Text = LocalizationManager.T("main.ctxFix");
+ restoreMarkedFeatureToolStripMenuItem.Text = LocalizationManager.T("main.ctxRestore");
+ helpMarkedFeatureToolStripMenuItem.Text = LocalizationManager.T("main.ctxHelp");
+ btnFix.Text = LocalizationManager.T("main.btnFix");
+ btnRestore.Text = LocalizationManager.T("main.btnRestore");
+ linkUpdateCheck.Text = LocalizationManager.T("main.linkUpdate");
+ btnTools.Text = LocalizationManager.T("main.btnTools");
+ btnFixer.Text = LocalizationManager.T("main.btnFixer");
+ linkSelection.Text = LocalizationManager.T("main.linkSelection");
+
+ var appsPlaceholder = LocalizationManager.T("main.appsPlaceholder");
+ if (checkedListBoxApps.Items.Count == 1 &&
+ checkedListBoxApps.Items[0] is string placeholder &&
+ (placeholder == "No analysis yet" || placeholder == appsPlaceholder))
+ {
+ checkedListBoxApps.Items[0] = appsPlaceholder;
+ }
+
+ toolTip.SetToolTip(btnGitHub, LocalizationManager.T("main.tooltipSupport"));
+ toolTip.SetToolTip(lblHeader, LocalizationManager.T("main.tooltipGithub"));
+ toolTip.SetToolTip(pictureHeader, LocalizationManager.T("main.tooltipGithub"));
+ }
+
+ public void RefreshLocalization()
+ {
+ ApplyLocalization();
+ RefreshFeatureTreeLocalization();
+ if (panelContainer.Controls.Count > 0 && panelContainer.Controls[0] is ILocalizedControl localizedControl)
+ {
+ localizedControl.RefreshLocalization();
+ }
+ }
+
+ private void RefreshFeatureTreeLocalization()
+ {
+ var checkedMap = CaptureNodeCheckedState(treeFeatures.Nodes);
+ FeatureNodeManager.LoadFeatures(treeFeatures);
+ PluginManager.LoadPlugins(treeFeatures);
+ RestoreNodeCheckedState(treeFeatures.Nodes, checkedMap);
+ }
+
+ private static System.Collections.Generic.Dictionary CaptureNodeCheckedState(TreeNodeCollection nodes)
+ {
+ var map = new System.Collections.Generic.Dictionary(StringComparer.OrdinalIgnoreCase);
+ CaptureNodeCheckedStateRecursive(nodes, map);
+ return map;
+ }
+
+ private static void CaptureNodeCheckedStateRecursive(TreeNodeCollection nodes, System.Collections.Generic.Dictionary map)
+ {
+ foreach (TreeNode node in nodes)
+ {
+ string key = string.IsNullOrWhiteSpace(node.Name) ? node.Text.Trim() : node.Name.Trim();
+ map[key] = node.Checked;
+
+ if (node.Nodes.Count > 0)
+ {
+ CaptureNodeCheckedStateRecursive(node.Nodes, map);
+ }
+ }
+ }
+
+ private static void RestoreNodeCheckedState(TreeNodeCollection nodes, System.Collections.Generic.Dictionary map)
+ {
+ foreach (TreeNode node in nodes)
+ {
+ string key = string.IsNullOrWhiteSpace(node.Name) ? node.Text.Trim() : node.Name.Trim();
+ if (map.TryGetValue(key, out bool checkedState))
+ {
+ node.Checked = checkedState;
+ }
+
+ if (node.Nodes.Count > 0)
+ {
+ RestoreNodeCheckedState(node.Nodes, map);
+ }
+ }
+ }
+
+ private void LocalizationManager_LanguageChanged(object sender, EventArgs e)
+ {
+ if (IsDisposed) return;
+
+ if (InvokeRequired)
+ {
+ BeginInvoke(new Action(RefreshLocalization));
+ }
+ else
+ {
+ RefreshLocalization();
+ }
+ }
+
// Handles navigation button clicks and switches views accordingly
private void NavigationHandler_NavigationButtonClicked(Button button)
{
@@ -163,10 +264,8 @@ private async void btnFix_Click(object sender, EventArgs e)
private void btnRestore_Click(object sender, EventArgs e)
{
var result = MessageBox.Show(
- "⚠️ This will restore all selected features to their original state.\n" +
- "Changes made by previous configurations may be reverted.\n\n" +
- "Are you sure you want to proceed?",
- "Restore Selected Features",
+ LocalizationManager.T("main.restoreMessage"),
+ LocalizationManager.T("main.restoreTitle"),
MessageBoxButtons.YesNo,
MessageBoxIcon.Warning);
@@ -176,7 +275,7 @@ private void btnRestore_Click(object sender, EventArgs e)
foreach (TreeNode node in treeFeatures.Nodes)
FeatureNodeManager.RestoreChecked(node);
- Logger.Log("↩️ All selected features have been restored.", LogLevel.Info);
+ Logger.Log(LocalizationManager.T("main.restoreLog"), LogLevel.Info);
}
}
@@ -358,6 +457,8 @@ private void linkUpdateCheck_LinkClicked(object sender, LinkLabelLinkClickedEven
private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
{
+ LocalizationManager.LanguageChanged -= LocalizationManager_LanguageChanged;
+
if (IniStateManager.IsViewSettingEnabled("SETTINGS", "checkSaveToINI"))
{
IniStateManager.Save(treeFeatures, this);
@@ -371,4 +472,4 @@ private void btnGitHub_Click(object sender, EventArgs e)
_navigationManager.SwitchView(new OptionsView());
}
}
-}
\ No newline at end of file
+}
diff --git a/CFixer/PluginManager.cs b/CFixer/PluginManager.cs
index 5aeaf52..cef6c7c 100644
--- a/CFixer/PluginManager.cs
+++ b/CFixer/PluginManager.cs
@@ -65,7 +65,7 @@ public static void LoadPlugins(TreeView treeView)
return;
}
- var pluginsNode = new TreeNode("Plugins")
+ var pluginsNode = new TreeNode(LocalizationManager.T("options.plugins"))
{
BackColor = Color.Magenta,
ForeColor = Color.White
@@ -394,4 +394,4 @@ public static string GetPluginHelpInfo(TreeNode node)
return null; // No info found or invalid node
}
-}
\ No newline at end of file
+}
diff --git a/CFixer/Program.cs b/CFixer/Program.cs
index 3ec39ac..386734b 100644
--- a/CFixer/Program.cs
+++ b/CFixer/Program.cs
@@ -1,6 +1,7 @@
using System;
using System.Reflection;
using System.Windows.Forms;
+using CFixer;
namespace CrapFixer
{
@@ -12,6 +13,9 @@ internal static class Program
[STAThread]
private static void Main()
{
+ var language = IniStateManager.LoadViewStringSetting("SETTINGS", "Language", LocalizationManager.DefaultLanguage);
+ LocalizationManager.SetLanguage(language);
+
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new MainForm());
diff --git a/CFixer/Views/AboutView.cs b/CFixer/Views/AboutView.cs
index 5ca4a72..bb9bcfd 100644
--- a/CFixer/Views/AboutView.cs
+++ b/CFixer/Views/AboutView.cs
@@ -1,17 +1,21 @@
using CrapFixer;
+using CFixer;
using System;
using System.Diagnostics;
using System.Windows.Forms;
namespace Views
{
- public partial class AboutView : UserControl
+ public partial class AboutView : UserControl, ILocalizedControl
{
public AboutView()
{
InitializeComponent();
InitializeUI();
+ ApplyLocalization();
+ LocalizationManager.LanguageChanged += LocalizationManager_LanguageChanged;
+ Disposed += AboutView_Disposed;
}
private void InitializeUI()
@@ -33,6 +37,38 @@ private void InitializeUI()
comboBoxCurrency.SelectedIndex = 0;
}
+ private void ApplyLocalization()
+ {
+ label1.Text = LocalizationManager.T("about.description");
+ btnDonate.Text = LocalizationManager.T("about.donate");
+ lblCopyright.Text = LocalizationManager.T("about.copyright");
+ }
+
+ public void RefreshLocalization()
+ {
+ ApplyLocalization();
+ }
+
+ private void LocalizationManager_LanguageChanged(object sender, EventArgs e)
+ {
+ if (IsDisposed) return;
+
+ if (InvokeRequired)
+ {
+ BeginInvoke(new Action(RefreshLocalization));
+ }
+ else
+ {
+ RefreshLocalization();
+ }
+ }
+
+ private void AboutView_Disposed(object sender, EventArgs e)
+ {
+ LocalizationManager.LanguageChanged -= LocalizationManager_LanguageChanged;
+ Disposed -= AboutView_Disposed;
+ }
+
private void linkGitHub_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
{
Process.Start("https://github.com/builtbybel/CrapFixer/releases");
@@ -45,12 +81,12 @@ private void btnDonate_Click(object sender, EventArgs e)
if (string.IsNullOrEmpty(amount) || string.IsNullOrEmpty(currency))
{
- MessageBox.Show("Please select an amount and a currency.");
+ MessageBox.Show(LocalizationManager.T("about.donationValidation"));
return;
}
string email = "belim@builtbybel.com";
- string purpose = Uri.EscapeDataString("Support Development of the CrapFixer app.");
+ string purpose = Uri.EscapeDataString(LocalizationManager.T("about.donationPurpose"));
string returnUrl = Uri.EscapeDataString("https://github.com/Belim/support");
string cancelUrl = Uri.EscapeDataString("https://github.com/builtbybel/CrapFixer");
diff --git a/CFixer/Views/OptionsView.cs b/CFixer/Views/OptionsView.cs
index ce764b2..7847d5e 100644
--- a/CFixer/Views/OptionsView.cs
+++ b/CFixer/Views/OptionsView.cs
@@ -4,17 +4,57 @@
namespace CFixer.Views
{
- public partial class OptionsView : UserControl
+ public partial class OptionsView : UserControl, ILocalizedControl
{
private NavigationManager subNavigation;
public OptionsView()
{
InitializeComponent();
+ ApplyLocalization();
+ LocalizationManager.LanguageChanged += LocalizationManager_LanguageChanged;
+ Disposed += OptionsView_Disposed;
subNavigation = new NavigationManager(panelSubContent);
subNavigation.SwitchView(new AboutView()); // Startsite
}
+ private void ApplyLocalization()
+ {
+ btnSettingsMenu.Text = LocalizationManager.T("options.settings");
+ btnPluginsMenu.Text = LocalizationManager.T("options.plugins");
+ btnViveMenu.Text = LocalizationManager.T("options.features");
+ btnAboutMenu.Text = LocalizationManager.T("options.about");
+ }
+
+ public void RefreshLocalization()
+ {
+ ApplyLocalization();
+ if (panelSubContent.Controls.Count > 0 && panelSubContent.Controls[0] is ILocalizedControl localizedControl)
+ {
+ localizedControl.RefreshLocalization();
+ }
+ }
+
+ private void LocalizationManager_LanguageChanged(object sender, EventArgs e)
+ {
+ if (IsDisposed) return;
+
+ if (InvokeRequired)
+ {
+ BeginInvoke(new Action(RefreshLocalization));
+ }
+ else
+ {
+ RefreshLocalization();
+ }
+ }
+
+ private void OptionsView_Disposed(object sender, EventArgs e)
+ {
+ LocalizationManager.LanguageChanged -= LocalizationManager_LanguageChanged;
+ Disposed -= OptionsView_Disposed;
+ }
+
private void btnAboutMenu_Click(object sender, EventArgs e)
{
subNavigation.SwitchView(new AboutView());
diff --git a/CFixer/Views/PluginsView.cs b/CFixer/Views/PluginsView.cs
index 211cd21..8aba16b 100644
--- a/CFixer/Views/PluginsView.cs
+++ b/CFixer/Views/PluginsView.cs
@@ -9,11 +9,12 @@
namespace CFixer.Views
{
- public partial class PluginsView : UserControl
+ public partial class PluginsView : UserControl, ILocalizedControl
{
private List plugins = new List(); // All plugins from the manifest, so full squad
private List visiblePlugins = new List(); // Plugins currently shown in the UI (filtered or not)
private HashSet installedPlugins = new HashSet(); // Names of plugins already installed on disk
+ private string _searchPlaceholder = string.Empty;
private const string manifestUrl = "https://raw.githubusercontent.com/builtbybel/CrapFixer/main/plugins/plugins_manifest.txt";
@@ -27,6 +28,60 @@ public class PluginEntry
public PluginsView()
{
InitializeComponent();
+ ApplyLocalization();
+ LocalizationManager.LanguageChanged += LocalizationManager_LanguageChanged;
+ Disposed += PluginsView_Disposed;
+ }
+
+ private void ApplyLocalization()
+ {
+ btnDescription.Text = LocalizationManager.T("plugins.description");
+ btnPluginInstall.Text = LocalizationManager.T("plugins.install");
+ btnPluginUpdateAll.Text = LocalizationManager.T("plugins.updateAll");
+ btnPluginRemove.Text = LocalizationManager.T("plugins.remove");
+ btnPluginEdit.Text = LocalizationManager.T("plugins.edit");
+ btnHelp.Text = LocalizationManager.T("plugins.help");
+ btnPluginSubmit.Text = LocalizationManager.T("plugins.submit");
+ var placeholder = LocalizationManager.T("plugins.search");
+ if (string.IsNullOrWhiteSpace(textSearch.Text) || textSearch.Text.Equals(_searchPlaceholder, StringComparison.OrdinalIgnoreCase))
+ {
+ textSearch.Text = placeholder;
+ }
+ _searchPlaceholder = placeholder;
+ linkPluginUsage.Text = LocalizationManager.T("plugins.usage");
+ columnHeader1.Text = LocalizationManager.T("plugins.colPlugin");
+ columnHeader2.Text = LocalizationManager.T("plugins.colInstalled");
+ columnHeader3.Text = LocalizationManager.T("plugins.colType");
+ }
+
+ public void RefreshLocalization()
+ {
+ ApplyLocalization();
+ var query = textSearch.Text.Trim().ToLower();
+ if (query == _searchPlaceholder.ToLower())
+ query = string.Empty;
+
+ UpdateVisiblePlugins(query);
+ }
+
+ private void LocalizationManager_LanguageChanged(object sender, EventArgs e)
+ {
+ if (IsDisposed) return;
+
+ if (InvokeRequired)
+ {
+ BeginInvoke(new Action(RefreshLocalization));
+ }
+ else
+ {
+ RefreshLocalization();
+ }
+ }
+
+ private void PluginsView_Disposed(object sender, EventArgs e)
+ {
+ LocalizationManager.LanguageChanged -= LocalizationManager_LanguageChanged;
+ Disposed -= PluginsView_Disposed;
}
private async void PluginsView_Load(object sender, EventArgs e)
@@ -75,7 +130,7 @@ private async Task LoadPlugins()
}
catch (Exception ex)
{
- MessageBox.Show("Error loading plugins: " + ex.Message);
+ MessageBox.Show(LocalizationManager.T("plugins.msgLoadError") + ex.Message);
}
}
@@ -153,7 +208,7 @@ private async Task InstallPlugins(bool force = false)
var checkedItems = listPlugins.CheckedItems.Cast().ToList();
if (checkedItems.Count == 0)
{
- MessageBox.Show("Please check one or more plugins to download.");
+ MessageBox.Show(LocalizationManager.T("plugins.msgSelectForDownload"));
return;
}
@@ -187,12 +242,12 @@ private async Task InstallPlugins(bool force = false)
{
await client.DownloadFileTaskAsync(new Uri(plugin.Url), file);
installedPlugins.Add(Path.GetFileName(plugin.Url));
- item.SubItems[1].Text = "Yes"; // Update Installed column
+ item.SubItems[1].Text = LocalizationManager.T("plugins.stateYes"); // Update Installed column
//item.Checked = true; // Ensure checked
}
catch (Exception ex)
{
- MessageBox.Show($"Failed to download {plugin.Name}: {ex.Message}");
+ MessageBox.Show(string.Format(LocalizationManager.T("plugins.msgDownloadFailed"), plugin.Name, ex.Message));
}
progressBarDownload.Value = ++done;
@@ -224,10 +279,10 @@ private async void btnPluginUpdateAll_Click(object sender, EventArgs e)
// Update the status of all plugins to "Updated"
foreach (ListViewItem item in listPlugins.Items)
{
- item.SubItems[1].Text = "Updated";
+ item.SubItems[1].Text = LocalizationManager.T("plugins.stateUpdated");
}
- MessageBox.Show("All plugins updated.");
+ MessageBox.Show(LocalizationManager.T("plugins.msgAllUpdated"));
}
private void btnPluginRemove_Click(object sender, EventArgs e)
@@ -238,7 +293,7 @@ private void btnPluginRemove_Click(object sender, EventArgs e)
if (checkedItems.Count == 0)
{
- MessageBox.Show("No plugins selected.");
+ MessageBox.Show(LocalizationManager.T("plugins.msgNoneSelected"));
return;
}
@@ -254,18 +309,18 @@ private void btnPluginRemove_Click(object sender, EventArgs e)
File.Delete(path);
installedPlugins.Remove(Path.GetFileName(plugin.Url));
- item.SubItems[1].Text = "No";
+ item.SubItems[1].Text = LocalizationManager.T("plugins.stateNo");
item.Checked = false;
}
- MessageBox.Show("Selected plugins removed.");
+ MessageBox.Show(LocalizationManager.T("plugins.msgRemoved"));
}
private void btnPluginEdit_Click(object sender, EventArgs e)
{
if (listPlugins.SelectedItems.Count == 0)
{
- MessageBox.Show("Please select a plugin first.");
+ MessageBox.Show(LocalizationManager.T("plugins.msgSelectFirst"));
return;
}
@@ -276,7 +331,7 @@ private void btnPluginEdit_Click(object sender, EventArgs e)
if (!File.Exists(path))
{
- MessageBox.Show("Plugin file not found. Please install the plugin first.");
+ MessageBox.Show(LocalizationManager.T("plugins.msgPluginNotFound"));
return;
}
@@ -288,7 +343,7 @@ private void btnPluginEdit_Click(object sender, EventArgs e)
}
catch (Exception ex)
{
- MessageBox.Show("Could not open plugin: " + ex.Message);
+ MessageBox.Show(LocalizationManager.T("plugins.msgOpenPluginFailed") + ex.Message);
}
}
@@ -299,14 +354,14 @@ private void btnHelp_Click(object sender, EventArgs e)
{
if (listPlugins.SelectedItems.Count == 0)
{
- MessageBox.Show("Please select a plugin first.");
+ MessageBox.Show(LocalizationManager.T("plugins.msgSelectFirst"));
return;
}
var plugin = listPlugins.SelectedItems[0].Tag as PluginEntry;
if (plugin != null)
{
- MessageBox.Show(plugin.Description, $"Info: {plugin.Name}");
+ MessageBox.Show(plugin.Description, string.Format(LocalizationManager.T("plugins.msgInfoTitle"), plugin.Name));
}
}
@@ -351,15 +406,15 @@ private void UpdateVisiblePlugins(string query = "")
}
else if (Path.GetExtension(plugin.Url).Equals(".ps1", StringComparison.OrdinalIgnoreCase))
{
- type = "Powershell";
+ type = LocalizationManager.T("plugins.typePowershell");
}
else
{
- type = "Other";
+ type = LocalizationManager.T("plugins.typeOther");
}
- var item = new ListViewItem(plugin.Name);
- item.SubItems.Add(isInstalled ? "Yes" : "No");
+ var item = new ListViewItem(GetLocalizedPluginName(plugin.Name));
+ item.SubItems.Add(isInstalled ? LocalizationManager.T("plugins.stateYes") : LocalizationManager.T("plugins.stateNo"));
item.SubItems.Add(type);
item.Tag = plugin;
item.Checked = isInstalled;
@@ -374,6 +429,13 @@ private void UpdateVisiblePlugins(string query = "")
}
}
+ private static string GetLocalizedPluginName(string pluginName)
+ {
+ var key = "plugins.name." + pluginName;
+ var localized = LocalizationManager.T(key);
+ return string.Equals(localized, key, StringComparison.Ordinal) ? pluginName : localized;
+ }
+
private void textSearch_TextChanged(object sender, EventArgs e)
{
@@ -400,4 +462,4 @@ private void linkPluginUsage_LinkClicked(object sender, LinkLabelLinkClickedEven
Process.Start("https://github.com/builtbybel/CrapFixer/blob/main/plugins/DemoPluginPack.ps1");
}
}
-}
\ No newline at end of file
+}
diff --git a/CFixer/Views/SettingsView.Designer.cs b/CFixer/Views/SettingsView.Designer.cs
index 652081a..a691179 100644
--- a/CFixer/Views/SettingsView.Designer.cs
+++ b/CFixer/Views/SettingsView.Designer.cs
@@ -32,6 +32,8 @@ private void InitializeComponent()
this.checkBox2 = new System.Windows.Forms.CheckBox();
this.button1 = new System.Windows.Forms.Button();
this.checkInstallIcons = new System.Windows.Forms.CheckBox();
+ this.labelLanguage = new System.Windows.Forms.Label();
+ this.comboLanguage = new System.Windows.Forms.ComboBox();
this.SuspendLayout();
//
// checkSaveToINI
@@ -83,11 +85,32 @@ private void InitializeComponent()
this.checkInstallIcons.UseVisualStyleBackColor = true;
this.checkInstallIcons.CheckedChanged += new System.EventHandler(this.checkInstallIcons_CheckedChanged);
//
+ // labelLanguage
+ //
+ this.labelLanguage.AutoSize = true;
+ this.labelLanguage.Location = new System.Drawing.Point(15, 141);
+ this.labelLanguage.Name = "labelLanguage";
+ this.labelLanguage.Size = new System.Drawing.Size(55, 13);
+ this.labelLanguage.TabIndex = 5;
+ this.labelLanguage.Text = "Language";
+ //
+ // comboLanguage
+ //
+ this.comboLanguage.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
+ this.comboLanguage.FormattingEnabled = true;
+ this.comboLanguage.Location = new System.Drawing.Point(76, 138);
+ this.comboLanguage.Name = "comboLanguage";
+ this.comboLanguage.Size = new System.Drawing.Size(170, 21);
+ this.comboLanguage.TabIndex = 6;
+ this.comboLanguage.SelectedIndexChanged += new System.EventHandler(this.comboLanguage_SelectedIndexChanged);
+ //
// SettingsView
//
this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi;
this.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(251)))), ((int)(((byte)(251)))), ((int)(((byte)(251)))));
+ this.Controls.Add(this.comboLanguage);
+ this.Controls.Add(this.labelLanguage);
this.Controls.Add(this.checkInstallIcons);
this.Controls.Add(this.button1);
this.Controls.Add(this.checkBox2);
@@ -106,5 +129,7 @@ private void InitializeComponent()
private System.Windows.Forms.CheckBox checkBox2;
private System.Windows.Forms.Button button1;
private System.Windows.Forms.CheckBox checkInstallIcons;
+ private System.Windows.Forms.Label labelLanguage;
+ private System.Windows.Forms.ComboBox comboLanguage;
}
}
diff --git a/CFixer/Views/SettingsView.cs b/CFixer/Views/SettingsView.cs
index 7c12551..ab47deb 100644
--- a/CFixer/Views/SettingsView.cs
+++ b/CFixer/Views/SettingsView.cs
@@ -7,13 +7,19 @@
namespace CFixer.Views
{
- public partial class SettingsView : UserControl
+ public partial class SettingsView : UserControl, ILocalizedControl
{
+ private bool _isInitializingLanguage;
+
public SettingsView()
{
InitializeComponent();
+ PopulateLanguageOptions(LocalizationManager.CurrentLanguageCode);
LoadSettings();
+ ApplyLocalization();
CheckIfIconsInstalled();
+ LocalizationManager.LanguageChanged += LocalizationManager_LanguageChanged;
+ Disposed += SettingsView_Disposed;
}
///
@@ -27,6 +33,7 @@ public void SaveSettings()
};
IniStateManager.SaveViewSettings("SETTINGS", settings);
+ IniStateManager.SaveViewStringSetting("SETTINGS", "Language", GetSelectedLanguageCode());
}
///
@@ -36,6 +43,89 @@ public void LoadSettings()
{
var settings = IniStateManager.LoadViewSettings("SETTINGS");
checkSaveToINI.Checked = settings.GetValueOrDefault(nameof(checkSaveToINI), false);
+
+ var savedLanguage = IniStateManager.LoadViewStringSetting("SETTINGS", "Language", LocalizationManager.CurrentLanguageCode);
+ SetLanguageSelection(savedLanguage);
+ }
+
+ private void ApplyLocalization()
+ {
+ button1.Text = LocalizationManager.T("settings.sectionBasic");
+ checkSaveToINI.Text = LocalizationManager.T("settings.saveIni");
+ checkBox2.Text = LocalizationManager.T("settings.superPlugins");
+ checkInstallIcons.Text = LocalizationManager.T("settings.installIcons");
+ labelLanguage.Text = LocalizationManager.T("settings.languageLabel");
+
+ PopulateLanguageOptions(GetSelectedLanguageCode());
+ }
+
+ public void RefreshLocalization()
+ {
+ ApplyLocalization();
+ }
+
+ private void PopulateLanguageOptions(string selectedLanguageCode)
+ {
+ _isInitializingLanguage = true;
+ try
+ {
+ comboLanguage.Items.Clear();
+
+ var languages = LocalizationManager.GetAvailableLanguages();
+ var options = languages
+ .OrderBy(l => l.Key.Equals(LocalizationManager.DefaultLanguage, StringComparison.OrdinalIgnoreCase) ? 0 : 1)
+ .ThenBy(l => l.Key, StringComparer.OrdinalIgnoreCase)
+ .ToList();
+
+ foreach (var option in options)
+ {
+ comboLanguage.Items.Add(option);
+ }
+
+ comboLanguage.DisplayMember = "Value";
+ comboLanguage.ValueMember = "Key";
+
+ var targetCode = languages.ContainsKey(selectedLanguageCode)
+ ? selectedLanguageCode
+ : LocalizationManager.DefaultLanguage;
+
+ int index = options.FindIndex(o => o.Key == targetCode);
+ comboLanguage.SelectedIndex = index >= 0 ? index : (options.Count > 0 ? 0 : -1);
+ }
+ finally
+ {
+ _isInitializingLanguage = false;
+ }
+ }
+
+ private string GetSelectedLanguageCode()
+ {
+ if (comboLanguage.SelectedItem is KeyValuePair selected)
+ {
+ return selected.Key;
+ }
+
+ return LocalizationManager.DefaultLanguage;
+ }
+
+ private void SetLanguageSelection(string languageCode)
+ {
+ var languages = LocalizationManager.GetAvailableLanguages();
+ var normalizedCode = languages.ContainsKey(languageCode)
+ ? languageCode
+ : LocalizationManager.DefaultLanguage;
+
+ for (int i = 0; i < comboLanguage.Items.Count; i++)
+ {
+ var option = (KeyValuePair)comboLanguage.Items[i];
+ if (option.Key == normalizedCode)
+ {
+ comboLanguage.SelectedIndex = i;
+ return;
+ }
+ }
+
+ comboLanguage.SelectedIndex = 0;
}
private void SettingsView_Leave(object sender, EventArgs e)
@@ -56,12 +146,11 @@ private void CheckIfIconsInstalled()
private async void checkInstallIcons_CheckedChanged(object sender, EventArgs e)
{
var result = MessageBox.Show(
- "By default, buttons have no icons to reduce app size. Enable this to download and display navigation icons." +
- "\nWould you like to install it now?",
- "Icons Pack Detected",
- MessageBoxButtons.YesNo,
- MessageBoxIcon.Information
- );
+ LocalizationManager.T("settings.iconPrompt"),
+ LocalizationManager.T("settings.iconPromptTitle"),
+ MessageBoxButtons.YesNo,
+ MessageBoxIcon.Information
+ );
if (result == DialogResult.Yes)
{
@@ -73,9 +162,9 @@ private async void checkInstallIcons_CheckedChanged(object sender, EventArgs e)
string[] iconFiles = new string[]
{
- "fixer.png",
- "options.png",
- "restore.png"
+ "fixer.png",
+ "options.png",
+ "restore.png"
};
string baseUrl = "https://raw.githubusercontent.com/builtbybel/CrapFixer/main/icons/";
@@ -91,8 +180,8 @@ private async void checkInstallIcons_CheckedChanged(object sender, EventArgs e)
}
MessageBox.Show(
- "All icons have been successfully installed in the 'icons' folder!\n\n💖 Love CrapFixer? Consider supporting me with a small donation to keep this tool alive and improving!",
- "Icons Installed",
+ LocalizationManager.T("settings.iconInstalled"),
+ LocalizationManager.T("settings.iconInstalledTitle"),
MessageBoxButtons.OK,
MessageBoxIcon.Information
);
@@ -102,12 +191,52 @@ private async void checkInstallIcons_CheckedChanged(object sender, EventArgs e)
}
catch (Exception ex)
{
- MessageBox.Show("❌ An error occurred while downloading the icons:\n" + ex.Message,
- "Download Failed",
+ MessageBox.Show(
+ LocalizationManager.T("settings.iconDownloadFailed") + ex.Message,
+ LocalizationManager.T("settings.iconDownloadFailedTitle"),
MessageBoxButtons.OK,
- MessageBoxIcon.Error);
+ MessageBoxIcon.Error
+ );
}
}
}
+
+ private void comboLanguage_SelectedIndexChanged(object sender, EventArgs e)
+ {
+ if (_isInitializingLanguage)
+ {
+ return;
+ }
+
+ var selectedLanguage = GetSelectedLanguageCode();
+ if (selectedLanguage == LocalizationManager.CurrentLanguageCode)
+ {
+ return;
+ }
+
+ LocalizationManager.SetLanguage(selectedLanguage);
+ SaveSettings();
+ ApplyLocalization();
+ }
+
+ private void LocalizationManager_LanguageChanged(object sender, EventArgs e)
+ {
+ if (IsDisposed) return;
+
+ if (InvokeRequired)
+ {
+ BeginInvoke(new Action(RefreshLocalization));
+ }
+ else
+ {
+ RefreshLocalization();
+ }
+ }
+
+ private void SettingsView_Disposed(object sender, EventArgs e)
+ {
+ LocalizationManager.LanguageChanged -= LocalizationManager_LanguageChanged;
+ Disposed -= SettingsView_Disposed;
+ }
}
-}
\ No newline at end of file
+}
diff --git a/CFixer/Views/ViveView.cs b/CFixer/Views/ViveView.cs
index 627e82d..9b6e8b3 100644
--- a/CFixer/Views/ViveView.cs
+++ b/CFixer/Views/ViveView.cs
@@ -10,7 +10,7 @@
namespace CFixer.Views
{
- public partial class ViveView : UserControl
+ public partial class ViveView : UserControl, ILocalizedControl
{
private List featureList;
private string viveToolPath;
@@ -18,6 +18,9 @@ public partial class ViveView : UserControl
public ViveView()
{
InitializeComponent();
+ ApplyLocalization();
+ LocalizationManager.LanguageChanged += LocalizationManager_LanguageChanged;
+ Disposed += ViveView_Disposed;
IsViveToolAvailable();
InitFeatureList();
LoadFeaturesToGrid();
@@ -26,17 +29,60 @@ public ViveView()
_ = UpdateFeatureStatusFromSystem();
}
+ private void ApplyLocalization()
+ {
+ btnApply.Text = LocalizationManager.T("vive.applySelected");
+ btnDescription.Text = LocalizationManager.T("vive.description");
+ linkPluginUsage.Text = LocalizationManager.T("vive.moreInfos");
+ lblCustomIds.Text = LocalizationManager.T("vive.customIds");
+ btnApplyCustom.Text = LocalizationManager.T("vive.applyCustom");
+
+ NameColumn.HeaderText = LocalizationManager.T("vive.colFeature");
+ IdColumn.HeaderText = LocalizationManager.T("vive.colId");
+ StatusColumn.HeaderText = LocalizationManager.T("vive.colStatus");
+ InfoColumn.HeaderText = LocalizationManager.T("vive.colInfo");
+ }
+
+ public void RefreshLocalization()
+ {
+ ApplyLocalization();
+ IsViveToolAvailable();
+ UpdateGridFeatureNamesFromLocalization();
+ _ = UpdateFeatureStatusFromSystem();
+ }
+
+ private void LocalizationManager_LanguageChanged(object sender, EventArgs e)
+ {
+ if (IsDisposed) return;
+
+ if (InvokeRequired)
+ {
+ BeginInvoke(new Action(RefreshLocalization));
+ }
+ else
+ {
+ RefreshLocalization();
+ }
+ }
+
+ private void ViveView_Disposed(object sender, EventArgs e)
+ {
+ LocalizationManager.LanguageChanged -= LocalizationManager_LanguageChanged;
+ Disposed -= ViveView_Disposed;
+ }
+
private void IsViveToolAvailable()
{
string pluginsDir = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "plugins");
- var viveFolder = Directory.GetDirectories(pluginsDir)
- .FirstOrDefault(dir => Path.GetFileName(dir).ToLower().StartsWith("vive"));
+ var viveFolder = Directory.Exists(pluginsDir)
+ ? Directory.GetDirectories(pluginsDir).FirstOrDefault(dir => Path.GetFileName(dir).ToLower().StartsWith("vive"))
+ : null;
if (viveFolder == null)
{
viveToolPath = null;
- btnDescription.Text = "Enable experimental and hidden features (Disabled)";
+ btnDescription.Text = LocalizationManager.T("vive.descriptionDisabled");
return;
}
@@ -44,7 +90,12 @@ private void IsViveToolAvailable()
if (File.Exists(exePath))
{
viveToolPath = exePath;
- btnDescription.Text = "Enable experimental and hidden features (Enabled)";
+ btnDescription.Text = LocalizationManager.T("vive.descriptionEnabled");
+ }
+ else
+ {
+ viveToolPath = null;
+ btnDescription.Text = LocalizationManager.T("vive.descriptionDisabled");
}
}
@@ -59,6 +110,7 @@ private void InitFeatureList()
{
Ids = new List {47205210, 49221331, 49381526, 49402389, 49820095, 55495322, 48433719},
Name = "Enable the redesigned Windows 11 Start menu",
+ NameKey = "vive.feature.windows11_redesigned_start_menu",
InfoUrl = "https://www.neowin.net/guides/how-to-enable-the-redesigned-windows-11-start-menu/",
Enabled = false
},
@@ -66,6 +118,7 @@ private void InitFeatureList()
{
Ids = new List {52467192,53079680},
Name = "Enable Text extractor in Snipping Tool",
+ NameKey = "vive.feature.snipping_tool_text_extractor",
InfoUrl = "https://blogs.windows.com/windows-insider/2025/04/15/text-extractor-in-snipping-tool-begins-rolling-out-to-windows-insiders/",
Enabled = false
}
@@ -73,6 +126,7 @@ private void InitFeatureList()
{
Ids = new List {45624564},
Name = "Enable Drag Tray Share UI",
+ NameKey = "vive.feature.drag_tray_share_ui",
InfoUrl = "https://www.neowin.net/news/windows-11-is-getting-a-quirky-new-way-to-share-files/",
Enabled = false
}
@@ -92,10 +146,35 @@ private void LoadFeaturesToGrid()
var row = dataGridView.Rows[rowIndex];
row.Cells["EnabledColumn"].Value = feature.Enabled;
- row.Cells["NameColumn"].Value = feature.Name;
+ row.Cells["NameColumn"].Value = LocalizeFeatureName(feature);
row.Cells["IdColumn"].Value = feature.IdsAsString;
row.Cells["InfoColumn"].Value = feature.InfoUrl;
- row.Cells["StatusColumn"].Value = "Unknown";
+ row.Cells["StatusColumn"].Value = LocalizationManager.T("vive.statusUnknown");
+ }
+ }
+
+ private string LocalizeFeatureName(ViveFeature feature)
+ {
+ if (feature == null || string.IsNullOrWhiteSpace(feature.NameKey))
+ {
+ return feature?.Name ?? string.Empty;
+ }
+
+ string localized = LocalizationManager.T(feature.NameKey);
+ return string.Equals(localized, feature.NameKey, StringComparison.OrdinalIgnoreCase)
+ ? feature.Name
+ : localized;
+ }
+
+ private void UpdateGridFeatureNamesFromLocalization()
+ {
+ int count = Math.Min(dataGridView.Rows.Count, featureList?.Count ?? 0);
+ for (int i = 0; i < count; i++)
+ {
+ var row = dataGridView.Rows[i];
+ if (row.IsNewRow) continue;
+
+ row.Cells["NameColumn"].Value = LocalizeFeatureName(featureList[i]);
}
}
@@ -106,7 +185,11 @@ private void ApplyFeature(List ids, bool enable)
{
if (string.IsNullOrEmpty(viveToolPath))
{
- MessageBox.Show("ViVeTool not found. Please ensure it is installed in the plugins folder.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
+ MessageBox.Show(
+ LocalizationManager.T("vive.msgToolMissing"),
+ LocalizationManager.T("vive.msgErrorTitle"),
+ MessageBoxButtons.OK,
+ MessageBoxIcon.Error);
return;
}
@@ -144,7 +227,11 @@ private async void btnViveApply_Click(object sender, EventArgs e)
Task.Delay(1000).Wait();
await UpdateFeatureStatusFromSystem(); // Refresh the status after applying changes
- MessageBox.Show("Features have been applied.", "Done", MessageBoxButtons.OK, MessageBoxIcon.Information);
+ MessageBox.Show(
+ LocalizationManager.T("vive.msgFeaturesApplied"),
+ LocalizationManager.T("vive.msgDoneTitle"),
+ MessageBoxButtons.OK,
+ MessageBoxIcon.Information);
}
///
@@ -157,7 +244,11 @@ private Dictionary QueryCurrentFeatureStates()
if (string.IsNullOrEmpty(viveToolPath))
{
- MessageBox.Show("ViVeTool not found. Please ensure it is installed in the plugins folder.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
+ MessageBox.Show(
+ LocalizationManager.T("vive.msgToolMissing"),
+ LocalizationManager.T("vive.msgErrorTitle"),
+ MessageBoxButtons.OK,
+ MessageBoxIcon.Error);
return statusMap;
}
@@ -223,11 +314,11 @@ private void UpdateGridWithStatus(Dictionary systemStatus)
int enabledCount = ids.Count(id => systemStatus.ContainsKey(id) && systemStatus[id]);
- string status = "Disabled";
+ string status = LocalizationManager.T("vive.statusDisabled");
if (enabledCount == ids.Count)
- status = "All Enabled";
+ status = LocalizationManager.T("vive.statusAllEnabled");
else if (enabledCount > 0)
- status = "Partially Enabled";
+ status = LocalizationManager.T("vive.statusPartiallyEnabled");
row.Cells["StatusColumn"].Value = status;
}
@@ -250,7 +341,11 @@ private void dataGridView_CellContentClick(object sender, DataGridViewCellEventA
}
catch (Exception ex)
{
- MessageBox.Show($"Failed to open link:\n{ex.Message}", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
+ MessageBox.Show(
+ string.Format(LocalizationManager.T("vive.msgOpenLinkFailed"), ex.Message),
+ LocalizationManager.T("vive.msgErrorTitle"),
+ MessageBoxButtons.OK,
+ MessageBoxIcon.Error);
}
}
}
@@ -263,6 +358,7 @@ public class ViveFeature
{
public List Ids { get; set; }
public string Name { get; set; }
+ public string NameKey { get; set; }
public bool Enabled { get; set; }
public string InfoUrl { get; set; }
@@ -271,10 +367,7 @@ public class ViveFeature
private void linkPluginUsage_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
{
- MessageBox.Show("This plugin uses ViVeTool to enable hidden Windows features.\n" +
- "Please download ViVeTool (e.g. 'ViVeTool-v0.3.x-IntelAmd') from:\n" +
- "https://github.com/thebookisclosed/ViVe/releases\n" +
- "Extract it and place the contents into a subfolder inside the 'plugins' directory.\n\n");
+ MessageBox.Show(LocalizationManager.T("vive.msgUsage"));
}
private void btnApplyCustom_Click(object sender, EventArgs e)
@@ -283,7 +376,11 @@ private void btnApplyCustom_Click(object sender, EventArgs e)
if (string.IsNullOrWhiteSpace(input))
{
- MessageBox.Show("Please enter one or more feature IDs.", "Invalid Input", MessageBoxButtons.OK, MessageBoxIcon.Warning);
+ MessageBox.Show(
+ LocalizationManager.T("vive.msgEnterIds"),
+ LocalizationManager.T("vive.msgInvalidInputTitle"),
+ MessageBoxButtons.OK,
+ MessageBoxIcon.Warning);
return;
}
@@ -299,24 +396,31 @@ private void btnApplyCustom_Click(object sender, EventArgs e)
}
else
{
- MessageBox.Show($"Invalid ID: '{part}'", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
+ MessageBox.Show(
+ string.Format(LocalizationManager.T("vive.msgInvalidId"), part),
+ LocalizationManager.T("vive.msgErrorTitle"),
+ MessageBoxButtons.OK,
+ MessageBoxIcon.Error);
return;
}
}
if (idList.Count == 0)
{
- MessageBox.Show("No valid IDs found.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
+ MessageBox.Show(
+ LocalizationManager.T("vive.msgNoValidIds"),
+ LocalizationManager.T("vive.msgErrorTitle"),
+ MessageBoxButtons.OK,
+ MessageBoxIcon.Error);
return;
}
// Ask whether to enable or disable
var result = MessageBox.Show(
- $"Do you want to ENABLE these features?\n\n{string.Join(", ", idList)}\n\n" +
- "Yes = Enable\nNo = Disable\nCancel = Abort",
- "Confirm Action",
- MessageBoxButtons.YesNoCancel,
- MessageBoxIcon.Question);
+ string.Format(LocalizationManager.T("vive.msgConfirm"), string.Join(", ", idList)),
+ LocalizationManager.T("vive.msgConfirmTitle"),
+ MessageBoxButtons.YesNoCancel,
+ MessageBoxIcon.Question);
if (result == DialogResult.Cancel) return;
@@ -324,7 +428,11 @@ private void btnApplyCustom_Click(object sender, EventArgs e)
ApplyFeature(idList, enable);
- MessageBox.Show("Custom feature action sent to ViVeTool.", "Done", MessageBoxButtons.OK, MessageBoxIcon.Information);
+ MessageBox.Show(
+ LocalizationManager.T("vive.msgCustomSent"),
+ LocalizationManager.T("vive.msgDoneTitle"),
+ MessageBoxButtons.OK,
+ MessageBoxIcon.Information);
}
}
-}
\ No newline at end of file
+}
diff --git a/CFixer/lang/en-US.json b/CFixer/lang/en-US.json
new file mode 100644
index 0000000..2330872
--- /dev/null
+++ b/CFixer/lang/en-US.json
@@ -0,0 +1,168 @@
+{
+ "__meta.languageName": "English (United States)",
+ "about.copyright": "Copyright (c) 2025 A Belim app creation",
+ "about.description": "You can download the latest version, report bugs and submit feature requests at the following GitHub page.",
+ "about.donate": "Donate",
+ "about.donationPurpose": "Support Development of the CrapFixer app.",
+ "about.donationValidation": "Please select an amount and a currency.",
+ "common.chinese": "Chinese (Simplified)",
+ "common.english": "English",
+ "common.language": "Language",
+ "feature.category.Ads": "Ads",
+ "feature.category.AI": "AI",
+ "feature.category.Gaming": "Gaming",
+ "feature.category.Issues": "Issues",
+ "feature.category.MS Edge": "MS Edge",
+ "feature.category.Privacy": "Privacy",
+ "feature.category.System": "System",
+ "feature.category.UI": "UI",
+ "feature.name.ads.disable_file_explorer": "Disable File Explorer Ads",
+ "feature.name.ads.disable_finish_setup": "Disable Finish Setup Ads",
+ "feature.name.ads.disable_lock_screen_tips": "Disable Lock Screen Tips and Ads",
+ "feature.name.ads.disable_personalized": "Disable Personalized Ads",
+ "feature.name.ads.disable_settings": "Disable Settings Ads",
+ "feature.name.ads.disable_start_menu": "Disable Start menu Ads",
+ "feature.name.ads.disable_tailored_experiences": "Disable Tailored experiences",
+ "feature.name.ads.disable_tips_suggestions": "Disable General Tips and Ads",
+ "feature.name.ads.disable_welcome_experience": "Disable Welcome Experience Ads",
+ "feature.name.ai.disable_click_to_do": "Disable Click to Do (Only Copilot+ PCs)",
+ "feature.name.ai.disable_recall": "Turn off Recall in Windows 11",
+ "feature.name.ai.hide_taskbar_copilot": "Don't Show Copilot in Taskbar",
+ "feature.name.ai.remove_ask_copilot_context_menu": "Remove Ask Copilot from context menu",
+ "feature.name.edge.disable_browser_signin_sync": "Disable Browser sign in and sync services",
+ "feature.name.edge.disable_collections_access": "Disable Access to Collections feature",
+ "feature.name.edge.disable_copilot_symbol": "Disable Copilot Symbol in Edge",
+ "feature.name.edge.disable_cross_browser_import_on_launch": "Don't Allow to Import of data from other browsers on each launch",
+ "feature.name.edge.disable_default_browser_setting": "Disable Microsoft Edge as default browser",
+ "feature.name.edge.disable_gamer_mode": "Disable Gamer Mode",
+ "feature.name.edge.disable_shopping_assistant": "Disable Shopping assistant",
+ "feature.name.edge.disable_startup_boost": "Disable Start Boost",
+ "feature.name.edge.disable_user_feedback_submit": "Don't Submit user feedback option",
+ "feature.name.edge.hide_first_run_experience": "Don't Show First Run Experience",
+ "feature.name.edge.hide_new_tab_quick_links": "Don't Show Quick links in new tab page",
+ "feature.name.edge.hide_new_tab_sponsored_links": "Don't Show Sponsored links in new tab page",
+ "feature.name.gaming.disable_game_dvr": "Disable Game DVR",
+ "feature.name.gaming.disable_power_throttling": "Disable Power Throttling",
+ "feature.name.gaming.disable_visual_effects": "Disable Visual Effects",
+ "feature.name.issues.disk_cleanup_basic": "Basic Disk Cleanup",
+ "feature.name.issues.upgrade_all_apps_winget": "Winget App Updates",
+ "feature.name.privacy.disable_activity_history": "Disable activity history",
+ "feature.name.privacy.disable_location_tracking": "Disable location tracking",
+ "feature.name.privacy.disable_signin_privacy_experience": "Disable Privacy Settings Experience at sign-in",
+ "feature.name.privacy.disable_telemetry": "Turn off Telemetry data collection",
+ "feature.name.system.disable_network_throttling": "Disable Network Throttling",
+ "feature.name.system.enable_logon_verbose_status": "Enable Verbose Logon status messages",
+ "feature.name.system.enable_taskbar_end_task": "Enable End Task",
+ "feature.name.system.optimize_responsiveness": "Optimize System Responsiveness",
+ "feature.name.system.reduce_menu_show_delay": "Speed Up Menu Show Delay",
+ "feature.name.system.show_bsod_details": "Show BSOD details instead of sad smiley",
+ "feature.name.system.speed_up_shutdown": "Speed Up Shutdown Time",
+ "feature.name.ui.align_taskbar_start_left": "Align Start button to left",
+ "feature.name.ui.disable_bing_search": "Disable Bing Search",
+ "feature.name.ui.disable_personalized_lock_screen": "Don't use personalized lock screen",
+ "feature.name.ui.disable_search_box_suggestions": "Disable Search Box Suggestions",
+ "feature.name.ui.disable_snap_assist_flyout": "Disable Snap Assist Flyout",
+ "feature.name.ui.disable_transparency_effects": "Disable Transparency Effects",
+ "feature.name.ui.enable_dark_mode_apps": "Enable Dark Mode for Apps",
+ "feature.name.ui.enable_dark_mode_system": "Enable Dark Mode for System",
+ "feature.name.ui.enable_start_menu_more_pins": "Pin more Apps on start menu",
+ "feature.name.ui.hide_start_menu_most_used_apps": "Hide Most used apps in start menu",
+ "feature.name.ui.hide_taskbar_search_box": "Hide search box on taskbar",
+ "feature.name.ui.hide_taskbar_task_view_button": "Hide Task view button on taskbar",
+ "feature.name.ui.show_windows11_full_context_menu": "Show Full context menus in Windows 11",
+ "main.appsPlaceholder": "No analysis yet",
+ "main.btnAnalyze": "&Analyze",
+ "main.btnFix": "Run &Fixer",
+ "main.btnFixer": "&Fixer",
+ "main.btnRestore": "&Restore",
+ "main.btnTools": "&Tools",
+ "main.ctxAnalyze": "Analyze",
+ "main.ctxFix": "Fix",
+ "main.ctxHelp": "Help",
+ "main.ctxRestore": "Restore",
+ "main.linkSelection": "Select all",
+ "main.linkUpdate": "Check for updates...",
+ "main.restoreLog": "↩️ All selected features have been restored.",
+ "main.restoreMessage": "This will restore all selected features to their original state.\nChanges made by previous configurations may be reverted.\n\nAre you sure you want to proceed?",
+ "main.restoreTitle": "Restore Selected Features",
+ "main.tabApplications": "Applications",
+ "main.tabWindows": "Windows",
+ "main.tooltipGithub": "Click here to visit the CrapFixer website at github.com/builtbybel/crapfixer",
+ "main.tooltipSupport": "Love CrapFixer? It's open source - but your support keeps it alive!",
+ "options.about": "About",
+ "options.features": "Features",
+ "options.plugins": "Plugins",
+ "options.settings": "Settings",
+ "plugins.colInstalled": "Installed",
+ "plugins.colPlugin": "Plugin",
+ "plugins.colType": "Type",
+ "plugins.description": "Plugins Gallery (App restart needed after install)",
+ "plugins.edit": "Edit",
+ "plugins.help": "Help",
+ "plugins.install": "Install",
+ "plugins.msgAllUpdated": "All plugins updated.",
+ "plugins.msgDownloadFailed": "Failed to download {0}: {1}",
+ "plugins.msgInfoTitle": "Info: {0}",
+ "plugins.msgLoadError": "Error loading plugins: ",
+ "plugins.msgNoneSelected": "No plugins selected.",
+ "plugins.msgOpenPluginFailed": "Could not open plugin: ",
+ "plugins.msgPluginNotFound": "Plugin file not found. Please install the plugin first.",
+ "plugins.msgRemoved": "Selected plugins removed.",
+ "plugins.msgSelectFirst": "Please select a plugin first.",
+ "plugins.msgSelectForDownload": "Please check one or more plugins to download.",
+ "plugins.remove": "Remove",
+ "plugins.search": "Search",
+ "plugins.stateNo": "No",
+ "plugins.stateUpdated": "Updated",
+ "plugins.stateYes": "Yes",
+ "plugins.submit": "Submit Plugin",
+ "plugins.typeOther": "Other",
+ "plugins.typePowershell": "Powershell",
+ "plugins.updateAll": "Update All",
+ "plugins.usage": "Usage notes",
+ "settings.iconDownloadFailed": "An error occurred while downloading the icons:\n",
+ "settings.iconDownloadFailedTitle": "Download Failed",
+ "settings.iconInstalled": "All icons have been successfully installed in the 'icons' folder!\n\nLove CrapFixer? Consider supporting me with a small donation to keep this tool alive and improving!",
+ "settings.iconInstalledTitle": "Icons Installed",
+ "settings.iconPrompt": "By default, buttons have no icons to reduce app size. Enable this to download and display navigation icons.\nWould you like to install it now?",
+ "settings.iconPromptTitle": "Icons Pack Detected",
+ "settings.installIcons": "Download optional icons to enhance navigation UI",
+ "settings.languageLabel": "Language",
+ "settings.restartPrompt": "Language has changed. Restart now to apply it?",
+ "settings.restartTitle": "Restart Required",
+ "settings.saveIni": "Save all settings to INI file",
+ "settings.sectionBasic": "Basic settings",
+ "settings.superPlugins": "Activate Plugins for PowerShell Tooling (Super Plugins)",
+ "vive.applyCustom": "Apply Custom",
+ "vive.applySelected": "Apply selected",
+ "vive.colFeature": "Feature",
+ "vive.colId": "ID",
+ "vive.colInfo": "Info",
+ "vive.colStatus": "Status",
+ "vive.customIds": "Custom IDs",
+ "vive.description": "ViVe Tool",
+ "vive.descriptionDisabled": "Enable experimental and hidden features (Disabled)",
+ "vive.descriptionEnabled": "Enable experimental and hidden features (Enabled)",
+ "vive.feature.drag_tray_share_ui": "Enable Drag Tray Share UI",
+ "vive.feature.snipping_tool_text_extractor": "Enable Text extractor in Snipping Tool",
+ "vive.feature.windows11_redesigned_start_menu": "Enable the redesigned Windows 11 Start menu",
+ "vive.moreInfos": "More infos",
+ "vive.msgConfirm": "Do you want to ENABLE these features?\n\n{0}\n\nYes = Enable\nNo = Disable\nCancel = Abort",
+ "vive.msgConfirmTitle": "Confirm Action",
+ "vive.msgCustomSent": "Custom feature action sent to ViVeTool.",
+ "vive.msgDoneTitle": "Done",
+ "vive.msgEnterIds": "Please enter one or more feature IDs.",
+ "vive.msgErrorTitle": "Error",
+ "vive.msgFeaturesApplied": "Features have been applied.",
+ "vive.msgInvalidId": "Invalid ID: '{0}'",
+ "vive.msgInvalidInputTitle": "Invalid Input",
+ "vive.msgNoValidIds": "No valid IDs found.",
+ "vive.msgOpenLinkFailed": "Failed to open link:\n{0}",
+ "vive.msgToolMissing": "ViVeTool not found. Please ensure it is installed in the plugins folder.",
+ "vive.msgUsage": "This plugin uses ViVeTool to enable hidden Windows features.\nPlease download ViVeTool (e.g. 'ViVeTool-v0.3.x-IntelAmd') from:\nhttps://github.com/thebookisclosed/ViVe/releases\nExtract it and place the contents into a subfolder inside the 'plugins' directory.\n",
+ "vive.statusAllEnabled": "All Enabled",
+ "vive.statusDisabled": "Disabled",
+ "vive.statusPartiallyEnabled": "Partially Enabled",
+ "vive.statusUnknown": "Unknown"
+}
+
diff --git a/CFixer/lang/zh-CN.json b/CFixer/lang/zh-CN.json
new file mode 100644
index 0000000..7bf58cd
--- /dev/null
+++ b/CFixer/lang/zh-CN.json
@@ -0,0 +1,184 @@
+{
+ "__meta.languageName": "中文(简体)",
+ "about.copyright": "版权所有 (c) 2025 A Belim app creation",
+ "about.description": "你可以在以下 GitHub 页面下载最新版本、提交问题并提出功能建议。",
+ "about.donate": "赞助",
+ "about.donationPurpose": "支持 CrapFixer 应用持续开发。",
+ "about.donationValidation": "请选择金额和币种。",
+ "common.chinese": "中文(简体)",
+ "common.english": "英文",
+ "common.language": "语言",
+ "feature.category.Ads": "广告",
+ "feature.category.AI": "AI",
+ "feature.category.Gaming": "游戏",
+ "feature.category.Issues": "问题修复",
+ "feature.category.MS Edge": "Edge 浏览器",
+ "feature.category.Privacy": "隐私",
+ "feature.category.System": "系统",
+ "feature.category.UI": "界面",
+ "feature.name.ads.disable_file_explorer": "禁用文件资源管理器广告",
+ "feature.name.ads.disable_finish_setup": "禁用完成设置广告",
+ "feature.name.ads.disable_lock_screen_tips": "禁用锁屏提示和广告",
+ "feature.name.ads.disable_personalized": "禁用个性化广告",
+ "feature.name.ads.disable_settings": "禁用设置广告",
+ "feature.name.ads.disable_start_menu": "禁用开始菜单广告",
+ "feature.name.ads.disable_tailored_experiences": "禁用个性化体验",
+ "feature.name.ads.disable_tips_suggestions": "禁用常规提示和广告",
+ "feature.name.ads.disable_welcome_experience": "禁用欢迎体验广告",
+ "feature.name.ai.disable_click_to_do": "禁用 Click to Do(仅 Copilot + PCs)",
+ "feature.name.ai.disable_recall": "关闭 Windows 11 Recall",
+ "feature.name.ai.hide_taskbar_copilot": "不在任务栏显示 Copilot",
+ "feature.name.ai.remove_ask_copilot_context_menu": "从右键菜单移除 Ask Copilot",
+ "feature.name.edge.disable_browser_signin_sync": "禁用浏览器登录与同步服务",
+ "feature.name.edge.disable_collections_access": "禁用 Collections 功能访问",
+ "feature.name.edge.disable_copilot_symbol": "禁用 Edge 中的 Copilot 图标",
+ "feature.name.edge.disable_cross_browser_import_on_launch": "禁止每次启动时从其他浏览器导入数据",
+ "feature.name.edge.disable_default_browser_setting": "禁用 Microsoft Edge 默认浏览器设置",
+ "feature.name.edge.disable_gamer_mode": "禁用游戏模式",
+ "feature.name.edge.disable_shopping_assistant": "禁用购物助手",
+ "feature.name.edge.disable_startup_boost": "禁用启动加速",
+ "feature.name.edge.disable_user_feedback_submit": "禁用提交用户反馈选项",
+ "feature.name.edge.hide_first_run_experience": "不显示首次运行体验",
+ "feature.name.edge.hide_new_tab_quick_links": "不在新标签页显示快速链接",
+ "feature.name.edge.hide_new_tab_sponsored_links": "不在新标签页显示赞助链接",
+ "feature.name.gaming.disable_game_dvr": "禁用游戏录制",
+ "feature.name.gaming.disable_power_throttling": "禁用电源限制",
+ "feature.name.gaming.disable_visual_effects": "禁用视觉效果",
+ "feature.name.issues.disk_cleanup_basic": "基础磁盘清理",
+ "feature.name.issues.upgrade_all_apps_winget": "Winget 应用更新",
+ "feature.name.privacy.disable_activity_history": "禁用活动历史记录",
+ "feature.name.privacy.disable_location_tracking": "禁用位置跟踪",
+ "feature.name.privacy.disable_signin_privacy_experience": "禁用登录时隐私设置体验",
+ "feature.name.privacy.disable_telemetry": "关闭遥测数据收集",
+ "feature.name.system.disable_network_throttling": "禁用网络限流",
+ "feature.name.system.enable_logon_verbose_status": "启用详细登录状态消息",
+ "feature.name.system.enable_taskbar_end_task": "启用结束任务",
+ "feature.name.system.optimize_responsiveness": "优化系统响应性",
+ "feature.name.system.reduce_menu_show_delay": "加快菜单弹出速度",
+ "feature.name.system.show_bsod_details": "显示蓝屏详细信息(替代简化哭脸)",
+ "feature.name.system.speed_up_shutdown": "加快关机速度",
+ "feature.name.ui.align_taskbar_start_left": "任务栏靠左对齐",
+ "feature.name.ui.disable_bing_search": "禁用必应搜索",
+ "feature.name.ui.disable_personalized_lock_screen": "不使用个性化锁屏",
+ "feature.name.ui.disable_search_box_suggestions": "禁用搜索框建议",
+ "feature.name.ui.disable_snap_assist_flyout": "禁用贴靠窗口",
+ "feature.name.ui.disable_transparency_effects": "禁用透明效果",
+ "feature.name.ui.enable_dark_mode_apps": "为应用启用深色模式",
+ "feature.name.ui.enable_dark_mode_system": "为系统启用深色模式",
+ "feature.name.ui.enable_start_menu_more_pins": "开始菜单显示更多已固定应用",
+ "feature.name.ui.hide_start_menu_most_used_apps": "隐藏开始菜单常用应用",
+ "feature.name.ui.hide_taskbar_search_box": "隐藏任务栏搜索框",
+ "feature.name.ui.hide_taskbar_task_view_button": "隐藏任务栏任务视图按钮",
+ "feature.name.ui.show_windows11_full_context_menu": "在 Windows 11 显示完整右键菜单",
+ "main.appsPlaceholder": "尚未分析",
+ "main.btnAnalyze": "分析(&A)",
+ "main.btnFix": "执行修复(&F)",
+ "main.btnFixer": "修复器(&F)",
+ "main.btnRestore": "还原(&R)",
+ "main.btnTools": "工具(&T)",
+ "main.ctxAnalyze": "分析",
+ "main.ctxFix": "修复",
+ "main.ctxHelp": "帮助",
+ "main.ctxRestore": "还原",
+ "main.linkSelection": "全选",
+ "main.linkUpdate": "检查更新...",
+ "main.restoreLog": "↩️ 已还原所有已选择的功能。",
+ "main.restoreMessage": "这将把所选功能恢复为初始状态。\n之前配置带来的更改可能会被撤销。\n\n确定要继续吗?",
+ "main.restoreTitle": "还原所选功能",
+ "main.tabApplications": "应用程序",
+ "main.tabWindows": "系统功能",
+ "main.tooltipGithub": "点击访问 CrapFixer 项目主页:github.com/builtbybel/crapfixer",
+ "main.tooltipSupport": "喜欢 CrapFixer 吗?它是开源项目,你的支持会让它持续更新。",
+ "options.about": "关于",
+ "options.features": "功能",
+ "options.plugins": "插件",
+ "options.settings": "设置",
+ "plugins.colInstalled": "已安装",
+ "plugins.colPlugin": "插件",
+ "plugins.colType": "类型",
+ "plugins.description": "插件库(安装后需要重启应用)",
+ "plugins.edit": "编辑",
+ "plugins.help": "帮助",
+ "plugins.install": "安装",
+ "plugins.msgAllUpdated": "所有插件已更新。",
+ "plugins.msgDownloadFailed": "下载失败 {0}: {1}",
+ "plugins.msgInfoTitle": "信息:{0}",
+ "plugins.msgLoadError": "加载插件失败:",
+ "plugins.msgNoneSelected": "未选择任何插件。",
+ "plugins.msgOpenPluginFailed": "无法打开插件:",
+ "plugins.msgPluginNotFound": "未找到插件文件,请先安装该插件。",
+ "plugins.msgRemoved": "已移除所选插件。",
+ "plugins.msgSelectFirst": "请先选择一个插件。",
+ "plugins.msgSelectForDownload": "请至少勾选一个要下载的插件。",
+ "plugins.remove": "移除",
+ "plugins.search": "搜索",
+ "plugins.stateNo": "否",
+ "plugins.stateUpdated": "已更新",
+ "plugins.stateYes": "是",
+ "plugins.submit": "提交插件",
+ "plugins.typeOther": "其他",
+ "plugins.typePowershell": "PowerShell 脚本",
+ "plugins.name.ChrisTitusApp": "ChrisTitusApp",
+ "plugins.name.Create Restore Point": "创建还原点",
+ "plugins.name.Remove default apps": "移除默认应用",
+ "plugins.name.Remove Windows AI": "移除 Windows AI",
+ "plugins.name.Restart Explorer": "重启资源管理器",
+ "plugins.name.Restore all built-in apps": "恢复所有内置应用",
+ "plugins.name.Uninstall OneDrive": "卸载 OneDrive",
+ "plugins.name.CFEnhancer": "CFEnhancer",
+ "plugins.name.Disable Snap Assist Flyout (NX)": "禁用贴靠窗口提醒(NX)",
+ "plugins.name.File Extensions Visibility (NX)": "文件扩展名可见性(NX)",
+ "plugins.name.Shutdown Time (NX)": "关机时间(NX)",
+ "plugins.name.User Account Control (NX)": "用户账户控制(NX)",
+ "plugins.name.Remove Edit with Clipchamp (NX)": "移除“使用 Clipchamp 编辑”(NX)",
+ "plugins.name.Remove Edit with Notepad (NX)": "移除“使用记事本编辑”(NX)",
+ "plugins.name.Remove Edit with Photos (NX)": "移除“使用照片编辑”(NX)",
+ "plugins.name.Remove Ask Copilot (NX)": "移除“询问 Copilot”(NX)",
+ "plugins.updateAll": "全部更新",
+ "plugins.usage": "使用说明",
+ "settings.iconDownloadFailed": "下载图标时发生错误:\n",
+ "settings.iconDownloadFailedTitle": "下载失败",
+ "settings.iconInstalled": "所有图标已成功安装到 'icons' 文件夹。\n\n如果你喜欢 CrapFixer,欢迎小额赞助支持持续开发。",
+ "settings.iconInstalledTitle": "图标安装完成",
+ "settings.iconPrompt": "默认不带图标以减小体积。启用后会下载并显示导航图标。\n现在安装吗?",
+ "settings.iconPromptTitle": "检测到图标包",
+ "settings.installIcons": "下载可选图标以增强导航界面",
+ "settings.languageLabel": "语言",
+ "settings.restartPrompt": "语言已更改,是否立即重启应用以生效?",
+ "settings.restartTitle": "需要重启",
+ "settings.saveIni": "将所有设置保存到 INI 文件",
+ "settings.sectionBasic": "基础设置",
+ "settings.superPlugins": "启用 PowerShell 插件支持(Super Plugins)",
+ "vive.applyCustom": "应用自定义",
+ "vive.applySelected": "应用所选",
+ "vive.colFeature": "功能",
+ "vive.colId": "ID",
+ "vive.colInfo": "信息",
+ "vive.colStatus": "状态",
+ "vive.customIds": "自定义 ID",
+ "vive.description": "ViVe 工具",
+ "vive.descriptionDisabled": "启用实验/隐藏功能(未启用)",
+ "vive.descriptionEnabled": "启用实验/隐藏功能(已启用)",
+ "vive.feature.drag_tray_share_ui": "启用拖拽托盘分享界面",
+ "vive.feature.snipping_tool_text_extractor": "启用截图工具文本提取器",
+ "vive.feature.windows11_redesigned_start_menu": "启用 Windows 11 全新开始菜单",
+ "vive.moreInfos": "更多信息",
+ "vive.msgConfirm": "是否要启用这些功能?\n\n{0}\n\n是 = 启用\n否 = 禁用\n取消 = 终止",
+ "vive.msgConfirmTitle": "确认操作",
+ "vive.msgCustomSent": "已向 ViVeTool 发送自定义功能操作。",
+ "vive.msgDoneTitle": "完成",
+ "vive.msgEnterIds": "请输入一个或多个功能 ID。",
+ "vive.msgErrorTitle": "错误",
+ "vive.msgFeaturesApplied": "功能已应用。",
+ "vive.msgInvalidId": "无效 ID:'{0}'",
+ "vive.msgInvalidInputTitle": "输入无效",
+ "vive.msgNoValidIds": "未找到有效 ID。",
+ "vive.msgOpenLinkFailed": "打开链接失败:\n{0}",
+ "vive.msgToolMissing": "未找到 ViVeTool,请确认它已安装到 plugins 文件夹中。",
+ "vive.msgUsage": "该插件使用 ViVeTool 启用 Windows 隐藏功能。\n请从以下地址下载 ViVeTool(例如 'ViVeTool-v0.3.x-IntelAmd'):\nhttps://github.com/thebookisclosed/ViVe/releases\n解压后放入 'plugins' 目录下的子文件夹。\n",
+ "vive.statusAllEnabled": "全部启用",
+ "vive.statusDisabled": "已禁用",
+ "vive.statusPartiallyEnabled": "部分启用",
+ "vive.statusUnknown": "未知"
+}
+