diff --git a/Flow.Launcher.Core/ExternalPlugins/Environments/AbstractPluginEnvironment.cs b/Flow.Launcher.Core/ExternalPlugins/Environments/AbstractPluginEnvironment.cs index 40eb1be3eae..30e812c6f05 100644 --- a/Flow.Launcher.Core/ExternalPlugins/Environments/AbstractPluginEnvironment.cs +++ b/Flow.Launcher.Core/ExternalPlugins/Environments/AbstractPluginEnvironment.cs @@ -7,6 +7,7 @@ using System.IO; using System.Linq; using System.Windows.Forms; +using Flow.Launcher.Core.Resource; namespace Flow.Launcher.Core.ExternalPlugins.Environments { @@ -50,14 +51,15 @@ internal IEnumerable Setup() return SetPathForPluginPairs(PluginsSettingsFilePath, Language); } - if (MessageBox.Show($"Flow detected you have installed {Language} plugins, which " + - $"will require {EnvName} to run. Would you like to download {EnvName}? " + - Environment.NewLine + Environment.NewLine + - "Click no if it's already installed, " + - $"and you will be prompted to select the folder that contains the {EnvName} executable", - string.Empty, MessageBoxButtons.YesNo) == DialogResult.No) + var noRuntimeMessage = string.Format( + InternationalizationManager.Instance.GetTranslation("runtimePluginInstalledChooseRuntimePrompt"), + Language, + EnvName, + Environment.NewLine + ); + if (MessageBox.Show(noRuntimeMessage, string.Empty, MessageBoxButtons.YesNo) == DialogResult.No) { - var msg = $"Please select the {EnvName} executable"; + var msg = string.Format(InternationalizationManager.Instance.GetTranslation("runtimePluginChooseRuntimeExecutable"), EnvName); string selectedFile; selectedFile = GetFileFromDialog(msg, FileDialogFilter); @@ -80,8 +82,7 @@ internal IEnumerable Setup() } else { - MessageBox.Show( - $"Unable to set {Language} executable path, please try from Flow's settings (scroll down to the bottom)."); + MessageBox.Show(string.Format(InternationalizationManager.Instance.GetTranslation("runtimePluginUnableToSetExecutablePath"), Language)); Log.Error("PluginsLoader", $"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/Plugin/PluginManager.cs b/Flow.Launcher.Core/Plugin/PluginManager.cs index a827e9e7ade..f5fa5e51818 100644 --- a/Flow.Launcher.Core/Plugin/PluginManager.cs +++ b/Flow.Launcher.Core/Plugin/PluginManager.cs @@ -13,6 +13,7 @@ using ISavable = Flow.Launcher.Plugin.ISavable; using Flow.Launcher.Plugin.SharedCommands; using System.Text.Json; +using Flow.Launcher.Core.Resource; namespace Flow.Launcher.Core.Plugin { @@ -51,7 +52,7 @@ private static void DeletePythonBinding() } /// - /// Save json and ISavable + /// Save json and ISavable /// public static void Save() { @@ -202,12 +203,21 @@ public static async Task InitializePluginsAsync(IPublicAPI api) } } + InternationalizationManager.Instance.AddPluginLanguageDirectories(GetPluginsForInterface()); + InternationalizationManager.Instance.ChangeLanguage(InternationalizationManager.Instance.Settings.Language); + if (failedPlugins.Any()) { var failed = string.Join(",", failedPlugins.Select(x => x.Metadata.Name)); - API.ShowMsg($"Fail to Init Plugins", - $"Plugins: {failed} - fail to load and would be disabled, please contact plugin creator for help", - "", false); + API.ShowMsg( + InternationalizationManager.Instance.GetTranslation("failedToInitializePluginsTitle"), + string.Format( + InternationalizationManager.Instance.GetTranslation("failedToInitializePluginsMessage"), + failed + ), + "", + false + ); } } @@ -215,11 +225,11 @@ public static ICollection ValidPluginsForQuery(Query query) { if (query is null) return Array.Empty(); - + if (!NonGlobalPlugins.ContainsKey(query.ActionKeyword)) return GlobalPlugins; - - + + var plugin = NonGlobalPlugins[query.ActionKeyword]; return new List { @@ -279,8 +289,8 @@ public static void UpdatePluginMetadata(List results, PluginMetadata met r.PluginID = metadata.ID; r.OriginQuery = query; - // ActionKeywordAssigned is used for constructing MainViewModel's query text auto-complete suggestions - // Plugins may have multi-actionkeywords eg. WebSearches. In this scenario it needs to be overriden on the plugin level + // ActionKeywordAssigned is used for constructing MainViewModel's query text auto-complete suggestions + // Plugins may have multi-actionkeywords eg. WebSearches. In this scenario it needs to be overriden on the plugin level if (metadata.ActionKeywords.Count == 1) r.ActionKeywordAssigned = query.ActionKeyword; } @@ -463,7 +473,7 @@ internal static void InstallPlugin(UserPlugin plugin, string zipFilePath, bool c // Unzip plugin files to temp folder var tempFolderPluginPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); System.IO.Compression.ZipFile.ExtractToDirectory(zipFilePath, tempFolderPluginPath); - + if(!plugin.IsFromLocalInstallPath) File.Delete(zipFilePath); diff --git a/Flow.Launcher.Core/Resource/Internationalization.cs b/Flow.Launcher.Core/Resource/Internationalization.cs index 06eb868b88e..192ed2e8142 100644 --- a/Flow.Launcher.Core/Resource/Internationalization.cs +++ b/Flow.Launcher.Core/Resource/Internationalization.cs @@ -25,10 +25,6 @@ public class Internationalization public Internationalization() { - AddPluginLanguageDirectories(); - LoadDefaultLanguage(); - // we don't want to load /Languages/en.xaml twice - // so add flowlauncher language directory after load plugin language files AddFlowLauncherLanguageDirectory(); } @@ -40,9 +36,9 @@ private void AddFlowLauncherLanguageDirectory() } - private void AddPluginLanguageDirectories() + internal void AddPluginLanguageDirectories(IEnumerable plugins) { - foreach (var plugin in PluginManager.GetPluginsForInterface()) + foreach (var plugin in plugins) { var location = Assembly.GetAssembly(plugin.Plugin.GetType()).Location; var dir = Path.GetDirectoryName(location); @@ -56,6 +52,8 @@ private void AddPluginLanguageDirectories() Log.Error($"|Internationalization.AddPluginLanguageDirectories|Can't find plugin path <{location}> for <{plugin.Metadata.Name}>"); } } + + LoadDefaultLanguage(); } private void LoadDefaultLanguage() @@ -140,11 +138,14 @@ private void RemoveOldLanguageFiles() private void LoadLanguage(Language language) { + var flowEnglishFile = Path.Combine(Constant.ProgramDirectory, Folder, DefaultFile); var dicts = Application.Current.Resources.MergedDictionaries; var filename = $"{language.LanguageCode}{Extension}"; var files = _languageDirectories .Select(d => LanguageFile(d, filename)) - .Where(f => !string.IsNullOrEmpty(f)) + // Exclude Flow's English language file since it's built into the binary, and there's no need to load + // it again from the file system. + .Where(f => !string.IsNullOrEmpty(f) && f != flowEnglishFile) .ToArray(); if (files.Length > 0) diff --git a/Flow.Launcher/App.xaml.cs b/Flow.Launcher/App.xaml.cs index 765a1a5593a..4d1adc6cd51 100644 --- a/Flow.Launcher/App.xaml.cs +++ b/Flow.Launcher/App.xaml.cs @@ -71,6 +71,9 @@ await Stopwatch.NormalAsync("|App.OnStartup|Startup cost", async () => StringMatcher.Instance = _stringMatcher; _stringMatcher.UserSettingSearchPrecision = _settings.QuerySearchPrecision; + InternationalizationManager.Instance.Settings = _settings; + InternationalizationManager.Instance.ChangeLanguage(_settings.Language); + PluginManager.LoadPlugins(_settings.PluginSettings); _mainVM = new MainViewModel(_settings); @@ -89,11 +92,6 @@ await Stopwatch.NormalAsync("|App.OnStartup|Startup cost", async () => Current.MainWindow = window; Current.MainWindow.Title = Constant.FlowLauncher; - // todo temp fix for instance code logic - // load plugin before change language, because plugin language also needs be changed - InternationalizationManager.Instance.Settings = _settings; - InternationalizationManager.Instance.ChangeLanguage(_settings.Language); - HotKeyMapper.Initialize(_mainVM); // main windows needs initialized before theme change because of blur settings diff --git a/Flow.Launcher/Languages/en.xaml b/Flow.Launcher/Languages/en.xaml index 966f55ba19e..2f11f118b09 100644 --- a/Flow.Launcher/Languages/en.xaml +++ b/Flow.Launcher/Languages/en.xaml @@ -2,6 +2,17 @@ xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:system="clr-namespace:System;assembly=mscorlib"> + + + Flow detected you have installed {0} plugins, which will require {1} to run. Would you like to download {1}? + {2}{2} + Click no if it's already installed, and you will be prompted to select the folder that contains the {1} executable + + Please select the {0} executable + Unable to set {0} executable path, please try from Flow's settings (scroll down to the bottom). + Fail to Init Plugins + Plugins: {0} - fail to load and would be disabled, please contact plugin creator for help + Failed to register hotkey "{0}". The hotkey may be in use by another program. Change to a different hotkey, or exit another program. Flow Launcher diff --git a/Flow.Launcher/Languages/ru.xaml b/Flow.Launcher/Languages/ru.xaml index 416ca67fa0d..b94b1f5401e 100644 --- a/Flow.Launcher/Languages/ru.xaml +++ b/Flow.Launcher/Languages/ru.xaml @@ -3,6 +3,14 @@ xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:system="clr-namespace:System;assembly=mscorlib"> + + + Flow определил, что вы установили {0} плагины, которым требуется {1} для работы. Скачать {1}? + {2}{2} + Кликните нет, если он уже установлен, и вам будет предложено выбрать папку, где находится исполняемый файл {1} + + Пожалуйста, выберите исполняемый файл {0} + Не удалось установить путь к исполняемому файлу {0}, пожалуйста, попробуйте через настройки Flow (прокрутите вниз). Не удалось зарегистрировать сочетание клавиш "{0}". Возможно, оно используется другой программой. Измените сочетание клавиш или закройте другую программу. Flow Launcher