From 174d44f95948e55c4f44ef9746703674d5e843f5 Mon Sep 17 00:00:00 2001 From: Stefan Markovic Date: Tue, 17 Sep 2024 22:16:44 +0200 Subject: [PATCH] WIP --- PowerToys.sln | 1 + src/common/ManagedCommon/LanguageHelper.cs | 50 +++++++++++++++++++ src/common/utils/language_helper.h | 22 ++++++++ .../AdvancedPasteXAML/App.xaml.cs | 6 +++ .../EnvironmentVariablesXAML/App.xaml.cs | 6 +++ .../FileLocksmithXAML/App.xaml.cs | 6 +++ src/modules/Hosts/Hosts/HostsXAML/App.xaml.cs | 6 +++ .../MeasureToolUI/MeasureToolXAML/App.xaml.cs | 6 +++ src/modules/PowerOCR/PowerOCR/App.xaml.cs | 14 ++++++ .../Workspaces/WorkspacesEditor/App.xaml.cs | 15 ++++++ .../WorkspacesLauncherUI/App.xaml.cs | 17 ++++++- .../colorPicker/ColorPickerUI/App.xaml.cs | 14 ++++++ .../editor/FancyZonesEditor/App.xaml.cs | 15 ++++++ src/modules/imageresizer/ui/App.xaml.cs | 14 ++++++ .../imageresizer/ui/ImageResizerUI.csproj | 1 + .../launcher/PowerLauncher/App.xaml.cs | 13 +++++ src/modules/peek/Peek.UI/PeekXAML/App.xaml.cs | 6 +++ .../PowerRenameXAML/App.xaml.cpp | 7 +++ .../powerrename/PowerRenameUILib/pch.h | 1 + .../RegistryPreviewXAML/App.xaml.cs | 8 +++ src/runner/settings_window.cpp | 6 +++ .../Settings.UI/SettingsXAML/App.xaml.cs | 6 +++ 22 files changed, 238 insertions(+), 2 deletions(-) create mode 100644 src/common/ManagedCommon/LanguageHelper.cs create mode 100644 src/common/utils/language_helper.h diff --git a/PowerToys.sln b/PowerToys.sln index d6c33873e21..85a6798afa2 100644 --- a/PowerToys.sln +++ b/PowerToys.sln @@ -273,6 +273,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "utils", "utils", "{B39DC643 src\common\utils\HDropIterator.h = src\common\utils\HDropIterator.h src\common\utils\HttpClient.h = src\common\utils\HttpClient.h src\common\utils\json.h = src\common\utils\json.h + src\common\utils\language_helper.h = src\common\utils\language_helper.h src\common\utils\logger_helper.h = src\common\utils\logger_helper.h src\common\utils\modulesRegistry.h = src\common\utils\modulesRegistry.h src\common\utils\MsiUtils.h = src\common\utils\MsiUtils.h diff --git a/src/common/ManagedCommon/LanguageHelper.cs b/src/common/ManagedCommon/LanguageHelper.cs new file mode 100644 index 00000000000..90791a03bc5 --- /dev/null +++ b/src/common/ManagedCommon/LanguageHelper.cs @@ -0,0 +1,50 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.IO; +using System.IO.Abstractions; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace ManagedCommon +{ + public static class LanguageHelper + { + public const string SettingsFilePath = "\\Microsoft\\PowerToys\\"; + public const string SettingsFile = "language.json"; + + internal sealed class OutGoingLanguageSettings + { + [JsonPropertyName("language")] + public string LanguageTag { get; set; } + } + + public static string LoadLanguage() + { + FileSystem fileSystem = new FileSystem(); + var localAppDataDir = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData); + var file = localAppDataDir + SettingsFilePath + SettingsFile; + + if (fileSystem.File.Exists(file)) + { + try + { + Stream inputStream = fileSystem.File.Open(file, FileMode.Open); + StreamReader reader = new StreamReader(inputStream); + string data = reader.ReadToEnd(); + inputStream.Close(); + reader.Dispose(); + + return JsonSerializer.Deserialize(data).LanguageTag; + } + catch (Exception) + { + } + } + + return string.Empty; + } + } +} diff --git a/src/common/utils/language_helper.h b/src/common/utils/language_helper.h new file mode 100644 index 00000000000..85448efef38 --- /dev/null +++ b/src/common/utils/language_helper.h @@ -0,0 +1,22 @@ +#pragma once + +#include +#include +#include + +namespace LanguageHelpers +{ + inline std::wstring load_language() + { + std::filesystem::path languageJsonFilePath(PTSettingsHelper::get_root_save_folder_location() + L"\\language.json"); + + auto langJson = json::from_file(languageJsonFilePath.c_str()); + if (!langJson.has_value()) + { + return {}; + } + + std::wstring language = langJson->GetNamedString(L"language", L"").c_str(); + return language; + } +} diff --git a/src/modules/AdvancedPaste/AdvancedPaste/AdvancedPasteXAML/App.xaml.cs b/src/modules/AdvancedPaste/AdvancedPaste/AdvancedPasteXAML/App.xaml.cs index 3e3fc9c4b50..3f990ef6faf 100644 --- a/src/modules/AdvancedPaste/AdvancedPaste/AdvancedPasteXAML/App.xaml.cs +++ b/src/modules/AdvancedPaste/AdvancedPaste/AdvancedPasteXAML/App.xaml.cs @@ -50,6 +50,12 @@ public partial class App : Application, IDisposable /// public App() { + string appLanguage = LanguageHelper.LoadLanguage(); + if (!string.IsNullOrEmpty(appLanguage)) + { + Microsoft.Windows.Globalization.ApplicationLanguages.PrimaryLanguageOverride = appLanguage; + } + this.InitializeComponent(); Host = Microsoft.Extensions.Hosting.Host.CreateDefaultBuilder().UseContentRoot(AppContext.BaseDirectory).ConfigureServices((context, services) => diff --git a/src/modules/EnvironmentVariables/EnvironmentVariables/EnvironmentVariablesXAML/App.xaml.cs b/src/modules/EnvironmentVariables/EnvironmentVariables/EnvironmentVariablesXAML/App.xaml.cs index 61c8e33f178..bc6f5aa1dac 100644 --- a/src/modules/EnvironmentVariables/EnvironmentVariables/EnvironmentVariablesXAML/App.xaml.cs +++ b/src/modules/EnvironmentVariables/EnvironmentVariables/EnvironmentVariablesXAML/App.xaml.cs @@ -44,6 +44,12 @@ public static T GetService() /// public App() { + string appLanguage = LanguageHelper.LoadLanguage(); + if (!string.IsNullOrEmpty(appLanguage)) + { + Microsoft.Windows.Globalization.ApplicationLanguages.PrimaryLanguageOverride = appLanguage; + } + this.InitializeComponent(); Host = Microsoft.Extensions.Hosting.Host.CreateDefaultBuilder().UseContentRoot(AppContext.BaseDirectory).ConfigureServices((context, services) => diff --git a/src/modules/FileLocksmith/FileLocksmithUI/FileLocksmithXAML/App.xaml.cs b/src/modules/FileLocksmith/FileLocksmithUI/FileLocksmithXAML/App.xaml.cs index 8e5ff669de5..e6186c46c8f 100644 --- a/src/modules/FileLocksmith/FileLocksmithUI/FileLocksmithXAML/App.xaml.cs +++ b/src/modules/FileLocksmith/FileLocksmithUI/FileLocksmithXAML/App.xaml.cs @@ -23,6 +23,12 @@ public partial class App : Application /// public App() { + string appLanguage = LanguageHelper.LoadLanguage(); + if (!string.IsNullOrEmpty(appLanguage)) + { + Microsoft.Windows.Globalization.ApplicationLanguages.PrimaryLanguageOverride = appLanguage; + } + Logger.InitializeLogger("\\File Locksmith\\FileLocksmithUI\\Logs"); this.InitializeComponent(); diff --git a/src/modules/Hosts/Hosts/HostsXAML/App.xaml.cs b/src/modules/Hosts/Hosts/HostsXAML/App.xaml.cs index 27ab33fc873..cd4d8b177fe 100644 --- a/src/modules/Hosts/Hosts/HostsXAML/App.xaml.cs +++ b/src/modules/Hosts/Hosts/HostsXAML/App.xaml.cs @@ -38,6 +38,12 @@ public partial class App : Application /// public App() { + string appLanguage = LanguageHelper.LoadLanguage(); + if (!string.IsNullOrEmpty(appLanguage)) + { + Microsoft.Windows.Globalization.ApplicationLanguages.PrimaryLanguageOverride = appLanguage; + } + InitializeComponent(); Host.HostInstance = Microsoft.Extensions.Hosting.Host. diff --git a/src/modules/MeasureTool/MeasureToolUI/MeasureToolXAML/App.xaml.cs b/src/modules/MeasureTool/MeasureToolUI/MeasureToolXAML/App.xaml.cs index 8e08ac0afdc..728d541207d 100644 --- a/src/modules/MeasureTool/MeasureToolUI/MeasureToolXAML/App.xaml.cs +++ b/src/modules/MeasureTool/MeasureToolUI/MeasureToolXAML/App.xaml.cs @@ -24,6 +24,12 @@ public App() { Logger.InitializeLogger("\\Measure Tool\\MeasureToolUI\\Logs"); + string appLanguage = LanguageHelper.LoadLanguage(); + if (!string.IsNullOrEmpty(appLanguage)) + { + Microsoft.Windows.Globalization.ApplicationLanguages.PrimaryLanguageOverride = appLanguage; + } + this.InitializeComponent(); } diff --git a/src/modules/PowerOCR/PowerOCR/App.xaml.cs b/src/modules/PowerOCR/PowerOCR/App.xaml.cs index 045a961e267..57bd357792a 100644 --- a/src/modules/PowerOCR/PowerOCR/App.xaml.cs +++ b/src/modules/PowerOCR/PowerOCR/App.xaml.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System; +using System.Globalization; using System.Threading; using System.Windows; @@ -28,6 +29,19 @@ public App() { Logger.InitializeLogger("\\TextExtractor\\Logs"); + try + { + string appLanguage = LanguageHelper.LoadLanguage(); + if (!string.IsNullOrEmpty(appLanguage)) + { + PowerOCR.Properties.Resources.Culture = new System.Globalization.CultureInfo(appLanguage); + } + } + catch (CultureNotFoundException ex) + { + Logger.LogError("CultureNotFoundException: " + ex.Message); + } + NativeThreadCTS = new CancellationTokenSource(); } diff --git a/src/modules/Workspaces/WorkspacesEditor/App.xaml.cs b/src/modules/Workspaces/WorkspacesEditor/App.xaml.cs index 2edfd5c4b8f..20dfed7712c 100644 --- a/src/modules/Workspaces/WorkspacesEditor/App.xaml.cs +++ b/src/modules/Workspaces/WorkspacesEditor/App.xaml.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System; +using System.Globalization; using System.Threading; using System.Windows; @@ -40,6 +41,20 @@ private void OnStartup(object sender, StartupEventArgs e) Logger.InitializeLogger("\\Workspaces\\Logs"); AppDomain.CurrentDomain.UnhandledException += OnUnhandledException; + var languageTag = LanguageHelper.LoadLanguage(); + + if (!string.IsNullOrEmpty(languageTag)) + { + try + { + WorkspacesEditor.Properties.Resources.Culture = new System.Globalization.CultureInfo(languageTag); + } + catch (CultureNotFoundException ex) + { + Logger.LogError("CultureNotFoundException: " + ex.Message); + } + } + const string appName = "Local\\PowerToys_Workspaces_Editor_InstanceMutex"; bool createdNew; _instanceMutex = new Mutex(true, appName, out createdNew); diff --git a/src/modules/Workspaces/WorkspacesLauncherUI/App.xaml.cs b/src/modules/Workspaces/WorkspacesLauncherUI/App.xaml.cs index 0709a59c09f..4b9f0f66345 100644 --- a/src/modules/Workspaces/WorkspacesLauncherUI/App.xaml.cs +++ b/src/modules/Workspaces/WorkspacesLauncherUI/App.xaml.cs @@ -3,13 +3,12 @@ // See the LICENSE file in the project root for more information. using System; +using System.Globalization; using System.Threading; using System.Windows; -using System.Windows.Forms.Design.Behavior; using Common.UI; using ManagedCommon; -using WorkspacesLauncherUI.Utils; using WorkspacesLauncherUI.ViewModels; namespace WorkspacesLauncherUI @@ -38,6 +37,20 @@ private void OnStartup(object sender, StartupEventArgs e) Logger.InitializeLogger("\\Workspaces\\Logs"); AppDomain.CurrentDomain.UnhandledException += OnUnhandledException; + var languageTag = LanguageHelper.LoadLanguage(); + + if (!string.IsNullOrEmpty(languageTag)) + { + try + { + WorkspacesLauncherUI.Properties.Resources.Culture = new System.Globalization.CultureInfo(languageTag); + } + catch (CultureNotFoundException ex) + { + Logger.LogError("CultureNotFoundException: " + ex.Message); + } + } + const string appName = "Local\\PowerToys_Workspaces_Launcher_InstanceMutex"; bool createdNew; _instanceMutex = new Mutex(true, appName, out createdNew); diff --git a/src/modules/colorPicker/ColorPickerUI/App.xaml.cs b/src/modules/colorPicker/ColorPickerUI/App.xaml.cs index b90e2318924..09bf74b3283 100644 --- a/src/modules/colorPicker/ColorPickerUI/App.xaml.cs +++ b/src/modules/colorPicker/ColorPickerUI/App.xaml.cs @@ -4,6 +4,7 @@ using System; using System.ComponentModel.Composition; +using System.Globalization; using System.Threading; using System.Windows; @@ -29,6 +30,19 @@ public partial class App : Application, IDisposable protected override void OnStartup(StartupEventArgs e) { + try + { + string appLanguage = LanguageHelper.LoadLanguage(); + if (!string.IsNullOrEmpty(appLanguage)) + { + ColorPicker.Properties.Resources.Culture = new System.Globalization.CultureInfo(appLanguage); + } + } + catch (CultureNotFoundException ex) + { + Logger.LogError("CultureNotFoundException: " + ex.Message); + } + NativeThreadCTS = new CancellationTokenSource(); ExitToken = NativeThreadCTS.Token; diff --git a/src/modules/fancyzones/editor/FancyZonesEditor/App.xaml.cs b/src/modules/fancyzones/editor/FancyZonesEditor/App.xaml.cs index 2edb096018b..2906c759f1b 100644 --- a/src/modules/fancyzones/editor/FancyZonesEditor/App.xaml.cs +++ b/src/modules/fancyzones/editor/FancyZonesEditor/App.xaml.cs @@ -4,6 +4,7 @@ using System; using System.Diagnostics; +using System.Globalization; using System.Threading; using System.Windows; using System.Windows.Input; @@ -55,6 +56,20 @@ private void DebugModeCheck() public App() { + var languageTag = LanguageHelper.LoadLanguage(); + + if (!string.IsNullOrEmpty(languageTag)) + { + try + { + FancyZonesEditor.Properties.Resources.Culture = new System.Globalization.CultureInfo(languageTag); + } + catch (CultureNotFoundException ex) + { + Logger.LogError("CultureNotFoundException: " + ex.Message); + } + } + Logger.InitializeLogger("\\FancyZones\\Editor\\Logs"); // DebugModeCheck(); diff --git a/src/modules/imageresizer/ui/App.xaml.cs b/src/modules/imageresizer/ui/App.xaml.cs index db5a43e905e..3e7a95631f5 100644 --- a/src/modules/imageresizer/ui/App.xaml.cs +++ b/src/modules/imageresizer/ui/App.xaml.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. Code forked from Brice Lambson's https://github.com/bricelam/ImageResizer/ using System; +using System.Globalization; using System.Text; using System.Windows; @@ -19,6 +20,19 @@ public partial class App : Application, IDisposable { static App() { + try + { + string appLanguage = LanguageHelper.LoadLanguage(); + if (!string.IsNullOrEmpty(appLanguage)) + { + ImageResizer.Properties.Resources.Culture = new System.Globalization.CultureInfo(appLanguage); + } + } + catch (CultureNotFoundException) + { + // error + } + Console.InputEncoding = Encoding.Unicode; } diff --git a/src/modules/imageresizer/ui/ImageResizerUI.csproj b/src/modules/imageresizer/ui/ImageResizerUI.csproj index b1f25927e8f..3a7701607e6 100644 --- a/src/modules/imageresizer/ui/ImageResizerUI.csproj +++ b/src/modules/imageresizer/ui/ImageResizerUI.csproj @@ -46,6 +46,7 @@ + diff --git a/src/modules/launcher/PowerLauncher/App.xaml.cs b/src/modules/launcher/PowerLauncher/App.xaml.cs index 30613d2cf5a..dbe0a2a79d2 100644 --- a/src/modules/launcher/PowerLauncher/App.xaml.cs +++ b/src/modules/launcher/PowerLauncher/App.xaml.cs @@ -54,6 +54,19 @@ public static void Main() { NativeThreadCTS = new CancellationTokenSource(); + try + { + string appLanguage = LanguageHelper.LoadLanguage(); + if (!string.IsNullOrEmpty(appLanguage)) + { + PowerLauncher.Properties.Resources.Culture = new System.Globalization.CultureInfo(appLanguage); + } + } + catch (CultureNotFoundException ex) + { + Logger.LogError("CultureNotFoundException: " + ex.Message); + } + Log.Info($"Starting PowerToys Run with PID={Environment.ProcessId}", typeof(App)); if (PowerToys.GPOWrapperProjection.GPOWrapper.GetConfiguredPowerLauncherEnabledValue() == PowerToys.GPOWrapperProjection.GpoRuleConfigured.Disabled) { diff --git a/src/modules/peek/Peek.UI/PeekXAML/App.xaml.cs b/src/modules/peek/Peek.UI/PeekXAML/App.xaml.cs index 9a9dd783aab..6df56d6f464 100644 --- a/src/modules/peek/Peek.UI/PeekXAML/App.xaml.cs +++ b/src/modules/peek/Peek.UI/PeekXAML/App.xaml.cs @@ -40,6 +40,12 @@ public IHost Host /// public App() { + string appLanguage = LanguageHelper.LoadLanguage(); + if (!string.IsNullOrEmpty(appLanguage)) + { + Microsoft.Windows.Globalization.ApplicationLanguages.PrimaryLanguageOverride = appLanguage; + } + InitializeComponent(); Logger.InitializeLogger("\\Peek\\Logs"); diff --git a/src/modules/powerrename/PowerRenameUILib/PowerRenameXAML/App.xaml.cpp b/src/modules/powerrename/PowerRenameUILib/PowerRenameXAML/App.xaml.cpp index 415acad0d44..71c510e3573 100644 --- a/src/modules/powerrename/PowerRenameUILib/PowerRenameXAML/App.xaml.cpp +++ b/src/modules/powerrename/PowerRenameUILib/PowerRenameXAML/App.xaml.cpp @@ -9,6 +9,7 @@ #include #include +#include #include #include @@ -33,6 +34,12 @@ const std::wstring moduleName = L"PowerRename"; /// App::App() { + std::wstring appLanguage = LanguageHelpers::load_language(); + if (!appLanguage.empty()) + { + Microsoft::Windows::Globalization::ApplicationLanguages::PrimaryLanguageOverride(appLanguage); + } + InitializeComponent(); #if defined _DEBUG && !defined DISABLE_XAML_GENERATED_BREAK_ON_UNHANDLED_EXCEPTION diff --git a/src/modules/powerrename/PowerRenameUILib/pch.h b/src/modules/powerrename/PowerRenameUILib/pch.h index 66720e53c3b..f3b4f298f3c 100644 --- a/src/modules/powerrename/PowerRenameUILib/pch.h +++ b/src/modules/powerrename/PowerRenameUILib/pch.h @@ -37,4 +37,5 @@ #include #include #include +#include #include diff --git a/src/modules/registrypreview/RegistryPreview/RegistryPreviewXAML/App.xaml.cs b/src/modules/registrypreview/RegistryPreview/RegistryPreviewXAML/App.xaml.cs index 01007f56100..a6a3898f0f3 100644 --- a/src/modules/registrypreview/RegistryPreview/RegistryPreviewXAML/App.xaml.cs +++ b/src/modules/registrypreview/RegistryPreview/RegistryPreviewXAML/App.xaml.cs @@ -5,6 +5,7 @@ using System; using System.Web; +using ManagedCommon; using Microsoft.UI.Xaml; using Microsoft.Windows.AppLifecycle; using Windows.ApplicationModel.Activation; @@ -25,6 +26,13 @@ public partial class App : Application /// public App() { + string appLanguage = LanguageHelper.LoadLanguage(); + + if (!string.IsNullOrEmpty(appLanguage)) + { + Microsoft.Windows.Globalization.ApplicationLanguages.PrimaryLanguageOverride = appLanguage; + } + this.InitializeComponent(); } diff --git a/src/runner/settings_window.cpp b/src/runner/settings_window.cpp index 6b48cfaadc8..d6058e62e19 100644 --- a/src/runner/settings_window.cpp +++ b/src/runner/settings_window.cpp @@ -233,6 +233,12 @@ void dispatch_received_json(const std::wstring& json_to_parse) SendMessageW(pt_main_window, WM_CLOSE, 0, 0); } } + else if (name == L"language") + { + constexpr const wchar_t* language_filename = L"\\language.json"; + const std::wstring save_file_location = PTSettingsHelper::get_root_save_folder_location() + language_filename; + json::to_file(save_file_location, j); + } } return; } diff --git a/src/settings-ui/Settings.UI/SettingsXAML/App.xaml.cs b/src/settings-ui/Settings.UI/SettingsXAML/App.xaml.cs index f624ff1bbaa..15bbc243bf7 100644 --- a/src/settings-ui/Settings.UI/SettingsXAML/App.xaml.cs +++ b/src/settings-ui/Settings.UI/SettingsXAML/App.xaml.cs @@ -78,6 +78,12 @@ public App() { Logger.InitializeLogger(@"\Settings\Logs"); + string appLanguage = LanguageHelper.LoadLanguage(); + if (!string.IsNullOrEmpty(appLanguage)) + { + Microsoft.Windows.Globalization.ApplicationLanguages.PrimaryLanguageOverride = appLanguage; + } + InitializeComponent(); UnhandledException += App_UnhandledException;