diff --git a/.github/actions/spelling/expect.txt b/.github/actions/spelling/expect.txt index 6ba41e410ea..5b3419041a6 100644 --- a/.github/actions/spelling/expect.txt +++ b/.github/actions/spelling/expect.txt @@ -1,3 +1,5 @@ +# This file should contain names of products, companies, or individuals that aren't in a standard dictionary (e.g., GitHub, Keptn, VSCode). + crowdin DWM workflows @@ -34,7 +36,6 @@ mscorlib pythonw dotnet winget -jjw24 wolframalpha gmail duckduckgo @@ -49,7 +50,6 @@ srchadmin EWX dlgtext CMD -appref-ms appref TSource runas @@ -57,7 +57,6 @@ dpi popup ptr pluginindicator -TobiasSekan img resx bak @@ -68,9 +67,6 @@ dlg ddd dddd clearlogfolder -ACCENT_ENABLE_TRANSPARENTGRADIENT -ACCENT_ENABLE_BLURBEHIND -WCA_ACCENT_POLICY HGlobal dopusrt firefox @@ -91,22 +87,19 @@ keyevent KListener requery vkcode -čeština Polski Srpski -Português -Português (Brasil) Italiano -Slovenský quicklook -Tiếng Việt Droplex Preinstalled errormetadatafile noresult pluginsmanager alreadyexists -JsonRPC -JsonRPCV2 Softpedia img +Reloadable +metadatas +WMP +VSTHRD diff --git a/.github/actions/spelling/patterns.txt b/.github/actions/spelling/patterns.txt index f29f57ad50e..f308ec5993a 100644 --- a/.github/actions/spelling/patterns.txt +++ b/.github/actions/spelling/patterns.txt @@ -1,4 +1,6 @@ # See https://github.com/check-spelling/check-spelling/wiki/Configuration-Examples:-patterns +# This file should contain strings that contain a mix of letters and numbers, or specific symbols + # Questionably acceptable forms of `in to` # Personally, I prefer `log into`, but people object @@ -121,3 +123,14 @@ # version suffix v# (?:(?<=[A-Z]{2})V|(?<=[a-z]{2}|[A-Z]{2})v)\d+(?:\b|(?=[a-zA-Z_])) + +\bjjw24\b +\bappref-ms\b +\bTobiasSekan\b +\bJsonRPC\b +\bJsonRPCV2\b +\bTiếng Việt\b +\bPortuguês (Brasil)\b +\bčeština\b +\bPortuguês\b +\bIoc\b diff --git a/.github/update_release_pr.py b/.github/update_release_pr.py index ccea511b31f..37d4a8683c8 100644 --- a/.github/update_release_pr.py +++ b/.github/update_release_pr.py @@ -1,11 +1,12 @@ from os import getenv +from typing import Optional import requests def get_github_prs(token: str, owner: str, repo: str, label: str = "", state: str = "all") -> list[dict]: """ - Fetches pull requests from a GitHub repository that match a given milestone and label. + Fetches pull requests from a GitHub repository that match a given label and state. Args: token (str): GitHub token. @@ -23,39 +24,10 @@ def get_github_prs(token: str, owner: str, repo: str, label: str = "", state: st "Accept": "application/vnd.github.v3+json", } - milestone_id = None - milestone_url = f"https://api.github.com/repos/{owner}/{repo}/milestones" - params = {"state": "open"} - - try: - response = requests.get(milestone_url, headers=headers, params=params) - response.raise_for_status() - milestones = response.json() - - if len(milestones) > 2: - print("More than two milestones found, unable to determine the milestone required.") - exit(1) - - # milestones.pop() - for ms in milestones: - if ms["title"] != "Future": - milestone_id = ms["number"] - print(f"Gathering PRs with milestone {ms['title']}...") - break - - if not milestone_id: - print(f"No suitable milestone found in repository '{owner}/{repo}'.") - exit(1) - - except requests.exceptions.RequestException as e: - print(f"Error fetching milestones: {e}") - exit(1) - - # This endpoint allows filtering by milestone and label. A PR in GH's perspective is a type of issue. + # This endpoint allows filtering by label(and milestone). A PR in GH's perspective is a type of issue. prs_url = f"https://api.github.com/repos/{owner}/{repo}/issues" params = { "state": state, - "milestone": milestone_id, "labels": label, "per_page": 100, } @@ -83,7 +55,9 @@ def get_github_prs(token: str, owner: str, repo: str, label: str = "", state: st return all_prs -def get_prs(pull_request_items: list[dict], label: str = "", state: str = "all") -> list[dict]: +def get_prs( + pull_request_items: list[dict], label: str = "", state: str = "all", milestone_title: Optional[str] = None +) -> list[dict]: """ Returns a list of pull requests after applying the label and state filters. @@ -91,6 +65,8 @@ def get_prs(pull_request_items: list[dict], label: str = "", state: str = "all") pull_request_items (list[dict]): List of PR items. label (str): The label name. Filter is not applied when empty string. state (str): State of PR, e.g. open, closed, all + milestone_title (Optional[str]): The milestone title to filter by. This is the milestone number you created + in GitHub, e.g. '1.20.0'. If None, no milestone filtering is applied. Returns: list: A list of dictionaries, where each dictionary represents a pull request. @@ -99,22 +75,32 @@ def get_prs(pull_request_items: list[dict], label: str = "", state: str = "all") pr_list = [] count = 0 for pr in pull_request_items: - if state in [pr["state"], "all"] and (not label or [item for item in pr["labels"] if item["name"] == label]): - pr_list.append(pr) - count += 1 + if state not in [pr["state"], "all"]: + continue + + if label and not [item for item in pr["labels"] if item["name"] == label]: + continue - print(f"Found {count} PRs with {label if label else 'no filter on'} label and state as {state}") + if milestone_title: + if pr["milestone"] is None or pr["milestone"]["title"] != milestone_title: + continue + + pr_list.append(pr) + count += 1 + + print( + f"Found {count} PRs with {label if label else 'no filter on'} label, state as {state}, and milestone {pr["milestone"] if pr["milestone"] is not None else "None"}" + ) return pr_list -def get_prs_assignees(pull_request_items: list[dict], label: str = "", state: str = "all") -> list[str]: + +def get_prs_assignees(pull_request_items: list[dict]) -> list[str]: """ - Returns a list of pull request assignees after applying the label and state filters, excludes jjw24. + Returns a list of pull request assignees, excludes jjw24. Args: - pull_request_items (list[dict]): List of PR items. - label (str): The label name. Filter is not applied when empty string. - state (str): State of PR, e.g. open, closed, all + pull_request_items (list[dict]): List of PR items to get the assignees from. Returns: list: A list of strs, where each string is an assignee name. List is not distinct, so can contain @@ -123,13 +109,13 @@ def get_prs_assignees(pull_request_items: list[dict], label: str = "", state: st """ assignee_list = [] for pr in pull_request_items: - if state in [pr["state"], "all"] and (not label or [item for item in pr["labels"] if item["name"] == label]): - [assignee_list.append(assignee["login"]) for assignee in pr["assignees"] if assignee["login"] != "jjw24" ] + [assignee_list.append(assignee["login"]) for assignee in pr["assignees"] if assignee["login"] != "jjw24"] - print(f"Found {len(assignee_list)} assignees with {label if label else 'no filter on'} label and state as {state}") + print(f"Found {len(assignee_list)} assignees") return assignee_list + def get_pr_descriptions(pull_request_items: list[dict]) -> str: """ Returns the concatenated string of pr title and number in the format of @@ -207,15 +193,16 @@ def update_pull_request_description(token: str, owner: str, repo: str, pr_number print(f"Fetching {state} PRs for {repository_owner}/{repository_name} ...") - pull_requests = get_github_prs(github_token, repository_owner, repository_name) + # First, get all PRs to find the release PR and determine the milestone + all_pull_requests = get_github_prs(github_token, repository_owner, repository_name) - if not pull_requests: - print("No matching pull requests found") + if not all_pull_requests: + print("No pull requests found") exit(1) - print(f"\nFound total of {len(pull_requests)} pull requests") + print(f"\nFound total of {len(all_pull_requests)} pull requests") - release_pr = get_prs(pull_requests, "release", "open") + release_pr = get_prs(all_pull_requests, "release", "open") if len(release_pr) != 1: print(f"Unable to find the exact release PR. Returned result: {release_pr}") @@ -223,14 +210,25 @@ def update_pull_request_description(token: str, owner: str, repo: str, pr_number print(f"Found release PR: {release_pr[0]['title']}") - enhancement_prs = get_prs(pull_requests, "enhancement", "closed") - bug_fix_prs = get_prs(pull_requests, "bug", "closed") + release_milestone_title = release_pr[0].get("milestone", {}).get("title", None) + + if not release_milestone_title: + print("Release PR does not have a milestone assigned.") + exit(1) + + print(f"Using milestone number: {release_milestone_title}") + + enhancement_prs = get_prs(all_pull_requests, "enhancement", "closed", release_milestone_title) + bug_fix_prs = get_prs(all_pull_requests, "bug", "closed", release_milestone_title) + + if len(enhancement_prs) == 0 and len(bug_fix_prs) == 0: + print(f"No PRs with {release_milestone_title} milestone were found") description_content = "# Release notes\n" description_content += f"## Features\n{get_pr_descriptions(enhancement_prs)}" if enhancement_prs else "" description_content += f"## Bug fixes\n{get_pr_descriptions(bug_fix_prs)}" if bug_fix_prs else "" - assignees = list(set(get_prs_assignees(pull_requests, "enhancement", "closed") + get_prs_assignees(pull_requests, "bug", "closed"))) + assignees = list(set(get_prs_assignees(enhancement_prs) + get_prs_assignees(bug_fix_prs))) assignees.sort(key=str.lower) description_content += f"### Authors:\n{', '.join(assignees)}" diff --git a/Flow.Launcher.Core/Plugin/PluginInstaller.cs b/Flow.Launcher.Core/Plugin/PluginInstaller.cs new file mode 100644 index 00000000000..33963c01a5b --- /dev/null +++ b/Flow.Launcher.Core/Plugin/PluginInstaller.cs @@ -0,0 +1,353 @@ +using System; +using System.IO; +using System.IO.Compression; +using System.Linq; +using System.Text.Json; +using System.Threading; +using System.Threading.Tasks; +using System.Windows; +using CommunityToolkit.Mvvm.DependencyInjection; +using Flow.Launcher.Infrastructure.UserSettings; +using Flow.Launcher.Plugin; + +namespace Flow.Launcher.Core.Plugin; + +/// +/// Class for installing, updating, and uninstalling plugins. +/// +public static class PluginInstaller +{ + private static readonly string ClassName = nameof(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. + /// + /// The plugin to install. + /// A Task representing the asynchronous install operation. + public static async Task InstallPluginAndCheckRestartAsync(UserPlugin newPlugin) + { + if (API.PluginModified(newPlugin.ID)) + { + API.ShowMsgError(string.Format(API.GetTranslation("pluginModifiedAlreadyTitle"), newPlugin.Name), + API.GetTranslation("pluginModifiedAlreadyMessage")); + return; + } + + if (API.ShowMsgBox( + string.Format( + API.GetTranslation("InstallPromptSubtitle"), + newPlugin.Name, newPlugin.Author, Environment.NewLine), + API.GetTranslation("InstallPromptTitle"), + button: MessageBoxButton.YesNo) != MessageBoxResult.Yes) return; + + try + { + // at minimum should provide a name, but handle plugin that is not downloaded from plugins manifest and is a url download + var downloadFilename = string.IsNullOrEmpty(newPlugin.Version) + ? $"{newPlugin.Name}-{Guid.NewGuid()}.zip" + : $"{newPlugin.Name}-{newPlugin.Version}.zip"; + + var filePath = Path.Combine(Path.GetTempPath(), downloadFilename); + + using var cts = new CancellationTokenSource(); + + if (!newPlugin.IsFromLocalInstallPath) + { + await DownloadFileAsync( + $"{API.GetTranslation("DownloadingPlugin")} {newPlugin.Name}", + newPlugin.UrlDownload, filePath, cts); + } + else + { + filePath = newPlugin.LocalInstallPath; + } + + // check if user cancelled download before installing plugin + if (cts.IsCancellationRequested) + { + return; + } + + if (!File.Exists(filePath)) + { + throw new FileNotFoundException($"Plugin {newPlugin.ID} zip file not found at {filePath}", filePath); + } + + if (!API.InstallPlugin(newPlugin, filePath)) + { + return; + } + + if (!newPlugin.IsFromLocalInstallPath) + { + File.Delete(filePath); + } + } + catch (Exception e) + { + API.LogException(ClassName, "Failed to install plugin", e); + API.ShowMsgError(API.GetTranslation("ErrorInstallingPlugin")); + return; // do not restart on failure + } + + if (Settings.AutoRestartAfterChanging) + { + API.RestartApp(); + } + else + { + API.ShowMsg( + API.GetTranslation("installbtn"), + string.Format( + API.GetTranslation( + "InstallSuccessNoRestart"), + newPlugin.Name)); + } + } + + /// + /// Installs a plugin from a local zip file and restarts the application if required by settings. Validates the zip and prompts user for confirmation. + /// + /// The path to the plugin zip file. + /// A Task representing the asynchronous install operation. + public static async Task InstallPluginAndCheckRestartAsync(string filePath) + { + UserPlugin plugin; + try + { + using ZipArchive archive = ZipFile.OpenRead(filePath); + var pluginJsonEntry = archive.Entries.FirstOrDefault(x => x.Name == "plugin.json") ?? + throw new FileNotFoundException("The zip file does not contain a plugin.json file."); + + using Stream stream = pluginJsonEntry.Open(); + plugin = JsonSerializer.Deserialize(stream); + plugin.IcoPath = "Images\\zipfolder.png"; + plugin.LocalInstallPath = filePath; + } + catch (Exception e) + { + API.LogException(ClassName, "Failed to validate zip file", e); + API.ShowMsgError(API.GetTranslation("ZipFileNotHavePluginJson")); + return; + } + + if (API.PluginModified(plugin.ID)) + { + API.ShowMsgError(string.Format(API.GetTranslation("pluginModifiedAlreadyTitle"), plugin.Name), + API.GetTranslation("pluginModifiedAlreadyMessage")); + return; + } + + if (Settings.ShowUnknownSourceWarning) + { + if (!InstallSourceKnown(plugin.Website) + && API.ShowMsgBox(string.Format( + API.GetTranslation("InstallFromUnknownSourceSubtitle"), Environment.NewLine), + API.GetTranslation("InstallFromUnknownSourceTitle"), + MessageBoxButton.YesNo) == MessageBoxResult.No) + return; + } + + await InstallPluginAndCheckRestartAsync(plugin); + } + + /// + /// Uninstalls a plugin and restarts the application if required by settings. Prompts user for confirmation and whether to keep plugin settings. + /// + /// The plugin metadata to uninstall. + /// A Task representing the asynchronous uninstall operation. + public static async Task UninstallPluginAndCheckRestartAsync(PluginMetadata oldPlugin) + { + if (API.PluginModified(oldPlugin.ID)) + { + API.ShowMsgError(string.Format(API.GetTranslation("pluginModifiedAlreadyTitle"), oldPlugin.Name), + API.GetTranslation("pluginModifiedAlreadyMessage")); + return; + } + + if (API.ShowMsgBox( + string.Format( + API.GetTranslation("UninstallPromptSubtitle"), + oldPlugin.Name, oldPlugin.Author, Environment.NewLine), + API.GetTranslation("UninstallPromptTitle"), + button: MessageBoxButton.YesNo) != MessageBoxResult.Yes) return; + + var removePluginSettings = API.ShowMsgBox( + API.GetTranslation("KeepPluginSettingsSubtitle"), + API.GetTranslation("KeepPluginSettingsTitle"), + button: MessageBoxButton.YesNo) == MessageBoxResult.No; + + try + { + if (!await API.UninstallPluginAsync(oldPlugin, removePluginSettings)) + { + return; + } + } + catch (Exception e) + { + API.LogException(ClassName, "Failed to uninstall plugin", e); + API.ShowMsgError(API.GetTranslation("ErrorUninstallingPlugin")); + return; // don not restart on failure + } + + if (Settings.AutoRestartAfterChanging) + { + API.RestartApp(); + } + else + { + API.ShowMsg( + API.GetTranslation("uninstallbtn"), + string.Format( + API.GetTranslation( + "UninstallSuccessNoRestart"), + oldPlugin.Name)); + } + } + + /// + /// Updates a plugin to a new version and restarts the application if required by settings. Prompts user for confirmation and handles download if needed. + /// + /// The new plugin version to install. + /// The existing plugin metadata to update. + /// A Task representing the asynchronous update operation. + 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"), + button: MessageBoxButton.YesNo) != MessageBoxResult.Yes) return; + + try + { + var filePath = Path.Combine(Path.GetTempPath(), $"{newPlugin.Name}-{newPlugin.Version}.zip"); + + using var cts = new CancellationTokenSource(); + + if (!newPlugin.IsFromLocalInstallPath) + { + await DownloadFileAsync( + $"{API.GetTranslation("DownloadingPlugin")} {newPlugin.Name}", + newPlugin.UrlDownload, filePath, cts); + } + else + { + filePath = newPlugin.LocalInstallPath; + } + + // check if user cancelled download before installing plugin + if (cts.IsCancellationRequested) + { + return; + } + + if (!await API.UpdatePluginAsync(oldPlugin, newPlugin, filePath)) + { + return; + } + } + catch (Exception e) + { + API.LogException(ClassName, "Failed to update plugin", e); + API.ShowMsgError(API.GetTranslation("ErrorUpdatingPlugin")); + return; // do not restart on failure + } + + if (Settings.AutoRestartAfterChanging) + { + API.RestartApp(); + } + else + { + API.ShowMsg( + API.GetTranslation("updatebtn"), + string.Format( + API.GetTranslation( + "UpdateSuccessNoRestart"), + newPlugin.Name)); + } + } + + /// + /// Downloads a file from a URL to a local path, optionally showing a progress box and handling cancellation. + /// + /// The title for the progress box. + /// The URL to download from. + /// The local file path to save to. + /// Cancellation token source for cancelling the download. + /// Whether to delete the file if it already exists. + /// Whether to show a progress box during download. + /// A Task representing the asynchronous download operation. + private static async Task DownloadFileAsync(string progressBoxTitle, string downloadUrl, string filePath, CancellationTokenSource cts, bool deleteFile = true, bool showProgress = true) + { + if (deleteFile && File.Exists(filePath)) + File.Delete(filePath); + + if (showProgress) + { + var exceptionHappened = false; + await API.ShowProgressBoxAsync(progressBoxTitle, + async (reportProgress) => + { + if (reportProgress == null) + { + // when reportProgress is null, it means there is exception with the progress box + // so we record it with exceptionHappened and return so that progress box will close instantly + exceptionHappened = true; + return; + } + else + { + await API.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); + } + else + { + await API.HttpDownloadAsync(downloadUrl, filePath, token: cts.Token).ConfigureAwait(false); + } + } + + /// + /// Determines if the plugin install source is a known/approved source (e.g., GitHub and matches an existing plugin author). + /// + /// The URL to check. + /// True if the source is known, otherwise false. + private static bool InstallSourceKnown(string url) + { + if (string.IsNullOrEmpty(url)) + return false; + + var pieces = url.Split('/'); + + if (pieces.Length < 4) + return false; + + var author = pieces[3]; + var acceptedHost = "github.com"; + var acceptedSource = "https://github.com"; + var constructedUrlPart = string.Format("{0}/{1}/", acceptedSource, author); + + if (!Uri.TryCreate(url, UriKind.Absolute, out var uri) || uri.Host != acceptedHost) + return false; + + return API.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 9b525f331d2..d88f2f050e5 100644 --- a/Flow.Launcher.Core/Plugin/PluginManager.cs +++ b/Flow.Launcher.Core/Plugin/PluginManager.cs @@ -18,15 +18,12 @@ namespace Flow.Launcher.Core.Plugin { /// - /// The entry for managing Flow Launcher plugins + /// Class for co-ordinating and managing all plugin lifecycle. /// public static class PluginManager { private static readonly string ClassName = nameof(PluginManager); - private static IEnumerable _contextMenuPlugins; - private static IEnumerable _homePlugins; - public static List AllPlugins { get; private set; } public static readonly HashSet GlobalPlugins = new(); public static readonly Dictionary NonGlobalPlugins = new(); @@ -36,8 +33,12 @@ public static class PluginManager private static IPublicAPI API => api ??= Ioc.Default.GetRequiredService(); private static PluginsSettings Settings; - private static List _metadatas; - private static readonly List _modifiedPlugins = new(); + private static readonly ConcurrentBag ModifiedPlugins = new(); + + private static IEnumerable _contextMenuPlugins; + private static IEnumerable _homePlugins; + private static IEnumerable _resultUpdatePlugin; + private static IEnumerable _translationPlugins; /// /// Directories that will hold Flow Launcher plugin directory @@ -173,12 +174,18 @@ static PluginManager() /// public static void LoadPlugins(PluginsSettings settings) { - _metadatas = PluginConfig.Parse(Directories); + var metadatas = PluginConfig.Parse(Directories); Settings = settings; - Settings.UpdatePluginSettings(_metadatas); - AllPlugins = PluginsLoader.Plugins(_metadatas, Settings); + Settings.UpdatePluginSettings(metadatas); + AllPlugins = PluginsLoader.Plugins(metadatas, Settings); // Since dotnet plugins need to get assembly name first, we should update plugin directory after loading plugins - UpdatePluginDirectory(_metadatas); + UpdatePluginDirectory(metadatas); + + // Initialize plugin enumerable after all plugins are initialized + _contextMenuPlugins = GetPluginsForInterface(); + _homePlugins = GetPluginsForInterface(); + _resultUpdatePlugin = GetPluginsForInterface(); + _translationPlugins = GetPluginsForInterface(); } private static void UpdatePluginDirectory(List metadatas) @@ -248,9 +255,6 @@ public static async Task InitializePluginsAsync() await Task.WhenAll(InitTasks); - _contextMenuPlugins = GetPluginsForInterface(); - _homePlugins = GetPluginsForInterface(); - foreach (var plugin in AllPlugins) { // set distinct on each plugin's action keywords helps only firing global(*) and action keywords once where a plugin @@ -290,7 +294,14 @@ public static ICollection ValidPluginsForQuery(Query query) return Array.Empty(); if (!NonGlobalPlugins.TryGetValue(query.ActionKeyword, out var plugin)) - return GlobalPlugins; + { + return GlobalPlugins.Where(p => !PluginModified(p.Metadata.ID)).ToList(); + } + + if (API.PluginModified(plugin.Metadata.ID)) + { + return Array.Empty(); + } return new List { @@ -300,7 +311,7 @@ public static ICollection ValidPluginsForQuery(Query query) public static ICollection ValidPluginsForHomeQuery() { - return _homePlugins.ToList(); + return _homePlugins.Where(p => !PluginModified(p.Metadata.ID)).ToList(); } public static async Task> QueryForPluginAsync(PluginPair pair, Query query, CancellationToken token) @@ -402,16 +413,26 @@ public static PluginPair GetPluginForId(string id) return AllPlugins.FirstOrDefault(o => o.Metadata.ID == id); } - public static IEnumerable GetPluginsForInterface() where T : IFeatures + private static IEnumerable GetPluginsForInterface() where T : IFeatures { // Handle scenario where this is called before all plugins are instantiated, e.g. language change on startup return AllPlugins?.Where(p => p.Plugin is T) ?? Array.Empty(); } + public static IList GetResultUpdatePlugin() + { + return _resultUpdatePlugin.Where(p => !PluginModified(p.Metadata.ID)).ToList(); + } + + public static IList GetTranslationPlugins() + { + return _translationPlugins.Where(p => !PluginModified(p.Metadata.ID)).ToList(); + } + public static List GetContextMenusForPlugin(Result result) { var results = new List(); - var pluginPair = _contextMenuPlugins.FirstOrDefault(o => o.Metadata.ID == result.PluginID); + var pluginPair = _contextMenuPlugins.Where(p => !PluginModified(p.Metadata.ID)).FirstOrDefault(o => o.Metadata.ID == result.PluginID); if (pluginPair != null) { var plugin = (IContextMenu)pluginPair.Plugin; @@ -439,7 +460,7 @@ public static List GetContextMenusForPlugin(Result result) public static bool IsHomePlugin(string id) { - return _homePlugins.Any(p => p.Metadata.ID == id); + return _homePlugins.Where(p => !PluginModified(p.Metadata.ID)).Any(p => p.Metadata.ID == id); } public static bool ActionKeywordRegistered(string actionKeyword) @@ -529,44 +550,62 @@ private static string GetContainingFolderPathAfterUnzip(string unzippedParentFol private static bool SameOrLesserPluginVersionExists(string metadataPath) { var newMetadata = JsonSerializer.Deserialize(File.ReadAllText(metadataPath)); + + if (!Version.TryParse(newMetadata.Version, out var newVersion)) + return true; // If version is not valid, we assume it is lesser than any existing version + return AllPlugins.Any(x => x.Metadata.ID == newMetadata.ID - && newMetadata.Version.CompareTo(x.Metadata.Version) <= 0); + && Version.TryParse(x.Metadata.Version, out var version) + && newVersion <= version); } #region Public functions public static bool PluginModified(string id) { - return _modifiedPlugins.Contains(id); + return ModifiedPlugins.Contains(id); } - public static async Task UpdatePluginAsync(PluginMetadata existingVersion, UserPlugin newVersion, string zipFilePath) + public static async Task UpdatePluginAsync(PluginMetadata existingVersion, UserPlugin newVersion, string zipFilePath) { - InstallPlugin(newVersion, zipFilePath, checkModified:false); - await UninstallPluginAsync(existingVersion, removePluginFromSettings:false, removePluginSettings:false, checkModified: false); - _modifiedPlugins.Add(existingVersion.ID); + if (PluginModified(existingVersion.ID)) + { + API.ShowMsgError(string.Format(API.GetTranslation("pluginModifiedAlreadyTitle"), existingVersion.Name), + API.GetTranslation("pluginModifiedAlreadyMessage")); + return false; + } + + var installSuccess = InstallPlugin(newVersion, zipFilePath, checkModified: false); + if (!installSuccess) return false; + + var uninstallSuccess = await UninstallPluginAsync(existingVersion, removePluginFromSettings: false, removePluginSettings: false, checkModified: false); + if (!uninstallSuccess) return false; + + ModifiedPlugins.Add(existingVersion.ID); + return true; } - public static void InstallPlugin(UserPlugin plugin, string zipFilePath) + public static bool InstallPlugin(UserPlugin plugin, string zipFilePath) { - InstallPlugin(plugin, zipFilePath, checkModified: true); + return InstallPlugin(plugin, zipFilePath, checkModified: true); } - public static async Task UninstallPluginAsync(PluginMetadata plugin, bool removePluginFromSettings = true, bool removePluginSettings = false) + public static async Task UninstallPluginAsync(PluginMetadata plugin, bool removePluginSettings = false) { - await UninstallPluginAsync(plugin, removePluginFromSettings, removePluginSettings, true); + return await UninstallPluginAsync(plugin, removePluginFromSettings: true, removePluginSettings: removePluginSettings, checkModified: true); } #endregion #region Internal functions - internal static void InstallPlugin(UserPlugin plugin, string zipFilePath, bool checkModified) + internal static bool InstallPlugin(UserPlugin plugin, string zipFilePath, bool checkModified) { if (checkModified && PluginModified(plugin.ID)) { - // Distinguish exception from installing same or less version - throw new ArgumentException($"Plugin {plugin.Name} {plugin.ID} has been modified.", nameof(plugin)); + API.ShowMsgError(string.Format(API.GetTranslation("pluginModifiedAlreadyTitle"), plugin.Name), + API.GetTranslation("pluginModifiedAlreadyMessage")); + return false; } // Unzip plugin files to temp folder @@ -584,12 +623,16 @@ internal static void InstallPlugin(UserPlugin plugin, string zipFilePath, bool c if (string.IsNullOrEmpty(metadataJsonFilePath) || string.IsNullOrEmpty(pluginFolderPath)) { - throw new FileNotFoundException($"Unable to find plugin.json from the extracted zip file, or this path {pluginFolderPath} does not exist"); + API.ShowMsgError(string.Format(API.GetTranslation("failedToInstallPluginTitle"), plugin.Name), + string.Format(API.GetTranslation("fileNotFoundMessage"), pluginFolderPath)); + return false; } if (SameOrLesserPluginVersionExists(metadataJsonFilePath)) { - throw new InvalidOperationException($"A plugin with the same ID and version already exists, or the version is greater than this downloaded plugin {plugin.Name}"); + API.ShowMsgError(string.Format(API.GetTranslation("failedToInstallPluginTitle"), plugin.Name), + API.GetTranslation("pluginExistAlreadyMessage")); + return false; } var folderName = string.IsNullOrEmpty(plugin.Version) ? $"{plugin.Name}-{Guid.NewGuid()}" : $"{plugin.Name}-{plugin.Version}"; @@ -631,15 +674,19 @@ internal static void InstallPlugin(UserPlugin plugin, string zipFilePath, bool c if (checkModified) { - _modifiedPlugins.Add(plugin.ID); + ModifiedPlugins.Add(plugin.ID); } + + return true; } - internal static async Task UninstallPluginAsync(PluginMetadata plugin, bool removePluginFromSettings, bool removePluginSettings, bool checkModified) + internal static async Task UninstallPluginAsync(PluginMetadata plugin, bool removePluginFromSettings, bool removePluginSettings, bool checkModified) { if (checkModified && PluginModified(plugin.ID)) { - throw new ArgumentException($"Plugin {plugin.Name} has been modified"); + API.ShowMsgError(string.Format(API.GetTranslation("pluginModifiedAlreadyTitle"), plugin.Name), + API.GetTranslation("pluginModifiedAlreadyMessage")); + return false; } if (removePluginSettings || removePluginFromSettings) @@ -693,6 +740,12 @@ internal static async Task UninstallPluginAsync(PluginMetadata plugin, bool remo } Settings.RemovePluginSettings(plugin.ID); AllPlugins.RemoveAll(p => p.Metadata.ID == plugin.ID); + GlobalPlugins.RemoveWhere(p => p.Metadata.ID == plugin.ID); + var keysToRemove = NonGlobalPlugins.Where(p => p.Value.Metadata.ID == plugin.ID).Select(p => p.Key).ToList(); + foreach (var key in keysToRemove) + { + NonGlobalPlugins.Remove(key); + } } // Marked for deletion. Will be deleted on next start up @@ -700,8 +753,10 @@ internal static async Task UninstallPluginAsync(PluginMetadata plugin, bool remo if (checkModified) { - _modifiedPlugins.Add(plugin.ID); + ModifiedPlugins.Add(plugin.ID); } + + return true; } #endregion diff --git a/Flow.Launcher.Core/Resource/Internationalization.cs b/Flow.Launcher.Core/Resource/Internationalization.cs index b32b09e8fc8..7b7d6eef661 100644 --- a/Flow.Launcher.Core/Resource/Internationalization.cs +++ b/Flow.Launcher.Core/Resource/Internationalization.cs @@ -1,16 +1,17 @@ using System; using System.Collections.Generic; +using System.Globalization; using System.IO; using System.Linq; using System.Reflection; +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; using Flow.Launcher.Plugin; -using System.Globalization; -using System.Threading.Tasks; -using CommunityToolkit.Mvvm.DependencyInjection; namespace Flow.Launcher.Core.Resource { @@ -29,13 +30,12 @@ public class Internationalization private readonly Settings _settings; private readonly List _languageDirectories = new(); private readonly List _oldResources = new(); - private readonly string SystemLanguageCode; + private static string SystemLanguageCode; public Internationalization(Settings settings) { _settings = settings; AddFlowLauncherLanguageDirectory(); - SystemLanguageCode = GetSystemLanguageCodeAtStartup(); } private void AddFlowLauncherLanguageDirectory() @@ -44,7 +44,7 @@ private void AddFlowLauncherLanguageDirectory() _languageDirectories.Add(directory); } - private static string GetSystemLanguageCodeAtStartup() + public static void InitSystemLanguageCode() { var availableLanguages = AvailableLanguages.GetAvailableLanguages(); @@ -65,16 +65,16 @@ private static string GetSystemLanguageCodeAtStartup() string.Equals(languageCode, threeLetterCode, StringComparison.OrdinalIgnoreCase) || string.Equals(languageCode, fullName, StringComparison.OrdinalIgnoreCase)) { - return languageCode; + SystemLanguageCode = languageCode; } } - return DefaultLanguageCode; + SystemLanguageCode = DefaultLanguageCode; } private void AddPluginLanguageDirectories() { - foreach (var plugin in PluginManager.GetPluginsForInterface()) + foreach (var plugin in PluginManager.GetTranslationPlugins()) { var location = Assembly.GetAssembly(plugin.Plugin.GetType()).Location; var dir = Path.GetDirectoryName(location); @@ -173,15 +173,33 @@ private async Task ChangeLanguageAsync(Language language) LoadLanguage(language); } - // Culture of main thread - // Use CreateSpecificCulture to preserve possible user-override settings in Windows, if Flow's language culture is the same as Windows's - CultureInfo.CurrentCulture = CultureInfo.CreateSpecificCulture(language.LanguageCode); - CultureInfo.CurrentUICulture = CultureInfo.CurrentCulture; + // Change culture info + ChangeCultureInfo(language.LanguageCode); // Raise event for plugins after culture is set await Task.Run(UpdatePluginMetadataTranslations); } + public static void ChangeCultureInfo(string languageCode) + { + // Culture of main thread + // Use CreateSpecificCulture to preserve possible user-override settings in Windows, if Flow's language culture is the same as Windows's + CultureInfo currentCulture; + try + { + currentCulture = CultureInfo.CreateSpecificCulture(languageCode); + } + catch (CultureNotFoundException) + { + currentCulture = CultureInfo.CreateSpecificCulture(SystemLanguageCode); + } + CultureInfo.CurrentCulture = currentCulture; + CultureInfo.CurrentUICulture = currentCulture; + var thread = Thread.CurrentThread; + thread.CurrentCulture = currentCulture; + thread.CurrentUICulture = currentCulture; + } + public bool PromptShouldUsePinyin(string languageCodeToSet) { var languageToSet = GetLanguageByLanguageCode(languageCodeToSet); @@ -260,7 +278,8 @@ public static string GetTranslation(string key) private void UpdatePluginMetadataTranslations() { - foreach (var p in PluginManager.GetPluginsForInterface()) + // Update plugin metadata name & description + foreach (var p in PluginManager.GetTranslationPlugins()) { if (p.Plugin is not IPluginI18n pluginI18N) return; try diff --git a/Flow.Launcher.Infrastructure/UserSettings/PluginHotkey.cs b/Flow.Launcher.Infrastructure/UserSettings/PluginHotkey.cs index 9dc395acaea..0c5c3802879 100644 --- a/Flow.Launcher.Infrastructure/UserSettings/PluginHotkey.cs +++ b/Flow.Launcher.Infrastructure/UserSettings/PluginHotkey.cs @@ -1,4 +1,5 @@ -using Flow.Launcher.Plugin; +using System; +using Flow.Launcher.Plugin; namespace Flow.Launcher.Infrastructure.UserSettings { @@ -6,5 +7,26 @@ public class CustomPluginHotkey : BaseModel { public string Hotkey { get; set; } public string ActionKeyword { get; set; } + + public CustomPluginHotkey(string hotkey, string actionKeyword) + { + Hotkey = hotkey; + ActionKeyword = actionKeyword; + } + + public override bool Equals(object other) + { + if (other is CustomPluginHotkey otherHotkey) + { + return Hotkey == otherHotkey.Hotkey && ActionKeyword == otherHotkey.ActionKeyword; + } + + return false; + } + + public override int GetHashCode() + { + return HashCode.Combine(Hotkey, ActionKeyword); + } } } diff --git a/Flow.Launcher.Infrastructure/UserSettings/Settings.cs b/Flow.Launcher.Infrastructure/UserSettings/Settings.cs index 892045994bd..3ec84ff1762 100644 --- a/Flow.Launcher.Infrastructure/UserSettings/Settings.cs +++ b/Flow.Launcher.Infrastructure/UserSettings/Settings.cs @@ -25,7 +25,13 @@ public void SetStorage(FlowLauncherJsonStorage storage) public void Initialize() { + // Initialize dependency injection instances after Ioc.Default is created _stringMatcher = Ioc.Default.GetRequiredService(); + + // Initialize application resources after application is created + var settingWindowFont = new FontFamily(SettingWindowFont); + Application.Current.Resources["SettingWindowFont"] = settingWindowFont; + Application.Current.Resources["ContentControlThemeFontFamily"] = settingWindowFont; } public void Save() @@ -35,9 +41,37 @@ public void Save() private string _theme = Constant.DefaultTheme; public string Hotkey { get; set; } = $"{KeyConstant.Alt} + {KeyConstant.Space}"; - public string OpenResultModifiers { get; set; } = KeyConstant.Alt; + + private string _openResultModifiers = KeyConstant.Alt; + public string OpenResultModifiers + { + get => _openResultModifiers; + set + { + if (_openResultModifiers != value) + { + _openResultModifiers = value; + OnPropertyChanged(); + } + } + } + public string ColorScheme { get; set; } = "System"; - public bool ShowOpenResultHotkey { get; set; } = true; + + private bool _showOpenResultHotkey = true; + public bool ShowOpenResultHotkey + { + get => _showOpenResultHotkey; + set + { + if (_showOpenResultHotkey != value) + { + _showOpenResultHotkey = value; + OnPropertyChanged(); + } + } + } + public double WindowSize { get; set; } = 580; public string PreviewHotkey { get; set; } = $"F1"; public string AutoCompleteHotkey { get; set; } = $"{KeyConstant.Ctrl} + Tab"; @@ -115,8 +149,11 @@ public string SettingWindowFont { _settingWindowFont = value; OnPropertyChanged(); - Application.Current.Resources["SettingWindowFont"] = new FontFamily(value); - Application.Current.Resources["ContentControlThemeFontFamily"] = new FontFamily(value); + if (Application.Current != null) + { + Application.Current.Resources["SettingWindowFont"] = new FontFamily(value); + Application.Current.Resources["ContentControlThemeFontFamily"] = new FontFamily(value); + } } } } @@ -190,6 +227,9 @@ public bool ShowHistoryResultsForHomePage public int MaxHistoryResultsToShowForHomePage { get; set; } = 5; + public bool AutoRestartAfterChanging { get; set; } = false; + public bool ShowUnknownSourceWarning { get; set; } = true; + public int CustomExplorerIndex { get; set; } = 0; [JsonIgnore] diff --git a/Flow.Launcher.Plugin/Flow.Launcher.Plugin.csproj b/Flow.Launcher.Plugin/Flow.Launcher.Plugin.csproj index 4a49e9589f2..1831bf46f24 100644 --- a/Flow.Launcher.Plugin/Flow.Launcher.Plugin.csproj +++ b/Flow.Launcher.Plugin/Flow.Launcher.Plugin.csproj @@ -14,10 +14,10 @@ - 4.6.0 - 4.6.0 - 4.6.0 - 4.6.0 + 4.7.0 + 4.7.0 + 4.7.0 + 4.7.0 Flow.Launcher.Plugin Flow-Launcher MIT @@ -27,6 +27,7 @@ true true Readme.md + true diff --git a/Flow.Launcher.Plugin/Interfaces/IPublicAPI.cs b/Flow.Launcher.Plugin/Interfaces/IPublicAPI.cs index 09c402bcfff..74c97fa6e4b 100644 --- a/Flow.Launcher.Plugin/Interfaces/IPublicAPI.cs +++ b/Flow.Launcher.Plugin/Interfaces/IPublicAPI.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.ComponentModel; using System.IO; @@ -23,8 +23,8 @@ public interface IPublicAPI /// /// query text /// - /// Force requery. By default, Flow Launcher will not fire query if your query is same with existing one. - /// Set this to to force Flow Launcher requerying + /// Force requery. By default, Flow Launcher will not fire query if your query is same with existing one. + /// Set this to to force Flow Launcher re-querying /// void ChangeQuery(string query, bool requery = false); @@ -49,7 +49,7 @@ public interface IPublicAPI /// /// Text to save on clipboard /// When true it will directly copy the file/folder from the path specified in text - /// Whether to show the default notification from this method after copy is done. + /// Whether to show the default notification from this method after copy is done. /// It will show file/folder/text is copied successfully. /// Turn this off to show your own notification after copy is done.> public void CopyToClipboard(string text, bool directCopy = false, bool showDefaultNotification = true); @@ -65,7 +65,7 @@ public interface IPublicAPI void SavePluginSettings(); /// - /// Reloads any Plugins that have the + /// Reloads any Plugins that have the /// IReloadable implemented. It refeshes /// Plugin's in memory data with new content /// added by user. @@ -88,7 +88,7 @@ public interface IPublicAPI /// Show the MainWindow when hiding /// void ShowMainWindow(); - + /// /// Focus the query text box in the main window /// @@ -106,7 +106,7 @@ public interface IPublicAPI bool IsMainWindowVisible(); /// - /// Invoked when the visibility of the main window has changed. Currently, the plugin will continue to be subscribed even if it is turned off. + /// Invoked when the visibility of the main window has changed. Currently, the plugin will continue to be subscribed even if it is turned off. /// event VisibilityChangedEventHandler VisibilityChanged; @@ -141,7 +141,7 @@ public interface IPublicAPI string GetTranslation(string key); /// - /// Get all loaded plugins + /// Get all loaded plugins /// /// List GetAllPlugins(); @@ -199,7 +199,7 @@ public interface IPublicAPI MatchResult FuzzySearch(string query, string stringToCompare); /// - /// Http download the spefic url and return as string + /// Http download the specific url and return as string /// /// URL to call Http Get /// Cancellation Token @@ -207,7 +207,7 @@ public interface IPublicAPI Task HttpGetStringAsync(string url, CancellationToken token = default); /// - /// Http download the spefic url and return as stream + /// Http download the specific url and return as stream /// /// URL to call Http Get /// Cancellation Token @@ -275,8 +275,8 @@ public interface IPublicAPI void LogError(string className, string message, [CallerMemberName] string methodName = ""); /// - /// Log an Exception. Will throw if in debug mode so developer will be aware, - /// otherwise logs the eror message. This is the primary logging method used for Flow + /// Log an Exception. Will throw if in debug mode so developer will be aware, + /// otherwise logs the eror message. This is the primary logging method used for Flow /// void LogException(string className, string message, Exception e, [CallerMemberName] string methodName = ""); @@ -363,7 +363,7 @@ public interface IPublicAPI /// /// Reloads the query. - /// When current results are from context menu or history, it will go back to query results before requerying. + /// When current results are from context menu or history, it will go back to query results before re-querying. /// /// Choose the first result after reload if true; keep the last selected result if false. Default is true. public void ReQuery(bool reselect = true); @@ -517,8 +517,10 @@ public interface IPublicAPI /// /// Path to the zip file containing the plugin. It will be unzipped to the temporary directory, removed and installed. /// - /// - public Task UpdatePluginAsync(PluginMetadata pluginMetadata, UserPlugin plugin, string zipFilePath); + /// + /// True if the plugin is updated successfully, false otherwise. + /// + public Task UpdatePluginAsync(PluginMetadata pluginMetadata, UserPlugin plugin, string zipFilePath); /// /// Install a plugin. By default will remove the zip file if installation is from url, @@ -528,7 +530,10 @@ public interface IPublicAPI /// /// Path to the zip file containing the plugin. It will be unzipped to the temporary directory, removed and installed. /// - public void InstallPlugin(UserPlugin plugin, string zipFilePath); + /// + /// True if the plugin is installed successfully, false otherwise. + /// + public bool InstallPlugin(UserPlugin plugin, string zipFilePath); /// /// Uninstall a plugin @@ -537,8 +542,10 @@ public interface IPublicAPI /// /// Plugin has their own settings. If this is set to true, the plugin settings will be removed. /// - /// - public Task UninstallPluginAsync(PluginMetadata pluginMetadata, bool removePluginSettings = false); + /// + /// True if the plugin is updated successfully, false otherwise. + /// + public Task UninstallPluginAsync(PluginMetadata pluginMetadata, bool removePluginSettings = false); /// /// Log debug message of the time taken to execute a method diff --git a/Flow.Launcher/App.xaml.cs b/Flow.Launcher/App.xaml.cs index cedced181f2..72bef9bdd65 100644 --- a/Flow.Launcher/App.xaml.cs +++ b/Flow.Launcher/App.xaml.cs @@ -41,9 +41,9 @@ public partial class App : IDisposable, ISingleInstanceApp private static readonly string ClassName = nameof(App); private static bool _disposed; + private static Settings _settings; private static MainWindow _mainWindow; private readonly MainViewModel _mainVM; - private readonly Settings _settings; // To prevent two disposals running at the same time. private static readonly object _disposingLock = new(); @@ -55,18 +55,7 @@ public partial class App : IDisposable, ISingleInstanceApp public App() { // Initialize settings - try - { - var storage = new FlowLauncherJsonStorage(); - _settings = storage.Load(); - _settings.SetStorage(storage); - _settings.WMPInstalled = WindowsMediaPlayerHelper.IsWindowsMediaPlayerInstalled(); - } - catch (Exception e) - { - ShowErrorMsgBoxAndFailFast("Cannot load setting storage, please check local data directory", e); - return; - } + _settings.WMPInstalled = WindowsMediaPlayerHelper.IsWindowsMediaPlayerInstalled(); // Configure the dependency injection container try @@ -123,16 +112,6 @@ public App() ShowErrorMsgBoxAndFailFast("Cannot initialize api and settings, please open new issue in Flow.Launcher", e); return; } - - // Local function - static void ShowErrorMsgBoxAndFailFast(string message, Exception e) - { - // Firstly show users the message - MessageBox.Show(e.ToString(), message, MessageBoxButton.OK, MessageBoxImage.Error); - - // Flow cannot construct its App instance, so ensure Flow crashes w/ the exception info. - Environment.FailFast(message, e); - } } #endregion @@ -142,6 +121,29 @@ static void ShowErrorMsgBoxAndFailFast(string message, Exception e) [STAThread] public static void Main() { + // Initialize settings so that we can get language code + try + { + var storage = new FlowLauncherJsonStorage(); + _settings = storage.Load(); + _settings.SetStorage(storage); + } + catch (Exception e) + { + ShowErrorMsgBoxAndFailFast("Cannot load setting storage, please check local data directory", e); + return; + } + + // Initialize system language before changing culture info + Internationalization.InitSystemLanguageCode(); + + // Change culture info before application creation to localize WinForm windows + if (_settings.Language != Constant.SystemLanguageCode) + { + Internationalization.ChangeCultureInfo(_settings.Language); + } + + // Start the application as a single instance if (SingleInstance.InitializeAsFirstInstance()) { using var application = new App(); @@ -152,6 +154,19 @@ public static void Main() #endregion + #region Fail Fast + + private static void ShowErrorMsgBoxAndFailFast(string message, Exception e) + { + // Firstly show users the message + MessageBox.Show(e.ToString(), message, MessageBoxButton.OK, MessageBoxImage.Error); + + // Flow cannot construct its App instance, so ensure Flow crashes w/ the exception info. + Environment.FailFast(message, e); + } + + #endregion + #region App Events #pragma warning disable VSTHRD100 // Avoid async void methods @@ -191,6 +206,9 @@ await API.StopwatchLogInfoAsync(ClassName, "Startup cost", async () => Http.Proxy = _settings.Proxy; + // Initialize plugin manifest before initializing plugins so that they can use the manifest instantly + await API.UpdatePluginManifestAsync(); + await PluginManager.InitializePluginsAsync(); // Change language after all plugins are initialized because we need to update plugin title based on their api diff --git a/Flow.Launcher/CustomQueryHotkeySetting.xaml b/Flow.Launcher/CustomQueryHotkeySetting.xaml index 0171e6d79c3..db99b704a52 100644 --- a/Flow.Launcher/CustomQueryHotkeySetting.xaml +++ b/Flow.Launcher/CustomQueryHotkeySetting.xaml @@ -119,7 +119,8 @@ Grid.Column="1" Margin="10" HorizontalAlignment="Stretch" - VerticalAlignment="Center" /> + VerticalAlignment="Center" + Text="{Binding ActionKeyword}" /> diff --git a/Flow.Launcher/CustomQueryHotkeySetting.xaml.cs b/Flow.Launcher/CustomQueryHotkeySetting.xaml.cs index 77febde9d5b..2ee08bf85c0 100644 --- a/Flow.Launcher/CustomQueryHotkeySetting.xaml.cs +++ b/Flow.Launcher/CustomQueryHotkeySetting.xaml.cs @@ -1,73 +1,52 @@ -using System.Collections.ObjectModel; -using System.Linq; -using System.Windows; -using System.Windows.Input; +using System.Windows; using System.Windows.Controls; -using Flow.Launcher.Helper; +using System.Windows.Input; using Flow.Launcher.Infrastructure.UserSettings; namespace Flow.Launcher { public partial class CustomQueryHotkeySetting : Window { - private readonly Settings _settings; + public string Hotkey { get; set; } = string.Empty; + public string ActionKeyword { get; set; } = string.Empty; - private bool update; - private CustomPluginHotkey updateCustomHotkey; + private readonly bool update; + private readonly CustomPluginHotkey originalCustomHotkey; - public CustomQueryHotkeySetting(Settings settings) + public CustomQueryHotkeySetting() { - _settings = settings; InitializeComponent(); + tbAdd.Visibility = Visibility.Visible; } - private void BtnCancel_OnClick(object sender, RoutedEventArgs e) + public CustomQueryHotkeySetting(CustomPluginHotkey hotkey) { - Close(); + originalCustomHotkey = hotkey; + update = true; + ActionKeyword = originalCustomHotkey.ActionKeyword; + InitializeComponent(); + tbUpdate.Visibility = Visibility.Visible; + HotkeyControl.SetHotkey(originalCustomHotkey.Hotkey, false); } - private void btnAdd_OnClick(object sender, RoutedEventArgs e) + private void BtnCancel_OnClick(object sender, RoutedEventArgs e) { - if (!update) - { - _settings.CustomPluginHotkeys ??= new ObservableCollection(); - - var pluginHotkey = new CustomPluginHotkey - { - Hotkey = HotkeyControl.CurrentHotkey.ToString(), ActionKeyword = tbAction.Text - }; - _settings.CustomPluginHotkeys.Add(pluginHotkey); - - HotKeyMapper.SetCustomQueryHotkey(pluginHotkey); - } - else - { - var oldHotkey = updateCustomHotkey.Hotkey; - updateCustomHotkey.ActionKeyword = tbAction.Text; - updateCustomHotkey.Hotkey = HotkeyControl.CurrentHotkey.ToString(); - //remove origin hotkey - HotKeyMapper.RemoveHotkey(oldHotkey); - HotKeyMapper.SetCustomQueryHotkey(updateCustomHotkey); - } - + DialogResult = false; Close(); } - public void UpdateItem(CustomPluginHotkey item) + private void btnAdd_OnClick(object sender, RoutedEventArgs e) { - updateCustomHotkey = _settings.CustomPluginHotkeys.FirstOrDefault(o => - o.ActionKeyword == item.ActionKeyword && o.Hotkey == item.Hotkey); - if (updateCustomHotkey == null) + Hotkey = HotkeyControl.CurrentHotkey.ToString(); + + if (string.IsNullOrEmpty(Hotkey) && string.IsNullOrEmpty(ActionKeyword)) { - App.API.ShowMsgBox(App.API.GetTranslation("invalidPluginHotkey")); - Close(); + App.API.ShowMsgBox(App.API.GetTranslation("emptyPluginHotkey")); return; } - tbAction.Text = updateCustomHotkey.ActionKeyword; - HotkeyControl.SetHotkey(updateCustomHotkey.Hotkey, false); - update = true; - lblAdd.Text = App.API.GetTranslation("update"); + DialogResult = !update || originalCustomHotkey.Hotkey != Hotkey || originalCustomHotkey.ActionKeyword != ActionKeyword; + Close(); } private void BtnTestActionKeyword_OnClick(object sender, RoutedEventArgs e) @@ -79,6 +58,7 @@ private void BtnTestActionKeyword_OnClick(object sender, RoutedEventArgs e) private void cmdEsc_OnPress(object sender, ExecutedRoutedEventArgs e) { + DialogResult = false; Close(); } diff --git a/Flow.Launcher/CustomShortcutSetting.xaml b/Flow.Launcher/CustomShortcutSetting.xaml index d8623753f63..5185354e772 100644 --- a/Flow.Launcher/CustomShortcutSetting.xaml +++ b/Flow.Launcher/CustomShortcutSetting.xaml @@ -118,24 +118,26 @@ FontSize="14" Text="{DynamicResource customShortcutExpansion}" /> - - + - + - + - + - + - + + Sub="{Binding BackdropSubText}" + Type="First"> + Icon="" + Type="Last"> - + - + ()) + foreach (var pair in PluginManager.GetResultUpdatePlugin()) { var plugin = (IResultUpdated)pair.Plugin; plugin.ResultsUpdated += (s, e) => diff --git a/Flow.Launcher/ViewModel/PluginStoreItemViewModel.cs b/Flow.Launcher/ViewModel/PluginStoreItemViewModel.cs index d1cf7450121..f5523212e13 100644 --- a/Flow.Launcher/ViewModel/PluginStoreItemViewModel.cs +++ b/Flow.Launcher/ViewModel/PluginStoreItemViewModel.cs @@ -1,5 +1,5 @@ using System; -using System.Linq; +using System.Threading.Tasks; using CommunityToolkit.Mvvm.Input; using Flow.Launcher.Core.Plugin; using Flow.Launcher.Plugin; @@ -9,27 +9,28 @@ namespace Flow.Launcher.ViewModel { public partial class PluginStoreItemViewModel : BaseModel { - private PluginPair PluginManagerData => PluginManager.GetPluginForId("9f8f9b14-2518-4907-b211-35ab6290dee7"); + private readonly UserPlugin _newPlugin; + private readonly PluginPair _oldPluginPair; + public PluginStoreItemViewModel(UserPlugin plugin) { - _plugin = plugin; + _newPlugin = plugin; + _oldPluginPair = PluginManager.GetPluginForId(plugin.ID); } - private UserPlugin _plugin; - - public string ID => _plugin.ID; - public string Name => _plugin.Name; - public string Description => _plugin.Description; - public string Author => _plugin.Author; - public string Version => _plugin.Version; - public string Language => _plugin.Language; - public string Website => _plugin.Website; - public string UrlDownload => _plugin.UrlDownload; - public string UrlSourceCode => _plugin.UrlSourceCode; - public string IcoPath => _plugin.IcoPath; + public string ID => _newPlugin.ID; + public string Name => _newPlugin.Name; + public string Description => _newPlugin.Description; + public string Author => _newPlugin.Author; + public string Version => _newPlugin.Version; + public string Language => _newPlugin.Language; + public string Website => _newPlugin.Website; + public string UrlDownload => _newPlugin.UrlDownload; + public string UrlSourceCode => _newPlugin.UrlSourceCode; + public string IcoPath => _newPlugin.IcoPath; - public bool LabelInstalled => PluginManager.GetPluginForId(_plugin.ID) != null; - public bool LabelUpdate => LabelInstalled && new Version(_plugin.Version) > new Version(PluginManager.GetPluginForId(_plugin.ID).Metadata.Version); + public bool LabelInstalled => _oldPluginPair != null; + public bool LabelUpdate => LabelInstalled && new Version(_newPlugin.Version) > new Version(_oldPluginPair.Metadata.Version); internal const string None = "None"; internal const string RecentlyUpdated = "RecentlyUpdated"; @@ -41,15 +42,15 @@ public string Category get { string category = None; - if (DateTime.Now - _plugin.LatestReleaseDate < TimeSpan.FromDays(7)) + if (DateTime.Now - _newPlugin.LatestReleaseDate < TimeSpan.FromDays(7)) { category = RecentlyUpdated; } - if (DateTime.Now - _plugin.DateAdded < TimeSpan.FromDays(7)) + if (DateTime.Now - _newPlugin.DateAdded < TimeSpan.FromDays(7)) { category = NewRelease; } - if (PluginManager.GetPluginForId(_plugin.ID) != null) + if (_oldPluginPair != null) { category = Installed; } @@ -59,11 +60,22 @@ public string Category } [RelayCommand] - private void ShowCommandQuery(string action) + private async Task ShowCommandQueryAsync(string action) { - var actionKeyword = PluginManagerData.Metadata.ActionKeywords.Any() ? PluginManagerData.Metadata.ActionKeywords[0] + " " : String.Empty; - App.API.ChangeQuery($"{actionKeyword}{action} {_plugin.Name}"); - App.API.ShowMainWindow(); + switch (action) + { + case "install": + await PluginInstaller.InstallPluginAndCheckRestartAsync(_newPlugin); + break; + case "uninstall": + await PluginInstaller.UninstallPluginAndCheckRestartAsync(_oldPluginPair.Metadata); + break; + case "update": + await PluginInstaller.UpdatePluginAndCheckRestartAsync(_newPlugin, _oldPluginPair.Metadata); + break; + default: + break; + } } } } diff --git a/Flow.Launcher/ViewModel/PluginViewModel.cs b/Flow.Launcher/ViewModel/PluginViewModel.cs index 01fa3d20326..ea222d02374 100644 --- a/Flow.Launcher/ViewModel/PluginViewModel.cs +++ b/Flow.Launcher/ViewModel/PluginViewModel.cs @@ -1,5 +1,4 @@ -using System.Linq; -using System.Threading.Tasks; +using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Media; @@ -32,21 +31,6 @@ public PluginPair PluginPair } } - private static string PluginManagerActionKeyword - { - get - { - var keyword = PluginManager - .GetPluginForId("9f8f9b14-2518-4907-b211-35ab6290dee7") - .Metadata.ActionKeywords.FirstOrDefault(); - return keyword switch - { - null or "*" => string.Empty, - _ => keyword - }; - } - } - private async Task LoadIconAsync() { Image = await App.API.LoadImageAsync(PluginPair.Metadata.IcoPath); @@ -186,10 +170,9 @@ private void OpenSourceCodeLink() } [RelayCommand] - private void OpenDeletePluginWindow() + private async Task OpenDeletePluginWindowAsync() { - App.API.ChangeQuery($"{PluginManagerActionKeyword} uninstall {PluginPair.Metadata.Name}".Trim(), true); - App.API.ShowMainWindow(); + await PluginInstaller.UninstallPluginAndCheckRestartAsync(PluginPair.Metadata); } [RelayCommand] diff --git a/Flow.Launcher/ViewModel/ResultViewModel.cs b/Flow.Launcher/ViewModel/ResultViewModel.cs index 648ac49bbf2..c58abae28ee 100644 --- a/Flow.Launcher/ViewModel/ResultViewModel.cs +++ b/Flow.Launcher/ViewModel/ResultViewModel.cs @@ -64,9 +64,6 @@ public ResultViewModel(Result result, Settings settings) public Settings Settings { get; } - public Visibility ShowOpenResultHotkey => - Settings.ShowOpenResultHotkey ? Visibility.Visible : Visibility.Collapsed; - public Visibility ShowDefaultPreview => Result.PreviewPanel == null ? Visibility.Visible : Visibility.Collapsed; public Visibility ShowCustomizedPreview => Result.PreviewPanel == null ? Visibility.Collapsed : Visibility.Visible; @@ -152,8 +149,6 @@ public Visibility ShowBadge private bool PreviewImageAvailable => !string.IsNullOrEmpty(Result.Preview.PreviewImagePath) || Result.Preview.PreviewDelegate != null; - public string OpenResultModifiers => Settings.OpenResultModifiers; - public string ShowTitleToolTip => string.IsNullOrEmpty(Result.TitleToolTip) ? Result.Title : Result.TitleToolTip; diff --git a/Flow.Launcher/ViewModel/SelectBrowserViewModel.cs b/Flow.Launcher/ViewModel/SelectBrowserViewModel.cs index 1eee6dba5d3..67bbbd9301f 100644 --- a/Flow.Launcher/ViewModel/SelectBrowserViewModel.cs +++ b/Flow.Launcher/ViewModel/SelectBrowserViewModel.cs @@ -1,6 +1,5 @@ using System.Collections.ObjectModel; using System.Linq; -using System.Windows; using CommunityToolkit.Mvvm.Input; using Flow.Launcher.Infrastructure.UserSettings; using Flow.Launcher.Plugin; diff --git a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Languages/de.xaml b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Languages/de.xaml index 4a764bbfab9..66e30855f73 100644 --- a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Languages/de.xaml +++ b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Languages/de.xaml @@ -26,6 +26,6 @@ Wenn Sie nicht Chrome, Firefox oder Edge verwenden oder deren portable Version nutzen, müssen Sie das Lesezeichen-Datenverzeichnis hinzufügen und die richtige Browser-Engine auswählen, damit dieses Plug-in funktioniert. Zum Beispiel: Die Engine von Brave ist Chromium, und deren Standardspeicherort der Lesezeichen-Daten ist: %LOCALAPPDATA%\BraveSoftware\Brave-Browser\UserData". Bei der Firefox-Engine ist das Lesezeichenverzeichnis der Ordner userdata, der die Datei places.sqlite enthält. - Load favicons (can be time consuming during startup) + Favicons laden (kann während des Starts zeitaufwendig sein) diff --git a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Languages/pl.xaml b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Languages/pl.xaml index 2dff7543f24..9f92d86b153 100644 --- a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Languages/pl.xaml +++ b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Languages/pl.xaml @@ -19,12 +19,12 @@ Ścieżka katalogu danych Dodaj Edytuj - Usu + Usuń Przeglądaj Inne Silnik przeglądarki Jeśli nie używasz Chrome, Firefox lub Edge, lub używasz ich wersji przenośnej, musisz dodać katalog danych zakładek i wybrać poprawny silnik przeglądarki, aby wtyczka działała. Na przykład: silnikiem przeglądarki Brave jest Chromium, a domyślna lokalizacja danych zakładek to: "%LOCALAPPDATA%\BraveSoftware\Brave-Browser\UserData". W przypadku silnika Firefoksa, katalog zakładek to folder danych użytkownika zawierający plik places.sqlite. - Load favicons (can be time consuming during startup) + Wczytaj ikony ulubionych (może być czasochłonne podczas uruchamiania) diff --git a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Languages/uk-UA.xaml b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Languages/uk-UA.xaml index b8fd4fb8367..07ccc2ea4ce 100644 --- a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Languages/uk-UA.xaml +++ b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Languages/uk-UA.xaml @@ -25,6 +25,6 @@ Браузерний рушій Якщо ви не використовуєте Chrome, Firefox або Edge, або використовуєте їхні портативні версії, вам потрібно додати каталог даних закладок і вибрати правильний рушій браузера, щоб цей плагін працював. Наприклад: Рушій Brave - Chromium, і за замовчуванням розташування даних закладок: "%LOCALAPPDATA%\BraveSoftware\Brave-Browser\UserData". Для браузера Firefox директорія закладок - це папка userdata, що містить файл places.sqlite. - Load favicons (can be time consuming during startup) + Завантажити піктограми (може зайняти багато часу під час запуску) diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/ar.xaml b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/ar.xaml index d327dcebbe4..ebdb0ff3518 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/ar.xaml +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/ar.xaml @@ -96,8 +96,11 @@ حذف حذف الملف الحالي نهائيًا حذف المجلد الحالي نهائيًا - المسار: - Name: + اسم البرنامج + Type + المسار + ملف + مجلد حذف المحدد تشغيل كمستخدم مختلف تشغيل العنصر المحدد باستخدام حساب مستخدم مختلف diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/cs.xaml b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/cs.xaml index 80e181a8f45..38f4f145ed7 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/cs.xaml +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/cs.xaml @@ -96,8 +96,11 @@ Smazat Trvale odstranit aktuální soubor Trvale smazat aktuální složku - Cesta: - Name: + Jméno + Type + Cesta + Soubor + Složka Odstranit vybraný Spustit jako jiný uživatel Spustí vybranou položku jako uživatel s jiným účtem diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/da.xaml b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/da.xaml index 7cbe281d8d3..8c011814c3a 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/da.xaml +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/da.xaml @@ -96,8 +96,11 @@ Slet Permanently delete current file Permanently delete current folder - Path: - Name: + Name + Type + Path + File + Folder Delete the selected Run as different user Run the selected using a different user account diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/de.xaml b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/de.xaml index 10700a0ee16..1d8c3937ab0 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/de.xaml +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/de.xaml @@ -3,8 +3,8 @@ Bitte treffen Sie zuerst eine Auswahl - Please select a folder path. - Please choose a different name or folder path. + Bitte wählen Sie einen Ordnerpfad aus. + Bitte wählen Sie einen anderen Namen oder Ordnerpfad. Bitte wählen Sie einen Ordner-Link aus Sind Sie sicher, dass Sie {0} löschen wollen? Sind Sie sicher, dass Sie diese Datei dauerhaft löschen möchten? @@ -27,14 +27,14 @@ Hinzufügen Allgemeine Einstellung Aktions-Schlüsselwörter individuell anpassen - Customise Quick Access + Schnellzugriff individuell anpassen Schnellzugriff-Links Everyhting-Einstellung Vorschau-Panel Größe Erstellungsdatum Änderungsdatum - File Age + Dateialter Datei-Info anzeigen Datums- und Zeitformat Sortieroption: @@ -44,7 +44,7 @@ Shell-Pfad Indexsuche ausgeschlossene Pfade Ort des Suchergebnisses als Arbeitsverzeichnis der ausführbaren Datei verwenden - Display more information like size and age in tooltips + Mehr Informationen wie Größe und Alter in Tooltips anzeigen Drücken Sie Enter, um Ordner im Default-Dateimanager zu öffnen Indexsuche für Pfadsuche verwenden Indexierungsoptionen @@ -81,23 +81,26 @@ Strg+Enter, um das Verzeichnis zu öffnen Strg+Enter, um den enthaltenden Ordner zu öffnen - {0}{4}Size: {1}{4}Date created: {2}{4}Date modified: {3} + {0}{4}Größe: {1}{4}Erstellungsdatum: {2}{4}Änderunsgdatum: {3} Unbekannt - {0}{3}Space free: {1}{3}Total size: {2} + {0}{3}Platz frei: {1}{3}Größe total: {2} Pfad kopieren Pfad des aktuellen Elements in Zwischenablage kopieren - Copy name - Copy name of current item to clipboard + Name kopieren + Name des aktuellen Elements in Zwischenablage kopieren Kopieren Aktuelle Datei in Zwischenablage kopieren Aktuellen Ordner in Zwischenablage kopieren Löschen Aktuelle Datei dauerhaft löschen Aktuellen Ordner dauerhaft löschen - Pfad: - Name: + Name + Typ + Pfad + Datei + Ordner Ausgewähltes löschen Als anderer Benutzer ausführen Ausgewähltes unter Verwendung eines anderen Benutzerkontos ausführen @@ -164,12 +167,12 @@ Everything-Dienst erfolgreich installiert Der Everything-Dienst konnte nicht automatisch installiert werden. Bitte installieren Sie ihn manuell über https://www.voidtools.com Klicken Sie hier, um es zu starten - Es kann keine Everything-Installation gefuinden werden, möchten Sie einen Ort manuell auswählen?{0}{0}Klicken Sie auf Nein und Everything wird automatisch für Sie installiert + Es kann keine Everything-Installation gefunden werden, möchten Sie einen Ort manuell auswählen?{0}{0}Klicken Sie auf Nein und Everything wird automatisch für Sie installiert Möchten Sie die Inhaltssuche für Everything aktivieren? Es kann sehr langsam sein ohne Index (was nur in Everything v1.5+ unterstützt wird) - Unable to find Everything.exe - Failed to install Everything, please install it manually + Everything.exe kann nicht gefunden werden + Everything konnte nicht installiert werden, bitte installieren Sie es manuell Natives Kontextmenü @@ -178,10 +181,10 @@ Unten können Sie die Elemente angeben, die Sie aus dem Kontextmenü ausschließen möchten. Diese können partiell (z. B. „Stift mit“) oder vollständig („Öffnen mit“) sein. - Today - {0} days ago - 1 month ago - {0} months ago - 1 year ago - {0} years ago + Heute + Vor {0} Tagen + Vor 1 Monat + Vor {0} Monaten + Vor 1 Jahr + Vor {0} Jahren diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/es-419.xaml b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/es-419.xaml index 4e372adbe2e..ae541a7457e 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/es-419.xaml +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/es-419.xaml @@ -96,8 +96,11 @@ Eliminar Permanently delete current file Permanently delete current folder - Path: - Name: + Name + Type + Ruta + Archivo + Carpeta Delete the selected Run as different user Run the selected using a different user account diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/es.xaml b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/es.xaml index 4f5977fa1a6..10ee4a9a494 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/es.xaml +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/es.xaml @@ -34,7 +34,7 @@ Tamaño Fecha de creación Fecha de modificación - Edad del archivo + Antigüedad del archivo Mostrar información del archivo Formato de fecha y hora Ordenar por: @@ -44,7 +44,7 @@ Ruta del Shell Rutas excluídas del índice de búsqueda Usar la ubicación de los resultados de búsqueda como directorio de trabajo del ejecutable - Mostrar más información, como tamaño y antigüedad, en los consejos (tooltips) + Mostrar más información, como el tamaño y la antigüedad, en los mensajes emergentes Pulsar Entrar para abrir la carpeta en el administrador de archivos predeterminado Usar búsqueda indexada para buscar rutas Opciones de indexación @@ -96,8 +96,11 @@ Eliminar Elimina permanentemente el archivo actual Elimina permanentemente la carpeta actual - Ruta: - Nombre: + Nombre + Tipo + Ruta + Archivo + Carpeta Elimina el seleccionado Ejecutar como usuario diferente Ejecuta la selección usando una cuenta de usuario diferente diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/fr.xaml b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/fr.xaml index 726fbe7d71f..8809004c724 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/fr.xaml +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/fr.xaml @@ -96,8 +96,11 @@ Supprimer Supprimer définitivement le fichier actuel Supprimer définitivement le dossier actuel - Chemin : - Nom : + Nom + Type + Chemin + Fichier + Dossier Supprimer la sélection Exécuter en tant qu'utilisateur différent Exécuter le programme sélectionné en utilisant un autre compte d'utilisateur diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/he.xaml b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/he.xaml index 6e955848dd3..a18a15aad4a 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/he.xaml +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/he.xaml @@ -96,8 +96,11 @@ מחק מחק לצמיתות את הקובץ הנוכחי מחק לצמיתות את התיקייה הנוכחית - נתיב: - שם: + שם + Type + נתיב + קובץ + תיקייה מחק את הפריט שנבחר הפעל כמשתמש אחר הפעל את הפריט שנבחר באמצעות חשבון משתמש אחר diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/it.xaml b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/it.xaml index df756e88626..3afaf2f250f 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/it.xaml +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/it.xaml @@ -96,8 +96,11 @@ Cancella Elimina permanentemente il file corrente Elimina definitivamente la cartella corrente - Percorso: - Name: + Nome + Type + Percorso + File + Cartella Elimina il selezionato Esegui come utente differente Esegui la selezione utilizzando un altro account utente diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/ja.xaml b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/ja.xaml index 3d944a2b82a..648efe642c0 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/ja.xaml +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/ja.xaml @@ -96,8 +96,11 @@ 削除 現在のファイルを完全に削除 現在のフォルダーを完全に削除 - Path: - Name: + 名前 + Type + Path + ファイル + フォルダー Delete the selected Run as different user Run the selected using a different user account @@ -138,7 +141,7 @@ Warning: Everything service is not running Error while querying Everything Sort By - Name + 名前 Path サイズ Extension diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/ko.xaml b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/ko.xaml index 27e4aa76ac4..cf4d8b8ad7f 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/ko.xaml +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/ko.xaml @@ -96,8 +96,11 @@ 삭제 이 파일을 영구적으로 삭제 이 폴더를 영구적으로 삭제 - 경로: - Name: + Name + Type + Path + 파일 + 폴더 선택 항목을 삭제 다른 유저 권한으로 실행 선택한 사용자 계정으로 실행 diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/nb.xaml b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/nb.xaml index e5ef88959ea..733136c36f5 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/nb.xaml +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/nb.xaml @@ -96,8 +96,11 @@ Slett Slett gjeldende fil permanent Slett gjeldende mappe permanent - Sti: - Name: + Navn + Type + Sti + Fil + Mappe Slett valgte Kjør som annen bruker Kjør valgte med en annen brukerkonto diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/nl.xaml b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/nl.xaml index d8178a1f3c4..c4bfb639d8b 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/nl.xaml +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/nl.xaml @@ -96,8 +96,11 @@ Verwijder Permanently delete current file Permanently delete current folder - Path: - Name: + Name + Type + Pad + Bestand + Map Delete the selected Run as different user Run the selected using a different user account diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/pl.xaml b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/pl.xaml index 5a29fd9aba3..ddf56ab3757 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/pl.xaml +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/pl.xaml @@ -2,9 +2,9 @@ - Pierw dokonaj wyboru - Please select a folder path. - Please choose a different name or folder path. + Najpierw dokonaj wyboru + Wybierz ścieżkę folderu. + Wybierz inną nazwę lub ścieżkę folderu. Musisz wybrać któryś folder z listy Czy jesteś pewien że chcesz usunąć {0}? Jesteś pewny, że chcesz usunąć ten plik trwale? @@ -27,14 +27,14 @@ Dodaj Ustawienia ogólne Zmień słowa kluczowe akcji - Customise Quick Access + Dostosuj Szybki Dostęp Linki szybkiego dostępu Ustawienia Everything Panel podglądu Rozmiar Data utworzenia Data modyfikacji - File Age + Wiek pliku Wyświetl informacje o pliku Format daty i czasu Opcje sortowania: @@ -44,7 +44,7 @@ Ścieżka powłoki Wykluczone ścieżki indeksu Użyj lokalizacji wyników wyszukiwania jako katalogu roboczego pliku wykonywalnego - Display more information like size and age in tooltips + Wyświetlaj więcej informacji, takich jak rozmiar i wiek w podpowiedziach Naciśnij Enter, aby otworzyć folder w domyślnym menedżerze plików Użyj wyszukiwania indeksowego do przeszukiwania ścieżek Opcje indeksowania @@ -81,23 +81,26 @@ Ctrl + Enter, aby otworzyć folder Ctrl + Enter, aby otworzyć folder zawierający - {0}{4}Size: {1}{4}Date created: {2}{4}Date modified: {3} + {0} {4}Rozmiar: {1} {4}Data utworzenia: {2} {4} Data modyfikacji: {3} Nieznane - {0}{3}Space free: {1}{3}Total size: {2} + {0} {3}Wolna przestrzeń: {1} {3}Rozmiar całkowity: {2} Skopiuj Ścieżkę Kopiuj ścieżkę bieżącego elementu do schowka - Copy name - Copy name of current item to clipboard + Kopiuj nazwę + Kopiuj nazwę bieżącego elementu do schowka Kopiuj Kopiuj bieżący plik do schowka Kopiuj bieżący folder do schowka Usu Trwale usuń bieżący plik Trwale usuń bieżący folder - Ścieżka: - Name: + Nazwa + Type + Ścieżka + Plik + Folder Usuń zaznaczone Uruchom jako inny użytkownik Uruchom wybrane używając innego konta użytkownika @@ -168,8 +171,8 @@ Czy chcesz włączyć wyszukiwanie zawartości dla programu Everything? Może działać bardzo wolno bez indeksu (który jest obsługiwany tylko w Everything w wersji 1.5 i nowszych) - Unable to find Everything.exe - Failed to install Everything, please install it manually + Nie znaleziono pliku Everything.exe + Błąd instalacji Everything, zainstaluj aplikację samodzielnie Natywne menu kontekstowe @@ -178,10 +181,10 @@ Poniżej możesz określić elementy, które chcesz wykluczyć z menu kontekstowego. Mogą być one częściowe (np. "otw w") lub pełne ("Otwórz za pomocą"). - Today - {0} days ago - 1 month ago - {0} months ago - 1 year ago - {0} years ago + Dzisiaj + {0} dni wcześniej + 1 miesiąc wcześniej + {0} miesięcy wcześniej + 1 rok temu + {0} lat temu diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/pt-br.xaml b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/pt-br.xaml index 5785a561679..d0a26029055 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/pt-br.xaml +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/pt-br.xaml @@ -96,8 +96,11 @@ Apagar Permanently delete current file Permanently delete current folder - Caminho: - Name: + Nome + Type + Path + Arquivo + Pasta Delete the selected Run as different user Run the selected using a different user account diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/pt-pt.xaml b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/pt-pt.xaml index 5db8251e270..98a77d06a4f 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/pt-pt.xaml +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/pt-pt.xaml @@ -96,8 +96,11 @@ Eliminar Eliminar permanentemente o ficheiro atual Eliminar permanentemente a pasta atual - Caminho: - Nome: + Nome + Tipo + Caminho + Ficheiro + Pasta Eliminar seleção Executar com outro utilizador Executar ações com uma conta de utilizador diferente diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/ru.xaml b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/ru.xaml index 35bd1d87def..44a3a9b65e8 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/ru.xaml +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/ru.xaml @@ -96,8 +96,11 @@ Удалить Permanently delete current file Permanently delete current folder - Путь: - Name: + Name + Type + Path + Файл + Папка Delete the selected Запустить от имени другого пользователя Run the selected using a different user account diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/sk.xaml b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/sk.xaml index 1323d6ae071..7c3f5deff06 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/sk.xaml +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/sk.xaml @@ -96,8 +96,11 @@ Odstrániť Natrvalo odstrániť aktuálny súbor Natrvalo odstrániť aktuálny priečinok - Cesta: - Názov: + Názov + Typ + Cesta + Súbor + Priečinok Odstrániť vybraný Spustiť ako iný používateľ Spustí vybranú položku ako používateľ s iným kontom diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/sr.xaml b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/sr.xaml index 6af5508843a..4df64e4d57c 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/sr.xaml +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/sr.xaml @@ -96,8 +96,11 @@ Obriši Permanently delete current file Permanently delete current folder - Path: - Name: + Name + Type + Path + File + Folder Delete the selected Run as different user Run the selected using a different user account diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/tr.xaml b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/tr.xaml index 44674952b6c..ed327e1bced 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/tr.xaml +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/tr.xaml @@ -96,8 +96,11 @@ Sil Mevcut dosyayı kalıcı olarak sil Mevcut klasörü kalıcı olarak sil - Yol: - Name: + Name + Type + Path + Dosya + Klasör Seçileni sil Başka bir kullanıcı olarak çalıştır Run the selected using a different user account diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/uk-UA.xaml b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/uk-UA.xaml index b203687e98a..38829112c72 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/uk-UA.xaml +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/uk-UA.xaml @@ -3,8 +3,8 @@ Будь ласка, спочатку зробіть вибір - Please select a folder path. - Please choose a different name or folder path. + Виберіть шлях до теки. + Виберіть інше ім'я або шлях до теки. Будь ласка, оберіть посилання на теку Ви впевнені, що хочете видалити {0}? Ви впевнені, що хочете назавжди видалити цей файл? @@ -27,14 +27,14 @@ Додати Загальні налаштування Налаштувати ключові слова дії - Customise Quick Access + Налаштування швидкого доступу Посилання швидкого доступу Налаштування Everything Панель поперегляду Розмір Дата створення Дата останньої зміни - File Age + Дата створення Показати інформацію про файл Формат дати й часу Варіант сортування: @@ -44,7 +44,7 @@ Шлях до оболонки Shell Виключені шляхи індексного пошуку Використовувати розташування результату пошуку як робочу директорію виконуваного файлу - Display more information like size and age in tooltips + Показувати більше інформації, наприклад розмір і дату створення, у підказках Натисніть Enter, щоб відкрити папку у файловому менеджері за замовчуванням Використовуйте індексний пошук для пошуку шляху Параметри індексації @@ -81,23 +81,26 @@ Ctrl + Enter, щоб відкрити каталог Ctrl + Enter, щоб відкрити відповідну папку - {0}{4}Size: {1}{4}Date created: {2}{4}Date modified: {3} + {0}{4}Розмір: {1}{4}Дата створення: {2}{4}Дата змінення: {3} Невідомо - {0}{3}Space free: {1}{3}Total size: {2} + {0}{3}Вільного місця: {1}{3}Загальний розмір: {2} Копіювати шлях Копіювати шлях до поточного елемента в буфер обміну - Copy name - Copy name of current item to clipboard + Копіювати назву + Скопіювати назву поточного елемента в буфер обміну Копіювати Копіювання поточного файлу в буфер обміну Копіювати поточну папку в буфер обміну Видалити Безповоротно видалити поточний файл Назавжди видалити поточну папку - Шлях: - Name: + Назва + Тип + Шлях + Файл + Тека Видалити вибране Запустити від імені іншого користувача Запустити вибране під обліковим записом іншого користувача @@ -156,7 +159,7 @@ Попередження: Це не швидке сортування, пошук може бути повільним Шукати повний шлях - Enable File/Folder Run Count + Увімкнути підрахунок запусків файлів / тек Натисніть, щоб запустити або встановити Everything Встановлення програми Everything @@ -168,20 +171,20 @@ Бажаєте увімкнути пошук контенту для Everything? Без індексу (який підтримується лише у версії Everything v1.5+) воно може працювати дуже повільно - Unable to find Everything.exe - Failed to install Everything, please install it manually + Не вдалося знайти Everything.exe + Не вдалося встановити Everything, встановіть його вручну Рідне контекстне меню Відображати рідне контекстне меню (експериментально) Нижче ви можете вказати елементи, які хочете включити до контекстного меню, вони можуть бути частковими (наприклад, «шир пера») або повними («Відкрити за допомогою»). - Below you can specify items you want to exclude from context menu, they can be partial (e.g. 'pen wit') or complete ('Open with'). + Нижче ви можете вказати елементи, які ви хочете виключити з контекстного меню. Вони можуть бути частковими (наприклад, «pen wit») або повними («Відкрити за допомогою»). - Today - {0} days ago - 1 month ago - {0} months ago - 1 year ago - {0} years ago + Сьогодні + {0} дн. тому + Місяць тому + {0} міс. тому + Рік тому + {0} р. тому diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/vi.xaml b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/vi.xaml index f41276a670c..de6f9a6a4c7 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/vi.xaml +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/vi.xaml @@ -96,8 +96,11 @@ Xóa Permanently delete current file Permanently delete current folder - Đường dẫn: - Name: + Tên + Type + Path + Ngày tháng + Thư Mục Xóa đã chọn Xóa lựa chọn đã chọn Chạy phần đã chọn bằng tài khoản người dùng khác diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/zh-cn.xaml b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/zh-cn.xaml index 9bce7206284..79d8fc66db9 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/zh-cn.xaml +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/zh-cn.xaml @@ -96,8 +96,11 @@ 删除 永久删除当前文件 永久删除当前文件夹 - 路径: - 名称: + 名称 + 类型 + 路径 + 文件 + 目录 删除所选内容 以其他用户身份运行 使用其他用户帐户运行所选内容 diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/zh-tw.xaml b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/zh-tw.xaml index 931c9de296a..c4e22066dda 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/zh-tw.xaml +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/zh-tw.xaml @@ -96,8 +96,11 @@ 刪除 Permanently delete current file Permanently delete current folder - 路徑: - Name: + 名稱 + Type + 路徑 + 檔案 + 資料夾 刪除所選內容 Run as different user Run the selected using a different user account diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Views/PreviewPanel.xaml b/Plugins/Flow.Launcher.Plugin.Explorer/Views/PreviewPanel.xaml index 22aa8f5971c..2b03bdc2080 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Views/PreviewPanel.xaml +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Views/PreviewPanel.xaml @@ -1,4 +1,4 @@ - - + @@ -47,7 +44,7 @@ TextWrapping="Wrap" /> - + - - - - - - - - - - - - - + + + + + + + + + + + + - - + + - - - - + + + diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/ar.xaml b/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/ar.xaml index 6fb809d9007..8a75cde72b5 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/ar.xaml +++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/ar.xaml @@ -43,10 +43,15 @@ تم تحديث الإضافة {0} بنجاح. يرجى إعادة تشغيل Flow. تم تحديث {0} إضافات بنجاح. يرجى إعادة تشغيل Flow. تم تعديل الإضافة {0} بالفعل. يرجى إعادة تشغيل Flow قبل إجراء أي تغييرات أخرى. + {0} modified already + Please restart Flow before making any further changes + + Invalid zip installer file + Please check if there is a plugin.json in {0} مدير الإضافات - إدارة تثبيت وإلغاء تثبيت أو تحديث إضافات Flow Launcher + Install, uninstall or update Flow Launcher plugins via the search window مؤلف غير معروف @@ -61,5 +66,5 @@ تحذير التثبيت من مصدر غير معروف - إعادة تشغيل Flow Launcher تلقائيًا بعد تثبيت/إلغاء تثبيت/تحديث الإضافات + Restart Flow Launcher automatically after installing/uninstalling/updating plugin via Plugins Manager diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/cs.xaml b/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/cs.xaml index d47e1814be0..5ca1700d444 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/cs.xaml +++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/cs.xaml @@ -43,10 +43,15 @@ Plugin {0} successfully updated. Please restart Flow. {0} plugins successfully updated. Please restart Flow. Plugin {0} has already been modified. Please restart Flow before making any further changes. + {0} modified already + Please restart Flow before making any further changes + + Invalid zip installer file + Please check if there is a plugin.json in {0} Správce pluginů - Správa instalace, odinstalace nebo aktualizace pluginů Flow Launcheru + Install, uninstall or update Flow Launcher plugins via the search window Neznámý autor @@ -61,5 +66,5 @@ Upozornění na instalaci z neznámého zdroje - Automatically restart Flow Launcher after installing/uninstalling/updating plugins + Restart Flow Launcher automatically after installing/uninstalling/updating plugin via Plugins Manager diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/da.xaml b/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/da.xaml index 616ce779b33..a5d0231ceae 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/da.xaml +++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/da.xaml @@ -43,10 +43,15 @@ Plugin {0} successfully updated. Please restart Flow. {0} plugins successfully updated. Please restart Flow. Plugin {0} has already been modified. Please restart Flow before making any further changes. + {0} modified already + Please restart Flow before making any further changes + + Invalid zip installer file + Please check if there is a plugin.json in {0} Plugins Manager - Management of installing, uninstalling or updating Flow Launcher plugins + Install, uninstall or update Flow Launcher plugins via the search window Unknown Author @@ -61,5 +66,5 @@ Install from unknown source warning - Automatically restart Flow Launcher after installing/uninstalling/updating plugins + Restart Flow Launcher automatically after installing/uninstalling/updating plugin via Plugins Manager diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/de.xaml b/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/de.xaml index 47ea31cceee..c7ef7780198 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/de.xaml +++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/de.xaml @@ -43,10 +43,15 @@ Plug-in {0} erfolgreich aktualisiert. Bitte starten Sie Flow neu. {0} Plug-ins erfolgreich aktualisiert. Bitte starten Sie Flow neu. Plug-in {0} ist bereits modifiziert worden. Bitte starten Sie Flow neu, bevor Sie irgendwelche weitere Änderungen vornehmen. + {0} modified already + Please restart Flow before making any further changes + + Invalid zip installer file + Please check if there is a plugin.json in {0} Plug-ins-Manager - Verwaltung der Installation, Deinstallation oder Aktualisierung der Plug-ins von Flow Launcher + Install, uninstall or update Flow Launcher plugins via the search window Unbekannter Autor @@ -61,5 +66,5 @@ Warnung vor Installation aus unbekannter Quelle - Automatischer Neustart von Flow Launcher nach Installation/Deinstallation/Aktualisierung von Plug-ins + Restart Flow Launcher automatically after installing/uninstalling/updating plugin via Plugins Manager diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/en.xaml b/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/en.xaml index 573ca90519e..fa2e65240ae 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/en.xaml +++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/en.xaml @@ -45,10 +45,15 @@ Plugin {0} successfully updated. Please restart Flow. {0} plugins successfully updated. Please restart Flow. Plugin {0} has already been modified. Please restart Flow before making any further changes. + {0} modified already + Please restart Flow before making any further changes + + Invalid zip installer file + Please check if there is a plugin.json in {0} Plugins Manager - Management of installing, uninstalling or updating Flow Launcher plugins + Install, uninstall or update Flow Launcher plugins via the search window Unknown Author @@ -63,5 +68,5 @@ Install from unknown source warning - Automatically restart Flow Launcher after installing/uninstalling/updating plugins + Restart Flow Launcher automatically after installing/uninstalling/updating plugin via Plugins Manager \ No newline at end of file diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/es-419.xaml b/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/es-419.xaml index 616ce779b33..a5d0231ceae 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/es-419.xaml +++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/es-419.xaml @@ -43,10 +43,15 @@ Plugin {0} successfully updated. Please restart Flow. {0} plugins successfully updated. Please restart Flow. Plugin {0} has already been modified. Please restart Flow before making any further changes. + {0} modified already + Please restart Flow before making any further changes + + Invalid zip installer file + Please check if there is a plugin.json in {0} Plugins Manager - Management of installing, uninstalling or updating Flow Launcher plugins + Install, uninstall or update Flow Launcher plugins via the search window Unknown Author @@ -61,5 +66,5 @@ Install from unknown source warning - Automatically restart Flow Launcher after installing/uninstalling/updating plugins + Restart Flow Launcher automatically after installing/uninstalling/updating plugin via Plugins Manager diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/es.xaml b/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/es.xaml index b0f25f3ea2b..b6a3a6cbcb5 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/es.xaml +++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/es.xaml @@ -43,10 +43,15 @@ Complemento {0} actualizado correctamente. Por favor, reinicie Flow. {0} complementos se han actualizado correctamente. Por favor, reinicie Flow. El complemento {0} ya ha sido modificado. Por favor, reinicie Flow antes de realizar más cambios. + {0} ya está modificado + Reiniciar Flow antes de realizar más cambios + + Archivo de instalación zip no válido + Por favor, compruebe si hay un plugin.json en {0} Administrador de complementos - Administración de instalación, desinstalación o actualización de los complementos de Flow Launcher + Instalar, desinstalar o actualizar complementos de Flow Launcher desde la ventana de búsqueda Autor desconocido @@ -61,5 +66,5 @@ Aviso de instalación desde fuentes desconocidas - Reiniciar automáticamente Flow Launcher después de instalar/desinstalar/actualizar complementos + Reiniciar Flow Launcher automáticamente después de instalar/desinstalar/actualizar el complemento a través del Administrador de complementos diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/fr.xaml b/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/fr.xaml index 3142ef86dc9..c95c9723102 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/fr.xaml +++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/fr.xaml @@ -43,10 +43,15 @@ Plugin {0} mis à jour avec succès. Veuillez redémarrer Flow. {0} plugins mis à jour avec succès. Veuillez redémarrer Flow. Le plugin {0} a déjà été modifié. Veuillez redémarrer Flow avant de faire d'autres modifications. + {0} est déjà modifié + Veuillez redémarrer Flow avant d'apporter d'autres modifications + + Fichier d'installation zip invalide + Veuillez vérifier s'il y a un plugin.json dans {0} Gestionnaire de plugins - Gestion de l'installation, de la désinstallation ou de la mise à jour des plugins Flow Launcher + Installer, désinstaller ou mettre à jour les plugins Flow Launcher via la fenêtre de recherche Auteur inconnu @@ -61,5 +66,5 @@ Avertissement d'installation à partir d'une source inconnue - Redémarrer automatiquement Flow Launcher après l'installation/désinstallation/mise à jour des plugins + Redémarrer Flow Launcher automatiquement après l'installation/désinstallation/mise à jour du plugin via le gestionnaire de plugins diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/he.xaml b/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/he.xaml index 8c7f0cf028e..3fe7fd96818 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/he.xaml +++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/he.xaml @@ -43,10 +43,15 @@ התוסף {0} עודכן בהצלחה. נא הפעל מחדש את Flow. {0} תוספים עודכנו בהצלחה. נא הפעל מחדש את Flow. התוסף {0} כבר השתנה. נא הפעל מחדש את Flow לפני ביצוע שינויים נוספים. + {0} modified already + Please restart Flow before making any further changes + + Invalid zip installer file + Please check if there is a plugin.json in {0} מנהל תוספים - ניהול התקנה, הסרה או עדכון של תוספים עבור Flow Launcher + Install, uninstall or update Flow Launcher plugins via the search window מחבר לא ידוע @@ -61,5 +66,5 @@ אזהרה בעת התקנה ממקור לא ידוע - הפעל מחדש את Flow Launcher באופן אוטומטי לאחר התקנה/הסרה/עדכון של תוספים + Restart Flow Launcher automatically after installing/uninstalling/updating plugin via Plugins Manager diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/it.xaml b/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/it.xaml index d154e59dc81..3ccefa2db15 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/it.xaml +++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/it.xaml @@ -43,10 +43,15 @@ Il plugin {0} aggiornato con successo. Riavviare Flow. {0} plugin aggiornato con successo. Riavviare Flow. Il plugin {0} è già stato modificato. Riavviare Flow prima di fare altre modifiche. + {0} modified already + Please restart Flow before making any further changes + + Invalid zip installer file + Please check if there is a plugin.json in {0} Gestore dei plugin - Gestione dell'installazione, disinstallazione o aggiornamento dei plugin di Flow Launcher + Install, uninstall or update Flow Launcher plugins via the search window Autore Sconosciuto @@ -61,5 +66,5 @@ Avviso di installazione da sorgenti sconosciute - Riavvia automaticamente Flow Launcher dopo l'installazione/disinstallazione/aggiornamento dei plugin + Restart Flow Launcher automatically after installing/uninstalling/updating plugin via Plugins Manager diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/ja.xaml b/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/ja.xaml index 616ce779b33..d62f0f61bfc 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/ja.xaml +++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/ja.xaml @@ -43,10 +43,15 @@ Plugin {0} successfully updated. Please restart Flow. {0} plugins successfully updated. Please restart Flow. Plugin {0} has already been modified. Please restart Flow before making any further changes. + {0} modified already + Please restart Flow before making any further changes + + Invalid zip installer file + Please check if there is a plugin.json in {0} Plugins Manager - Management of installing, uninstalling or updating Flow Launcher plugins + Install, uninstall or update Flow Launcher plugins via the search window Unknown Author @@ -60,6 +65,6 @@ Visit the PluginsManifest repository to see community-made plugin submissions - Install from unknown source warning - Automatically restart Flow Launcher after installing/uninstalling/updating plugins + 不明な提供元からインストールするとき警告する + Restart Flow Launcher automatically after installing/uninstalling/updating plugin via Plugins Manager diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/ko.xaml b/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/ko.xaml index f6f46448d83..8c15f27ad70 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/ko.xaml +++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/ko.xaml @@ -43,10 +43,15 @@ Plugin {0} successfully updated. Please restart Flow. {0} plugins successfully updated. Please restart Flow. Plugin {0} has already been modified. Please restart Flow before making any further changes. + {0} modified already + Please restart Flow before making any further changes + + Invalid zip installer file + Please check if there is a plugin.json in {0} 플러그인 관리자 - 플러그인의 설치/삭제/업데이트를 관리하는 플러그인 + Install, uninstall or update Flow Launcher plugins via the search window 알수없는 제작자 @@ -61,5 +66,5 @@ Install from unknown source warning - Automatically restart Flow Launcher after installing/uninstalling/updating plugins + Restart Flow Launcher automatically after installing/uninstalling/updating plugin via Plugins Manager diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/nb.xaml b/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/nb.xaml index b0fd2d10ac7..bccd55459ae 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/nb.xaml +++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/nb.xaml @@ -43,10 +43,15 @@ Programtillegg {0} oppdatert. Vennligst restart Flow. {0} programtillegg oppdatert. Start Flow på nytt. Programtillegg {0} er allerede endret. Start Flow på nytt før nye endringer foretas. + {0} modified already + Please restart Flow before making any further changes + + Invalid zip installer file + Please check if there is a plugin.json in {0} Programtilleggsbehandling - Administrasjon av installasjon, avinstallere eller oppdatere Flow Launcher programtillegg + Install, uninstall or update Flow Launcher plugins via the search window Ukjent utvikler @@ -61,5 +66,5 @@ Advarsel om installering fra ukjent kilde - Start Flow Launcher automatisk på nytt etter installasjon/avinstallering/oppdatering av programtillegg + Restart Flow Launcher automatically after installing/uninstalling/updating plugin via Plugins Manager diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/nl.xaml b/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/nl.xaml index 616ce779b33..a5d0231ceae 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/nl.xaml +++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/nl.xaml @@ -43,10 +43,15 @@ Plugin {0} successfully updated. Please restart Flow. {0} plugins successfully updated. Please restart Flow. Plugin {0} has already been modified. Please restart Flow before making any further changes. + {0} modified already + Please restart Flow before making any further changes + + Invalid zip installer file + Please check if there is a plugin.json in {0} Plugins Manager - Management of installing, uninstalling or updating Flow Launcher plugins + Install, uninstall or update Flow Launcher plugins via the search window Unknown Author @@ -61,5 +66,5 @@ Install from unknown source warning - Automatically restart Flow Launcher after installing/uninstalling/updating plugins + Restart Flow Launcher automatically after installing/uninstalling/updating plugin via Plugins Manager diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/pl.xaml b/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/pl.xaml index 18790093100..3124cc63478 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/pl.xaml +++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/pl.xaml @@ -43,10 +43,15 @@ Wtyczka {0} została pomyślnie zaktualizowana. Proszę ponownie uruchomić Flow. {0} wtyczek zaktualizowano pomyślnie. Proszę ponownie uruchomić Flow. Wtyczka {0} została już zmodyfikowana. Proszę ponownie uruchomić Flow przed wprowadzeniem dalszych zmian. + {0} modified already + Please restart Flow before making any further changes + + Invalid zip installer file + Please check if there is a plugin.json in {0} Menadżer wtyczek - Zarządzanie instalowaniem, odinstalowywaniem i aktualizowaniem wtyczek Flow Launcher + Install, uninstall or update Flow Launcher plugins via the search window Nieznany autor @@ -61,5 +66,5 @@ Ostrzeżenie o instalacji z nieznanego źródła - Automatycznie uruchom ponownie Flow Launcher po zainstalowaniu/odinstalowaniu/zaktualizowaniu wtyczek + Restart Flow Launcher automatically after installing/uninstalling/updating plugin via Plugins Manager diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/pt-br.xaml b/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/pt-br.xaml index 179bcab9718..2407d5b6e7d 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/pt-br.xaml +++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/pt-br.xaml @@ -43,10 +43,15 @@ Plugin {0} successfully updated. Please restart Flow. {0} plugins successfully updated. Please restart Flow. Plugin {0} has already been modified. Please restart Flow before making any further changes. + {0} modified already + Please restart Flow before making any further changes + + Invalid zip installer file + Please check if there is a plugin.json in {0} Plugins Manager - Management of installing, uninstalling or updating Flow Launcher plugins + Install, uninstall or update Flow Launcher plugins via the search window Unknown Author @@ -61,5 +66,5 @@ Install from unknown source warning - Automatically restart Flow Launcher after installing/uninstalling/updating plugins + Restart Flow Launcher automatically after installing/uninstalling/updating plugin via Plugins Manager diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/pt-pt.xaml b/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/pt-pt.xaml index 01535c689f1..40cfc82538d 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/pt-pt.xaml +++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/pt-pt.xaml @@ -43,10 +43,15 @@ Plugin {0} atualizado com sucesso. Por favor, reinicie o Flow Launcher. {0} plugins atualizados com sucesso. Deve reiniciar Flow Launcher. O plugin {0} foi modificado. Por favor, reinicie o Flow Launcher antes de fazer mais alterações. + {0} já modificado + Reinicie Flow Launcher antes de fazer mais alterações + + Ficheiro Zip inválido + Verifique se existe o ficheiro "plugin.json" em {0} Gestor de plugins - Módulo para instalar, desinstalar e atualizar os plugins do Flow Launcher + Instalar, desinstalar ou atualizar plugins do Flow Launcher através da janela de pesquisa Autor desconhecido @@ -61,5 +66,5 @@ Aviso ao instalar de fontes desconhecidas - Reiniciar automaticamente após instalar/desinstalar/atualizar plugins + Reiniciar Flow Launcher após instalar/desinstalar/atualizar um plugin via Gestor de plugins diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/ru.xaml b/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/ru.xaml index 5b0a379b59a..18913c7c65a 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/ru.xaml +++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/ru.xaml @@ -43,10 +43,15 @@ Plugin {0} successfully updated. Please restart Flow. {0} plugins successfully updated. Please restart Flow. Plugin {0} has already been modified. Please restart Flow before making any further changes. + {0} modified already + Please restart Flow before making any further changes + + Invalid zip installer file + Please check if there is a plugin.json in {0} Plugins Manager - Management of installing, uninstalling or updating Flow Launcher plugins + Install, uninstall or update Flow Launcher plugins via the search window Автор неизвестен @@ -61,5 +66,5 @@ Install from unknown source warning - Automatically restart Flow Launcher after installing/uninstalling/updating plugins + Restart Flow Launcher automatically after installing/uninstalling/updating plugin via Plugins Manager diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/sk.xaml b/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/sk.xaml index 5529b2fc1a7..f788c9ce3c7 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/sk.xaml +++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/sk.xaml @@ -43,10 +43,15 @@ Plugin {0} bol úspešne aktualizovaný. Prosím, reštartuje Flow. Pluginy úspešne aktualizované ({0}). Reštartuje Flow. Plugin {0} už bol upravený. Prosím, reštartuje Flow pred ďalšími zmenami. + Plugin {0} už bol upravený + Pred vykonaním ďalších zmien reštartujte Flow Launcher + + Neplatný inštalačný súbor zip + Skontrolujte, či sa v {0} nachádza plugin.json Správca pluginov - Správa inštalácie, odinštalácie alebo aktualizácie pluginov programu Flow Launcher + Inštalovať, odinštalovať alebo aktualizovať pluginy Flow Launchera cez vyhľadávacie okno Neznámy autor @@ -61,5 +66,5 @@ Upozornenie na inštaláciu z neznámeho zdroja - Automaticky reštartovať Flow Launcher po inštalácií/odinštalácii/aktualizáciu pluginov + Automaticky reštartovať Flow Launcher po inštalácii/odinštalácii/aktualizáciu pluginu cez Správcu pluginov diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/sr.xaml b/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/sr.xaml index 616ce779b33..a5d0231ceae 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/sr.xaml +++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/sr.xaml @@ -43,10 +43,15 @@ Plugin {0} successfully updated. Please restart Flow. {0} plugins successfully updated. Please restart Flow. Plugin {0} has already been modified. Please restart Flow before making any further changes. + {0} modified already + Please restart Flow before making any further changes + + Invalid zip installer file + Please check if there is a plugin.json in {0} Plugins Manager - Management of installing, uninstalling or updating Flow Launcher plugins + Install, uninstall or update Flow Launcher plugins via the search window Unknown Author @@ -61,5 +66,5 @@ Install from unknown source warning - Automatically restart Flow Launcher after installing/uninstalling/updating plugins + Restart Flow Launcher automatically after installing/uninstalling/updating plugin via Plugins Manager diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/tr.xaml b/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/tr.xaml index 14f2e130982..ed9aaf4b32c 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/tr.xaml +++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/tr.xaml @@ -43,10 +43,15 @@ Plugin {0} successfully updated. Please restart Flow. {0} plugins successfully updated. Please restart Flow. Plugin {0} has already been modified. Please restart Flow before making any further changes. + {0} modified already + Please restart Flow before making any further changes + + Invalid zip installer file + Please check if there is a plugin.json in {0} Plugins Manager - Management of installing, uninstalling or updating Flow Launcher plugins + Install, uninstall or update Flow Launcher plugins via the search window Bilinmeyen Yazar @@ -61,5 +66,5 @@ Install from unknown source warning - Automatically restart Flow Launcher after installing/uninstalling/updating plugins + Restart Flow Launcher automatically after installing/uninstalling/updating plugin via Plugins Manager diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/uk-UA.xaml b/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/uk-UA.xaml index 3d2b50a78c1..e07f417c77d 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/uk-UA.xaml +++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/uk-UA.xaml @@ -13,8 +13,8 @@ Встановлення плагіна Завантажити та встановити {0} Видалення плагіна - Keep plugin settings - Do you want to keep the settings of the plugin for the next usage? + Зберегти налаштування плагіну + Хочете зберегти налаштування плагіну для наступного використання? Plugin successfully installed. Restarting Flow, please wait... Не вдалося знайти файл метаданих plugin.json у розпакованому zip-архіві. Помилка: Плагін, який має ідентичну або новішу версію з {0}, вже існує. @@ -43,10 +43,15 @@ Плагін {0} успішно оновлено. Будь ласка, перезапустіть Flow. {0} плагіни успішно оновлено. Будь ласка, перезапустіть Flow. Плагін {0} вже було змінено. Будь ласка, перезапустіть Flow, перш ніж вносити будь-які подальші зміни. + {0} вже змінено + Перезапустіть Flow перед тим, як вносити будь-які подальші зміни. + + Неправильний встановлюваний zip-файл + Перевірте, чи є файл plugin.json у {0}. Менеджер плагінів - Керування встановленням, видаленням або оновленням плагінів Flow Launcher + Встановити, видалити або оновити плагіни Flow Launcher через вікно пошуку. Невідомий автор @@ -61,5 +66,5 @@ Попередження про встановлення з невідомого джерела - Автоматичний перезапуск Flow Launcher після встановлення/видалення/оновлення плагінів + Автоматично перезапускати Flow Launcher після встановлення / видалення / оновлення плагіну за допомогою Менеджера плагінів diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/vi.xaml b/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/vi.xaml index 3f9315d6015..1a2a5c93a12 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/vi.xaml +++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/vi.xaml @@ -43,10 +43,15 @@ Plugin {0} successfully updated. Please restart Flow. {0} plugins successfully updated. Please restart Flow. Plugin {0} has already been modified. Please restart Flow before making any further changes. + {0} modified already + Please restart Flow before making any further changes + + Invalid zip installer file + Please check if there is a plugin.json in {0} Trình quản lý plugin - Quản lý cài đặt, gỡ cài đặt hoặc cập nhật plugin Flow Launcher + Install, uninstall or update Flow Launcher plugins via the search window Không rõ tác giả @@ -61,5 +66,5 @@ Cảnh báo cài đặt từ nguồn không xác định - Automatically restart Flow Launcher after installing/uninstalling/updating plugins + Restart Flow Launcher automatically after installing/uninstalling/updating plugin via Plugins Manager diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/zh-cn.xaml b/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/zh-cn.xaml index 1a4199965a9..446609850df 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/zh-cn.xaml +++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/zh-cn.xaml @@ -43,10 +43,15 @@ 成功更新插件{0}。请重新启动 Flow Launcher。 插件 {0} 更新成功。请重新启动 Flow Launcher。 插件 {0} 已被修改。请在进行任何进一步更改之前重新启动Flow。 + {0} 已被修改 + 请在进行任何进一步更改之前重新启动 Flow + + 无效的 zip 安装程序文件 + 请检查 {0} 中是否有plugin.json 插件管理 - 安装,卸载或更新 Flow Launcher 插件 + 通过搜索窗口安装、卸载或更新 Flow Launcher 插件 未知作者 @@ -61,5 +66,5 @@ 未知源安装警告 - 安装/卸载/更新插件后自动重启 Flow Launcher + 通过插件管理器安装/卸载/更新插件后自动重启 Flow Launcher diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/zh-tw.xaml b/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/zh-tw.xaml index f16feb05017..ddd24d0edac 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/zh-tw.xaml +++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/zh-tw.xaml @@ -43,10 +43,15 @@ Plugin {0} successfully updated. Please restart Flow. {0} plugins successfully updated. Please restart Flow. Plugin {0} has already been modified. Please restart Flow before making any further changes. + {0} modified already + Please restart Flow before making any further changes + + Invalid zip installer file + Please check if there is a plugin.json in {0} 擴充功能管理 - Management of installing, uninstalling or updating Flow Launcher plugins + Install, uninstall or update Flow Launcher plugins via the search window 未知的作者 @@ -61,5 +66,5 @@ Install from unknown source warning - Automatically restart Flow Launcher after installing/uninstalling/updating plugins + Restart Flow Launcher automatically after installing/uninstalling/updating plugin via Plugins Manager diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs b/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs index 25182f6d3d2..efbe8d7ba7d 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs +++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs @@ -114,6 +114,14 @@ internal async Task InstallOrUpdateAsync(UserPlugin plugin) return; } + if (Context.API.PluginModified(plugin.ID)) + { + Context.API.ShowMsgError( + string.Format(Context.API.GetTranslation("plugin_pluginsmanager_plugin_modified_error_title"), plugin.Name), + Context.API.GetTranslation("plugin_pluginsmanager_plugin_modified_error_message")); + return; + } + string message; if (Settings.AutoRestartAfterChanging) { @@ -158,7 +166,8 @@ await DownloadFileAsync( if (cts.IsCancellationRequested) return; else - Install(plugin, filePath); + if (!Install(plugin, filePath)) + return; } catch (HttpRequestException e) { @@ -196,7 +205,7 @@ await DownloadFileAsync( } } - private async Task DownloadFileAsync(string prgBoxTitle, string downloadUrl, string filePath, CancellationTokenSource cts, bool deleteFile = true, bool showProgress = true) + private async Task DownloadFileAsync(string progressBoxTitle, string downloadUrl, string filePath, CancellationTokenSource cts, bool deleteFile = true, bool showProgress = true) { if (deleteFile && File.Exists(filePath)) File.Delete(filePath); @@ -204,12 +213,12 @@ private async Task DownloadFileAsync(string prgBoxTitle, string downloadUrl, str if (showProgress) { var exceptionHappened = false; - await Context.API.ShowProgressBoxAsync(prgBoxTitle, + await Context.API.ShowProgressBoxAsync(progressBoxTitle, async (reportProgress) => { if (reportProgress == null) { - // when reportProgress is null, it means there is expcetion with the progress box + // when reportProgress is null, it means there is exception with the progress box // so we record it with exceptionHappened and return so that progress box will close instantly exceptionHappened = true; return; @@ -242,6 +251,18 @@ internal async ValueTask> RequestUpdateAsync(string search, Cancell if (FilesFolders.IsZipFilePath(search, checkFileExists: true)) { pluginFromLocalPath = Utilities.GetPluginInfoFromZip(search); + + if (pluginFromLocalPath == null) return new List + { + new() + { + Title = Context.API.GetTranslation("plugin_pluginsmanager_invalid_zip_title"), + SubTitle = string.Format(Context.API.GetTranslation("plugin_pluginsmanager_invalid_zip_subtitle"), + search), + IcoPath = icoPath + } + }; + pluginFromLocalPath.LocalInstallPath = search; updateFromLocalPath = true; } @@ -261,6 +282,7 @@ where string.Compare(existingPlugin.Metadata.Version, pluginUpdateSource.Version select new { + existingPlugin.Metadata.ID, pluginUpdateSource.Name, pluginUpdateSource.Author, CurrentVersion = existingPlugin.Metadata.Version, @@ -290,6 +312,14 @@ where string.Compare(existingPlugin.Metadata.Version, pluginUpdateSource.Version IcoPath = x.IcoPath, Action = e => { + if (Context.API.PluginModified(x.ID)) + { + Context.API.ShowMsgError( + string.Format(Context.API.GetTranslation("plugin_pluginsmanager_plugin_modified_error_title"), x.Name), + Context.API.GetTranslation("plugin_pluginsmanager_plugin_modified_error_message")); + return false; + } + string message; if (Settings.AutoRestartAfterChanging) { @@ -340,8 +370,11 @@ await DownloadFileAsync( } else { - await Context.API.UpdatePluginAsync(x.PluginExistingMetadata, x.PluginNewUserPlugin, - downloadToFilePath); + if (!await Context.API.UpdatePluginAsync(x.PluginExistingMetadata, x.PluginNewUserPlugin, + downloadToFilePath)) + { + return; + } if (Settings.AutoRestartAfterChanging) { @@ -406,6 +439,14 @@ await Context.API.UpdatePluginAsync(x.PluginExistingMetadata, x.PluginNewUserPlu IcoPath = icoPath, AsyncAction = async e => { + if (resultsForUpdate.All(x => Context.API.PluginModified(x.ID))) + { + Context.API.ShowMsgError(Context.API.GetTranslation("plugin_pluginsmanager_install_error_title"), + string.Format(Context.API.GetTranslation("plugin_pluginsmanager_plugin_modified_error"), + string.Join(" ", resultsForUpdate.Select(x => x.Name)))); + return false; + } + string message; if (Settings.AutoRestartAfterChanging) { @@ -427,6 +468,7 @@ await Context.API.UpdatePluginAsync(x.PluginExistingMetadata, x.PluginNewUserPlu return false; } + var anyPluginSuccess = false; await Task.WhenAll(resultsForUpdate.Select(async plugin => { var downloadToFilePath = Path.Combine(Path.GetTempPath(), @@ -444,8 +486,11 @@ await DownloadFileAsync( if (cts.IsCancellationRequested) return; else - await Context.API.UpdatePluginAsync(plugin.PluginExistingMetadata, plugin.PluginNewUserPlugin, - downloadToFilePath); + if (!await Context.API.UpdatePluginAsync(plugin.PluginExistingMetadata, plugin.PluginNewUserPlugin, + downloadToFilePath)) + return; + + anyPluginSuccess = true; } catch (Exception ex) { @@ -458,6 +503,8 @@ await Context.API.UpdatePluginAsync(plugin.PluginExistingMetadata, plugin.Plugin } })); + if (!anyPluginSuccess) return false; + if (Settings.AutoRestartAfterChanging) { Context.API.ShowMsg(Context.API.GetTranslation("plugin_pluginsmanager_update_title"), @@ -559,6 +606,20 @@ internal List InstallFromLocalPath(string localPath) { var plugin = Utilities.GetPluginInfoFromZip(localPath); + if (plugin == null) + { + return new List + { + new() + { + Title = Context.API.GetTranslation("plugin_pluginsmanager_invalid_zip_title"), + SubTitle = string.Format(Context.API.GetTranslation("plugin_pluginsmanager_invalid_zip_subtitle"), + localPath), + IcoPath = icoPath + } + }; + } + plugin.LocalInstallPath = localPath; return new List @@ -600,14 +661,17 @@ private bool InstallSourceKnown(string url) return false; var author = pieces[3]; + var acceptedHost = "github.com"; var acceptedSource = "https://github.com"; var constructedUrlPart = string.Format("{0}/{1}/", acceptedSource, author); - return url.StartsWith(acceptedSource) && - Context.API.GetAllPlugins().Any(x => - !string.IsNullOrEmpty(x.Metadata.Website) && - x.Metadata.Website.StartsWith(constructedUrlPart) - ); + if (!Uri.TryCreate(url, UriKind.Absolute, out var uri) || uri.Host != acceptedHost) + return false; + + return Context.API.GetAllPlugins().Any(x => + !string.IsNullOrEmpty(x.Metadata.Website) && + x.Metadata.Website.StartsWith(constructedUrlPart) + ); } internal async ValueTask> RequestInstallOrUpdateAsync(string search, CancellationToken token, @@ -649,7 +713,7 @@ internal async ValueTask> RequestInstallOrUpdateAsync(string search return Search(results, search); } - private void Install(UserPlugin plugin, string downloadedFilePath) + private bool Install(UserPlugin plugin, string downloadedFilePath) { if (!File.Exists(downloadedFilePath)) throw new FileNotFoundException($"Plugin {plugin.ID} zip file not found at {downloadedFilePath}", @@ -657,10 +721,13 @@ private void Install(UserPlugin plugin, string downloadedFilePath) try { - Context.API.InstallPlugin(plugin, downloadedFilePath); + if (!Context.API.InstallPlugin(plugin, downloadedFilePath)) + return false; if (!plugin.IsFromLocalInstallPath) File.Delete(downloadedFilePath); + + return true; } catch (FileNotFoundException e) { @@ -682,6 +749,8 @@ private void Install(UserPlugin plugin, string downloadedFilePath) plugin.Name)); Context.API.LogException(ClassName, e.Message, e); } + + return false; } internal List RequestUninstall(string search) @@ -696,6 +765,14 @@ internal List RequestUninstall(string search) IcoPath = x.Metadata.IcoPath, AsyncAction = async e => { + if (Context.API.PluginModified(x.Metadata.ID)) + { + Context.API.ShowMsgError( + string.Format(Context.API.GetTranslation("plugin_pluginsmanager_plugin_modified_error_title"), x.Metadata.Name), + Context.API.GetTranslation("plugin_pluginsmanager_plugin_modified_error_message")); + return false; + } + string message; if (Settings.AutoRestartAfterChanging) { @@ -717,7 +794,10 @@ internal List RequestUninstall(string search) MessageBoxButton.YesNo) == MessageBoxResult.Yes) { Context.API.HideMainWindow(); - await UninstallAsync(x.Metadata); + if (!await UninstallAsync(x.Metadata)) + { + return false; + } if (Settings.AutoRestartAfterChanging) { Context.API.RestartApp(); @@ -742,7 +822,7 @@ internal List RequestUninstall(string search) return Search(results, search); } - private async Task UninstallAsync(PluginMetadata plugin) + private async Task UninstallAsync(PluginMetadata plugin) { try { @@ -750,13 +830,14 @@ private async Task UninstallAsync(PluginMetadata plugin) Context.API.GetTranslation("plugin_pluginsmanager_keep_plugin_settings_subtitle"), Context.API.GetTranslation("plugin_pluginsmanager_keep_plugin_settings_title"), button: MessageBoxButton.YesNo) == MessageBoxResult.No; - await Context.API.UninstallPluginAsync(plugin, removePluginSettings); + return await Context.API.UninstallPluginAsync(plugin, removePluginSettings); } catch (ArgumentException e) { Context.API.LogException(ClassName, e.Message, e); Context.API.ShowMsgError(Context.API.GetTranslation("plugin_pluginsmanager_uninstall_error_title"), - Context.API.GetTranslation("plugin_pluginsmanager_plugin_modified_error")); + string.Format(Context.API.GetTranslation("plugin_pluginsmanager_plugin_modified_error"), plugin.Name)); + return false; } } } diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/Utilities.cs b/Plugins/Flow.Launcher.Plugin.PluginsManager/Utilities.cs index 4bb78f6ff8d..d76ce40c41e 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginsManager/Utilities.cs +++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/Utilities.cs @@ -65,9 +65,7 @@ internal static UserPlugin GetPluginInfoFromZip(string filePath) using (ZipArchive archive = System.IO.Compression.ZipFile.OpenRead(filePath)) { - var pluginJsonPath = archive.Entries.FirstOrDefault(x => x.Name == "plugin.json").ToString(); - ZipArchiveEntry pluginJsonEntry = archive.GetEntry(pluginJsonPath); - + var pluginJsonEntry = archive.Entries.FirstOrDefault(x => x.Name == "plugin.json"); if (pluginJsonEntry != null) { using Stream stream = pluginJsonEntry.Open(); diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/plugin.json b/Plugins/Flow.Launcher.Plugin.PluginsManager/plugin.json index 327011ac31d..949e9e9db8b 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginsManager/plugin.json +++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/plugin.json @@ -4,7 +4,7 @@ "pm" ], "Name": "Plugins Manager", - "Description": "Management of installing, uninstalling or updating Flow Launcher plugins", + "Description": "Install, uninstall or update Flow Launcher plugins via the search window", "Author": "Jeremy Wu", "Version": "1.0.0", "Language": "csharp", diff --git a/Plugins/Flow.Launcher.Plugin.ProcessKiller/Languages/pl.xaml b/Plugins/Flow.Launcher.Plugin.ProcessKiller/Languages/pl.xaml index 7e59db5ec06..1d7ff227b51 100644 --- a/Plugins/Flow.Launcher.Plugin.ProcessKiller/Languages/pl.xaml +++ b/Plugins/Flow.Launcher.Plugin.ProcessKiller/Languages/pl.xaml @@ -8,7 +8,7 @@ zamknij {0} procesów zamknij wszystkie instancje - Show title for processes with visible windows - Put processes with visible windows on the top + Pokaż tytuł dla procesów z widocznymi oknami + Umieść procesy z widocznymi oknami na górze diff --git a/Plugins/Flow.Launcher.Plugin.ProcessKiller/Languages/uk-UA.xaml b/Plugins/Flow.Launcher.Plugin.ProcessKiller/Languages/uk-UA.xaml index 56004028bb7..6d2086abc45 100644 --- a/Plugins/Flow.Launcher.Plugin.ProcessKiller/Languages/uk-UA.xaml +++ b/Plugins/Flow.Launcher.Plugin.ProcessKiller/Languages/uk-UA.xaml @@ -8,7 +8,7 @@ вбити {0} процесів вбити всі екземпляри - Show title for processes with visible windows - Put processes with visible windows on the top + Показувати назву процесів із видимими вікнами + Помістити процеси з видимими вікнами у верхній частині diff --git a/Plugins/Flow.Launcher.Plugin.Program/Languages/de.xaml b/Plugins/Flow.Launcher.Plugin.Program/Languages/de.xaml index 7919ae7cb60..cf716a15344 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Languages/de.xaml +++ b/Plugins/Flow.Launcher.Plugin.Program/Languages/de.xaml @@ -46,8 +46,8 @@ Bitte wählen Sie eine Programmquelle aus Sind Sie sicher, dass Sie die ausgewählten Programmquellen löschen wollen? - Please select program sources that are not added by you - Please select program sources that are added by you + Bitte wählen Sie die Programmquellen aus, die nicht von Ihnen hinzugefügt werden + Bitte wählen Sie die Programmquellen aus, die von Ihnen hinzugefügt werden Eine andere Programmquelle mit dem gleichen Ort ist bereits vorhanden. Programmquelle @@ -76,7 +76,7 @@ Als anderer Benutzer ausführen Als Administrator ausführen Enthaltenden Ordner öffnen - Hide + Ausblenden Zielordner öffnen Programm diff --git a/Plugins/Flow.Launcher.Plugin.Program/Languages/ja.xaml b/Plugins/Flow.Launcher.Plugin.Program/Languages/ja.xaml index 4a1d815ec68..0134627c5ec 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Languages/ja.xaml +++ b/Plugins/Flow.Launcher.Plugin.Program/Languages/ja.xaml @@ -6,14 +6,14 @@ 削除 編集 追加 - Name + 名前 有効 Enabled 無効 Status Enabled Disabled - Location + 場所 All Programs File Type Reindex diff --git a/Plugins/Flow.Launcher.Plugin.Program/Languages/uk-UA.xaml b/Plugins/Flow.Launcher.Plugin.Program/Languages/uk-UA.xaml index 290954d5fef..29158b2cefb 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Languages/uk-UA.xaml +++ b/Plugins/Flow.Launcher.Plugin.Program/Languages/uk-UA.xaml @@ -34,8 +34,8 @@ Приховує програми з поширеними назвами деінсталяторів, наприклад, unins000.exe Пошук в описі програми Flow буде шукати опис програми - Hide duplicated apps - Hide duplicated Win32 programs that are already in the UWP list + Приховати дублікати застосунків + Приховати дублікати програми Win32, які вже є в списку UWP Суфікси Максимальна глибина @@ -46,8 +46,8 @@ Будь ласка, виберіть джерело програми Ви впевнені, що хочете видалити вибрані джерела програм? - Please select program sources that are not added by you - Please select program sources that are added by you + Виберіть джерела програм, які не були додані вами. + Виберіть джерела програм, які були додані вами. Інше програмне джерело з тим самим розташуванням вже існує. Вихідний код програми @@ -76,7 +76,7 @@ Запустити від імені іншого користувача Запустити від імені адміністратора Відкрити папку - Hide + Приховати Відкрити цільову папку Програма @@ -86,7 +86,7 @@ Кастомізований провідник Аргументи - You can customize the explorer used for opening the container folder by inputing the Environmental Variable of the explorer you want to use. It will be useful to use CMD to test whether the Environmental Variable is available. + Ви можете налаштувати провідник, який використовується для відкриття теки контейнера, ввівши змінну середовища провідника, який ви хочете використовувати. Буде корисно використовувати CMD, аби перевірити, чи доступна змінна середовища. Введіть спеціальні аргументи, які ви хочете додати до вашого провідника. %s для батьківського каталогу, %f для повного шляху (працює лише для win32). Докладнішу інформацію можна знайти на веб-сайті провідника. diff --git a/Plugins/Flow.Launcher.Plugin.Program/Programs/UWPPackage.cs b/Plugins/Flow.Launcher.Plugin.Program/Programs/UWPPackage.cs index cb33250e15e..c2f4574a94a 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Programs/UWPPackage.cs +++ b/Plugins/Flow.Launcher.Plugin.Program/Programs/UWPPackage.cs @@ -290,12 +290,13 @@ private static IEnumerable CurrentUserPackages() } private static readonly Channel PackageChangeChannel = Channel.CreateBounded(1); + private static PackageCatalog? catalog; public static async Task WatchPackageChangeAsync() { if (Environment.OSVersion.Version.Major >= 10) { - var catalog = PackageCatalog.OpenForCurrentUser(); + catalog ??= PackageCatalog.OpenForCurrentUser(); catalog.PackageInstalling += (_, args) => { if (args.IsComplete) diff --git a/Plugins/Flow.Launcher.Plugin.Shell/Languages/uk-UA.xaml b/Plugins/Flow.Launcher.Plugin.Shell/Languages/uk-UA.xaml index d209cb739ca..d4747478445 100644 --- a/Plugins/Flow.Launcher.Plugin.Shell/Languages/uk-UA.xaml +++ b/Plugins/Flow.Launcher.Plugin.Shell/Languages/uk-UA.xaml @@ -6,7 +6,7 @@ Натисніть будь-яку клавішу, щоб закрити це вікно... Не закривати командний рядок після виконання команди Завжди запускати від імені адміністратора - Use Windows Terminal + Використовувати Термінал Windows Запустити від імені іншого користувача Shell Дозволяє виконувати системні команди з Flow Launcher diff --git a/Plugins/Flow.Launcher.Plugin.Shell/Main.cs b/Plugins/Flow.Launcher.Plugin.Shell/Main.cs index d0add9f3155..a51aadec7df 100644 --- a/Plugins/Flow.Launcher.Plugin.Shell/Main.cs +++ b/Plugins/Flow.Launcher.Plugin.Shell/Main.cs @@ -194,10 +194,13 @@ private ProcessStartInfo PrepareProcessStartInfo(string command, bool runAsAdmin var workingDirectory = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile); var runAsAdministratorArg = !runAsAdministrator && !_settings.RunAsAdministrator ? "" : "runas"; - ProcessStartInfo info = new() + var info = new ProcessStartInfo() { - Verb = runAsAdministratorArg, WorkingDirectory = workingDirectory, + Verb = runAsAdministratorArg, + WorkingDirectory = workingDirectory, }; + var notifyStr = Context.API.GetTranslation("flowlauncher_plugin_cmd_press_any_key_to_close"); + var addedCharacter = _settings.UseWindowsTerminal ? "\\" : ""; switch (_settings.Shell) { case Shell.Cmd: @@ -211,8 +214,19 @@ private ProcessStartInfo PrepareProcessStartInfo(string command, bool runAsAdmin { info.FileName = "cmd.exe"; } - - info.ArgumentList.Add($"{(_settings.LeaveShellOpen ? "/k" : "/c")} {command} {(_settings.CloseShellAfterPress ? $"&& echo {Context.API.GetTranslation("flowlauncher_plugin_cmd_press_any_key_to_close")} && pause > nul /c" : "")}"); + if (_settings.LeaveShellOpen) + { + info.ArgumentList.Add("/k"); + } + else + { + info.ArgumentList.Add("/c"); + } + info.ArgumentList.Add( + $"{command}" + + $"{(_settings.CloseShellAfterPress ? + $" && echo {notifyStr} && pause > nul /c" : + "")}"); break; } @@ -220,7 +234,6 @@ private ProcessStartInfo PrepareProcessStartInfo(string command, bool runAsAdmin { // Using just a ; doesn't work with wt, as it's used to create a new tab for the terminal window // \\ must be escaped for it to work properly, or breaking it into multiple arguments - var addedCharacter = _settings.UseWindowsTerminal ? "\\" : ""; if (_settings.UseWindowsTerminal) { info.FileName = "wt.exe"; @@ -238,7 +251,11 @@ private ProcessStartInfo PrepareProcessStartInfo(string command, bool runAsAdmin else { info.ArgumentList.Add("-Command"); - info.ArgumentList.Add($"{command}{addedCharacter}; {(_settings.CloseShellAfterPress ? $"Write-Host '{Context.API.GetTranslation("flowlauncher_plugin_cmd_press_any_key_to_close")}'{addedCharacter}; [System.Console]::ReadKey(){addedCharacter}; exit" : "")}"); + info.ArgumentList.Add( + $"{command}{addedCharacter};" + + $"{(_settings.CloseShellAfterPress ? + $" Write-Host '{notifyStr}'{addedCharacter}; [System.Console]::ReadKey(){addedCharacter}; exit" : + "")}"); } break; } @@ -247,7 +264,6 @@ private ProcessStartInfo PrepareProcessStartInfo(string command, bool runAsAdmin { // Using just a ; doesn't work with wt, as it's used to create a new tab for the terminal window // \\ must be escaped for it to work properly, or breaking it into multiple arguments - var addedCharacter = _settings.UseWindowsTerminal ? "\\" : ""; if (_settings.UseWindowsTerminal) { info.FileName = "wt.exe"; @@ -262,7 +278,11 @@ private ProcessStartInfo PrepareProcessStartInfo(string command, bool runAsAdmin info.ArgumentList.Add("-NoExit"); } info.ArgumentList.Add("-Command"); - info.ArgumentList.Add($"{command}{addedCharacter}; {(_settings.CloseShellAfterPress ? $"Write-Host '{Context.API.GetTranslation("flowlauncher_plugin_cmd_press_any_key_to_close")}'{addedCharacter}; [System.Console]::ReadKey(){addedCharacter}; exit" : "")}"); + info.ArgumentList.Add( + $"{command}{addedCharacter};" + + $"{(_settings.CloseShellAfterPress ? + $" Write-Host '{notifyStr}'{addedCharacter}; [System.Console]::ReadKey(){addedCharacter}; exit" : + "")}"); break; } diff --git a/Plugins/Flow.Launcher.Plugin.Sys/Languages/ja.xaml b/Plugins/Flow.Launcher.Plugin.Sys/Languages/ja.xaml index 27fee87be24..7d131d94426 100644 --- a/Plugins/Flow.Launcher.Plugin.Sys/Languages/ja.xaml +++ b/Plugins/Flow.Launcher.Plugin.Sys/Languages/ja.xaml @@ -2,7 +2,7 @@ - Name + 名前 説明 コマンド diff --git a/Plugins/Flow.Launcher.Plugin.Sys/Languages/pl.xaml b/Plugins/Flow.Launcher.Plugin.Sys/Languages/pl.xaml index c09a447d29c..33cee56d81e 100644 --- a/Plugins/Flow.Launcher.Plugin.Sys/Languages/pl.xaml +++ b/Plugins/Flow.Launcher.Plugin.Sys/Languages/pl.xaml @@ -69,7 +69,7 @@ Zresetuj Potwierdź Anuluj - Please enter a non-empty command keyword + Proszę wprowadzić niepuste słowo kluczowe polecenia Komendy systemowe Wykonywanie komend systemowych, np. wyłącz, zablokuj komputer, otwórz ustawienia itp. diff --git a/Plugins/Flow.Launcher.Plugin.Sys/Languages/uk-UA.xaml b/Plugins/Flow.Launcher.Plugin.Sys/Languages/uk-UA.xaml index 19d69511b10..c82be249adc 100644 --- a/Plugins/Flow.Launcher.Plugin.Sys/Languages/uk-UA.xaml +++ b/Plugins/Flow.Launcher.Plugin.Sys/Languages/uk-UA.xaml @@ -26,7 +26,7 @@ Поради щодо Flow Launcher Тека UserData Flow Launcher Перемкнути режим гри - Set the Flow Launcher Theme + Встановити тему Flow Launcher Редагувати @@ -51,7 +51,7 @@ Перегляньте документацію Flow Launcher для отримання додаткової допомоги та підказок щодо використання порад Відкрити каталог, де зберігаються налаштування Flow Launcher Перемкнути режим гри - Quickly change the Flow Launcher theme + Швидко змінити тему Flow Launcher Успішно @@ -62,14 +62,14 @@ Ви впевнені, що хочете перезавантажити комп'ютер за допомогою додаткових параметрів завантаження? Ви впевнені, що хочете вийти з системи? - Command Keyword Setting - Custom Command Keyword - Enter a keyword to search for command: {0}. This keyword is used to match your query. - Command Keyword + Налаштування ключового слова команди + Власне ключове слово команди + Введіть ключове слово для пошуку команди: {0}. Це ключове слово використовується для відповідності вашому запиту. + Ключове слово команди Скинути Підтвердити Скасувати - Please enter a non-empty command keyword + Введіть непорожнє ключове слово команди Системні команди Надає команди, пов'язані з системою, наприклад, вимкнення, блокування, налаштування тощо. diff --git a/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/pl.xaml b/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/pl.xaml index d693a3f28d2..05ee397778f 100644 --- a/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/pl.xaml +++ b/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/pl.xaml @@ -17,7 +17,7 @@ Wyzwalacz Adres URL Szukaj - Use Search Query Autocomplete + Użyj autouzupełniania zapytań wyszukiwania Autouzupełnianie danych z: Musisz wybrać coś z listy Czy jesteś pewien że chcesz usunąć {0}? diff --git a/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/uk-UA.xaml b/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/uk-UA.xaml index 5536a7e680e..51e1efc6eba 100644 --- a/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/uk-UA.xaml +++ b/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/uk-UA.xaml @@ -17,7 +17,7 @@ Ключове слово дії URL Пошук - Use Search Query Autocomplete + Використовувати автозаповнення пошукового запиту Автозаповнення даних з: Будь ласка, виберіть пошуковий запит в Інтернеті Ви впевнені, що хочете видалити {0}? @@ -29,8 +29,8 @@ Таким чином, загальна формула для пошуку на Netflix має вигляд https://www.netflix.com/search?q={q} - Copy URL - Copy search URL to clipboard + Копіювати URL + Скопіювати URL-адресу пошуку в буфер обміну Назва diff --git a/Plugins/Flow.Launcher.Plugin.WindowsSettings/Properties/Resources.de-DE.resx b/Plugins/Flow.Launcher.Plugin.WindowsSettings/Properties/Resources.de-DE.resx index d9de28e4bb7..e963e34da2b 100644 --- a/Plugins/Flow.Launcher.Plugin.WindowsSettings/Properties/Resources.de-DE.resx +++ b/Plugins/Flow.Launcher.Plugin.WindowsSettings/Properties/Resources.de-DE.resx @@ -348,7 +348,7 @@ Area UpdateAndSecurity - Sichern und wiederherstellen + Sichern und Wiederherstellen Area Control Panel (legacy settings) @@ -1324,7 +1324,7 @@ Area Control Panel (legacy settings) - Remotedesktop + Remote-Desktop Area System @@ -1752,7 +1752,7 @@ Einen Dateityp immer in einem spezifischen Programm öffnen lassen - Stimme ändern + Ändern der Stimme des Erzählers Tastaturprobleme finden und beheben @@ -1761,7 +1761,7 @@ Screenreader verwenden - Arbeitsgruppe auf diesem Computer Anzeigen + Anzeigen, zu welcher Arbeitsgruppe dieser Computer gehört Mausrad-Einstellungen ändern @@ -1773,7 +1773,7 @@ Probleme finden und beheben - Einstellung für empfangene Inhalte von Tippen und Senden + Ändern der Einstellungen für Inhalte, die über Tippen und Senden empfangen werden Change default settings for media or devices @@ -1812,7 +1812,7 @@ Ein Bluetooth-Gerät hinzufügen - Customise the mouse buttons + Individuelles Anpassen der Maustasten Set tablet buttons to perform certain tasks @@ -1869,16 +1869,16 @@ Scanner und Kameras ansehen - Microsoft IME Register Word (Japanese) + Microsoft IME Register Word (Japanisch) - Restore your files with File History + Ihre Dateien mit File History wiederherstellen Turn On-Screen keyboard on or off - Block or allow third-party cookies + Cookies von Drittanbietern blockieren oder zulassen Audioaufzeichnungsprobleme finden und beheben @@ -1902,7 +1902,7 @@ Preview, delete, show or hide fonts - Microsoft Quick Settings + Microsoft-Schnelleinstellungen View reliability history @@ -1917,7 +1917,7 @@ Sicherheitsrichtlinien zurücksetzen - Pop-ups blockieren oder erlauben + Pop-ups blockieren oder zulassen Autovervollständigung im Internet Explorer ein- oder ausschalten @@ -1938,7 +1938,7 @@ Automatische Fensteranordnung ausschalten - Troubleshooting History + Fehlerbehebungshistorie Speicherprobleme Ihres Computers diagnostizieren @@ -1968,13 +1968,13 @@ Specify single- or double-click to open - Select users who can use remote desktop + Benutzer auswählen, die den Remote-Desktop verwenden können - Show which programs are installed on your computer + Anzeigen, welche Programme auf Ihrem Computer installiert sind - Allow remote access to your computer + Remote-Zugriff auf Ihren Computer erlauben Erweiterte Systemeinstellungen ansehen @@ -2082,7 +2082,7 @@ Ihren Wiederherstellungsschlüssel sichern - Save backup copies of your files with File History + Backup-Kopien Ihrer Dateien mit File History speichern View current accessibility settings @@ -2143,7 +2143,7 @@ Windows-Features ein- oder ausschalten - Betriebssystem, welches auf deinem Computer läuft, anzeigen + Anzeigen, welches Betriebssystem auf Ihrem Computer ausgeführt wird Lokale Dienste ansehen @@ -2176,7 +2176,7 @@ Change advanced colour management settings for displays, scanners and printers - Lasse Windows Vereinfachte Zugriffseinstellungen vorschlagen + Windows die Einstellungen für erleichterte Bedienung vorschlagen lassen Clear disk space by deleting unnecessary files @@ -2191,16 +2191,16 @@ Record steps to reproduce a problem - Aussehen und Leistung von Windows anpassen + Anpassen des Erscheinungsbildes und der Leistung von Windows Einstellungen für Microsoft IME (Japanisch) - Lade jemanden ein, sich mit deinem PC zu verbinden und dir zu helfen oder anderen zu helfen + Laden Sie jemanden ein, eine Verbindung zu Ihrem PC herzustellen und Ihnen zu helfen, oder bieten Sie an, jemand anderem zu helfen - Programme für frühere Versionen von Windows ausführen + Programme ausführen, die für frühere Versionen von Windows entwickelt wurden Choose the order of how your screen rotates @@ -2239,7 +2239,7 @@ Wählen Sie, wie Sie Links öffnen - Allow Remote Assistance invitations to be sent from this computer + Erlauben, dass Einladungen zur Remote-Unterstützung von diesem Computer aus gesendet werden Task-Manager @@ -2257,7 +2257,7 @@ Lupe ein- oder ausschalten - See the name of this computer + Den Namen dieses Computers ansehen Netzwerkverbindungen ansehen @@ -2302,13 +2302,13 @@ How to change the size of virtual memory - Hear text read aloud with Narrator + Text mit Erzähler vorlesen lassen Set up USB game controllers - Show which domain your computer is on + Anzeigen, in welcher Domäne sich Ihr Computer befindet Alle Problemberichte ansehen @@ -2401,7 +2401,7 @@ Create and format hard disk partitions - Change date, time or number formats + Datums-, Zeit- oder Zahlenformate ändern Change PC wake-up settings @@ -2452,13 +2452,13 @@ Change the way measurements are displayed - Press key combinations one at a time + Tastenkombinationen nacheinander drücken - Restore data, files or computer from backup (Windows 7) + Daten, Dateien oder Computer aus Backup wiederherstellen (Windows 7) - Set your default programs + Ihre per Default vorgegebenen Programme festlegen Eine Breitbandverbindung einrichten @@ -2473,10 +2473,10 @@ Geplante Tasks - Ignore repeated keystrokes using FilterKeys + Wiederholte Tastenanschläge unter Verwendung von FilterKeys ignorieren - Find and fix bluescreen problems + Probleme mit Bluescreens finden und beheben Einen Ton hören, wenn Tasten gedrückt werden @@ -2485,16 +2485,16 @@ Browsing-Historie löschen - Change what the power buttons do + Ändern, was die Power-Tasten bewirken - Create standard user account + Standard-Benutzerkonto erstellen Take speech tutorials - View system resource usage in Task Manager + Systemressourcennutzung im Task-Manager ansehen Einen Account erstellen diff --git a/Plugins/Flow.Launcher.Plugin.WindowsSettings/Properties/Resources.ja-JP.resx b/Plugins/Flow.Launcher.Plugin.WindowsSettings/Properties/Resources.ja-JP.resx index 91be8a39225..6625a42ddc2 100644 --- a/Plugins/Flow.Launcher.Plugin.WindowsSettings/Properties/Resources.ja-JP.resx +++ b/Plugins/Flow.Launcher.Plugin.WindowsSettings/Properties/Resources.ja-JP.resx @@ -837,7 +837,7 @@ ライト モード - Location + 場所 Area Privacy diff --git a/appveyor.yml b/appveyor.yml index b38e5bb1a88..39e2a114c7c 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,4 +1,7 @@ -version: '1.20.1.{build}' +version: '1.20.2.{build}' + +# Do not build on tags because we create a release on merge to master. Otherwise will upload artifacts twice changing the hash, as well as triggering duplicate GitHub release action & NuGet deployments. +skip_tags: true init: - ps: | @@ -14,7 +17,6 @@ init: cache: - '%USERPROFILE%\.nuget\packages -> **.sln, **.csproj' # preserve nuget folder (packages) unless the solution or projects change - assembly_info: patch: true file: SolutionAssemblyInfo.cs @@ -67,7 +69,17 @@ deploy: - provider: GitHub repository: Flow-Launcher/Prereleases release: v$(prereleaseTag) - description: 'This is the early access build of our upcoming release. All changes contained here are reviewed, tested and stable to use.\n\nSee our [release](https://github.com/Flow-Launcher/Flow.Launcher/pulls?q=is%3Aopen+is%3Apr+label%3Arelease) Pull Request for details.\n\nFor latest production release visit [here](https://github.com/Flow-Launcher/Flow.Launcher/releases/latest)\n\nPlease report any bugs or issues over at the [main repository](https://github.com/Flow-Launcher/Flow.Launcher/issues)' + description: | + This is the early access build of our upcoming release. + All changes contained here are reviewed, tested and stable to use. + + This build includes new changes from commit: + $(APPVEYOR_REPO_COMMIT_MESSAGE) + + See all changes in this early access by going to the [milstones](https://github.com/Flow-Launcher/Flow.Launcher/milestones?sort=title&direction=asc) section and choosing the upcoming milestone. + For latest production release visit [here](https://github.com/Flow-Launcher/Flow.Launcher/releases/latest) + + Please report any bugs or issues over at the [main repository](https://github.com/Flow-Launcher/Flow.Launcher/issues)' auth_token: secure: ij4UeXUYQBDJxn2YRAAhUOjklOGVKDB87Hn5J8tKIzj13yatoI7sLM666QDQFEgv artifact: Squirrel Installer, Portable Version, Squirrel nupkg, Squirrel RELEASES