From b05c2c1e1af62d07a8bd27dc2e9ae409a362df0f Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Thu, 18 Sep 2025 16:25:21 +0800 Subject: [PATCH 1/5] Remove old dictionaries references to fix possible memory leak --- Flow.Launcher.Core/Resource/Internationalization.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Flow.Launcher.Core/Resource/Internationalization.cs b/Flow.Launcher.Core/Resource/Internationalization.cs index 8261feab3d0..e8711819c76 100644 --- a/Flow.Launcher.Core/Resource/Internationalization.cs +++ b/Flow.Launcher.Core/Resource/Internationalization.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Globalization; using System.IO; @@ -256,6 +256,7 @@ private void RemoveOldLanguageFiles() foreach (var r in _oldResources) { dicts.Remove(r); + _oldResources.Remove(r); } } From 6c695f09e71ef88571c40494747cb08f95e14979 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Thu, 18 Sep 2025 17:55:06 +0800 Subject: [PATCH 2/5] Use clear function --- Flow.Launcher.Core/Resource/Internationalization.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Flow.Launcher.Core/Resource/Internationalization.cs b/Flow.Launcher.Core/Resource/Internationalization.cs index e8711819c76..6df2a28c627 100644 --- a/Flow.Launcher.Core/Resource/Internationalization.cs +++ b/Flow.Launcher.Core/Resource/Internationalization.cs @@ -256,8 +256,8 @@ private void RemoveOldLanguageFiles() foreach (var r in _oldResources) { dicts.Remove(r); - _oldResources.Remove(r); } + _oldResources.Clear(); } private void LoadLanguage(Language language) From 330e6c09e7d8321ca268147de4a6490331d92bc0 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Thu, 18 Sep 2025 18:07:18 +0800 Subject: [PATCH 3/5] Add language change lock --- .../Resource/Internationalization.cs | 30 ++++++++++++------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/Flow.Launcher.Core/Resource/Internationalization.cs b/Flow.Launcher.Core/Resource/Internationalization.cs index 6df2a28c627..c1fa2ea1665 100644 --- a/Flow.Launcher.Core/Resource/Internationalization.cs +++ b/Flow.Launcher.Core/Resource/Internationalization.cs @@ -30,6 +30,7 @@ public class Internationalization private readonly List _languageDirectories = []; private readonly List _oldResources = []; private static string SystemLanguageCode; + private readonly SemaphoreSlim _langChangeLock = new(1, 1); public Internationalization(Settings settings) { @@ -185,20 +186,29 @@ private static Language GetLanguageByLanguageCode(string languageCode) private async Task ChangeLanguageAsync(Language language, bool updateMetadata = true) { - // Remove old language files and load language - RemoveOldLanguageFiles(); - if (language != AvailableLanguages.English) + await _langChangeLock.WaitAsync(); + + try { - LoadLanguage(language); - } + // Remove old language files and load language + RemoveOldLanguageFiles(); + if (language != AvailableLanguages.English) + { + LoadLanguage(language); + } - // Change culture info - ChangeCultureInfo(language.LanguageCode); + // Change culture info + ChangeCultureInfo(language.LanguageCode); - if (updateMetadata) + if (updateMetadata) + { + // Raise event for plugins after culture is set + await Task.Run(UpdatePluginMetadataTranslations); + } + } + finally { - // Raise event for plugins after culture is set - await Task.Run(UpdatePluginMetadataTranslations); + _langChangeLock.Release(); } } From 86581e6a00f0d7806a3e910fa39283c03c0f498b Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Thu, 18 Sep 2025 18:17:27 +0800 Subject: [PATCH 4/5] Add disposable for internalization --- Flow.Launcher.Core/Resource/Internationalization.cs | 12 +++++++++++- Flow.Launcher/App.xaml.cs | 5 ++++- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/Flow.Launcher.Core/Resource/Internationalization.cs b/Flow.Launcher.Core/Resource/Internationalization.cs index c1fa2ea1665..2e270a20bc3 100644 --- a/Flow.Launcher.Core/Resource/Internationalization.cs +++ b/Flow.Launcher.Core/Resource/Internationalization.cs @@ -14,7 +14,7 @@ namespace Flow.Launcher.Core.Resource { - public class Internationalization + public class Internationalization : IDisposable { private static readonly string ClassName = nameof(Internationalization); @@ -379,5 +379,15 @@ public static void UpdatePluginMetadataTranslations() } #endregion + + #region IDisposable + + public void Dispose() + { + RemoveOldLanguageFiles(); + _langChangeLock.Dispose(); + } + + #endregion } } diff --git a/Flow.Launcher/App.xaml.cs b/Flow.Launcher/App.xaml.cs index 0360c761e5a..8ec11e5ffb8 100644 --- a/Flow.Launcher/App.xaml.cs +++ b/Flow.Launcher/App.xaml.cs @@ -45,6 +45,7 @@ public partial class App : IDisposable, ISingleInstanceApp private static Settings _settings; private static MainWindow _mainWindow; private readonly MainViewModel _mainVM; + private readonly Internationalization _internationalization; // To prevent two disposals running at the same time. private static readonly object _disposingLock = new(); @@ -107,6 +108,7 @@ public App() API = Ioc.Default.GetRequiredService(); _settings.Initialize(); _mainVM = Ioc.Default.GetRequiredService(); + _internationalization = Ioc.Default.GetRequiredService(); } catch (Exception e) { @@ -193,7 +195,7 @@ await API.StopwatchLogInfoAsync(ClassName, "Startup cost", async () => Win32Helper.EnableWin32DarkMode(_settings.ColorScheme); // Initialize language before portable clean up since it needs translations - await Ioc.Default.GetRequiredService().InitializeLanguageAsync(); + await _internationalization.InitializeLanguageAsync(); Ioc.Default.GetRequiredService().PreStartCleanUpAfterPortabilityUpdate(); @@ -421,6 +423,7 @@ protected virtual void Dispose(bool disposing) _mainWindow?.Dispatcher.Invoke(_mainWindow.Dispose); _mainVM?.Dispose(); DialogJump.Dispose(); + _internationalization.Dispose(); } API.LogInfo(ClassName, "End Flow Launcher dispose ----------------------------------------------------"); From 0f6245a072f8ad7c7b7fa93b4915d2505c880f0f Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Thu, 18 Sep 2025 18:18:02 +0800 Subject: [PATCH 5/5] Handle exceptions inside ChangeLanguageAsync to avoid unobserved task crashes --- Flow.Launcher.Core/Resource/Internationalization.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Flow.Launcher.Core/Resource/Internationalization.cs b/Flow.Launcher.Core/Resource/Internationalization.cs index 2e270a20bc3..983f8b23495 100644 --- a/Flow.Launcher.Core/Resource/Internationalization.cs +++ b/Flow.Launcher.Core/Resource/Internationalization.cs @@ -206,6 +206,10 @@ private async Task ChangeLanguageAsync(Language language, bool updateMetadata = await Task.Run(UpdatePluginMetadataTranslations); } } + catch (Exception e) + { + API.LogException(ClassName, $"Failed to change language to <{language.LanguageCode}>", e); + } finally { _langChangeLock.Release();