Merge pull request #3566 from Flow-Launcher/winform_language_issue

Fix plugin culture info issue for those that use WinForm
This commit is contained in:
Jeremy Wu 2025-06-16 23:52:42 +10:00 committed by GitHub
commit 5e026c0c20
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 94 additions and 40 deletions

View file

@ -1,16 +1,17 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
using System.Windows; using System.Windows;
using CommunityToolkit.Mvvm.DependencyInjection;
using Flow.Launcher.Core.Plugin; using Flow.Launcher.Core.Plugin;
using Flow.Launcher.Infrastructure; using Flow.Launcher.Infrastructure;
using Flow.Launcher.Infrastructure.UserSettings; using Flow.Launcher.Infrastructure.UserSettings;
using Flow.Launcher.Plugin; using Flow.Launcher.Plugin;
using System.Globalization;
using System.Threading.Tasks;
using CommunityToolkit.Mvvm.DependencyInjection;
namespace Flow.Launcher.Core.Resource namespace Flow.Launcher.Core.Resource
{ {
@ -29,13 +30,12 @@ namespace Flow.Launcher.Core.Resource
private readonly Settings _settings; private readonly Settings _settings;
private readonly List<string> _languageDirectories = new(); private readonly List<string> _languageDirectories = new();
private readonly List<ResourceDictionary> _oldResources = new(); private readonly List<ResourceDictionary> _oldResources = new();
private readonly string SystemLanguageCode; private static string SystemLanguageCode;
public Internationalization(Settings settings) public Internationalization(Settings settings)
{ {
_settings = settings; _settings = settings;
AddFlowLauncherLanguageDirectory(); AddFlowLauncherLanguageDirectory();
SystemLanguageCode = GetSystemLanguageCodeAtStartup();
} }
private void AddFlowLauncherLanguageDirectory() private void AddFlowLauncherLanguageDirectory()
@ -44,7 +44,7 @@ namespace Flow.Launcher.Core.Resource
_languageDirectories.Add(directory); _languageDirectories.Add(directory);
} }
private static string GetSystemLanguageCodeAtStartup() public static void InitSystemLanguageCode()
{ {
var availableLanguages = AvailableLanguages.GetAvailableLanguages(); var availableLanguages = AvailableLanguages.GetAvailableLanguages();
@ -65,11 +65,11 @@ namespace Flow.Launcher.Core.Resource
string.Equals(languageCode, threeLetterCode, StringComparison.OrdinalIgnoreCase) || string.Equals(languageCode, threeLetterCode, StringComparison.OrdinalIgnoreCase) ||
string.Equals(languageCode, fullName, StringComparison.OrdinalIgnoreCase)) string.Equals(languageCode, fullName, StringComparison.OrdinalIgnoreCase))
{ {
return languageCode; SystemLanguageCode = languageCode;
} }
} }
return DefaultLanguageCode; SystemLanguageCode = DefaultLanguageCode;
} }
private void AddPluginLanguageDirectories() private void AddPluginLanguageDirectories()
@ -173,15 +173,33 @@ namespace Flow.Launcher.Core.Resource
LoadLanguage(language); LoadLanguage(language);
} }
// Culture of main thread // Change culture info
// Use CreateSpecificCulture to preserve possible user-override settings in Windows, if Flow's language culture is the same as Windows's ChangeCultureInfo(language.LanguageCode);
CultureInfo.CurrentCulture = CultureInfo.CreateSpecificCulture(language.LanguageCode);
CultureInfo.CurrentUICulture = CultureInfo.CurrentCulture;
// Raise event for plugins after culture is set // Raise event for plugins after culture is set
await Task.Run(UpdatePluginMetadataTranslations); await Task.Run(UpdatePluginMetadataTranslations);
} }
public static void ChangeCultureInfo(string languageCode)
{
// Culture of main thread
// Use CreateSpecificCulture to preserve possible user-override settings in Windows, if Flow's language culture is the same as Windows's
CultureInfo currentCulture;
try
{
currentCulture = CultureInfo.CreateSpecificCulture(languageCode);
}
catch (CultureNotFoundException)
{
currentCulture = CultureInfo.CreateSpecificCulture(SystemLanguageCode);
}
CultureInfo.CurrentCulture = currentCulture;
CultureInfo.CurrentUICulture = currentCulture;
var thread = Thread.CurrentThread;
thread.CurrentCulture = currentCulture;
thread.CurrentUICulture = currentCulture;
}
public bool PromptShouldUsePinyin(string languageCodeToSet) public bool PromptShouldUsePinyin(string languageCodeToSet)
{ {
var languageToSet = GetLanguageByLanguageCode(languageCodeToSet); var languageToSet = GetLanguageByLanguageCode(languageCodeToSet);

View file

@ -25,7 +25,13 @@ namespace Flow.Launcher.Infrastructure.UserSettings
public void Initialize() public void Initialize()
{ {
// Initialize dependency injection instances after Ioc.Default is created
_stringMatcher = Ioc.Default.GetRequiredService<StringMatcher>(); _stringMatcher = Ioc.Default.GetRequiredService<StringMatcher>();
// Initialize application resources after application is created
var settingWindowFont = new FontFamily(SettingWindowFont);
Application.Current.Resources["SettingWindowFont"] = settingWindowFont;
Application.Current.Resources["ContentControlThemeFontFamily"] = settingWindowFont;
} }
public void Save() public void Save()
@ -119,8 +125,11 @@ namespace Flow.Launcher.Infrastructure.UserSettings
{ {
_settingWindowFont = value; _settingWindowFont = value;
OnPropertyChanged(); OnPropertyChanged();
Application.Current.Resources["SettingWindowFont"] = new FontFamily(value); if (Application.Current != null)
Application.Current.Resources["ContentControlThemeFontFamily"] = new FontFamily(value); {
Application.Current.Resources["SettingWindowFont"] = new FontFamily(value);
Application.Current.Resources["ContentControlThemeFontFamily"] = new FontFamily(value);
}
} }
} }
} }

View file

@ -41,9 +41,9 @@ namespace Flow.Launcher
private static readonly string ClassName = nameof(App); private static readonly string ClassName = nameof(App);
private static bool _disposed; private static bool _disposed;
private static Settings _settings;
private static MainWindow _mainWindow; private static MainWindow _mainWindow;
private readonly MainViewModel _mainVM; private readonly MainViewModel _mainVM;
private readonly Settings _settings;
// To prevent two disposals running at the same time. // To prevent two disposals running at the same time.
private static readonly object _disposingLock = new(); private static readonly object _disposingLock = new();
@ -55,18 +55,7 @@ namespace Flow.Launcher
public App() public App()
{ {
// Initialize settings // Initialize settings
try _settings.WMPInstalled = WindowsMediaPlayerHelper.IsWindowsMediaPlayerInstalled();
{
var storage = new FlowLauncherJsonStorage<Settings>();
_settings = storage.Load();
_settings.SetStorage(storage);
_settings.WMPInstalled = WindowsMediaPlayerHelper.IsWindowsMediaPlayerInstalled();
}
catch (Exception e)
{
ShowErrorMsgBoxAndFailFast("Cannot load setting storage, please check local data directory", e);
return;
}
// Configure the dependency injection container // Configure the dependency injection container
try try
@ -123,16 +112,6 @@ namespace Flow.Launcher
ShowErrorMsgBoxAndFailFast("Cannot initialize api and settings, please open new issue in Flow.Launcher", e); ShowErrorMsgBoxAndFailFast("Cannot initialize api and settings, please open new issue in Flow.Launcher", e);
return; return;
} }
// Local function
static void ShowErrorMsgBoxAndFailFast(string message, Exception e)
{
// Firstly show users the message
MessageBox.Show(e.ToString(), message, MessageBoxButton.OK, MessageBoxImage.Error);
// Flow cannot construct its App instance, so ensure Flow crashes w/ the exception info.
Environment.FailFast(message, e);
}
} }
#endregion #endregion
@ -142,6 +121,29 @@ namespace Flow.Launcher
[STAThread] [STAThread]
public static void Main() public static void Main()
{ {
// Initialize settings so that we can get language code
try
{
var storage = new FlowLauncherJsonStorage<Settings>();
_settings = storage.Load();
_settings.SetStorage(storage);
}
catch (Exception e)
{
ShowErrorMsgBoxAndFailFast("Cannot load setting storage, please check local data directory", e);
return;
}
// Initialize system language before changing culture info
Internationalization.InitSystemLanguageCode();
// Change culture info before application creation to localize WinForm windows
if (_settings.Language != Constant.SystemLanguageCode)
{
Internationalization.ChangeCultureInfo(_settings.Language);
}
// Start the application as a single instance
if (SingleInstance<App>.InitializeAsFirstInstance()) if (SingleInstance<App>.InitializeAsFirstInstance())
{ {
using var application = new App(); using var application = new App();
@ -152,6 +154,19 @@ namespace Flow.Launcher
#endregion #endregion
#region Fail Fast
private static void ShowErrorMsgBoxAndFailFast(string message, Exception e)
{
// Firstly show users the message
MessageBox.Show(e.ToString(), message, MessageBoxButton.OK, MessageBoxImage.Error);
// Flow cannot construct its App instance, so ensure Flow crashes w/ the exception info.
Environment.FailFast(message, e);
}
#endregion
#region App Events #region App Events
#pragma warning disable VSTHRD100 // Avoid async void methods #pragma warning disable VSTHRD100 // Avoid async void methods

View file

@ -1,11 +1,23 @@
using Microsoft.Win32; using System;
using Microsoft.Win32;
namespace Flow.Launcher.Helper; namespace Flow.Launcher.Helper;
internal static class WindowsMediaPlayerHelper internal static class WindowsMediaPlayerHelper
{ {
private static readonly string ClassName = nameof(WindowsMediaPlayerHelper);
internal static bool IsWindowsMediaPlayerInstalled() internal static bool IsWindowsMediaPlayerInstalled()
{ {
using var key = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\MediaPlayer"); try
return key?.GetValue("Installation Directory") != null; {
using var key = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\MediaPlayer");
return key?.GetValue("Installation Directory") != null;
}
catch (Exception e)
{
App.API.LogException(ClassName, "Failed to check if Windows Media Player is installed", e);
return false;
}
} }
} }