diff --git a/Flow.Launcher.Core/Resource/Internationalization.cs b/Flow.Launcher.Core/Resource/Internationalization.cs index 8261feab3d0..983f8b23495 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; @@ -14,7 +14,7 @@ namespace Flow.Launcher.Core.Resource { - public class Internationalization + public class Internationalization : IDisposable { private static readonly string ClassName = nameof(Internationalization); @@ -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,33 @@ 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); + } + } + catch (Exception e) { - // Raise event for plugins after culture is set - await Task.Run(UpdatePluginMetadataTranslations); + API.LogException(ClassName, $"Failed to change language to <{language.LanguageCode}>", e); + } + finally + { + _langChangeLock.Release(); } } @@ -257,6 +271,7 @@ private void RemoveOldLanguageFiles() { dicts.Remove(r); } + _oldResources.Clear(); } private void LoadLanguage(Language language) @@ -368,5 +383,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 ----------------------------------------------------");