From 486522445889ac273122b43f3e515f312f06dde1 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Tue, 23 Sep 2025 14:09:21 +0800 Subject: [PATCH 1/7] Fix format --- Flow.Launcher/Flow.Launcher.csproj | 42 ++++++++++++++++++++++++++++-- 1 file changed, 40 insertions(+), 2 deletions(-) diff --git a/Flow.Launcher/Flow.Launcher.csproj b/Flow.Launcher/Flow.Launcher.csproj index a99d4d8c2..c486023d0 100644 --- a/Flow.Launcher/Flow.Launcher.csproj +++ b/Flow.Launcher/Flow.Launcher.csproj @@ -40,11 +40,49 @@ - + - + From d87650de08357bbbf1cc621718dff34038770998 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Tue, 23 Sep 2025 16:21:09 +0800 Subject: [PATCH 2/7] Use Flow.Launcher.Localization to improve code quality --- Flow.Launcher/ActionKeywords.xaml.cs | 4 +-- Flow.Launcher/App.xaml.cs | 2 +- .../CustomQueryHotkeySetting.xaml.cs | 2 +- Flow.Launcher/CustomShortcutSetting.xaml.cs | 4 +-- Flow.Launcher/Flow.Launcher.csproj | 6 ++++ Flow.Launcher/Helper/HotKeyMapper.cs | 12 ++++---- Flow.Launcher/HotkeyControl.xaml.cs | 4 +-- Flow.Launcher/HotkeyControlDialog.xaml.cs | 16 ++++------ Flow.Launcher/Languages/en.xaml | 4 ++- Flow.Launcher/MainWindow.xaml.cs | 30 +++++++++---------- Flow.Launcher/PluginUpdateWindow.xaml.cs | 9 ++---- Flow.Launcher/PublicAPIInstance.cs | 30 +++++++++---------- Flow.Launcher/ReleaseNotesWindow.xaml.cs | 4 +-- Flow.Launcher/ReportWindow.xaml.cs | 6 ++-- .../Resources/Pages/WelcomePage5.xaml.cs | 2 +- .../ViewModels/SettingsPaneAboutViewModel.cs | 21 ++++++------- .../SettingsPaneGeneralViewModel.cs | 15 ++++------ .../ViewModels/SettingsPaneHotkeyViewModel.cs | 24 +++++++-------- .../SettingsPanePluginStoreViewModel.cs | 6 ++-- .../ViewModels/SettingsPaneThemeViewModel.cs | 20 ++++++------- Flow.Launcher/ViewModel/MainViewModel.cs | 28 ++++++++--------- Flow.Launcher/ViewModel/PluginViewModel.cs | 13 ++++---- .../ViewModel/SelectBrowserViewModel.cs | 2 +- .../ViewModel/SelectFileManagerViewModel.cs | 7 ++--- Flow.Launcher/packages.lock.json | 6 ++++ 25 files changed, 134 insertions(+), 143 deletions(-) diff --git a/Flow.Launcher/ActionKeywords.xaml.cs b/Flow.Launcher/ActionKeywords.xaml.cs index 8e05686c9..a94b265fc 100644 --- a/Flow.Launcher/ActionKeywords.xaml.cs +++ b/Flow.Launcher/ActionKeywords.xaml.cs @@ -47,7 +47,7 @@ namespace Flow.Launcher if (addedActionKeywords.Any(App.API.ActionKeywordAssigned)) { - App.API.ShowMsgBox(App.API.GetTranslation("newActionKeywordsHasBeenAssigned")); + App.API.ShowMsgBox(Localize.newActionKeywordsHasBeenAssigned()); return; } @@ -63,7 +63,7 @@ namespace Flow.Launcher if (sortedOldActionKeywords.SequenceEqual(sortedNewActionKeywords)) { // User just changes the sequence of action keywords - App.API.ShowMsgBox(App.API.GetTranslation("newActionKeywordsSameAsOld")); + App.API.ShowMsgBox(Localize.newActionKeywordsSameAsOld()); } else { diff --git a/Flow.Launcher/App.xaml.cs b/Flow.Launcher/App.xaml.cs index 8ec11e5ff..58f8438d2 100644 --- a/Flow.Launcher/App.xaml.cs +++ b/Flow.Launcher/App.xaml.cs @@ -276,7 +276,7 @@ namespace Flow.Launcher // but if it fails (permissions, etc) then don't keep retrying // this also gives the user a visual indication in the Settings widget _settings.StartFlowLauncherOnSystemStartup = false; - API.ShowMsgError(API.GetTranslation("setAutoStartFailed"), e.Message); + API.ShowMsgError(Localize.setAutoStartFailed(), e.Message); } } } diff --git a/Flow.Launcher/CustomQueryHotkeySetting.xaml.cs b/Flow.Launcher/CustomQueryHotkeySetting.xaml.cs index 2ee08bf85..3bba2c5b8 100644 --- a/Flow.Launcher/CustomQueryHotkeySetting.xaml.cs +++ b/Flow.Launcher/CustomQueryHotkeySetting.xaml.cs @@ -41,7 +41,7 @@ namespace Flow.Launcher if (string.IsNullOrEmpty(Hotkey) && string.IsNullOrEmpty(ActionKeyword)) { - App.API.ShowMsgBox(App.API.GetTranslation("emptyPluginHotkey")); + App.API.ShowMsgBox(Localize.emptyPluginHotkey()); return; } diff --git a/Flow.Launcher/CustomShortcutSetting.xaml.cs b/Flow.Launcher/CustomShortcutSetting.xaml.cs index f4644a267..317d059a1 100644 --- a/Flow.Launcher/CustomShortcutSetting.xaml.cs +++ b/Flow.Launcher/CustomShortcutSetting.xaml.cs @@ -40,14 +40,14 @@ namespace Flow.Launcher { if (string.IsNullOrEmpty(Key) || string.IsNullOrEmpty(Value)) { - App.API.ShowMsgBox(App.API.GetTranslation("emptyShortcut")); + App.API.ShowMsgBox(Localize.emptyShortcut()); return; } // Check if key is modified or adding a new one if (((update && originalKey != Key) || !update) && _hotkeyVm.DoesShortcutExist(Key)) { - App.API.ShowMsgBox(App.API.GetTranslation("duplicateShortcut")); + App.API.ShowMsgBox(Localize.duplicateShortcut()); return; } diff --git a/Flow.Launcher/Flow.Launcher.csproj b/Flow.Launcher/Flow.Launcher.csproj index c486023d0..aa8e95429 100644 --- a/Flow.Launcher/Flow.Launcher.csproj +++ b/Flow.Launcher/Flow.Launcher.csproj @@ -37,6 +37,7 @@ prompt 4 false + $(NoWarn);FLSG0007 @@ -132,6 +133,7 @@ + all runtime; build; native; contentfiles; analyzers; buildtransitive @@ -161,6 +163,10 @@ + + true + + Always diff --git a/Flow.Launcher/Helper/HotKeyMapper.cs b/Flow.Launcher/Helper/HotKeyMapper.cs index 86a68475e..bb1cddc6c 100644 --- a/Flow.Launcher/Helper/HotKeyMapper.cs +++ b/Flow.Launcher/Helper/HotKeyMapper.cs @@ -61,8 +61,8 @@ internal static class HotKeyMapper string.Format("|HotkeyMapper.SetWithChefKeys|Error registering hotkey: {0} \nStackTrace:{1}", e.Message, e.StackTrace)); - string errorMsg = string.Format(App.API.GetTranslation("registerHotkeyFailed"), hotkeyStr); - string errorMsgTitle = App.API.GetTranslation("MessageBoxTitle"); + string errorMsg = Localize.registerHotkeyFailed(hotkeyStr); + string errorMsgTitle = Localize.MessageBoxTitle(); App.API.ShowMsgBox(errorMsg, errorMsgTitle); } } @@ -87,8 +87,8 @@ internal static class HotKeyMapper e.Message, e.StackTrace, hotkeyStr)); - string errorMsg = string.Format(App.API.GetTranslation("registerHotkeyFailed"), hotkeyStr); - string errorMsgTitle = App.API.GetTranslation("MessageBoxTitle"); + string errorMsg = Localize.registerHotkeyFailed(hotkeyStr); + string errorMsgTitle = Localize.MessageBoxTitle(); App.API.ShowMsgBox(errorMsg, errorMsgTitle); } } @@ -112,8 +112,8 @@ internal static class HotKeyMapper string.Format("|HotkeyMapper.RemoveHotkey|Error removing hotkey: {0} \nStackTrace:{1}", e.Message, e.StackTrace)); - string errorMsg = string.Format(App.API.GetTranslation("unregisterHotkeyFailed"), hotkeyStr); - string errorMsgTitle = App.API.GetTranslation("MessageBoxTitle"); + string errorMsg = Localize.unregisterHotkeyFailed(hotkeyStr); + string errorMsgTitle = Localize.MessageBoxTitle(); App.API.ShowMsgBox(errorMsg, errorMsgTitle); } } diff --git a/Flow.Launcher/HotkeyControl.xaml.cs b/Flow.Launcher/HotkeyControl.xaml.cs index 89bfde349..b920b53a7 100644 --- a/Flow.Launcher/HotkeyControl.xaml.cs +++ b/Flow.Launcher/HotkeyControl.xaml.cs @@ -1,4 +1,4 @@ -using System.Collections.ObjectModel; +using System.Collections.ObjectModel; using System.Threading.Tasks; using System.Windows; using System.Windows.Input; @@ -234,7 +234,7 @@ namespace Flow.Launcher private static bool CheckHotkeyAvailability(HotkeyModel hotkey, bool validateKeyGesture) => hotkey.Validate(validateKeyGesture) && HotKeyMapper.CheckAvailability(hotkey); - public string EmptyHotkey => App.API.GetTranslation("none"); + public string EmptyHotkey => Localize.none(); public ObservableCollection KeysToDisplay { get; set; } = new(); diff --git a/Flow.Launcher/HotkeyControlDialog.xaml.cs b/Flow.Launcher/HotkeyControlDialog.xaml.cs index c7af8c5b8..740425f8b 100644 --- a/Flow.Launcher/HotkeyControlDialog.xaml.cs +++ b/Flow.Launcher/HotkeyControlDialog.xaml.cs @@ -33,7 +33,7 @@ public partial class HotkeyControlDialog : ContentDialog public EResultType ResultType { get; private set; } = EResultType.Cancel; public string ResultValue { get; private set; } = string.Empty; - public static string EmptyHotkey => App.API.GetTranslation("none"); + public static string EmptyHotkey => Localize.none(); private static bool isOpenFlowHotkey; @@ -41,7 +41,7 @@ public partial class HotkeyControlDialog : ContentDialog { WindowTitle = windowTitle switch { - "" or null => App.API.GetTranslation("hotkeyRegTitle"), + "" or null => Localize.hotkeyRegTitle(), _ => windowTitle }; DefaultHotkey = defaultHotkey; @@ -146,10 +146,7 @@ public partial class HotkeyControlDialog : ContentDialog Alert.Visibility = Visibility.Visible; if (registeredHotkeyData.RemoveHotkey is not null) { - tbMsg.Text = string.Format( - App.API.GetTranslation("hotkeyUnavailableEditable"), - description - ); + tbMsg.Text = Localize.hotkeyUnavailableEditable(description); SaveBtn.IsEnabled = false; SaveBtn.Visibility = Visibility.Collapsed; OverwriteBtn.IsEnabled = true; @@ -158,10 +155,7 @@ public partial class HotkeyControlDialog : ContentDialog } else { - tbMsg.Text = string.Format( - App.API.GetTranslation("hotkeyUnavailableUneditable"), - description - ); + tbMsg.Text = Localize.hotkeyUnavailableUneditable(description); SaveBtn.IsEnabled = false; SaveBtn.Visibility = Visibility.Visible; OverwriteBtn.IsEnabled = false; @@ -175,7 +169,7 @@ public partial class HotkeyControlDialog : ContentDialog if (!CheckHotkeyAvailability(hotkey.Value, true)) { - tbMsg.Text = App.API.GetTranslation("hotkeyUnavailable"); + tbMsg.Text = Localize.hotkeyUnavailable(); Alert.Visibility = Visibility.Visible; SaveBtn.IsEnabled = false; SaveBtn.Visibility = Visibility.Visible; diff --git a/Flow.Launcher/Languages/en.xaml b/Flow.Launcher/Languages/en.xaml index 561bb277e..a51782f40 100644 --- a/Flow.Launcher/Languages/en.xaml +++ b/Flow.Launcher/Languages/en.xaml @@ -209,6 +209,8 @@ Version Website Uninstall + Search delay time: default + Search delay time: {0}ms Fail to remove plugin settings Plugins: {0} - Fail to remove plugin settings files, please remove them manually Fail to remove plugin cache @@ -588,7 +590,7 @@ The specified file manager could not be found. Please check the Custom File Manager setting under Settings > General. Error - An error occurred while opening the folder. {0} + An error occurred while opening the folder. An error occurred while opening the URL in the browser. Please check your Default Web Browser configuration in the General section of the settings window File or directory not found: {0} diff --git a/Flow.Launcher/MainWindow.xaml.cs b/Flow.Launcher/MainWindow.xaml.cs index 7b6a0d79b..21cb124b0 100644 --- a/Flow.Launcher/MainWindow.xaml.cs +++ b/Flow.Launcher/MainWindow.xaml.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.ComponentModel; using System.Linq; using System.Media; @@ -145,8 +145,8 @@ namespace Flow.Launcher _settings.ReleaseNotesVersion = Constant.Version; // Show release note popup with button App.API.ShowMsgWithButton( - string.Format(App.API.GetTranslation("appUpdateTitle"), Constant.Version), - App.API.GetTranslation("appUpdateButtonContent"), + Localize.appUpdateTitle(Constant.Version), + Localize.appUpdateButtonContent(), () => { Application.Current.Dispatcher.Invoke(() => @@ -753,12 +753,12 @@ namespace Flow.Launcher private void UpdateNotifyIconText() { var menu = _contextMenu; - ((MenuItem)menu.Items[0]).Header = App.API.GetTranslation("iconTrayOpen") + + ((MenuItem)menu.Items[0]).Header = Localize.iconTrayOpen()+ " (" + _settings.Hotkey + ")"; - ((MenuItem)menu.Items[1]).Header = App.API.GetTranslation("GameMode"); - ((MenuItem)menu.Items[2]).Header = App.API.GetTranslation("PositionReset"); - ((MenuItem)menu.Items[3]).Header = App.API.GetTranslation("iconTraySettings"); - ((MenuItem)menu.Items[4]).Header = App.API.GetTranslation("iconTrayExit"); + ((MenuItem)menu.Items[1]).Header = Localize.GameMode(); + ((MenuItem)menu.Items[2]).Header = Localize.PositionReset(); + ((MenuItem)menu.Items[3]).Header = Localize.iconTraySettings(); + ((MenuItem)menu.Items[4]).Header = Localize.iconTrayExit(); } private void InitializeContextMenu() @@ -768,31 +768,31 @@ namespace Flow.Launcher var openIcon = new FontIcon { Glyph = "\ue71e" }; var open = new MenuItem { - Header = App.API.GetTranslation("iconTrayOpen") + " (" + _settings.Hotkey + ")", + Header = Localize.iconTrayOpen()+ " (" + _settings.Hotkey + ")", Icon = openIcon }; var gamemodeIcon = new FontIcon { Glyph = "\ue7fc" }; var gamemode = new MenuItem { - Header = App.API.GetTranslation("GameMode"), + Header = Localize.GameMode(), Icon = gamemodeIcon }; var positionresetIcon = new FontIcon { Glyph = "\ue73f" }; var positionreset = new MenuItem { - Header = App.API.GetTranslation("PositionReset"), + Header = Localize.PositionReset(), Icon = positionresetIcon }; var settingsIcon = new FontIcon { Glyph = "\ue713" }; var settings = new MenuItem { - Header = App.API.GetTranslation("iconTraySettings"), + Header = Localize.iconTraySettings(), Icon = settingsIcon }; var exitIcon = new FontIcon { Glyph = "\ue7e8" }; var exit = new MenuItem { - Header = App.API.GetTranslation("iconTrayExit"), + Header = Localize.iconTrayExit(), Icon = exitIcon }; @@ -802,8 +802,8 @@ namespace Flow.Launcher settings.Click += (o, e) => App.API.OpenSettingDialog(); exit.Click += (o, e) => Close(); - gamemode.ToolTip = App.API.GetTranslation("GameModeToolTip"); - positionreset.ToolTip = App.API.GetTranslation("PositionResetToolTip"); + gamemode.ToolTip = Localize.GameModeToolTip(); + positionreset.ToolTip = Localize.PositionResetToolTip(); _contextMenu.Items.Add(open); _contextMenu.Items.Add(gamemode); diff --git a/Flow.Launcher/PluginUpdateWindow.xaml.cs b/Flow.Launcher/PluginUpdateWindow.xaml.cs index 20f033425..4b56e5836 100644 --- a/Flow.Launcher/PluginUpdateWindow.xaml.cs +++ b/Flow.Launcher/PluginUpdateWindow.xaml.cs @@ -23,7 +23,7 @@ namespace Flow.Launcher { var checkBox = new CheckBox { - Content = string.Format(App.API.GetTranslation("updatePluginCheckboxContent"), plugin.Name, plugin.CurrentVersion, plugin.NewVersion), + Content = Localize.updatePluginCheckboxContent(plugin.Name, plugin.CurrentVersion, plugin.NewVersion), IsChecked = true, Margin = new Thickness(0, 5, 0, 5), Tag = plugin, @@ -50,10 +50,7 @@ namespace Flow.Launcher { if (sender is not CheckBox cb) return; if (cb.Tag is not PluginUpdateInfo plugin) return; - if (Plugins.Contains(plugin)) - { - Plugins.Remove(plugin); - } + Plugins.Remove(plugin); } private void BtnCancel_OnClick(object sender, RoutedEventArgs e) @@ -66,7 +63,7 @@ namespace Flow.Launcher { if (Plugins.Count == 0) { - App.API.ShowMsgBox(App.API.GetTranslation("updatePluginNoSelected")); + App.API.ShowMsgBox(Localize.updatePluginNoSelected()); return; } diff --git a/Flow.Launcher/PublicAPIInstance.cs b/Flow.Launcher/PublicAPIInstance.cs index b4c3aa92b..bd2f80743 100644 --- a/Flow.Launcher/PublicAPIInstance.cs +++ b/Flow.Launcher/PublicAPIInstance.cs @@ -184,14 +184,14 @@ namespace Flow.Launcher if (showDefaultNotification) { ShowMsg( - $"{GetTranslation("copy")} {(isFile ? GetTranslation("fileTitle") : GetTranslation("folderTitle"))}", - GetTranslation("completedSuccessfully")); + $"{Localize.copy()} {(isFile ? Localize.fileTitle(): Localize.folderTitle())}", + Localize.completedSuccessfully()); } } else { LogException(nameof(PublicAPIInstance), "Failed to copy file/folder to clipboard", exception); - ShowMsgError(GetTranslation("failedToCopy")); + ShowMsgError(Localize.failedToCopy()); } } else @@ -209,14 +209,14 @@ namespace Flow.Launcher if (showDefaultNotification) { ShowMsg( - $"{GetTranslation("copy")} {GetTranslation("textTitle")}", - GetTranslation("completedSuccessfully")); + $"{Localize.copy()} {Localize.textTitle()}", + Localize.completedSuccessfully()); } } else { LogException(nameof(PublicAPIInstance), "Failed to copy text to clipboard", exception); - ShowMsgError(GetTranslation("failedToCopy")); + ShowMsgError(Localize.failedToCopy()); } } } @@ -393,18 +393,18 @@ namespace Flow.Launcher } catch (Win32Exception ex) when (ex.NativeErrorCode == 2) { - LogError(ClassName, "File Manager not found"); + LogException(ClassName, "File Manager not found", ex); ShowMsgError( - GetTranslation("fileManagerNotFoundTitle"), - string.Format(GetTranslation("fileManagerNotFound"), ex.Message) + Localize.fileManagerNotFoundTitle(), + Localize.fileManagerNotFound() ); } catch (Exception ex) { LogException(ClassName, "Failed to open folder", ex); ShowMsgError( - GetTranslation("errorTitle"), - string.Format(GetTranslation("folderOpenError"), ex.Message) + Localize.errorTitle(), + Localize.folderOpenError() ); } } @@ -413,7 +413,7 @@ namespace Flow.Launcher { if (uri.IsFile && !FilesFolders.FileOrLocationExists(uri.LocalPath)) { - ShowMsgError(GetTranslation("errorTitle"), string.Format(GetTranslation("fileNotFoundError"), uri.LocalPath)); + ShowMsgError(Localize.errorTitle(), Localize.fileNotFoundError(uri.LocalPath)); return; } @@ -439,8 +439,8 @@ namespace Flow.Launcher var tabOrWindow = browserInfo.OpenInTab ? "tab" : "window"; LogException(ClassName, $"Failed to open URL in browser {tabOrWindow}: {path}, {inPrivate ?? browserInfo.EnablePrivate}, {browserInfo.PrivateArg}", e); ShowMsgError( - GetTranslation("errorTitle"), - GetTranslation("browserOpenError") + Localize.errorTitle(), + Localize.browserOpenError() ); } } @@ -457,7 +457,7 @@ namespace Flow.Launcher catch (Exception e) { LogException(ClassName, $"Failed to open: {uri.AbsoluteUri}", e); - ShowMsgError(GetTranslation("errorTitle"), e.Message); + ShowMsgError(Localize.errorTitle(), e.Message); } } } diff --git a/Flow.Launcher/ReleaseNotesWindow.xaml.cs b/Flow.Launcher/ReleaseNotesWindow.xaml.cs index ce7a3e084..4e3f30d30 100644 --- a/Flow.Launcher/ReleaseNotesWindow.xaml.cs +++ b/Flow.Launcher/ReleaseNotesWindow.xaml.cs @@ -132,8 +132,8 @@ namespace Flow.Launcher RefreshButton.Visibility = Visibility.Visible; MarkdownViewer.Visibility = Visibility.Collapsed; App.API.ShowMsgError( - App.API.GetTranslation("checkNetworkConnectionTitle"), - App.API.GetTranslation("checkNetworkConnectionSubTitle")); + Localize.checkNetworkConnectionTitle(), + Localize.checkNetworkConnectionSubTitle()); } else { diff --git a/Flow.Launcher/ReportWindow.xaml.cs b/Flow.Launcher/ReportWindow.xaml.cs index ae0767934..bb0ce0073 100644 --- a/Flow.Launcher/ReportWindow.xaml.cs +++ b/Flow.Launcher/ReportWindow.xaml.cs @@ -48,10 +48,10 @@ namespace Flow.Launcher _ => Constant.IssuesUrl }; - var paragraph = Hyperlink(App.API.GetTranslation("reportWindow_please_open_issue"), websiteUrl); - paragraph.Inlines.Add(string.Format(App.API.GetTranslation("reportWindow_upload_log"), log.FullName)); + var paragraph = Hyperlink(Localize.reportWindow_please_open_issue(), websiteUrl); + paragraph.Inlines.Add(Localize.reportWindow_upload_log(log.FullName)); paragraph.Inlines.Add("\n"); - paragraph.Inlines.Add(App.API.GetTranslation("reportWindow_copy_below")); + paragraph.Inlines.Add(Localize.reportWindow_copy_below()); ErrorTextbox.Document.Blocks.Add(paragraph); StringBuilder content = new StringBuilder(); diff --git a/Flow.Launcher/Resources/Pages/WelcomePage5.xaml.cs b/Flow.Launcher/Resources/Pages/WelcomePage5.xaml.cs index 10cd18821..5e3ab6815 100644 --- a/Flow.Launcher/Resources/Pages/WelcomePage5.xaml.cs +++ b/Flow.Launcher/Resources/Pages/WelcomePage5.xaml.cs @@ -59,7 +59,7 @@ namespace Flow.Launcher.Resources.Pages } catch (Exception e) { - App.API.ShowMsgError(App.API.GetTranslation("setAutoStartFailed"), e.Message); + App.API.ShowMsgError(Localize.setAutoStartFailed(), e.Message); } } diff --git a/Flow.Launcher/SettingPages/ViewModels/SettingsPaneAboutViewModel.cs b/Flow.Launcher/SettingPages/ViewModels/SettingsPaneAboutViewModel.cs index 647b36701..f906bf55c 100644 --- a/Flow.Launcher/SettingPages/ViewModels/SettingsPaneAboutViewModel.cs +++ b/Flow.Launcher/SettingPages/ViewModels/SettingsPaneAboutViewModel.cs @@ -25,7 +25,7 @@ public partial class SettingsPaneAboutViewModel : BaseModel get { var size = GetLogFiles().Sum(file => file.Length); - return $"{App.API.GetTranslation("clearlogfolder")} ({BytesToReadableString(size)})"; + return $"{Localize.clearlogfolder()} ({BytesToReadableString(size)})"; } } @@ -34,7 +34,7 @@ public partial class SettingsPaneAboutViewModel : BaseModel get { var size = GetCacheFiles().Sum(file => file.Length); - return $"{App.API.GetTranslation("clearcachefolder")} ({BytesToReadableString(size)})"; + return $"{Localize.clearcachefolder()} ({BytesToReadableString(size)})"; } } @@ -51,10 +51,7 @@ public partial class SettingsPaneAboutViewModel : BaseModel _ => Constant.Version }; - public string ActivatedTimes => string.Format( - App.API.GetTranslation("about_activate_times"), - _settings.ActivateTimes - ); + public string ActivatedTimes => Localize.about_activate_times(_settings.ActivateTimes); public class LogLevelData : DropdownDataGeneric { } @@ -98,8 +95,8 @@ public partial class SettingsPaneAboutViewModel : BaseModel private void AskClearLogFolderConfirmation() { var confirmResult = App.API.ShowMsgBox( - App.API.GetTranslation("clearlogfolderMessage"), - App.API.GetTranslation("clearlogfolder"), + Localize.clearlogfolderMessage(), + Localize.clearlogfolder(), MessageBoxButton.YesNo ); @@ -107,7 +104,7 @@ public partial class SettingsPaneAboutViewModel : BaseModel { if (!ClearLogFolder()) { - App.API.ShowMsgBox(App.API.GetTranslation("clearfolderfailMessage")); + App.API.ShowMsgBox(Localize.clearfolderfailMessage()); } } } @@ -116,8 +113,8 @@ public partial class SettingsPaneAboutViewModel : BaseModel private void AskClearCacheFolderConfirmation() { var confirmResult = App.API.ShowMsgBox( - App.API.GetTranslation("clearcachefolderMessage"), - App.API.GetTranslation("clearcachefolder"), + Localize.clearcachefolderMessage(), + Localize.clearcachefolder(), MessageBoxButton.YesNo ); @@ -125,7 +122,7 @@ public partial class SettingsPaneAboutViewModel : BaseModel { if (!ClearCacheFolder()) { - App.API.ShowMsgBox(App.API.GetTranslation("clearfolderfailMessage")); + App.API.ShowMsgBox(Localize.clearfolderfailMessage()); } } } diff --git a/Flow.Launcher/SettingPages/ViewModels/SettingsPaneGeneralViewModel.cs b/Flow.Launcher/SettingPages/ViewModels/SettingsPaneGeneralViewModel.cs index 885330b8c..6641ac689 100644 --- a/Flow.Launcher/SettingPages/ViewModels/SettingsPaneGeneralViewModel.cs +++ b/Flow.Launcher/SettingPages/ViewModels/SettingsPaneGeneralViewModel.cs @@ -65,7 +65,7 @@ public partial class SettingsPaneGeneralViewModel : BaseModel } catch (Exception e) { - App.API.ShowMsgError(App.API.GetTranslation("setAutoStartFailed"), e.Message); + App.API.ShowMsgError(Localize.setAutoStartFailed(), e.Message); } } } @@ -92,7 +92,7 @@ public partial class SettingsPaneGeneralViewModel : BaseModel } catch (Exception e) { - App.API.ShowMsgError(App.API.GetTranslation("setAutoStartFailed"), e.Message); + App.API.ShowMsgError(Localize.setAutoStartFailed(), e.Message); } } } @@ -257,7 +257,7 @@ public partial class SettingsPaneGeneralViewModel : BaseModel else { // Since this is rarely seen text, language support is not provided. - App.API.ShowMsgError(App.API.GetTranslation("KoreanImeSettingChangeFailTitle"), App.API.GetTranslation("KoreanImeSettingChangeFailSubTitle")); + App.API.ShowMsgError(Localize.KoreanImeSettingChangeFailTitle(), Localize.KoreanImeSettingChangeFailSubTitle()); } } } @@ -325,10 +325,7 @@ public partial class SettingsPaneGeneralViewModel : BaseModel public List Languages => _translater.LoadAvailableLanguages(); - public string AlwaysPreviewToolTip => string.Format( - App.API.GetTranslation("AlwaysPreviewToolTip"), - Settings.PreviewHotkey - ); + public string AlwaysPreviewToolTip => Localize.AlwaysPreviewToolTip(Settings.PreviewHotkey); private static string GetFileFromDialog(string title, string filter = "") { @@ -372,7 +369,7 @@ public partial class SettingsPaneGeneralViewModel : BaseModel private void SelectPython() { var selectedFile = GetFileFromDialog( - App.API.GetTranslation("selectPythonExecutable"), + Localize.selectPythonExecutable(), "Python|pythonw.exe" ); @@ -384,7 +381,7 @@ public partial class SettingsPaneGeneralViewModel : BaseModel private void SelectNode() { var selectedFile = GetFileFromDialog( - App.API.GetTranslation("selectNodeExecutable"), + Localize.selectNodeExecutable(), "node|*.exe" ); diff --git a/Flow.Launcher/SettingPages/ViewModels/SettingsPaneHotkeyViewModel.cs b/Flow.Launcher/SettingPages/ViewModels/SettingsPaneHotkeyViewModel.cs index 9e6a31dc7..3e7c3cb83 100644 --- a/Flow.Launcher/SettingPages/ViewModels/SettingsPaneHotkeyViewModel.cs +++ b/Flow.Launcher/SettingPages/ViewModels/SettingsPaneHotkeyViewModel.cs @@ -50,15 +50,13 @@ public partial class SettingsPaneHotkeyViewModel : BaseModel var item = SelectedCustomPluginHotkey; if (item is null) { - App.API.ShowMsgBox(App.API.GetTranslation("pleaseSelectAnItem")); + App.API.ShowMsgBox(Localize.pleaseSelectAnItem()); return; } var result = App.API.ShowMsgBox( - string.Format( - App.API.GetTranslation("deleteCustomHotkeyWarning"), item.Hotkey - ), - App.API.GetTranslation("delete"), + Localize.deleteCustomHotkeyWarning(item.Hotkey), + Localize.delete(), MessageBoxButton.YesNo ); @@ -75,7 +73,7 @@ public partial class SettingsPaneHotkeyViewModel : BaseModel var item = SelectedCustomPluginHotkey; if (item is null) { - App.API.ShowMsgBox(App.API.GetTranslation("pleaseSelectAnItem")); + App.API.ShowMsgBox(Localize.pleaseSelectAnItem()); return; } @@ -83,7 +81,7 @@ public partial class SettingsPaneHotkeyViewModel : BaseModel o.ActionKeyword == item.ActionKeyword && o.Hotkey == item.Hotkey); if (settingItem == null) { - App.API.ShowMsgBox(App.API.GetTranslation("invalidPluginHotkey")); + App.API.ShowMsgBox(Localize.invalidPluginHotkey()); return; } @@ -114,15 +112,13 @@ public partial class SettingsPaneHotkeyViewModel : BaseModel var item = SelectedCustomShortcut; if (item is null) { - App.API.ShowMsgBox(App.API.GetTranslation("pleaseSelectAnItem")); + App.API.ShowMsgBox(Localize.pleaseSelectAnItem()); return; } var result = App.API.ShowMsgBox( - string.Format( - App.API.GetTranslation("deleteCustomShortcutWarning"), item.Key, item.Value - ), - App.API.GetTranslation("delete"), + Localize.deleteCustomShortcutWarning(item.Key, item.Value), + Localize.delete(), MessageBoxButton.YesNo ); @@ -138,7 +134,7 @@ public partial class SettingsPaneHotkeyViewModel : BaseModel var item = SelectedCustomShortcut; if (item is null) { - App.API.ShowMsgBox(App.API.GetTranslation("pleaseSelectAnItem")); + App.API.ShowMsgBox(Localize.pleaseSelectAnItem()); return; } @@ -146,7 +142,7 @@ public partial class SettingsPaneHotkeyViewModel : BaseModel o.Key == item.Key && o.Value == item.Value); if (settingItem == null) { - App.API.ShowMsgBox(App.API.GetTranslation("invalidShortcut")); + App.API.ShowMsgBox(Localize.invalidShortcut()); return; } diff --git a/Flow.Launcher/SettingPages/ViewModels/SettingsPanePluginStoreViewModel.cs b/Flow.Launcher/SettingPages/ViewModels/SettingsPanePluginStoreViewModel.cs index f133b7d2b..d67695a75 100644 --- a/Flow.Launcher/SettingPages/ViewModels/SettingsPanePluginStoreViewModel.cs +++ b/Flow.Launcher/SettingPages/ViewModels/SettingsPanePluginStoreViewModel.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; @@ -103,8 +103,8 @@ public partial class SettingsPanePluginStoreViewModel : BaseModel private async Task InstallPluginAsync() { var file = GetFileFromDialog( - App.API.GetTranslation("SelectZipFile"), - $"{App.API.GetTranslation("ZipFiles")} (*.zip)|*.zip"); + Localize.SelectZipFile(), + $"{Localize.ZipFiles()} (*.zip)|*.zip"); if (!string.IsNullOrEmpty(file)) await PluginInstaller.InstallPluginAndCheckRestartAsync(file); diff --git a/Flow.Launcher/SettingPages/ViewModels/SettingsPaneThemeViewModel.cs b/Flow.Launcher/SettingPages/ViewModels/SettingsPaneThemeViewModel.cs index 98dac499f..70bcbcc18 100644 --- a/Flow.Launcher/SettingPages/ViewModels/SettingsPaneThemeViewModel.cs +++ b/Flow.Launcher/SettingPages/ViewModels/SettingsPaneThemeViewModel.cs @@ -26,7 +26,7 @@ public partial class SettingsPaneThemeViewModel : BaseModel private readonly Theme _theme; private readonly string DefaultFont = Win32Helper.GetSystemDefaultFont(); - public string BackdropSubText => !Win32Helper.IsBackdropSupported() ? App.API.GetTranslation("BackdropTypeDisabledToolTip") : ""; + public string BackdropSubText => !Win32Helper.IsBackdropSupported() ? Localize.BackdropTypeDisabledToolTip(): ""; public static string LinkHowToCreateTheme => @"https://www.flowlauncher.com/theme-builder/"; public static string LinkThemeGallery => "https://github.com/Flow-Launcher/Flow.Launcher/discussions/1438"; @@ -272,7 +272,7 @@ public partial class SettingsPaneThemeViewModel : BaseModel public string PlaceholderTextTip { - get => string.Format(App.API.GetTranslation("PlaceholderTextTip"), App.API.GetTranslation("queryTextBoxPlaceholder")); + get => Localize.PlaceholderTextTip(Localize.queryTextBoxPlaceholder()); } public string PlaceholderText @@ -447,8 +447,8 @@ public partial class SettingsPaneThemeViewModel : BaseModel { new() { - Title = App.API.GetTranslation("SampleTitleExplorer"), - SubTitle = App.API.GetTranslation("SampleSubTitleExplorer"), + Title = Localize.SampleTitleExplorer(), + SubTitle = Localize.SampleSubTitleExplorer(), IcoPath = Path.Combine( Constant.ProgramDirectory, @"Plugins\Flow.Launcher.Plugin.Explorer\Images\explorer.png" @@ -456,8 +456,8 @@ public partial class SettingsPaneThemeViewModel : BaseModel }, new() { - Title = App.API.GetTranslation("SampleTitleWebSearch"), - SubTitle = App.API.GetTranslation("SampleSubTitleWebSearch"), + Title = Localize.SampleTitleWebSearch(), + SubTitle = Localize.SampleSubTitleWebSearch(), IcoPath = Path.Combine( Constant.ProgramDirectory, @"Plugins\Flow.Launcher.Plugin.WebSearch\Images\web_search.png" @@ -465,8 +465,8 @@ public partial class SettingsPaneThemeViewModel : BaseModel }, new() { - Title = App.API.GetTranslation("SampleTitleProgram"), - SubTitle = App.API.GetTranslation("SampleSubTitleProgram"), + Title = Localize.SampleTitleProgram(), + SubTitle = Localize.SampleSubTitleProgram(), IcoPath = Path.Combine( Constant.ProgramDirectory, @"Plugins\Flow.Launcher.Plugin.Program\Images\program.png" @@ -474,8 +474,8 @@ public partial class SettingsPaneThemeViewModel : BaseModel }, new() { - Title = App.API.GetTranslation("SampleTitleProcessKiller"), - SubTitle = App.API.GetTranslation("SampleSubTitleProcessKiller"), + Title = Localize.SampleTitleProcessKiller(), + SubTitle = Localize.SampleSubTitleProcessKiller(), IcoPath = Path.Combine( Constant.ProgramDirectory, @"Plugins\Flow.Launcher.Plugin.ProcessKiller\Images\app.png" diff --git a/Flow.Launcher/ViewModel/MainViewModel.cs b/Flow.Launcher/ViewModel/MainViewModel.cs index d492f28c5..66fa70682 100644 --- a/Flow.Launcher/ViewModel/MainViewModel.cs +++ b/Flow.Launcher/ViewModel/MainViewModel.cs @@ -342,8 +342,8 @@ namespace Flow.Launcher.ViewModel Hide(); await PluginManager.ReloadDataAsync().ConfigureAwait(false); - App.API.ShowMsg(App.API.GetTranslation("success"), - App.API.GetTranslation("completedSuccessfully")); + App.API.ShowMsg(Localize.success(), + Localize.completedSuccessfully()); } [RelayCommand] @@ -908,7 +908,7 @@ namespace Flow.Launcher.ViewModel private string _placeholderText; public string PlaceholderText { - get => string.IsNullOrEmpty(_placeholderText) ? App.API.GetTranslation("queryTextBoxPlaceholder") : _placeholderText; + get => string.IsNullOrEmpty(_placeholderText) ? Localize.queryTextBoxPlaceholder(): _placeholderText; set { _placeholderText = value; @@ -1312,12 +1312,10 @@ namespace Flow.Launcher.ViewModel var results = new List(); foreach (var h in historyItems) { - var title = App.API.GetTranslation("executeQuery"); - var time = App.API.GetTranslation("lastExecuteTime"); var result = new Result { - Title = string.Format(title, h.Query), - SubTitle = string.Format(time, h.ExecutedDateTime), + Title = Localize.executeQuery(h.Query), + SubTitle = Localize.lastExecuteTime(h.ExecutedDateTime), IcoPath = Constant.HistoryIcon, OriginQuery = new Query { RawQuery = h.Query }, Action = _ => @@ -1714,13 +1712,13 @@ namespace Flow.Launcher.ViewModel { menu = new Result { - Title = App.API.GetTranslation("cancelTopMostInThisQuery"), + Title = Localize.cancelTopMostInThisQuery(), IcoPath = "Images\\down.png", PluginDirectory = Constant.ProgramDirectory, Action = _ => { _topMostRecord.Remove(result); - App.API.ShowMsg(App.API.GetTranslation("success")); + App.API.ShowMsg(Localize.success()); App.API.ReQuery(); return false; }, @@ -1732,13 +1730,13 @@ namespace Flow.Launcher.ViewModel { menu = new Result { - Title = App.API.GetTranslation("setAsTopMostInThisQuery"), + Title = Localize.setAsTopMostInThisQuery(), IcoPath = "Images\\up.png", PluginDirectory = Constant.ProgramDirectory, Action = _ => { _topMostRecord.AddOrUpdate(result); - App.API.ShowMsg(App.API.GetTranslation("success")); + App.API.ShowMsg(Localize.success()); App.API.ReQuery(); return false; }, @@ -1756,10 +1754,10 @@ namespace Flow.Launcher.ViewModel var metadata = PluginManager.GetPluginForId(id).Metadata; var translator = App.API; - var author = translator.GetTranslation("author"); - var website = translator.GetTranslation("website"); - var version = translator.GetTranslation("version"); - var plugin = translator.GetTranslation("plugin"); + var author = Localize.author(); + var website = Localize.website(); + var version = Localize.version(); + var plugin = Localize.plugin(); var title = $"{plugin}: {metadata.Name}"; var icon = metadata.IcoPath; var subtitle = $"{author} {metadata.Author}"; diff --git a/Flow.Launcher/ViewModel/PluginViewModel.cs b/Flow.Launcher/ViewModel/PluginViewModel.cs index 29f2b9b43..87d1839c7 100644 --- a/Flow.Launcher/ViewModel/PluginViewModel.cs +++ b/Flow.Launcher/ViewModel/PluginViewModel.cs @@ -155,8 +155,7 @@ namespace Flow.Launcher.ViewModel App.API.LogException(ClassName, $"Failed to create setting panel for {pair.Metadata.Name}", e); // Show error message in UI - var errorMsg = string.Format(App.API.GetTranslation("errorCreatingSettingPanel"), - pair.Metadata.Name, Environment.NewLine, e.Message); + var errorMsg = Localize.errorCreatingSettingPanel(pair.Metadata.Name, Environment.NewLine, e.Message); return CreateErrorSettingPanel(errorMsg); } } @@ -165,16 +164,16 @@ namespace Flow.Launcher.ViewModel Visibility.Collapsed : Visibility.Visible; public string InitializeTime => PluginPair.Metadata.InitTime + "ms"; public string QueryTime => PluginPair.Metadata.AvgQueryTime + "ms"; - public string Version => App.API.GetTranslation("plugin_query_version") + " " + PluginPair.Metadata.Version; + public string Version => Localize.plugin_query_version()+ " " + PluginPair.Metadata.Version; public string InitAndQueryTime => - App.API.GetTranslation("plugin_init_time") + " " + + Localize.plugin_init_time()+ " " + PluginPair.Metadata.InitTime + "ms, " + - App.API.GetTranslation("plugin_query_time") + " " + + Localize.plugin_query_time()+ " " + PluginPair.Metadata.AvgQueryTime + "ms"; public string ActionKeywordsText => string.Join(Query.ActionKeywordSeparator, PluginPair.Metadata.ActionKeywords); public string SearchDelayTimeText => PluginPair.Metadata.SearchDelayTime == null ? - App.API.GetTranslation("default") : - App.API.GetTranslation($"SearchDelayTime{PluginPair.Metadata.SearchDelayTime}"); + Localize.plugin_default_search_delay_time() : + Localize.plugin_search_delay_time(PluginPair.Metadata.SearchDelayTime); public Infrastructure.UserSettings.Plugin PluginSettingsObject{ get; init; } public bool SearchDelayEnabled => Settings.SearchQueryResultsWithDelay; public string DefaultSearchDelay => Settings.SearchDelayTime.ToString(); diff --git a/Flow.Launcher/ViewModel/SelectBrowserViewModel.cs b/Flow.Launcher/ViewModel/SelectBrowserViewModel.cs index e3a0e4e44..04602dcae 100644 --- a/Flow.Launcher/ViewModel/SelectBrowserViewModel.cs +++ b/Flow.Launcher/ViewModel/SelectBrowserViewModel.cs @@ -50,7 +50,7 @@ public partial class SelectBrowserViewModel : BaseModel { CustomBrowsers.Add(new() { - Name = App.API.GetTranslation("defaultBrowser_new_profile") + Name = Localize.defaultBrowser_new_profile() }); SelectedCustomBrowserIndex = CustomBrowsers.Count - 1; } diff --git a/Flow.Launcher/ViewModel/SelectFileManagerViewModel.cs b/Flow.Launcher/ViewModel/SelectFileManagerViewModel.cs index f6a32e3fe..42c818042 100644 --- a/Flow.Launcher/ViewModel/SelectFileManagerViewModel.cs +++ b/Flow.Launcher/ViewModel/SelectFileManagerViewModel.cs @@ -48,9 +48,8 @@ public partial class SelectFileManagerViewModel : BaseModel if (!IsFileManagerValid(CustomExplorer.Path)) { var result = App.API.ShowMsgBox( - string.Format(App.API.GetTranslation("fileManagerPathNotFound"), - CustomExplorer.Name, CustomExplorer.Path), - App.API.GetTranslation("fileManagerPathError"), + Localize.fileManagerPathNotFound(CustomExplorer.Name, CustomExplorer.Path), + Localize.fileManagerPathError(), MessageBoxButton.YesNo, MessageBoxImage.Warning); @@ -105,7 +104,7 @@ public partial class SelectFileManagerViewModel : BaseModel { CustomExplorers.Add(new() { - Name = App.API.GetTranslation("defaultBrowser_new_profile") + Name = Localize.defaultBrowser_new_profile() }); SelectedCustomExplorerIndex = CustomExplorers.Count - 1; } diff --git a/Flow.Launcher/packages.lock.json b/Flow.Launcher/packages.lock.json index c90db6b0c..c3c8f60e3 100644 --- a/Flow.Launcher/packages.lock.json +++ b/Flow.Launcher/packages.lock.json @@ -14,6 +14,12 @@ "resolved": "8.4.0", "contentHash": "tqVU8yc/ADO9oiTRyTnwhFN68hCwvkliMierptWOudIAvWY1mWCh5VFh+guwHJmpMwfg0J0rY+yyd5Oy7ty9Uw==" }, + "Flow.Launcher.Localization": { + "type": "Direct", + "requested": "[0.0.6, )", + "resolved": "0.0.6", + "contentHash": "Wwh5lrnmAf66go456h9sSrkdIW3G/IaKPE3+qWZLRAQ86kIe1JovHRj+ljHZXnFOWu1cbFmHg3l1RuqzPLAHow==" + }, "Fody": { "type": "Direct", "requested": "[6.9.3, )", From 7350c1d4d547504f7b3c10fd8875859a2617cc11 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Tue, 23 Sep 2025 17:14:30 +0800 Subject: [PATCH 3/7] Use Flow.Launcher.Localization to improve code quality --- Flow.Launcher.Core/Configuration/Portable.cs | 11 ++- .../Environments/AbstractPluginEnvironment.cs | 18 +--- .../Environments/PythonEnvironment.cs | 2 +- .../Environments/TypeScriptEnvironment.cs | 2 +- .../Environments/TypeScriptV2Environment.cs | 2 +- Flow.Launcher.Core/Flow.Launcher.Core.csproj | 15 +++- .../Plugin/JsonRPCPluginSettings.cs | 2 +- Flow.Launcher.Core/Plugin/PluginInstaller.cs | 90 ++++++++----------- Flow.Launcher.Core/Plugin/PluginManager.cs | 39 ++++---- Flow.Launcher.Core/Plugin/PluginsLoader.cs | 6 +- Flow.Launcher.Core/Resource/Theme.cs | 4 +- Flow.Launcher.Core/Updater.cs | 25 +++--- Flow.Launcher.Core/packages.lock.json | 7 ++ .../Flow.Launcher.Infrastructure.csproj | 13 +++ Flow.Launcher.Infrastructure/Http/Http.cs | 2 +- .../UserSettings/CustomBrowserViewModel.cs | 7 +- .../UserSettings/CustomExplorerViewModel.cs | 7 +- .../packages.lock.json | 6 ++ Flow.Launcher/packages.lock.json | 2 + 19 files changed, 129 insertions(+), 131 deletions(-) diff --git a/Flow.Launcher.Core/Configuration/Portable.cs b/Flow.Launcher.Core/Configuration/Portable.cs index 721e14dca..b6ecd8bae 100644 --- a/Flow.Launcher.Core/Configuration/Portable.cs +++ b/Flow.Launcher.Core/Configuration/Portable.cs @@ -45,7 +45,7 @@ namespace Flow.Launcher.Core.Configuration #endif IndicateDeletion(DataLocation.PortableDataPath); - API.ShowMsgBox(API.GetTranslation("restartToDisablePortableMode")); + API.ShowMsgBox(Localize.restartToDisablePortableMode()); UpdateManager.RestartApp(Constant.ApplicationFileName); } @@ -68,7 +68,7 @@ namespace Flow.Launcher.Core.Configuration #endif IndicateDeletion(DataLocation.RoamingDataPath); - API.ShowMsgBox(API.GetTranslation("restartToEnablePortableMode")); + API.ShowMsgBox(Localize.restartToEnablePortableMode()); UpdateManager.RestartApp(Constant.ApplicationFileName); } @@ -152,7 +152,7 @@ namespace Flow.Launcher.Core.Configuration { FilesFolders.RemoveFolderIfExists(roamingDataDir, (s) => API.ShowMsgBox(s)); - if (API.ShowMsgBox(API.GetTranslation("moveToDifferentLocation"), + if (API.ShowMsgBox(Localize.moveToDifferentLocation(), string.Empty, MessageBoxButton.YesNo) == MessageBoxResult.Yes) { FilesFolders.OpenPath(Constant.RootDirectory, (s) => API.ShowMsgBox(s)); @@ -166,7 +166,7 @@ namespace Flow.Launcher.Core.Configuration { FilesFolders.RemoveFolderIfExists(portableDataDir, (s) => API.ShowMsgBox(s)); - API.ShowMsgBox(API.GetTranslation("shortcutsUninstallerCreated")); + API.ShowMsgBox(Localize.shortcutsUninstallerCreated()); } } @@ -177,8 +177,7 @@ namespace Flow.Launcher.Core.Configuration if (roamingLocationExists && portableLocationExists) { - API.ShowMsgBox(string.Format(API.GetTranslation("userDataDuplicated"), - DataLocation.PortableDataPath, DataLocation.RoamingDataPath, Environment.NewLine)); + API.ShowMsgBox(Localize.userDataDuplicated(DataLocation.PortableDataPath, DataLocation.RoamingDataPath, Environment.NewLine)); return false; } diff --git a/Flow.Launcher.Core/ExternalPlugins/Environments/AbstractPluginEnvironment.cs b/Flow.Launcher.Core/ExternalPlugins/Environments/AbstractPluginEnvironment.cs index 14796a87a..dcec19020 100644 --- a/Flow.Launcher.Core/ExternalPlugins/Environments/AbstractPluginEnvironment.cs +++ b/Flow.Launcher.Core/ExternalPlugins/Environments/AbstractPluginEnvironment.cs @@ -58,15 +58,10 @@ namespace Flow.Launcher.Core.ExternalPlugins.Environments return SetPathForPluginPairs(PluginsSettingsFilePath, Language); } - var noRuntimeMessage = string.Format( - API.GetTranslation("runtimePluginInstalledChooseRuntimePrompt"), - Language, - EnvName, - Environment.NewLine - ); + var noRuntimeMessage = Localize.runtimePluginInstalledChooseRuntimePrompt(Language, EnvName, Environment.NewLine); if (API.ShowMsgBox(noRuntimeMessage, string.Empty, MessageBoxButton.YesNo) == MessageBoxResult.No) { - var msg = string.Format(API.GetTranslation("runtimePluginChooseRuntimeExecutable"), EnvName); + var msg = Localize.runtimePluginChooseRuntimeExecutable(EnvName); var selectedFile = GetFileFromDialog(msg, FileDialogFilter); @@ -77,12 +72,7 @@ namespace Flow.Launcher.Core.ExternalPlugins.Environments // Nothing selected because user pressed cancel from the file dialog window else { - var forceDownloadMessage = string.Format( - API.GetTranslation("runtimeExecutableInvalidChooseDownload"), - Language, - EnvName, - Environment.NewLine - ); + var forceDownloadMessage = Localize.runtimeExecutableInvalidChooseDownload(Language, EnvName, Environment.NewLine); // Let users select valid path or choose to download while (string.IsNullOrEmpty(selectedFile)) @@ -120,7 +110,7 @@ namespace Flow.Launcher.Core.ExternalPlugins.Environments } else { - API.ShowMsgBox(string.Format(API.GetTranslation("runtimePluginUnableToSetExecutablePath"), Language)); + API.ShowMsgBox(Localize.runtimePluginUnableToSetExecutablePath(Language)); API.LogError(ClassName, $"Not able to successfully set {EnvName} path, setting's plugin executable path variable is still an empty string.", $"{Language}Environment"); diff --git a/Flow.Launcher.Core/ExternalPlugins/Environments/PythonEnvironment.cs b/Flow.Launcher.Core/ExternalPlugins/Environments/PythonEnvironment.cs index 89286dfb0..76c775fb4 100644 --- a/Flow.Launcher.Core/ExternalPlugins/Environments/PythonEnvironment.cs +++ b/Flow.Launcher.Core/ExternalPlugins/Environments/PythonEnvironment.cs @@ -51,7 +51,7 @@ namespace Flow.Launcher.Core.ExternalPlugins.Environments } catch (System.Exception e) { - API.ShowMsgError(API.GetTranslation("failToInstallPythonEnv")); + API.ShowMsgError(Localize.failToInstallPythonEnv()); API.LogException(ClassName, "Failed to install Python environment", e); } }); diff --git a/Flow.Launcher.Core/ExternalPlugins/Environments/TypeScriptEnvironment.cs b/Flow.Launcher.Core/ExternalPlugins/Environments/TypeScriptEnvironment.cs index 724ae20f4..d8244cbf3 100644 --- a/Flow.Launcher.Core/ExternalPlugins/Environments/TypeScriptEnvironment.cs +++ b/Flow.Launcher.Core/ExternalPlugins/Environments/TypeScriptEnvironment.cs @@ -46,7 +46,7 @@ namespace Flow.Launcher.Core.ExternalPlugins.Environments } catch (System.Exception e) { - API.ShowMsgError(API.GetTranslation("failToInstallTypeScriptEnv")); + API.ShowMsgError(Localize.failToInstallTypeScriptEnv()); API.LogException(ClassName, "Failed to install TypeScript environment", e); } }); diff --git a/Flow.Launcher.Core/ExternalPlugins/Environments/TypeScriptV2Environment.cs b/Flow.Launcher.Core/ExternalPlugins/Environments/TypeScriptV2Environment.cs index 6a32664a1..e2de53e39 100644 --- a/Flow.Launcher.Core/ExternalPlugins/Environments/TypeScriptV2Environment.cs +++ b/Flow.Launcher.Core/ExternalPlugins/Environments/TypeScriptV2Environment.cs @@ -46,7 +46,7 @@ namespace Flow.Launcher.Core.ExternalPlugins.Environments } catch (System.Exception e) { - API.ShowMsgError(API.GetTranslation("failToInstallTypeScriptEnv")); + API.ShowMsgError(Localize.failToInstallTypeScriptEnv()); API.LogException(ClassName, "Failed to install TypeScript environment", e); } }); diff --git a/Flow.Launcher.Core/Flow.Launcher.Core.csproj b/Flow.Launcher.Core/Flow.Launcher.Core.csproj index 1369d7e5d..52eaf0501 100644 --- a/Flow.Launcher.Core/Flow.Launcher.Core.csproj +++ b/Flow.Launcher.Core/Flow.Launcher.Core.csproj @@ -1,4 +1,4 @@ - + net9.0-windows @@ -34,6 +34,7 @@ prompt 4 false + $(NoWarn);FLSG0007 @@ -55,6 +56,7 @@ + @@ -62,6 +64,17 @@ + + + true + + + + + + Languages\en.xaml + + diff --git a/Flow.Launcher.Core/Plugin/JsonRPCPluginSettings.cs b/Flow.Launcher.Core/Plugin/JsonRPCPluginSettings.cs index 9212dada6..abefd47bc 100644 --- a/Flow.Launcher.Core/Plugin/JsonRPCPluginSettings.cs +++ b/Flow.Launcher.Core/Plugin/JsonRPCPluginSettings.cs @@ -285,7 +285,7 @@ namespace Flow.Launcher.Core.Plugin HorizontalAlignment = HorizontalAlignment.Left, VerticalAlignment = VerticalAlignment.Center, Margin = SettingPanelItemLeftMargin, - Content = API.GetTranslation("select") + Content = Localize.select() }; Btn.Click += (_, _) => diff --git a/Flow.Launcher.Core/Plugin/PluginInstaller.cs b/Flow.Launcher.Core/Plugin/PluginInstaller.cs index d01b34ab6..5629da231 100644 --- a/Flow.Launcher.Core/Plugin/PluginInstaller.cs +++ b/Flow.Launcher.Core/Plugin/PluginInstaller.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.IO; using System.IO.Compression; @@ -35,16 +35,14 @@ public static class PluginInstaller { if (API.PluginModified(newPlugin.ID)) { - API.ShowMsgError(string.Format(API.GetTranslation("pluginModifiedAlreadyTitle"), newPlugin.Name), - API.GetTranslation("pluginModifiedAlreadyMessage")); + API.ShowMsgError(Localize.pluginModifiedAlreadyTitle(newPlugin.Name), + Localize.pluginModifiedAlreadyMessage()); return; } if (API.ShowMsgBox( - string.Format( - API.GetTranslation("InstallPromptSubtitle"), - newPlugin.Name, newPlugin.Author, Environment.NewLine), - API.GetTranslation("InstallPromptTitle"), + Localize.InstallPromptSubtitle(newPlugin.Name, newPlugin.Author, Environment.NewLine), + Localize.InstallPromptTitle(), button: MessageBoxButton.YesNo) != MessageBoxResult.Yes) return; try @@ -61,7 +59,7 @@ public static class PluginInstaller if (!newPlugin.IsFromLocalInstallPath) { await DownloadFileAsync( - $"{API.GetTranslation("DownloadingPlugin")} {newPlugin.Name}", + $"{Localize.DownloadingPlugin()} {newPlugin.Name}", newPlugin.UrlDownload, filePath, cts); } else @@ -93,7 +91,7 @@ public static class PluginInstaller catch (Exception e) { API.LogException(ClassName, "Failed to install plugin", e); - API.ShowMsgError(API.GetTranslation("ErrorInstallingPlugin")); + API.ShowMsgError(Localize.ErrorInstallingPlugin()); return; // do not restart on failure } @@ -104,11 +102,8 @@ public static class PluginInstaller else { API.ShowMsg( - API.GetTranslation("installbtn"), - string.Format( - API.GetTranslation( - "InstallSuccessNoRestart"), - newPlugin.Name)); + Localize.installbtn(), + Localize.InstallSuccessNoRestart(newPlugin.Name)); } } @@ -134,23 +129,22 @@ public static class PluginInstaller catch (Exception e) { API.LogException(ClassName, "Failed to validate zip file", e); - API.ShowMsgError(API.GetTranslation("ZipFileNotHavePluginJson")); + API.ShowMsgError(Localize.ZipFileNotHavePluginJson()); return; } if (API.PluginModified(plugin.ID)) { - API.ShowMsgError(string.Format(API.GetTranslation("pluginModifiedAlreadyTitle"), plugin.Name), - API.GetTranslation("pluginModifiedAlreadyMessage")); + API.ShowMsgError(Localize.pluginModifiedAlreadyTitle(plugin.Name), + Localize.pluginModifiedAlreadyMessage()); return; } if (Settings.ShowUnknownSourceWarning) { if (!InstallSourceKnown(plugin.Website) - && API.ShowMsgBox(string.Format( - API.GetTranslation("InstallFromUnknownSourceSubtitle"), Environment.NewLine), - API.GetTranslation("InstallFromUnknownSourceTitle"), + && API.ShowMsgBox(Localize.InstallFromUnknownSourceSubtitle(Environment.NewLine), + Localize.InstallFromUnknownSourceTitle(), MessageBoxButton.YesNo) == MessageBoxResult.No) return; } @@ -167,21 +161,19 @@ public static class PluginInstaller { if (API.PluginModified(oldPlugin.ID)) { - API.ShowMsgError(string.Format(API.GetTranslation("pluginModifiedAlreadyTitle"), oldPlugin.Name), - API.GetTranslation("pluginModifiedAlreadyMessage")); + API.ShowMsgError(Localize.pluginModifiedAlreadyTitle(oldPlugin.Name), + Localize.pluginModifiedAlreadyMessage()); return; } if (API.ShowMsgBox( - string.Format( - API.GetTranslation("UninstallPromptSubtitle"), - oldPlugin.Name, oldPlugin.Author, Environment.NewLine), - API.GetTranslation("UninstallPromptTitle"), + Localize.UninstallPromptSubtitle(oldPlugin.Name, oldPlugin.Author, Environment.NewLine), + Localize.UninstallPromptTitle(), button: MessageBoxButton.YesNo) != MessageBoxResult.Yes) return; var removePluginSettings = API.ShowMsgBox( - API.GetTranslation("KeepPluginSettingsSubtitle"), - API.GetTranslation("KeepPluginSettingsTitle"), + Localize.KeepPluginSettingsSubtitle(), + Localize.KeepPluginSettingsTitle(), button: MessageBoxButton.YesNo) == MessageBoxResult.No; try @@ -194,7 +186,7 @@ public static class PluginInstaller catch (Exception e) { API.LogException(ClassName, "Failed to uninstall plugin", e); - API.ShowMsgError(API.GetTranslation("ErrorUninstallingPlugin")); + API.ShowMsgError(Localize.ErrorUninstallingPlugin()); return; // don not restart on failure } @@ -205,11 +197,8 @@ public static class PluginInstaller else { API.ShowMsg( - API.GetTranslation("uninstallbtn"), - string.Format( - API.GetTranslation( - "UninstallSuccessNoRestart"), - oldPlugin.Name)); + Localize.uninstallbtn(), + Localize.UninstallSuccessNoRestart(oldPlugin.Name)); } } @@ -222,10 +211,8 @@ public static class PluginInstaller public static async Task UpdatePluginAndCheckRestartAsync(UserPlugin newPlugin, PluginMetadata oldPlugin) { if (API.ShowMsgBox( - string.Format( - API.GetTranslation("UpdatePromptSubtitle"), - oldPlugin.Name, oldPlugin.Author, Environment.NewLine), - API.GetTranslation("UpdatePromptTitle"), + Localize.UpdatePromptSubtitle(oldPlugin.Name, oldPlugin.Author, Environment.NewLine), + Localize.UpdatePromptTitle(), button: MessageBoxButton.YesNo) != MessageBoxResult.Yes) return; try @@ -237,7 +224,7 @@ public static class PluginInstaller if (!newPlugin.IsFromLocalInstallPath) { await DownloadFileAsync( - $"{API.GetTranslation("DownloadingPlugin")} {newPlugin.Name}", + $"{Localize.DownloadingPlugin()} {newPlugin.Name}", newPlugin.UrlDownload, filePath, cts); } else @@ -259,7 +246,7 @@ public static class PluginInstaller catch (Exception e) { API.LogException(ClassName, "Failed to update plugin", e); - API.ShowMsgError(API.GetTranslation("ErrorUpdatingPlugin")); + API.ShowMsgError(Localize.ErrorUpdatingPlugin()); return; // do not restart on failure } @@ -270,11 +257,8 @@ public static class PluginInstaller else { API.ShowMsg( - API.GetTranslation("updatebtn"), - string.Format( - API.GetTranslation( - "UpdateSuccessNoRestart"), - newPlugin.Name)); + Localize.updatebtn(), + Localize.UpdateSuccessNoRestart(newPlugin.Name)); } } @@ -314,11 +298,11 @@ public static class PluginInstaller }).ToList(); // No updates - if (!resultsForUpdate.Any()) + if (resultsForUpdate.Count == 0) { if (!silentUpdate) { - API.ShowMsg(API.GetTranslation("updateNoResultTitle"), API.GetTranslation("updateNoResultSubtitle")); + API.ShowMsg(Localize.updateNoResultTitle(), Localize.updateNoResultSubtitle()); } return; } @@ -331,8 +315,8 @@ public static class PluginInstaller // Show message box with button to update all plugins API.ShowMsgWithButton( - API.GetTranslation("updateAllPluginsTitle"), - API.GetTranslation("updateAllPluginsButtonContent"), + Localize.updateAllPluginsTitle(), + Localize.updateAllPluginsButtonContent(), () => { updateAllPlugins(resultsForUpdate); @@ -357,7 +341,7 @@ public static class PluginInstaller using var cts = new CancellationTokenSource(); await DownloadFileAsync( - $"{API.GetTranslation("DownloadingPlugin")} {plugin.PluginNewUserPlugin.Name}", + $"{Localize.DownloadingPlugin()} {plugin.PluginNewUserPlugin.Name}", plugin.PluginNewUserPlugin.UrlDownload, downloadToFilePath, cts); // check if user cancelled download before installing plugin @@ -376,7 +360,7 @@ public static class PluginInstaller catch (Exception e) { API.LogException(ClassName, "Failed to update plugin", e); - API.ShowMsgError(API.GetTranslation("ErrorUpdatingPlugin")); + API.ShowMsgError(Localize.ErrorUpdatingPlugin()); } })); @@ -389,8 +373,8 @@ public static class PluginInstaller else { API.ShowMsg( - API.GetTranslation("updatebtn"), - API.GetTranslation("PluginsUpdateSuccessNoRestart")); + Localize.updatebtn(), + Localize.PluginsUpdateSuccessNoRestart()); } } diff --git a/Flow.Launcher.Core/Plugin/PluginManager.cs b/Flow.Launcher.Core/Plugin/PluginManager.cs index a4ab8de08..ba101d4a7 100644 --- a/Flow.Launcher.Core/Plugin/PluginManager.cs +++ b/Flow.Launcher.Core/Plugin/PluginManager.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.IO; @@ -295,15 +295,12 @@ namespace Flow.Launcher.Core.Plugin } } - if (failedPlugins.Any()) + if (!failedPlugins.IsEmpty) { var failed = string.Join(",", failedPlugins.Select(x => x.Metadata.Name)); API.ShowMsg( - API.GetTranslation("failedToInitializePluginsTitle"), - string.Format( - API.GetTranslation("failedToInitializePluginsMessage"), - failed - ), + Localize.failedToInitializePluginsTitle(), + Localize.failedToInitializePluginsMessage(failed), "", false ); @@ -636,8 +633,8 @@ namespace Flow.Launcher.Core.Plugin { if (PluginModified(existingVersion.ID)) { - API.ShowMsgError(string.Format(API.GetTranslation("pluginModifiedAlreadyTitle"), existingVersion.Name), - API.GetTranslation("pluginModifiedAlreadyMessage")); + API.ShowMsgError(Localize.pluginModifiedAlreadyTitle(existingVersion.Name), + Localize.pluginModifiedAlreadyMessage()); return false; } @@ -669,8 +666,8 @@ namespace Flow.Launcher.Core.Plugin { if (checkModified && PluginModified(plugin.ID)) { - API.ShowMsgError(string.Format(API.GetTranslation("pluginModifiedAlreadyTitle"), plugin.Name), - API.GetTranslation("pluginModifiedAlreadyMessage")); + API.ShowMsgError(Localize.pluginModifiedAlreadyTitle(plugin.Name), + Localize.pluginModifiedAlreadyMessage()); return false; } @@ -689,15 +686,15 @@ namespace Flow.Launcher.Core.Plugin if (string.IsNullOrEmpty(metadataJsonFilePath) || string.IsNullOrEmpty(pluginFolderPath)) { - API.ShowMsgError(string.Format(API.GetTranslation("failedToInstallPluginTitle"), plugin.Name), - string.Format(API.GetTranslation("fileNotFoundMessage"), pluginFolderPath)); + API.ShowMsgError(Localize.failedToInstallPluginTitle(plugin.Name), + Localize.fileNotFoundMessage(pluginFolderPath)); return false; } if (SameOrLesserPluginVersionExists(metadataJsonFilePath)) { - API.ShowMsgError(string.Format(API.GetTranslation("failedToInstallPluginTitle"), plugin.Name), - API.GetTranslation("pluginExistAlreadyMessage")); + API.ShowMsgError(Localize.failedToInstallPluginTitle(plugin.Name), + Localize.pluginExistAlreadyMessage()); return false; } @@ -750,8 +747,8 @@ namespace Flow.Launcher.Core.Plugin { if (checkModified && PluginModified(plugin.ID)) { - API.ShowMsgError(string.Format(API.GetTranslation("pluginModifiedAlreadyTitle"), plugin.Name), - API.GetTranslation("pluginModifiedAlreadyMessage")); + API.ShowMsgError(Localize.pluginModifiedAlreadyTitle(plugin.Name), + Localize.pluginModifiedAlreadyMessage()); return false; } @@ -785,8 +782,8 @@ namespace Flow.Launcher.Core.Plugin catch (Exception e) { API.LogException(ClassName, $"Failed to delete plugin settings folder for {plugin.Name}", e); - API.ShowMsgError(API.GetTranslation("failedToRemovePluginSettingsTitle"), - string.Format(API.GetTranslation("failedToRemovePluginSettingsMessage"), plugin.Name)); + API.ShowMsgError(Localize.failedToRemovePluginSettingsTitle(), + Localize.failedToRemovePluginSettingsMessage(plugin.Name)); } } @@ -801,8 +798,8 @@ namespace Flow.Launcher.Core.Plugin catch (Exception e) { API.LogException(ClassName, $"Failed to delete plugin cache folder for {plugin.Name}", e); - API.ShowMsgError(API.GetTranslation("failedToRemovePluginCacheTitle"), - string.Format(API.GetTranslation("failedToRemovePluginCacheMessage"), plugin.Name)); + API.ShowMsgError(Localize.failedToRemovePluginCacheTitle(), + Localize.failedToRemovePluginCacheMessage(plugin.Name)); } Settings.RemovePluginSettings(plugin.ID); AllPlugins.RemoveAll(p => p.Metadata.ID == plugin.ID); diff --git a/Flow.Launcher.Core/Plugin/PluginsLoader.cs b/Flow.Launcher.Core/Plugin/PluginsLoader.cs index e9e5ee367..92dfef2c6 100644 --- a/Flow.Launcher.Core/Plugin/PluginsLoader.cs +++ b/Flow.Launcher.Core/Plugin/PluginsLoader.cs @@ -121,12 +121,12 @@ namespace Flow.Launcher.Core.Plugin var errorPluginString = string.Join(Environment.NewLine, erroredPlugins); var errorMessage = erroredPlugins.Count > 1 ? - API.GetTranslation("pluginsHaveErrored") : - API.GetTranslation("pluginHasErrored"); + Localize.pluginsHaveErrored(): + Localize.pluginHasErrored(); API.ShowMsgError($"{errorMessage}{Environment.NewLine}{Environment.NewLine}" + $"{errorPluginString}{Environment.NewLine}{Environment.NewLine}" + - API.GetTranslation("referToLogs")); + Localize.referToLogs()); } return plugins; diff --git a/Flow.Launcher.Core/Resource/Theme.cs b/Flow.Launcher.Core/Resource/Theme.cs index a6e8dc6bf..d1f7da2a2 100644 --- a/Flow.Launcher.Core/Resource/Theme.cs +++ b/Flow.Launcher.Core/Resource/Theme.cs @@ -444,7 +444,7 @@ namespace Flow.Launcher.Core.Resource _api.LogError(ClassName, $"Theme <{theme}> path can't be found"); if (theme != Constant.DefaultTheme) { - _api.ShowMsgBox(string.Format(_api.GetTranslation("theme_load_failure_path_not_exists"), theme)); + _api.ShowMsgBox(Localize.theme_load_failure_path_not_exists(theme)); ChangeTheme(Constant.DefaultTheme); } return false; @@ -454,7 +454,7 @@ namespace Flow.Launcher.Core.Resource _api.LogError(ClassName, $"Theme <{theme}> fail to parse"); if (theme != Constant.DefaultTheme) { - _api.ShowMsgBox(string.Format(_api.GetTranslation("theme_load_failure_parse_error"), theme)); + _api.ShowMsgBox(Localize.theme_load_failure_parse_error(theme)); ChangeTheme(Constant.DefaultTheme); } return false; diff --git a/Flow.Launcher.Core/Updater.cs b/Flow.Launcher.Core/Updater.cs index 45275696c..1f138e843 100644 --- a/Flow.Launcher.Core/Updater.cs +++ b/Flow.Launcher.Core/Updater.cs @@ -41,8 +41,8 @@ namespace Flow.Launcher.Core try { if (!silentUpdate) - _api.ShowMsg(_api.GetTranslation("pleaseWait"), - _api.GetTranslation("update_flowlauncher_update_check")); + _api.ShowMsg(Localize.pleaseWait(), + Localize.update_flowlauncher_update_check()); using var updateManager = await GitHubUpdateManagerAsync(GitHubRepository).ConfigureAwait(false); @@ -58,13 +58,13 @@ namespace Flow.Launcher.Core if (newReleaseVersion <= currentVersion) { if (!silentUpdate) - _api.ShowMsgBox(_api.GetTranslation("update_flowlauncher_already_on_latest")); + _api.ShowMsgBox(Localize.update_flowlauncher_already_on_latest()); return; } if (!silentUpdate) - _api.ShowMsg(_api.GetTranslation("update_flowlauncher_update_found"), - _api.GetTranslation("update_flowlauncher_updating")); + _api.ShowMsg(Localize.update_flowlauncher_update_found(), + Localize.update_flowlauncher_updating()); await updateManager.DownloadReleases(newUpdateInfo.ReleasesToApply).ConfigureAwait(false); @@ -77,10 +77,7 @@ namespace Flow.Launcher.Core FilesFolders.CopyAll(DataLocation.PortableDataPath, targetDestination, (s) => _api.ShowMsgBox(s)); if (!FilesFolders.VerifyBothFolderFilesEqual(DataLocation.PortableDataPath, targetDestination, (s) => _api.ShowMsgBox(s))) - _api.ShowMsgBox(string.Format( - _api.GetTranslation("update_flowlauncher_fail_moving_portable_user_profile_data"), - DataLocation.PortableDataPath, - targetDestination)); + _api.ShowMsgBox(Localize.update_flowlauncher_fail_moving_portable_user_profile_data(DataLocation.PortableDataPath, targetDestination)); } else { @@ -91,7 +88,7 @@ namespace Flow.Launcher.Core _api.LogInfo(ClassName, $"Update success:{newVersionTips}"); - if (_api.ShowMsgBox(newVersionTips, _api.GetTranslation("update_flowlauncher_new_update"), + if (_api.ShowMsgBox(newVersionTips, Localize.update_flowlauncher_new_update(), MessageBoxButton.YesNo) == MessageBoxResult.Yes) { UpdateManager.RestartApp(Constant.ApplicationFileName); @@ -111,8 +108,8 @@ namespace Flow.Launcher.Core } if (!silentUpdate) - _api.ShowMsgError(_api.GetTranslation("update_flowlauncher_fail"), - _api.GetTranslation("update_flowlauncher_check_connection")); + _api.ShowMsgError(Localize.update_flowlauncher_fail(), + Localize.update_flowlauncher_check_connection()); } finally { @@ -150,9 +147,9 @@ namespace Flow.Launcher.Core return manager; } - private string NewVersionTips(string version) + private static string NewVersionTips(string version) { - var tips = string.Format(_api.GetTranslation("newVersionTips"), version); + var tips = Localize.newVersionTips(version); return tips; } diff --git a/Flow.Launcher.Core/packages.lock.json b/Flow.Launcher.Core/packages.lock.json index b7a00d94d..ab2a1f718 100644 --- a/Flow.Launcher.Core/packages.lock.json +++ b/Flow.Launcher.Core/packages.lock.json @@ -11,6 +11,12 @@ "YamlDotNet": "9.1.0" } }, + "Flow.Launcher.Localization": { + "type": "Direct", + "requested": "[0.0.6, )", + "resolved": "0.0.6", + "contentHash": "Wwh5lrnmAf66go456h9sSrkdIW3G/IaKPE3+qWZLRAQ86kIe1JovHRj+ljHZXnFOWu1cbFmHg3l1RuqzPLAHow==" + }, "FSharp.Core": { "type": "Direct", "requested": "[9.0.303, )", @@ -254,6 +260,7 @@ "Ben.Demystifier": "[0.4.1, )", "BitFaster.Caching": "[2.5.4, )", "CommunityToolkit.Mvvm": "[8.4.0, )", + "Flow.Launcher.Localization": "[0.0.6, )", "Flow.Launcher.Plugin": "[5.0.0, )", "InputSimulator": "[1.0.4, )", "MemoryPack": "[1.21.4, )", diff --git a/Flow.Launcher.Infrastructure/Flow.Launcher.Infrastructure.csproj b/Flow.Launcher.Infrastructure/Flow.Launcher.Infrastructure.csproj index 5b4eaf893..df2ffba25 100644 --- a/Flow.Launcher.Infrastructure/Flow.Launcher.Infrastructure.csproj +++ b/Flow.Launcher.Infrastructure/Flow.Launcher.Infrastructure.csproj @@ -34,6 +34,7 @@ prompt 4 false + $(NoWarn);FLSG0007 @@ -56,6 +57,7 @@ + all runtime; build; native; contentfiles; analyzers; buildtransitive @@ -80,4 +82,15 @@ + + true + + + + + + Languages\en.xaml + + + \ No newline at end of file diff --git a/Flow.Launcher.Infrastructure/Http/Http.cs b/Flow.Launcher.Infrastructure/Http/Http.cs index 8afab419b..5a4371598 100644 --- a/Flow.Launcher.Infrastructure/Http/Http.cs +++ b/Flow.Launcher.Infrastructure/Http/Http.cs @@ -82,7 +82,7 @@ namespace Flow.Launcher.Infrastructure.Http } catch (UriFormatException e) { - API.ShowMsgError(API.GetTranslation("pleaseTryAgain"), API.GetTranslation("parseProxyFailed")); + API.ShowMsgError(Localize.pleaseTryAgain(), Localize.parseProxyFailed()); Log.Exception(ClassName, "Unable to parse Uri", e); } } diff --git a/Flow.Launcher.Infrastructure/UserSettings/CustomBrowserViewModel.cs b/Flow.Launcher.Infrastructure/UserSettings/CustomBrowserViewModel.cs index 9c795f952..849762867 100644 --- a/Flow.Launcher.Infrastructure/UserSettings/CustomBrowserViewModel.cs +++ b/Flow.Launcher.Infrastructure/UserSettings/CustomBrowserViewModel.cs @@ -1,18 +1,13 @@ using System.Text.Json.Serialization; -using CommunityToolkit.Mvvm.DependencyInjection; using Flow.Launcher.Plugin; namespace Flow.Launcher.Infrastructure.UserSettings { public class CustomBrowserViewModel : BaseModel { - // We should not initialize API in static constructor because it will create another API instance - private static IPublicAPI api = null; - private static IPublicAPI API => api ??= Ioc.Default.GetRequiredService(); - public string Name { get; set; } [JsonIgnore] - public string DisplayName => Name == "Default" ? API.GetTranslation("defaultBrowser_default") : Name; + public string DisplayName => Name == "Default" ? Localize.defaultBrowser_default(): Name; public string Path { get; set; } public string PrivateArg { get; set; } public bool EnablePrivate { get; set; } diff --git a/Flow.Launcher.Infrastructure/UserSettings/CustomExplorerViewModel.cs b/Flow.Launcher.Infrastructure/UserSettings/CustomExplorerViewModel.cs index 2af0bb0e5..ffc48b244 100644 --- a/Flow.Launcher.Infrastructure/UserSettings/CustomExplorerViewModel.cs +++ b/Flow.Launcher.Infrastructure/UserSettings/CustomExplorerViewModel.cs @@ -1,18 +1,13 @@ using System.Text.Json.Serialization; -using CommunityToolkit.Mvvm.DependencyInjection; using Flow.Launcher.Plugin; namespace Flow.Launcher.Infrastructure.UserSettings { public class CustomExplorerViewModel : BaseModel { - // We should not initialize API in static constructor because it will create another API instance - private static IPublicAPI api = null; - private static IPublicAPI API => api ??= Ioc.Default.GetRequiredService(); - public string Name { get; set; } [JsonIgnore] - public string DisplayName => Name == "Explorer" ? API.GetTranslation("fileManagerExplorer") : Name; + public string DisplayName => Name == "Explorer" ? Localize.fileManagerExplorer(): Name; public string Path { get; set; } public string FileArgument { get; set; } = "\"%d\""; public string DirectoryArgument { get; set; } = "\"%d\""; diff --git a/Flow.Launcher.Infrastructure/packages.lock.json b/Flow.Launcher.Infrastructure/packages.lock.json index 47c94d5f6..94adc16f1 100644 --- a/Flow.Launcher.Infrastructure/packages.lock.json +++ b/Flow.Launcher.Infrastructure/packages.lock.json @@ -23,6 +23,12 @@ "resolved": "8.4.0", "contentHash": "tqVU8yc/ADO9oiTRyTnwhFN68hCwvkliMierptWOudIAvWY1mWCh5VFh+guwHJmpMwfg0J0rY+yyd5Oy7ty9Uw==" }, + "Flow.Launcher.Localization": { + "type": "Direct", + "requested": "[0.0.6, )", + "resolved": "0.0.6", + "contentHash": "Wwh5lrnmAf66go456h9sSrkdIW3G/IaKPE3+qWZLRAQ86kIe1JovHRj+ljHZXnFOWu1cbFmHg3l1RuqzPLAHow==" + }, "Fody": { "type": "Direct", "requested": "[6.9.3, )", diff --git a/Flow.Launcher/packages.lock.json b/Flow.Launcher/packages.lock.json index c3c8f60e3..afbbff6b7 100644 --- a/Flow.Launcher/packages.lock.json +++ b/Flow.Launcher/packages.lock.json @@ -846,6 +846,7 @@ "Droplex": "[1.7.0, )", "FSharp.Core": "[9.0.303, )", "Flow.Launcher.Infrastructure": "[1.0.0, )", + "Flow.Launcher.Localization": "[0.0.6, )", "Flow.Launcher.Plugin": "[5.0.0, )", "Meziantou.Framework.Win32.Jobs": "[3.4.4, )", "Microsoft.IO.RecyclableMemoryStream": "[3.0.1, )", @@ -860,6 +861,7 @@ "Ben.Demystifier": "[0.4.1, )", "BitFaster.Caching": "[2.5.4, )", "CommunityToolkit.Mvvm": "[8.4.0, )", + "Flow.Launcher.Localization": "[0.0.6, )", "Flow.Launcher.Plugin": "[5.0.0, )", "InputSimulator": "[1.0.4, )", "MemoryPack": "[1.21.4, )", From 0e366a6269718e032b5438db8b9bd81a3b93836c Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Tue, 23 Sep 2025 17:40:54 +0800 Subject: [PATCH 4/7] Use PublicApi.Instance instead of private one --- Flow.Launcher.Core/Configuration/Portable.cs | 28 +++--- .../ExternalPlugins/CommunityPluginSource.cs | 23 ++--- .../Environments/AbstractPluginEnvironment.cs | 13 +-- .../ExternalPlugins/PluginsManifest.cs | 11 +-- Flow.Launcher.Core/Plugin/PluginConfig.cs | 17 ++-- Flow.Launcher.Core/Plugin/PluginInstaller.cs | 94 +++++++++---------- Flow.Launcher.Core/Plugin/PluginManager.cs | 69 +++++++------- Flow.Launcher.Core/Plugin/PluginsLoader.cs | 19 ++-- .../Resource/Internationalization.cs | 21 ++--- .../Resource/LocalizedDescriptionAttribute.cs | 8 +- .../DialogJump/DialogJump.cs | 29 +++--- Flow.Launcher.Infrastructure/Http/Http.cs | 8 +- .../UserSettings/CustomShortcutModel.cs | 8 +- 13 files changed, 140 insertions(+), 208 deletions(-) diff --git a/Flow.Launcher.Core/Configuration/Portable.cs b/Flow.Launcher.Core/Configuration/Portable.cs index b6ecd8bae..8b305263d 100644 --- a/Flow.Launcher.Core/Configuration/Portable.cs +++ b/Flow.Launcher.Core/Configuration/Portable.cs @@ -3,10 +3,8 @@ using System.IO; using System.Linq; using System.Reflection; using System.Windows; -using CommunityToolkit.Mvvm.DependencyInjection; using Flow.Launcher.Infrastructure; using Flow.Launcher.Infrastructure.UserSettings; -using Flow.Launcher.Plugin; using Flow.Launcher.Plugin.SharedCommands; using Microsoft.Win32; using Squirrel; @@ -17,8 +15,6 @@ namespace Flow.Launcher.Core.Configuration { private static readonly string ClassName = nameof(Portable); - private readonly IPublicAPI API = Ioc.Default.GetRequiredService(); - /// /// As at Squirrel.Windows version 1.5.2, UpdateManager needs to be disposed after finish /// @@ -45,13 +41,13 @@ namespace Flow.Launcher.Core.Configuration #endif IndicateDeletion(DataLocation.PortableDataPath); - API.ShowMsgBox(Localize.restartToDisablePortableMode()); + PublicApi.Instance.ShowMsgBox(Localize.restartToDisablePortableMode()); UpdateManager.RestartApp(Constant.ApplicationFileName); } catch (Exception e) { - API.LogException(ClassName, "Error occurred while disabling portable mode", e); + PublicApi.Instance.LogException(ClassName, "Error occurred while disabling portable mode", e); } } @@ -68,13 +64,13 @@ namespace Flow.Launcher.Core.Configuration #endif IndicateDeletion(DataLocation.RoamingDataPath); - API.ShowMsgBox(Localize.restartToEnablePortableMode()); + PublicApi.Instance.ShowMsgBox(Localize.restartToEnablePortableMode()); UpdateManager.RestartApp(Constant.ApplicationFileName); } catch (Exception e) { - API.LogException(ClassName, "Error occurred while enabling portable mode", e); + PublicApi.Instance.LogException(ClassName, "Error occurred while enabling portable mode", e); } } @@ -94,13 +90,13 @@ namespace Flow.Launcher.Core.Configuration public void MoveUserDataFolder(string fromLocation, string toLocation) { - FilesFolders.CopyAll(fromLocation, toLocation, (s) => API.ShowMsgBox(s)); + FilesFolders.CopyAll(fromLocation, toLocation, (s) => PublicApi.Instance.ShowMsgBox(s)); VerifyUserDataAfterMove(fromLocation, toLocation); } public void VerifyUserDataAfterMove(string fromLocation, string toLocation) { - FilesFolders.VerifyBothFolderFilesEqual(fromLocation, toLocation, (s) => API.ShowMsgBox(s)); + FilesFolders.VerifyBothFolderFilesEqual(fromLocation, toLocation, (s) => PublicApi.Instance.ShowMsgBox(s)); } public void CreateShortcuts() @@ -150,12 +146,12 @@ namespace Flow.Launcher.Core.Configuration // delete it and prompt the user to pick the portable data location if (File.Exists(roamingDataDeleteFilePath)) { - FilesFolders.RemoveFolderIfExists(roamingDataDir, (s) => API.ShowMsgBox(s)); + FilesFolders.RemoveFolderIfExists(roamingDataDir, (s) => PublicApi.Instance.ShowMsgBox(s)); - if (API.ShowMsgBox(Localize.moveToDifferentLocation(), + if (PublicApi.Instance.ShowMsgBox(Localize.moveToDifferentLocation(), string.Empty, MessageBoxButton.YesNo) == MessageBoxResult.Yes) { - FilesFolders.OpenPath(Constant.RootDirectory, (s) => API.ShowMsgBox(s)); + FilesFolders.OpenPath(Constant.RootDirectory, (s) => PublicApi.Instance.ShowMsgBox(s)); Environment.Exit(0); } @@ -164,9 +160,9 @@ namespace Flow.Launcher.Core.Configuration // delete it and notify the user about it. else if (File.Exists(portableDataDeleteFilePath)) { - FilesFolders.RemoveFolderIfExists(portableDataDir, (s) => API.ShowMsgBox(s)); + FilesFolders.RemoveFolderIfExists(portableDataDir, (s) => PublicApi.Instance.ShowMsgBox(s)); - API.ShowMsgBox(Localize.shortcutsUninstallerCreated()); + PublicApi.Instance.ShowMsgBox(Localize.shortcutsUninstallerCreated()); } } @@ -177,7 +173,7 @@ namespace Flow.Launcher.Core.Configuration if (roamingLocationExists && portableLocationExists) { - API.ShowMsgBox(Localize.userDataDuplicated(DataLocation.PortableDataPath, DataLocation.RoamingDataPath, Environment.NewLine)); + PublicApi.Instance.ShowMsgBox(Localize.userDataDuplicated(DataLocation.PortableDataPath, DataLocation.RoamingDataPath, Environment.NewLine)); return false; } diff --git a/Flow.Launcher.Core/ExternalPlugins/CommunityPluginSource.cs b/Flow.Launcher.Core/ExternalPlugins/CommunityPluginSource.cs index 841099dd1..7c0290b2a 100644 --- a/Flow.Launcher.Core/ExternalPlugins/CommunityPluginSource.cs +++ b/Flow.Launcher.Core/ExternalPlugins/CommunityPluginSource.cs @@ -8,7 +8,6 @@ using System.Text.Json; using System.Text.Json.Serialization; using System.Threading; using System.Threading.Tasks; -using CommunityToolkit.Mvvm.DependencyInjection; using Flow.Launcher.Infrastructure.Http; using Flow.Launcher.Plugin; @@ -18,13 +17,9 @@ namespace Flow.Launcher.Core.ExternalPlugins { private static readonly string ClassName = nameof(CommunityPluginSource); - // We should not initialize API in static constructor because it will create another API instance - private static IPublicAPI api = null; - private static IPublicAPI API => api ??= Ioc.Default.GetRequiredService(); - private string latestEtag = ""; - private List plugins = new(); + private List plugins = []; private static readonly JsonSerializerOptions PluginStoreItemSerializationOption = new() { @@ -41,7 +36,7 @@ namespace Flow.Launcher.Core.ExternalPlugins /// public async Task> FetchAsync(CancellationToken token) { - API.LogInfo(ClassName, $"Loading plugins from {ManifestFileUrl}"); + PublicApi.Instance.LogInfo(ClassName, $"Loading plugins from {ManifestFileUrl}"); var request = new HttpRequestMessage(HttpMethod.Get, ManifestFileUrl); @@ -59,40 +54,40 @@ namespace Flow.Launcher.Core.ExternalPlugins .ConfigureAwait(false); latestEtag = response.Headers.ETag?.Tag; - API.LogInfo(ClassName, $"Loaded {plugins.Count} plugins from {ManifestFileUrl}"); + PublicApi.Instance.LogInfo(ClassName, $"Loaded {plugins.Count} plugins from {ManifestFileUrl}"); return plugins; } else if (response.StatusCode == HttpStatusCode.NotModified) { - API.LogInfo(ClassName, $"Resource {ManifestFileUrl} has not been modified."); + PublicApi.Instance.LogInfo(ClassName, $"Resource {ManifestFileUrl} has not been modified."); return plugins; } else { - API.LogWarn(ClassName, $"Failed to load resource {ManifestFileUrl} with response {response.StatusCode}"); + PublicApi.Instance.LogWarn(ClassName, $"Failed to load resource {ManifestFileUrl} with response {response.StatusCode}"); return null; } } catch (OperationCanceledException) when (token.IsCancellationRequested) { - API.LogDebug(ClassName, $"Fetching from {ManifestFileUrl} was cancelled by caller."); + PublicApi.Instance.LogDebug(ClassName, $"Fetching from {ManifestFileUrl} was cancelled by caller."); return null; } catch (TaskCanceledException) { // Likely an HttpClient timeout or external cancellation not requested by our token - API.LogWarn(ClassName, $"Fetching from {ManifestFileUrl} timed out."); + PublicApi.Instance.LogWarn(ClassName, $"Fetching from {ManifestFileUrl} timed out."); return null; } catch (Exception e) { if (e is HttpRequestException or WebException or SocketException || e.InnerException is TimeoutException) { - API.LogException(ClassName, $"Check your connection and proxy settings to {ManifestFileUrl}.", e); + PublicApi.Instance.LogException(ClassName, $"Check your connection and proxy settings to {ManifestFileUrl}.", e); } else { - API.LogException(ClassName, "Error Occurred", e); + PublicApi.Instance.LogException(ClassName, "Error Occurred", e); } return null; } diff --git a/Flow.Launcher.Core/ExternalPlugins/Environments/AbstractPluginEnvironment.cs b/Flow.Launcher.Core/ExternalPlugins/Environments/AbstractPluginEnvironment.cs index dcec19020..d08ea88b3 100644 --- a/Flow.Launcher.Core/ExternalPlugins/Environments/AbstractPluginEnvironment.cs +++ b/Flow.Launcher.Core/ExternalPlugins/Environments/AbstractPluginEnvironment.cs @@ -4,7 +4,6 @@ using System.IO; using System.Linq; using System.Windows; using System.Windows.Forms; -using CommunityToolkit.Mvvm.DependencyInjection; using Flow.Launcher.Infrastructure.UserSettings; using Flow.Launcher.Plugin; using Flow.Launcher.Plugin.SharedCommands; @@ -15,8 +14,6 @@ namespace Flow.Launcher.Core.ExternalPlugins.Environments { private static readonly string ClassName = nameof(AbstractPluginEnvironment); - protected readonly IPublicAPI API = Ioc.Default.GetRequiredService(); - internal abstract string Language { get; } internal abstract string EnvName { get; } @@ -59,7 +56,7 @@ namespace Flow.Launcher.Core.ExternalPlugins.Environments } var noRuntimeMessage = Localize.runtimePluginInstalledChooseRuntimePrompt(Language, EnvName, Environment.NewLine); - if (API.ShowMsgBox(noRuntimeMessage, string.Empty, MessageBoxButton.YesNo) == MessageBoxResult.No) + if (PublicApi.Instance.ShowMsgBox(noRuntimeMessage, string.Empty, MessageBoxButton.YesNo) == MessageBoxResult.No) { var msg = Localize.runtimePluginChooseRuntimeExecutable(EnvName); @@ -77,7 +74,7 @@ namespace Flow.Launcher.Core.ExternalPlugins.Environments // Let users select valid path or choose to download while (string.IsNullOrEmpty(selectedFile)) { - if (API.ShowMsgBox(forceDownloadMessage, string.Empty, MessageBoxButton.YesNo) == MessageBoxResult.Yes) + if (PublicApi.Instance.ShowMsgBox(forceDownloadMessage, string.Empty, MessageBoxButton.YesNo) == MessageBoxResult.Yes) { // Continue select file selectedFile = GetFileFromDialog(msg, FileDialogFilter); @@ -110,8 +107,8 @@ namespace Flow.Launcher.Core.ExternalPlugins.Environments } else { - API.ShowMsgBox(Localize.runtimePluginUnableToSetExecutablePath(Language)); - API.LogError(ClassName, + PublicApi.Instance.ShowMsgBox(Localize.runtimePluginUnableToSetExecutablePath(Language)); + PublicApi.Instance.LogError(ClassName, $"Not able to successfully set {EnvName} path, setting's plugin executable path variable is still an empty string.", $"{Language}Environment"); @@ -125,7 +122,7 @@ namespace Flow.Launcher.Core.ExternalPlugins.Environments { if (expectedPath == currentPath) return; - FilesFolders.RemoveFolderIfExists(installedDirPath, (s) => API.ShowMsgBox(s)); + FilesFolders.RemoveFolderIfExists(installedDirPath, (s) => PublicApi.Instance.ShowMsgBox(s)); InstallEnvironment(); } diff --git a/Flow.Launcher.Core/ExternalPlugins/PluginsManifest.cs b/Flow.Launcher.Core/ExternalPlugins/PluginsManifest.cs index 1e845498c..eab9a8c43 100644 --- a/Flow.Launcher.Core/ExternalPlugins/PluginsManifest.cs +++ b/Flow.Launcher.Core/ExternalPlugins/PluginsManifest.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; -using CommunityToolkit.Mvvm.DependencyInjection; using Flow.Launcher.Plugin; using Flow.Launcher.Infrastructure; @@ -23,10 +22,6 @@ namespace Flow.Launcher.Core.ExternalPlugins private static DateTime lastFetchedAt = DateTime.MinValue; private static readonly TimeSpan fetchTimeout = TimeSpan.FromMinutes(2); - // We should not initialize API in static constructor because it will create another API instance - private static IPublicAPI api = null; - private static IPublicAPI API => api ??= Ioc.Default.GetRequiredService(); - public static List UserPlugins { get; private set; } public static async Task UpdateManifestAsync(bool usePrimaryUrlOnly = false, CancellationToken token = default) @@ -61,7 +56,7 @@ namespace Flow.Launcher.Core.ExternalPlugins } catch (Exception e) { - API.LogException(ClassName, "Http request failed", e); + PublicApi.Instance.LogException(ClassName, "Http request failed", e); } finally { @@ -83,12 +78,12 @@ namespace Flow.Launcher.Core.ExternalPlugins } catch (Exception e) { - API.LogException(ClassName, $"Failed to parse the minimum app version {plugin.MinimumAppVersion} for plugin {plugin.Name}. " + PublicApi.Instance.LogException(ClassName, $"Failed to parse the minimum app version {plugin.MinimumAppVersion} for plugin {plugin.Name}. " + "Plugin excluded from manifest", e); return false; } - API.LogInfo(ClassName, $"Plugin {plugin.Name} requires minimum Flow Launcher version {plugin.MinimumAppVersion}, " + PublicApi.Instance.LogInfo(ClassName, $"Plugin {plugin.Name} requires minimum Flow Launcher version {plugin.MinimumAppVersion}, " + $"but current version is {Constant.Version}. Plugin excluded from manifest."); return false; diff --git a/Flow.Launcher.Core/Plugin/PluginConfig.cs b/Flow.Launcher.Core/Plugin/PluginConfig.cs index f7457b4e1..c5f0f79a7 100644 --- a/Flow.Launcher.Core/Plugin/PluginConfig.cs +++ b/Flow.Launcher.Core/Plugin/PluginConfig.cs @@ -5,7 +5,6 @@ using System.IO; using Flow.Launcher.Infrastructure; using Flow.Launcher.Plugin; using System.Text.Json; -using CommunityToolkit.Mvvm.DependencyInjection; namespace Flow.Launcher.Core.Plugin { @@ -13,10 +12,6 @@ namespace Flow.Launcher.Core.Plugin { private static readonly string ClassName = nameof(PluginConfig); - // We should not initialize API in static constructor because it will create another API instance - private static IPublicAPI api = null; - private static IPublicAPI API => api ??= Ioc.Default.GetRequiredService(); - /// /// Parse plugin metadata in the given directories /// @@ -38,7 +33,7 @@ namespace Flow.Launcher.Core.Plugin } catch (Exception e) { - API.LogException(ClassName, $"Can't delete <{directory}>", e); + PublicApi.Instance.LogException(ClassName, $"Can't delete <{directory}>", e); } } else @@ -55,7 +50,7 @@ namespace Flow.Launcher.Core.Plugin duplicateList .ForEach( - x => API.LogWarn(ClassName, + x => PublicApi.Instance.LogWarn(ClassName, string.Format("Duplicate plugin name: {0}, id: {1}, version: {2} " + "not loaded due to version not the highest of the duplicates", x.Name, x.ID, x.Version), @@ -107,7 +102,7 @@ namespace Flow.Launcher.Core.Plugin string configPath = Path.Combine(pluginDirectory, Constant.PluginMetadataFileName); if (!File.Exists(configPath)) { - API.LogError(ClassName, $"Didn't find config file <{configPath}>"); + PublicApi.Instance.LogError(ClassName, $"Didn't find config file <{configPath}>"); return null; } @@ -123,19 +118,19 @@ namespace Flow.Launcher.Core.Plugin } catch (Exception e) { - API.LogException(ClassName, $"Invalid json for config <{configPath}>", e); + PublicApi.Instance.LogException(ClassName, $"Invalid json for config <{configPath}>", e); return null; } if (!AllowedLanguage.IsAllowed(metadata.Language)) { - API.LogError(ClassName, $"Invalid language <{metadata.Language}> for config <{configPath}>"); + PublicApi.Instance.LogError(ClassName, $"Invalid language <{metadata.Language}> for config <{configPath}>"); return null; } if (!File.Exists(metadata.ExecuteFilePath)) { - API.LogError(ClassName, $"Execute file path didn't exist <{metadata.ExecuteFilePath}> for conifg <{configPath}"); + PublicApi.Instance.LogError(ClassName, $"Execute file path didn't exist <{metadata.ExecuteFilePath}> for conifg <{configPath}"); return null; } diff --git a/Flow.Launcher.Core/Plugin/PluginInstaller.cs b/Flow.Launcher.Core/Plugin/PluginInstaller.cs index 5629da231..6027b712e 100644 --- a/Flow.Launcher.Core/Plugin/PluginInstaller.cs +++ b/Flow.Launcher.Core/Plugin/PluginInstaller.cs @@ -22,10 +22,6 @@ public static class PluginInstaller private static readonly Settings Settings = Ioc.Default.GetRequiredService(); - // We should not initialize API in static constructor because it will create another API instance - private static IPublicAPI api = null; - private static IPublicAPI API => api ??= Ioc.Default.GetRequiredService(); - /// /// Installs a plugin and restarts the application if required by settings. Prompts user for confirmation and handles download if needed. /// @@ -33,14 +29,14 @@ public static class PluginInstaller /// A Task representing the asynchronous install operation. public static async Task InstallPluginAndCheckRestartAsync(UserPlugin newPlugin) { - if (API.PluginModified(newPlugin.ID)) + if (PublicApi.Instance.PluginModified(newPlugin.ID)) { - API.ShowMsgError(Localize.pluginModifiedAlreadyTitle(newPlugin.Name), + PublicApi.Instance.ShowMsgError(Localize.pluginModifiedAlreadyTitle(newPlugin.Name), Localize.pluginModifiedAlreadyMessage()); return; } - if (API.ShowMsgBox( + if (PublicApi.Instance.ShowMsgBox( Localize.InstallPromptSubtitle(newPlugin.Name, newPlugin.Author, Environment.NewLine), Localize.InstallPromptTitle(), button: MessageBoxButton.YesNo) != MessageBoxResult.Yes) return; @@ -78,7 +74,7 @@ public static class PluginInstaller throw new FileNotFoundException($"Plugin {newPlugin.ID} zip file not found at {filePath}", filePath); } - if (!API.InstallPlugin(newPlugin, filePath)) + if (!PublicApi.Instance.InstallPlugin(newPlugin, filePath)) { return; } @@ -90,18 +86,18 @@ public static class PluginInstaller } catch (Exception e) { - API.LogException(ClassName, "Failed to install plugin", e); - API.ShowMsgError(Localize.ErrorInstallingPlugin()); + PublicApi.Instance.LogException(ClassName, "Failed to install plugin", e); + PublicApi.Instance.ShowMsgError(Localize.ErrorInstallingPlugin()); return; // do not restart on failure } if (Settings.AutoRestartAfterChanging) { - API.RestartApp(); + PublicApi.Instance.RestartApp(); } else { - API.ShowMsg( + PublicApi.Instance.ShowMsg( Localize.installbtn(), Localize.InstallSuccessNoRestart(newPlugin.Name)); } @@ -128,14 +124,14 @@ public static class PluginInstaller } catch (Exception e) { - API.LogException(ClassName, "Failed to validate zip file", e); - API.ShowMsgError(Localize.ZipFileNotHavePluginJson()); + PublicApi.Instance.LogException(ClassName, "Failed to validate zip file", e); + PublicApi.Instance.ShowMsgError(Localize.ZipFileNotHavePluginJson()); return; } - if (API.PluginModified(plugin.ID)) + if (PublicApi.Instance.PluginModified(plugin.ID)) { - API.ShowMsgError(Localize.pluginModifiedAlreadyTitle(plugin.Name), + PublicApi.Instance.ShowMsgError(Localize.pluginModifiedAlreadyTitle(plugin.Name), Localize.pluginModifiedAlreadyMessage()); return; } @@ -143,7 +139,7 @@ public static class PluginInstaller if (Settings.ShowUnknownSourceWarning) { if (!InstallSourceKnown(plugin.Website) - && API.ShowMsgBox(Localize.InstallFromUnknownSourceSubtitle(Environment.NewLine), + && PublicApi.Instance.ShowMsgBox(Localize.InstallFromUnknownSourceSubtitle(Environment.NewLine), Localize.InstallFromUnknownSourceTitle(), MessageBoxButton.YesNo) == MessageBoxResult.No) return; @@ -159,44 +155,44 @@ public static class PluginInstaller /// A Task representing the asynchronous uninstall operation. public static async Task UninstallPluginAndCheckRestartAsync(PluginMetadata oldPlugin) { - if (API.PluginModified(oldPlugin.ID)) + if (PublicApi.Instance.PluginModified(oldPlugin.ID)) { - API.ShowMsgError(Localize.pluginModifiedAlreadyTitle(oldPlugin.Name), + PublicApi.Instance.ShowMsgError(Localize.pluginModifiedAlreadyTitle(oldPlugin.Name), Localize.pluginModifiedAlreadyMessage()); return; } - if (API.ShowMsgBox( + if (PublicApi.Instance.ShowMsgBox( Localize.UninstallPromptSubtitle(oldPlugin.Name, oldPlugin.Author, Environment.NewLine), Localize.UninstallPromptTitle(), button: MessageBoxButton.YesNo) != MessageBoxResult.Yes) return; - var removePluginSettings = API.ShowMsgBox( + var removePluginSettings = PublicApi.Instance.ShowMsgBox( Localize.KeepPluginSettingsSubtitle(), Localize.KeepPluginSettingsTitle(), button: MessageBoxButton.YesNo) == MessageBoxResult.No; try { - if (!await API.UninstallPluginAsync(oldPlugin, removePluginSettings)) + if (!await PublicApi.Instance.UninstallPluginAsync(oldPlugin, removePluginSettings)) { return; } } catch (Exception e) { - API.LogException(ClassName, "Failed to uninstall plugin", e); - API.ShowMsgError(Localize.ErrorUninstallingPlugin()); + PublicApi.Instance.LogException(ClassName, "Failed to uninstall plugin", e); + PublicApi.Instance.ShowMsgError(Localize.ErrorUninstallingPlugin()); return; // don not restart on failure } if (Settings.AutoRestartAfterChanging) { - API.RestartApp(); + PublicApi.Instance.RestartApp(); } else { - API.ShowMsg( + PublicApi.Instance.ShowMsg( Localize.uninstallbtn(), Localize.UninstallSuccessNoRestart(oldPlugin.Name)); } @@ -210,7 +206,7 @@ public static class PluginInstaller /// A Task representing the asynchronous update operation. public static async Task UpdatePluginAndCheckRestartAsync(UserPlugin newPlugin, PluginMetadata oldPlugin) { - if (API.ShowMsgBox( + if (PublicApi.Instance.ShowMsgBox( Localize.UpdatePromptSubtitle(oldPlugin.Name, oldPlugin.Author, Environment.NewLine), Localize.UpdatePromptTitle(), button: MessageBoxButton.YesNo) != MessageBoxResult.Yes) return; @@ -238,25 +234,25 @@ public static class PluginInstaller return; } - if (!await API.UpdatePluginAsync(oldPlugin, newPlugin, filePath)) + if (!await PublicApi.Instance.UpdatePluginAsync(oldPlugin, newPlugin, filePath)) { return; } } catch (Exception e) { - API.LogException(ClassName, "Failed to update plugin", e); - API.ShowMsgError(Localize.ErrorUpdatingPlugin()); + PublicApi.Instance.LogException(ClassName, "Failed to update plugin", e); + PublicApi.Instance.ShowMsgError(Localize.ErrorUpdatingPlugin()); return; // do not restart on failure } if (Settings.AutoRestartAfterChanging) { - API.RestartApp(); + PublicApi.Instance.RestartApp(); } else { - API.ShowMsg( + PublicApi.Instance.ShowMsg( Localize.updatebtn(), Localize.UpdateSuccessNoRestart(newPlugin.Name)); } @@ -273,17 +269,17 @@ public static class PluginInstaller public static async Task CheckForPluginUpdatesAsync(Action> updateAllPlugins, bool silentUpdate = true, bool usePrimaryUrlOnly = false, CancellationToken token = default) { // Update the plugin manifest - await API.UpdatePluginManifestAsync(usePrimaryUrlOnly, token); + await PublicApi.Instance.UpdatePluginManifestAsync(usePrimaryUrlOnly, token); // Get all plugins that can be updated var resultsForUpdate = ( - from existingPlugin in API.GetAllPlugins() - join pluginUpdateSource in API.GetPluginManifest() + from existingPlugin in PublicApi.Instance.GetAllPlugins() + join pluginUpdateSource in PublicApi.Instance.GetPluginManifest() on existingPlugin.Metadata.ID equals pluginUpdateSource.ID where string.Compare(existingPlugin.Metadata.Version, pluginUpdateSource.Version, StringComparison.InvariantCulture) < 0 // if current version precedes version of the plugin from update source (e.g. PluginsManifest) - && !API.PluginModified(existingPlugin.Metadata.ID) + && !PublicApi.Instance.PluginModified(existingPlugin.Metadata.ID) select new PluginUpdateInfo() { @@ -302,19 +298,19 @@ public static class PluginInstaller { if (!silentUpdate) { - API.ShowMsg(Localize.updateNoResultTitle(), Localize.updateNoResultSubtitle()); + PublicApi.Instance.ShowMsg(Localize.updateNoResultTitle(), Localize.updateNoResultSubtitle()); } return; } // If all plugins are modified, just return - if (resultsForUpdate.All(x => API.PluginModified(x.ID))) + if (resultsForUpdate.All(x => PublicApi.Instance.PluginModified(x.ID))) { return; } // Show message box with button to update all plugins - API.ShowMsgWithButton( + PublicApi.Instance.ShowMsgWithButton( Localize.updateAllPluginsTitle(), Localize.updateAllPluginsButtonContent(), () => @@ -350,7 +346,7 @@ public static class PluginInstaller return; } - if (!await API.UpdatePluginAsync(plugin.PluginExistingMetadata, plugin.PluginNewUserPlugin, downloadToFilePath)) + if (!await PublicApi.Instance.UpdatePluginAsync(plugin.PluginExistingMetadata, plugin.PluginNewUserPlugin, downloadToFilePath)) { return; } @@ -359,8 +355,8 @@ public static class PluginInstaller } catch (Exception e) { - API.LogException(ClassName, "Failed to update plugin", e); - API.ShowMsgError(Localize.ErrorUpdatingPlugin()); + PublicApi.Instance.LogException(ClassName, "Failed to update plugin", e); + PublicApi.Instance.ShowMsgError(Localize.ErrorUpdatingPlugin()); } })); @@ -368,11 +364,11 @@ public static class PluginInstaller if (restart) { - API.RestartApp(); + PublicApi.Instance.RestartApp(); } else { - API.ShowMsg( + PublicApi.Instance.ShowMsg( Localize.updatebtn(), Localize.PluginsUpdateSuccessNoRestart()); } @@ -396,7 +392,7 @@ public static class PluginInstaller if (showProgress) { var exceptionHappened = false; - await API.ShowProgressBoxAsync(progressBoxTitle, + await PublicApi.Instance.ShowProgressBoxAsync(progressBoxTitle, async (reportProgress) => { if (reportProgress == null) @@ -408,18 +404,18 @@ public static class PluginInstaller } else { - await API.HttpDownloadAsync(downloadUrl, filePath, reportProgress, cts.Token).ConfigureAwait(false); + await PublicApi.Instance.HttpDownloadAsync(downloadUrl, filePath, reportProgress, cts.Token).ConfigureAwait(false); } }, cts.Cancel); // if exception happened while downloading and user does not cancel downloading, // we need to redownload the plugin if (exceptionHappened && (!cts.IsCancellationRequested)) - await API.HttpDownloadAsync(downloadUrl, filePath, token: cts.Token).ConfigureAwait(false); + await PublicApi.Instance.HttpDownloadAsync(downloadUrl, filePath, token: cts.Token).ConfigureAwait(false); } else { - await API.HttpDownloadAsync(downloadUrl, filePath, token: cts.Token).ConfigureAwait(false); + await PublicApi.Instance.HttpDownloadAsync(downloadUrl, filePath, token: cts.Token).ConfigureAwait(false); } } @@ -446,7 +442,7 @@ public static class PluginInstaller if (!Uri.TryCreate(url, UriKind.Absolute, out var uri) || uri.Host != acceptedHost) return false; - return API.GetAllPlugins().Any(x => + return PublicApi.Instance.GetAllPlugins().Any(x => !string.IsNullOrEmpty(x.Metadata.Website) && x.Metadata.Website.StartsWith(constructedUrlPart) ); diff --git a/Flow.Launcher.Core/Plugin/PluginManager.cs b/Flow.Launcher.Core/Plugin/PluginManager.cs index ba101d4a7..3090212ba 100644 --- a/Flow.Launcher.Core/Plugin/PluginManager.cs +++ b/Flow.Launcher.Core/Plugin/PluginManager.cs @@ -6,7 +6,6 @@ using System.Linq; using System.Text.Json; using System.Threading; using System.Threading.Tasks; -using CommunityToolkit.Mvvm.DependencyInjection; using Flow.Launcher.Core.ExternalPlugins; using Flow.Launcher.Infrastructure; using Flow.Launcher.Infrastructure.DialogJump; @@ -29,10 +28,6 @@ namespace Flow.Launcher.Core.Plugin public static readonly HashSet GlobalPlugins = new(); public static readonly Dictionary NonGlobalPlugins = new(); - // We should not initialize API in static constructor because it will create another API instance - private static IPublicAPI api = null; - private static IPublicAPI API => api ??= Ioc.Default.GetRequiredService(); - private static PluginsSettings Settings; private static readonly ConcurrentBag ModifiedPlugins = new(); @@ -75,12 +70,12 @@ namespace Flow.Launcher.Core.Plugin } catch (Exception e) { - API.LogException(ClassName, $"Failed to save plugin {pluginPair.Metadata.Name}", e); + PublicApi.Instance.LogException(ClassName, $"Failed to save plugin {pluginPair.Metadata.Name}", e); } } - API.SavePluginSettings(); - API.SavePluginCaches(); + PublicApi.Instance.SavePluginSettings(); + PublicApi.Instance.SavePluginCaches(); } public static async ValueTask DisposePluginsAsync() @@ -107,7 +102,7 @@ namespace Flow.Launcher.Core.Plugin } catch (Exception e) { - API.LogException(ClassName, $"Failed to dispose plugin {pluginPair.Metadata.Name}", e); + PublicApi.Instance.LogException(ClassName, $"Failed to dispose plugin {pluginPair.Metadata.Name}", e); } } @@ -218,7 +213,7 @@ namespace Flow.Launcher.Core.Plugin { if (string.IsNullOrEmpty(metadata.AssemblyName)) { - API.LogWarn(ClassName, $"AssemblyName is empty for plugin with metadata: {metadata.Name}"); + PublicApi.Instance.LogWarn(ClassName, $"AssemblyName is empty for plugin with metadata: {metadata.Name}"); continue; // Skip if AssemblyName is not set, which can happen for erroneous plugins } metadata.PluginSettingsDirectoryPath = Path.Combine(DataLocation.PluginSettingsDirectory, metadata.AssemblyName); @@ -228,7 +223,7 @@ namespace Flow.Launcher.Core.Plugin { if (string.IsNullOrEmpty(metadata.Name)) { - API.LogWarn(ClassName, $"Name is empty for plugin with metadata: {metadata.Name}"); + PublicApi.Instance.LogWarn(ClassName, $"Name is empty for plugin with metadata: {metadata.Name}"); continue; // Skip if Name is not set, which can happen for erroneous plugins } metadata.PluginSettingsDirectoryPath = Path.Combine(DataLocation.PluginSettingsDirectory, metadata.Name); @@ -249,28 +244,28 @@ namespace Flow.Launcher.Core.Plugin { try { - var milliseconds = await API.StopwatchLogDebugAsync(ClassName, $"Init method time cost for <{pair.Metadata.Name}>", - () => pair.Plugin.InitAsync(new PluginInitContext(pair.Metadata, API))); + var milliseconds = await PublicApi.Instance.StopwatchLogDebugAsync(ClassName, $"Init method time cost for <{pair.Metadata.Name}>", + () => pair.Plugin.InitAsync(new PluginInitContext(pair.Metadata, PublicApi.Instance))); pair.Metadata.InitTime += milliseconds; - API.LogInfo(ClassName, + PublicApi.Instance.LogInfo(ClassName, $"Total init cost for <{pair.Metadata.Name}> is <{pair.Metadata.InitTime}ms>"); } catch (Exception e) { - API.LogException(ClassName, $"Fail to Init plugin: {pair.Metadata.Name}", e); + PublicApi.Instance.LogException(ClassName, $"Fail to Init plugin: {pair.Metadata.Name}", e); if (pair.Metadata.Disabled && pair.Metadata.HomeDisabled) { // If this plugin is already disabled, do not show error message again // Or else it will be shown every time - API.LogDebug(ClassName, $"Skipped init for <{pair.Metadata.Name}> due to error"); + PublicApi.Instance.LogDebug(ClassName, $"Skipped init for <{pair.Metadata.Name}> due to error"); } else { pair.Metadata.Disabled = true; pair.Metadata.HomeDisabled = true; failedPlugins.Enqueue(pair); - API.LogDebug(ClassName, $"Disable plugin <{pair.Metadata.Name}> because init failed"); + PublicApi.Instance.LogDebug(ClassName, $"Disable plugin <{pair.Metadata.Name}> because init failed"); } } })); @@ -298,7 +293,7 @@ namespace Flow.Launcher.Core.Plugin if (!failedPlugins.IsEmpty) { var failed = string.Join(",", failedPlugins.Select(x => x.Metadata.Name)); - API.ShowMsg( + PublicApi.Instance.ShowMsg( Localize.failedToInitializePluginsTitle(), Localize.failedToInitializePluginsMessage(failed), "", @@ -323,7 +318,7 @@ namespace Flow.Launcher.Core.Plugin if (dialogJump && plugin.Plugin is not IAsyncDialogJump) return Array.Empty(); - if (API.PluginModified(plugin.Metadata.ID)) + if (PublicApi.Instance.PluginModified(plugin.Metadata.ID)) return Array.Empty(); return new List @@ -344,7 +339,7 @@ namespace Flow.Launcher.Core.Plugin try { - var milliseconds = await API.StopwatchLogDebugAsync(ClassName, $"Cost for {metadata.Name}", + var milliseconds = await PublicApi.Instance.StopwatchLogDebugAsync(ClassName, $"Cost for {metadata.Name}", async () => results = await pair.Plugin.QueryAsync(query, token).ConfigureAwait(false)); token.ThrowIfCancellationRequested(); @@ -388,7 +383,7 @@ namespace Flow.Launcher.Core.Plugin try { - var milliseconds = await API.StopwatchLogDebugAsync(ClassName, $"Cost for {metadata.Name}", + var milliseconds = await PublicApi.Instance.StopwatchLogDebugAsync(ClassName, $"Cost for {metadata.Name}", async () => results = await ((IAsyncHomeQuery)pair.Plugin).HomeQueryAsync(token).ConfigureAwait(false)); token.ThrowIfCancellationRequested(); @@ -405,7 +400,7 @@ namespace Flow.Launcher.Core.Plugin } catch (Exception e) { - API.LogException(ClassName, $"Failed to query home for plugin: {metadata.Name}", e); + PublicApi.Instance.LogException(ClassName, $"Failed to query home for plugin: {metadata.Name}", e); return null; } return results; @@ -418,7 +413,7 @@ namespace Flow.Launcher.Core.Plugin try { - var milliseconds = await API.StopwatchLogDebugAsync(ClassName, $"Cost for {metadata.Name}", + var milliseconds = await PublicApi.Instance.StopwatchLogDebugAsync(ClassName, $"Cost for {metadata.Name}", async () => results = await ((IAsyncDialogJump)pair.Plugin).QueryDialogJumpAsync(query, token).ConfigureAwait(false)); token.ThrowIfCancellationRequested(); @@ -435,7 +430,7 @@ namespace Flow.Launcher.Core.Plugin } catch (Exception e) { - API.LogException(ClassName, $"Failed to query Dialog Jump for plugin: {metadata.Name}", e); + PublicApi.Instance.LogException(ClassName, $"Failed to query Dialog Jump for plugin: {metadata.Name}", e); return null; } return results; @@ -502,7 +497,7 @@ namespace Flow.Launcher.Core.Plugin } catch (Exception e) { - API.LogException(ClassName, + PublicApi.Instance.LogException(ClassName, $"Can't load context menus for plugin <{pluginPair.Metadata.Name}>", e); } @@ -633,7 +628,7 @@ namespace Flow.Launcher.Core.Plugin { if (PluginModified(existingVersion.ID)) { - API.ShowMsgError(Localize.pluginModifiedAlreadyTitle(existingVersion.Name), + PublicApi.Instance.ShowMsgError(Localize.pluginModifiedAlreadyTitle(existingVersion.Name), Localize.pluginModifiedAlreadyMessage()); return false; } @@ -666,7 +661,7 @@ namespace Flow.Launcher.Core.Plugin { if (checkModified && PluginModified(plugin.ID)) { - API.ShowMsgError(Localize.pluginModifiedAlreadyTitle(plugin.Name), + PublicApi.Instance.ShowMsgError(Localize.pluginModifiedAlreadyTitle(plugin.Name), Localize.pluginModifiedAlreadyMessage()); return false; } @@ -686,14 +681,14 @@ namespace Flow.Launcher.Core.Plugin if (string.IsNullOrEmpty(metadataJsonFilePath) || string.IsNullOrEmpty(pluginFolderPath)) { - API.ShowMsgError(Localize.failedToInstallPluginTitle(plugin.Name), + PublicApi.Instance.ShowMsgError(Localize.failedToInstallPluginTitle(plugin.Name), Localize.fileNotFoundMessage(pluginFolderPath)); return false; } if (SameOrLesserPluginVersionExists(metadataJsonFilePath)) { - API.ShowMsgError(Localize.failedToInstallPluginTitle(plugin.Name), + PublicApi.Instance.ShowMsgError(Localize.failedToInstallPluginTitle(plugin.Name), Localize.pluginExistAlreadyMessage()); return false; } @@ -723,7 +718,7 @@ namespace Flow.Launcher.Core.Plugin var newPluginPath = Path.Combine(installDirectory, folderName); - FilesFolders.CopyAll(pluginFolderPath, newPluginPath, (s) => API.ShowMsgBox(s)); + FilesFolders.CopyAll(pluginFolderPath, newPluginPath, (s) => PublicApi.Instance.ShowMsgBox(s)); try { @@ -732,7 +727,7 @@ namespace Flow.Launcher.Core.Plugin } catch (Exception e) { - API.LogException(ClassName, $"Failed to delete temp folder {tempFolderPluginPath}", e); + PublicApi.Instance.LogException(ClassName, $"Failed to delete temp folder {tempFolderPluginPath}", e); } if (checkModified) @@ -747,7 +742,7 @@ namespace Flow.Launcher.Core.Plugin { if (checkModified && PluginModified(plugin.ID)) { - API.ShowMsgError(Localize.pluginModifiedAlreadyTitle(plugin.Name), + PublicApi.Instance.ShowMsgError(Localize.pluginModifiedAlreadyTitle(plugin.Name), Localize.pluginModifiedAlreadyMessage()); return false; } @@ -767,7 +762,7 @@ namespace Flow.Launcher.Core.Plugin if (removePluginSettings) { // For dotnet plugins, we need to remove their PluginJsonStorage and PluginBinaryStorage instances - if (AllowedLanguage.IsDotNet(plugin.Language) && API is IRemovable removable) + if (AllowedLanguage.IsDotNet(plugin.Language) && PublicApi.Instance is IRemovable removable) { removable.RemovePluginSettings(plugin.AssemblyName); removable.RemovePluginCaches(plugin.PluginCacheDirectoryPath); @@ -781,8 +776,8 @@ namespace Flow.Launcher.Core.Plugin } catch (Exception e) { - API.LogException(ClassName, $"Failed to delete plugin settings folder for {plugin.Name}", e); - API.ShowMsgError(Localize.failedToRemovePluginSettingsTitle(), + PublicApi.Instance.LogException(ClassName, $"Failed to delete plugin settings folder for {plugin.Name}", e); + PublicApi.Instance.ShowMsgError(Localize.failedToRemovePluginSettingsTitle(), Localize.failedToRemovePluginSettingsMessage(plugin.Name)); } } @@ -797,8 +792,8 @@ namespace Flow.Launcher.Core.Plugin } catch (Exception e) { - API.LogException(ClassName, $"Failed to delete plugin cache folder for {plugin.Name}", e); - API.ShowMsgError(Localize.failedToRemovePluginCacheTitle(), + PublicApi.Instance.LogException(ClassName, $"Failed to delete plugin cache folder for {plugin.Name}", e); + PublicApi.Instance.ShowMsgError(Localize.failedToRemovePluginCacheTitle(), Localize.failedToRemovePluginCacheMessage(plugin.Name)); } Settings.RemovePluginSettings(plugin.ID); diff --git a/Flow.Launcher.Core/Plugin/PluginsLoader.cs b/Flow.Launcher.Core/Plugin/PluginsLoader.cs index 92dfef2c6..a8a4fba3a 100644 --- a/Flow.Launcher.Core/Plugin/PluginsLoader.cs +++ b/Flow.Launcher.Core/Plugin/PluginsLoader.cs @@ -2,9 +2,6 @@ using System.Collections.Generic; using System.Linq; using System.Reflection; -using System.Threading.Tasks; -using System.Windows; -using CommunityToolkit.Mvvm.DependencyInjection; using Flow.Launcher.Core.ExternalPlugins.Environments; #pragma warning disable IDE0005 using Flow.Launcher.Infrastructure.Logger; @@ -18,10 +15,6 @@ namespace Flow.Launcher.Core.Plugin { private static readonly string ClassName = nameof(PluginsLoader); - // We should not initialize API in static constructor because it will create another API instance - private static IPublicAPI api = null; - private static IPublicAPI API => api ??= Ioc.Default.GetRequiredService(); - public static List Plugins(List metadatas, PluginsSettings settings) { var dotnetPlugins = DotNetPlugins(metadatas); @@ -64,7 +57,7 @@ namespace Flow.Launcher.Core.Plugin foreach (var metadata in metadatas) { - var milliseconds = API.StopwatchLogDebug(ClassName, $"Constructor init cost for {metadata.Name}", () => + var milliseconds = PublicApi.Instance.StopwatchLogDebug(ClassName, $"Constructor init cost for {metadata.Name}", () => { Assembly assembly = null; IAsyncPlugin plugin = null; @@ -89,19 +82,19 @@ namespace Flow.Launcher.Core.Plugin #else catch (Exception e) when (assembly == null) { - Log.Exception(ClassName, $"Couldn't load assembly for the plugin: {metadata.Name}", e); + PublicApi.Instance.LogException(ClassName, $"Couldn't load assembly for the plugin: {metadata.Name}", e); } catch (InvalidOperationException e) { - Log.Exception(ClassName, $"Can't find the required IPlugin interface for the plugin: <{metadata.Name}>", e); + PublicApi.Instance.LogException(ClassName, $"Can't find the required IPlugin interface for the plugin: <{metadata.Name}>", e); } catch (ReflectionTypeLoadException e) { - Log.Exception(ClassName, $"The GetTypes method was unable to load assembly types for the plugin: <{metadata.Name}>", e); + PublicApi.Instance.LogException(ClassName, $"The GetTypes method was unable to load assembly types for the plugin: <{metadata.Name}>", e); } catch (Exception e) { - Log.Exception(ClassName, $"The following plugin has errored and can not be loaded: <{metadata.Name}>", e); + PublicApi.Instance.LogException(ClassName, $"The following plugin has errored and can not be loaded: <{metadata.Name}>", e); } #endif @@ -124,7 +117,7 @@ namespace Flow.Launcher.Core.Plugin Localize.pluginsHaveErrored(): Localize.pluginHasErrored(); - API.ShowMsgError($"{errorMessage}{Environment.NewLine}{Environment.NewLine}" + + PublicApi.Instance.ShowMsgError($"{errorMessage}{Environment.NewLine}{Environment.NewLine}" + $"{errorPluginString}{Environment.NewLine}{Environment.NewLine}" + Localize.referToLogs()); } diff --git a/Flow.Launcher.Core/Resource/Internationalization.cs b/Flow.Launcher.Core/Resource/Internationalization.cs index 983f8b234..6f373746e 100644 --- a/Flow.Launcher.Core/Resource/Internationalization.cs +++ b/Flow.Launcher.Core/Resource/Internationalization.cs @@ -6,7 +6,6 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; using System.Windows; -using CommunityToolkit.Mvvm.DependencyInjection; using Flow.Launcher.Core.Plugin; using Flow.Launcher.Infrastructure; using Flow.Launcher.Infrastructure.UserSettings; @@ -18,10 +17,6 @@ namespace Flow.Launcher.Core.Resource { private static readonly string ClassName = nameof(Internationalization); - // We should not initialize API in static constructor because it will create another API instance - private static IPublicAPI api = null; - private static IPublicAPI API => api ??= Ioc.Default.GetRequiredService(); - private const string Folder = "Languages"; private const string DefaultLanguageCode = "en"; private const string DefaultFile = "en.xaml"; @@ -104,7 +99,7 @@ namespace Flow.Launcher.Core.Resource var directory = Path.Combine(Constant.ProgramDirectory, Folder); if (!Directory.Exists(directory)) { - API.LogError(ClassName, $"Flow Launcher language directory can't be found <{directory}>"); + PublicApi.Instance.LogError(ClassName, $"Flow Launcher language directory can't be found <{directory}>"); return; } @@ -175,7 +170,7 @@ namespace Flow.Launcher.Core.Resource FirstOrDefault(o => o.LanguageCode.Equals(languageCode, StringComparison.OrdinalIgnoreCase)); if (language == null) { - API.LogError(ClassName, $"Language code can't be found <{languageCode}>"); + PublicApi.Instance.LogError(ClassName, $"Language code can't be found <{languageCode}>"); return AvailableLanguages.English; } else @@ -208,7 +203,7 @@ namespace Flow.Launcher.Core.Resource } catch (Exception e) { - API.LogException(ClassName, $"Failed to change language to <{language.LanguageCode}>", e); + PublicApi.Instance.LogException(ClassName, $"Failed to change language to <{language.LanguageCode}>", e); } finally { @@ -254,7 +249,7 @@ namespace Flow.Launcher.Core.Resource // "Do you want to search with pinyin?" string text = languageToSet == AvailableLanguages.Chinese ? "是否启用拼音搜索?" : "是否啓用拼音搜索?"; - if (API.ShowMsgBox(text, string.Empty, MessageBoxButton.YesNo) == MessageBoxResult.No) + if (PublicApi.Instance.ShowMsgBox(text, string.Empty, MessageBoxButton.YesNo) == MessageBoxResult.No) return false; return true; @@ -311,7 +306,7 @@ namespace Flow.Launcher.Core.Resource } else { - API.LogError(ClassName, $"Language path can't be found <{path}>"); + PublicApi.Instance.LogError(ClassName, $"Language path can't be found <{path}>"); var english = Path.Combine(folder, DefaultFile); if (File.Exists(english)) { @@ -319,7 +314,7 @@ namespace Flow.Launcher.Core.Resource } else { - API.LogError(ClassName, $"Default English Language path can't be found <{path}>"); + PublicApi.Instance.LogError(ClassName, $"Default English Language path can't be found <{path}>"); return string.Empty; } } @@ -354,7 +349,7 @@ namespace Flow.Launcher.Core.Resource } else { - API.LogError(ClassName, $"No Translation for key {key}"); + PublicApi.Instance.LogError(ClassName, $"No Translation for key {key}"); return $"No Translation for key {key}"; } } @@ -377,7 +372,7 @@ namespace Flow.Launcher.Core.Resource } catch (Exception e) { - API.LogException(ClassName, $"Failed for <{p.Metadata.Name}>", e); + PublicApi.Instance.LogException(ClassName, $"Failed for <{p.Metadata.Name}>", e); } } } diff --git a/Flow.Launcher.Core/Resource/LocalizedDescriptionAttribute.cs b/Flow.Launcher.Core/Resource/LocalizedDescriptionAttribute.cs index 3e1a19a76..acd9d9eb7 100644 --- a/Flow.Launcher.Core/Resource/LocalizedDescriptionAttribute.cs +++ b/Flow.Launcher.Core/Resource/LocalizedDescriptionAttribute.cs @@ -1,15 +1,9 @@ using System.ComponentModel; -using CommunityToolkit.Mvvm.DependencyInjection; -using Flow.Launcher.Plugin; namespace Flow.Launcher.Core.Resource { public class LocalizedDescriptionAttribute : DescriptionAttribute { - // We should not initialize API in static constructor because it will create another API instance - private static IPublicAPI api = null; - private static IPublicAPI API => api ??= Ioc.Default.GetRequiredService(); - private readonly string _resourceKey; public LocalizedDescriptionAttribute(string resourceKey) @@ -21,7 +15,7 @@ namespace Flow.Launcher.Core.Resource { get { - string description = API.GetTranslation(_resourceKey); + string description = PublicApi.Instance.GetTranslation(_resourceKey); return string.IsNullOrWhiteSpace(description) ? string.Format("[[{0}]]", _resourceKey) : description; } diff --git a/Flow.Launcher.Infrastructure/DialogJump/DialogJump.cs b/Flow.Launcher.Infrastructure/DialogJump/DialogJump.cs index 65652878f..9035a541d 100644 --- a/Flow.Launcher.Infrastructure/DialogJump/DialogJump.cs +++ b/Flow.Launcher.Infrastructure/DialogJump/DialogJump.cs @@ -58,21 +58,17 @@ namespace Flow.Launcher.Infrastructure.DialogJump private static readonly Settings _settings = Ioc.Default.GetRequiredService(); - // We should not initialize API in static constructor because it will create another API instance - private static IPublicAPI api = null; - private static IPublicAPI API => api ??= Ioc.Default.GetRequiredService(); - private static HWND _mainWindowHandle = HWND.Null; private static readonly Dictionary _dialogJumpExplorers = new(); private static DialogJumpExplorerPair _lastExplorer = null; - private static readonly object _lastExplorerLock = new(); + private static readonly Lock _lastExplorerLock = new(); private static readonly Dictionary _dialogJumpDialogs = new(); private static IDialogJumpDialogWindow _dialogWindow = null; - private static readonly object _dialogWindowLock = new(); + private static readonly Lock _dialogWindowLock = new(); private static HWINEVENTHOOK _foregroundChangeHook = HWINEVENTHOOK.Null; private static HWINEVENTHOOK _locationChangeHook = HWINEVENTHOOK.Null; @@ -89,8 +85,8 @@ namespace Flow.Launcher.Infrastructure.DialogJump private static DispatcherTimer _dragMoveTimer = null; // A list of all file dialog windows that are auto switched already - private static readonly List _autoSwitchedDialogs = new(); - private static readonly object _autoSwitchedDialogsLock = new(); + private static readonly List _autoSwitchedDialogs = []; + private static readonly Lock _autoSwitchedDialogsLock = new(); private static HWINEVENTHOOK _moveSizeHook = HWINEVENTHOOK.Null; private static readonly WINEVENTPROC _moveProc = MoveSizeCallBack; @@ -315,7 +311,7 @@ namespace Flow.Launcher.Infrastructure.DialogJump { foreach (var explorer in _dialogJumpExplorers.Keys) { - if (API.PluginModified(explorer.Metadata.ID) || // Plugin is modified + if (PublicApi.Instance.PluginModified(explorer.Metadata.ID) || // Plugin is modified explorer.Metadata.Disabled) continue; // Plugin is disabled var explorerWindow = explorer.Plugin.CheckExplorerWindow(hWnd); @@ -493,7 +489,7 @@ namespace Flow.Launcher.Infrastructure.DialogJump var dialogWindowChanged = false; foreach (var dialog in _dialogJumpDialogs.Keys) { - if (API.PluginModified(dialog.Metadata.ID) || // Plugin is modified + if (PublicApi.Instance.PluginModified(dialog.Metadata.ID) || // Plugin is modified dialog.Metadata.Disabled) continue; // Plugin is disabled IDialogJumpDialogWindow dialogWindow; @@ -596,7 +592,7 @@ namespace Flow.Launcher.Infrastructure.DialogJump { foreach (var explorer in _dialogJumpExplorers.Keys) { - if (API.PluginModified(explorer.Metadata.ID) || // Plugin is modified + if (PublicApi.Instance.PluginModified(explorer.Metadata.ID) || // Plugin is modified explorer.Metadata.Disabled) continue; // Plugin is disabled var explorerWindow = explorer.Plugin.CheckExplorerWindow(hwnd); @@ -871,7 +867,7 @@ namespace Flow.Launcher.Infrastructure.DialogJump // Then check all dialog windows foreach (var dialog in _dialogJumpDialogs.Keys) { - if (API.PluginModified(dialog.Metadata.ID) || // Plugin is modified + if (PublicApi.Instance.PluginModified(dialog.Metadata.ID) || // Plugin is modified dialog.Metadata.Disabled) continue; // Plugin is disabled var dialogWindow = _dialogJumpDialogs[dialog]; @@ -884,7 +880,7 @@ namespace Flow.Launcher.Infrastructure.DialogJump // Finally search for the dialog window again foreach (var dialog in _dialogJumpDialogs.Keys) { - if (API.PluginModified(dialog.Metadata.ID) || // Plugin is modified + if (PublicApi.Instance.PluginModified(dialog.Metadata.ID) || // Plugin is modified dialog.Metadata.Disabled) continue; // Plugin is disabled IDialogJumpDialogWindow dialogWindow; @@ -1067,11 +1063,8 @@ namespace Flow.Launcher.Infrastructure.DialogJump _navigationLock.Dispose(); // Stop drag move timer - if (_dragMoveTimer != null) - { - _dragMoveTimer.Stop(); - _dragMoveTimer = null; - } + _dragMoveTimer?.Stop(); + _dragMoveTimer = null; } #endregion diff --git a/Flow.Launcher.Infrastructure/Http/Http.cs b/Flow.Launcher.Infrastructure/Http/Http.cs index 5a4371598..f8c111f36 100644 --- a/Flow.Launcher.Infrastructure/Http/Http.cs +++ b/Flow.Launcher.Infrastructure/Http/Http.cs @@ -4,10 +4,8 @@ using System.Net; using System.Net.Http; using System.Threading; using System.Threading.Tasks; -using CommunityToolkit.Mvvm.DependencyInjection; using Flow.Launcher.Infrastructure.Logger; using Flow.Launcher.Infrastructure.UserSettings; -using Flow.Launcher.Plugin; using JetBrains.Annotations; namespace Flow.Launcher.Infrastructure.Http @@ -20,10 +18,6 @@ namespace Flow.Launcher.Infrastructure.Http private static readonly HttpClient client = new(); - // We should not initialize API in static constructor because it will create another API instance - private static IPublicAPI api = null; - private static IPublicAPI API => api ??= Ioc.Default.GetRequiredService(); - static Http() { // need to be added so it would work on a win10 machine @@ -82,7 +76,7 @@ namespace Flow.Launcher.Infrastructure.Http } catch (UriFormatException e) { - API.ShowMsgError(Localize.pleaseTryAgain(), Localize.parseProxyFailed()); + PublicApi.Instance.ShowMsgError(Localize.pleaseTryAgain(), Localize.parseProxyFailed()); Log.Exception(ClassName, "Unable to parse Uri", e); } } diff --git a/Flow.Launcher.Infrastructure/UserSettings/CustomShortcutModel.cs b/Flow.Launcher.Infrastructure/UserSettings/CustomShortcutModel.cs index 2603d4675..a2e95b668 100644 --- a/Flow.Launcher.Infrastructure/UserSettings/CustomShortcutModel.cs +++ b/Flow.Launcher.Infrastructure/UserSettings/CustomShortcutModel.cs @@ -1,8 +1,6 @@ using System; using System.Text.Json.Serialization; using System.Threading.Tasks; -using CommunityToolkit.Mvvm.DependencyInjection; -using Flow.Launcher.Plugin; namespace Flow.Launcher.Infrastructure.UserSettings { @@ -55,11 +53,7 @@ namespace Flow.Launcher.Infrastructure.UserSettings { public string Description { get; set; } - public string LocalizedDescription => API.GetTranslation(Description); - - // We should not initialize API in static constructor because it will create another API instance - private static IPublicAPI api = null; - private static IPublicAPI API => api ??= Ioc.Default.GetRequiredService(); + public string LocalizedDescription => PublicApi.Instance.GetTranslation(Description); public BaseBuiltinShortcutModel(string key, string description) { From 0a7ed3b52f1fe886992209aaff14012522c77b40 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Tue, 23 Sep 2025 17:52:49 +0800 Subject: [PATCH 5/7] Add AbstractPluginEnvironment.API back --- .../Environments/AbstractPluginEnvironment.cs | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/Flow.Launcher.Core/ExternalPlugins/Environments/AbstractPluginEnvironment.cs b/Flow.Launcher.Core/ExternalPlugins/Environments/AbstractPluginEnvironment.cs index d08ea88b3..1a324a993 100644 --- a/Flow.Launcher.Core/ExternalPlugins/Environments/AbstractPluginEnvironment.cs +++ b/Flow.Launcher.Core/ExternalPlugins/Environments/AbstractPluginEnvironment.cs @@ -14,6 +14,8 @@ namespace Flow.Launcher.Core.ExternalPlugins.Environments { private static readonly string ClassName = nameof(AbstractPluginEnvironment); + protected readonly IPublicAPI API = PublicApi.Instance; + internal abstract string Language { get; } internal abstract string EnvName { get; } @@ -56,7 +58,7 @@ namespace Flow.Launcher.Core.ExternalPlugins.Environments } var noRuntimeMessage = Localize.runtimePluginInstalledChooseRuntimePrompt(Language, EnvName, Environment.NewLine); - if (PublicApi.Instance.ShowMsgBox(noRuntimeMessage, string.Empty, MessageBoxButton.YesNo) == MessageBoxResult.No) + if (API.ShowMsgBox(noRuntimeMessage, string.Empty, MessageBoxButton.YesNo) == MessageBoxResult.No) { var msg = Localize.runtimePluginChooseRuntimeExecutable(EnvName); @@ -74,7 +76,7 @@ namespace Flow.Launcher.Core.ExternalPlugins.Environments // Let users select valid path or choose to download while (string.IsNullOrEmpty(selectedFile)) { - if (PublicApi.Instance.ShowMsgBox(forceDownloadMessage, string.Empty, MessageBoxButton.YesNo) == MessageBoxResult.Yes) + if (API.ShowMsgBox(forceDownloadMessage, string.Empty, MessageBoxButton.YesNo) == MessageBoxResult.Yes) { // Continue select file selectedFile = GetFileFromDialog(msg, FileDialogFilter); @@ -107,8 +109,8 @@ namespace Flow.Launcher.Core.ExternalPlugins.Environments } else { - PublicApi.Instance.ShowMsgBox(Localize.runtimePluginUnableToSetExecutablePath(Language)); - PublicApi.Instance.LogError(ClassName, + API.ShowMsgBox(Localize.runtimePluginUnableToSetExecutablePath(Language)); + API.LogError(ClassName, $"Not able to successfully set {EnvName} path, setting's plugin executable path variable is still an empty string.", $"{Language}Environment"); @@ -122,7 +124,7 @@ namespace Flow.Launcher.Core.ExternalPlugins.Environments { if (expectedPath == currentPath) return; - FilesFolders.RemoveFolderIfExists(installedDirPath, (s) => PublicApi.Instance.ShowMsgBox(s)); + FilesFolders.RemoveFolderIfExists(installedDirPath, (s) => API.ShowMsgBox(s)); InstallEnvironment(); } @@ -235,7 +237,7 @@ namespace Flow.Launcher.Core.ExternalPlugins.Environments private static string GetUpdatedEnvironmentPath(string filePath) { var index = filePath.IndexOf(DataLocation.PluginEnvironments); - + // get the substring after "Environments" because we can not determine it dynamically var executablePathSubstring = filePath[(index + DataLocation.PluginEnvironments.Length)..]; return $"{DataLocation.PluginEnvironmentsPath}{executablePathSubstring}"; From 54622d675958cae10b1f01443d6e6a0aa0eb265c Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Tue, 23 Sep 2025 17:53:04 +0800 Subject: [PATCH 6/7] Fix Flow.Launcher.Localization contentHash issue --- Flow.Launcher.Core/packages.lock.json | 2 +- Flow.Launcher.Infrastructure/packages.lock.json | 2 +- Flow.Launcher/packages.lock.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Flow.Launcher.Core/packages.lock.json b/Flow.Launcher.Core/packages.lock.json index ab2a1f718..5561319ff 100644 --- a/Flow.Launcher.Core/packages.lock.json +++ b/Flow.Launcher.Core/packages.lock.json @@ -15,7 +15,7 @@ "type": "Direct", "requested": "[0.0.6, )", "resolved": "0.0.6", - "contentHash": "Wwh5lrnmAf66go456h9sSrkdIW3G/IaKPE3+qWZLRAQ86kIe1JovHRj+ljHZXnFOWu1cbFmHg3l1RuqzPLAHow==" + "contentHash": "WNI/TLGPDr3XdOW8gaALN0Uyz9h+bzqOaNZev2nHEuA3HW9o7XuqaM6C0PqNi96mNgxiypwWpVazBNzaylJ2Aw==" }, "FSharp.Core": { "type": "Direct", diff --git a/Flow.Launcher.Infrastructure/packages.lock.json b/Flow.Launcher.Infrastructure/packages.lock.json index 94adc16f1..b14f891b7 100644 --- a/Flow.Launcher.Infrastructure/packages.lock.json +++ b/Flow.Launcher.Infrastructure/packages.lock.json @@ -27,7 +27,7 @@ "type": "Direct", "requested": "[0.0.6, )", "resolved": "0.0.6", - "contentHash": "Wwh5lrnmAf66go456h9sSrkdIW3G/IaKPE3+qWZLRAQ86kIe1JovHRj+ljHZXnFOWu1cbFmHg3l1RuqzPLAHow==" + "contentHash": "WNI/TLGPDr3XdOW8gaALN0Uyz9h+bzqOaNZev2nHEuA3HW9o7XuqaM6C0PqNi96mNgxiypwWpVazBNzaylJ2Aw==" }, "Fody": { "type": "Direct", diff --git a/Flow.Launcher/packages.lock.json b/Flow.Launcher/packages.lock.json index afbbff6b7..f5ff0e55d 100644 --- a/Flow.Launcher/packages.lock.json +++ b/Flow.Launcher/packages.lock.json @@ -18,7 +18,7 @@ "type": "Direct", "requested": "[0.0.6, )", "resolved": "0.0.6", - "contentHash": "Wwh5lrnmAf66go456h9sSrkdIW3G/IaKPE3+qWZLRAQ86kIe1JovHRj+ljHZXnFOWu1cbFmHg3l1RuqzPLAHow==" + "contentHash": "WNI/TLGPDr3XdOW8gaALN0Uyz9h+bzqOaNZev2nHEuA3HW9o7XuqaM6C0PqNi96mNgxiypwWpVazBNzaylJ2Aw==" }, "Fody": { "type": "Direct", From ac62ebadf0a96cd03fa7d8bb5fd38b7246a8aba3 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Tue, 23 Sep 2025 18:07:15 +0800 Subject: [PATCH 7/7] Add space for code quality --- .../UserSettings/CustomBrowserViewModel.cs | 2 +- .../UserSettings/CustomExplorerViewModel.cs | 2 +- Flow.Launcher/MainWindow.xaml.cs | 4 ++-- Flow.Launcher/ViewModel/PluginViewModel.cs | 6 +++--- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Flow.Launcher.Infrastructure/UserSettings/CustomBrowserViewModel.cs b/Flow.Launcher.Infrastructure/UserSettings/CustomBrowserViewModel.cs index 849762867..009b27666 100644 --- a/Flow.Launcher.Infrastructure/UserSettings/CustomBrowserViewModel.cs +++ b/Flow.Launcher.Infrastructure/UserSettings/CustomBrowserViewModel.cs @@ -7,7 +7,7 @@ namespace Flow.Launcher.Infrastructure.UserSettings { public string Name { get; set; } [JsonIgnore] - public string DisplayName => Name == "Default" ? Localize.defaultBrowser_default(): Name; + public string DisplayName => Name == "Default" ? Localize.defaultBrowser_default() : Name; public string Path { get; set; } public string PrivateArg { get; set; } public bool EnablePrivate { get; set; } diff --git a/Flow.Launcher.Infrastructure/UserSettings/CustomExplorerViewModel.cs b/Flow.Launcher.Infrastructure/UserSettings/CustomExplorerViewModel.cs index ffc48b244..ae406f4c5 100644 --- a/Flow.Launcher.Infrastructure/UserSettings/CustomExplorerViewModel.cs +++ b/Flow.Launcher.Infrastructure/UserSettings/CustomExplorerViewModel.cs @@ -7,7 +7,7 @@ namespace Flow.Launcher.Infrastructure.UserSettings { public string Name { get; set; } [JsonIgnore] - public string DisplayName => Name == "Explorer" ? Localize.fileManagerExplorer(): Name; + public string DisplayName => Name == "Explorer" ? Localize.fileManagerExplorer() : Name; public string Path { get; set; } public string FileArgument { get; set; } = "\"%d\""; public string DirectoryArgument { get; set; } = "\"%d\""; diff --git a/Flow.Launcher/MainWindow.xaml.cs b/Flow.Launcher/MainWindow.xaml.cs index 21cb124b0..c4ed73a0d 100644 --- a/Flow.Launcher/MainWindow.xaml.cs +++ b/Flow.Launcher/MainWindow.xaml.cs @@ -753,7 +753,7 @@ namespace Flow.Launcher private void UpdateNotifyIconText() { var menu = _contextMenu; - ((MenuItem)menu.Items[0]).Header = Localize.iconTrayOpen()+ + ((MenuItem)menu.Items[0]).Header = Localize.iconTrayOpen() + " (" + _settings.Hotkey + ")"; ((MenuItem)menu.Items[1]).Header = Localize.GameMode(); ((MenuItem)menu.Items[2]).Header = Localize.PositionReset(); @@ -768,7 +768,7 @@ namespace Flow.Launcher var openIcon = new FontIcon { Glyph = "\ue71e" }; var open = new MenuItem { - Header = Localize.iconTrayOpen()+ " (" + _settings.Hotkey + ")", + Header = Localize.iconTrayOpen() + " (" + _settings.Hotkey + ")", Icon = openIcon }; var gamemodeIcon = new FontIcon { Glyph = "\ue7fc" }; diff --git a/Flow.Launcher/ViewModel/PluginViewModel.cs b/Flow.Launcher/ViewModel/PluginViewModel.cs index 87d1839c7..bf7720651 100644 --- a/Flow.Launcher/ViewModel/PluginViewModel.cs +++ b/Flow.Launcher/ViewModel/PluginViewModel.cs @@ -164,11 +164,11 @@ namespace Flow.Launcher.ViewModel Visibility.Collapsed : Visibility.Visible; public string InitializeTime => PluginPair.Metadata.InitTime + "ms"; public string QueryTime => PluginPair.Metadata.AvgQueryTime + "ms"; - public string Version => Localize.plugin_query_version()+ " " + PluginPair.Metadata.Version; + public string Version => Localize.plugin_query_version() + " " + PluginPair.Metadata.Version; public string InitAndQueryTime => - Localize.plugin_init_time()+ " " + + Localize.plugin_init_time() + " " + PluginPair.Metadata.InitTime + "ms, " + - Localize.plugin_query_time()+ " " + + Localize.plugin_query_time() + " " + PluginPair.Metadata.AvgQueryTime + "ms"; public string ActionKeywordsText => string.Join(Query.ActionKeywordSeparator, PluginPair.Metadata.ActionKeywords); public string SearchDelayTimeText => PluginPair.Metadata.SearchDelayTime == null ?