From e11bafcc9376b725fe8b243fce0a2a996a4b55a3 Mon Sep 17 00:00:00 2001 From: Heiko <61519853+htcfreek@users.noreply.github.com> Date: Wed, 20 Jul 2022 16:11:33 +0200 Subject: [PATCH] [PT Run] [Program plugin] Add localized name (#19149) * create common localization class * add loc name to prog plugin * fixes * Tool tip fixes and comments * cleanup and highlight fix * change * Improvements * Add GetLocalizedPath() * smal code improvements --- .github/actions/spell-check/expect.txt | 2 + .../Programs/Win32Program.cs | 14 ++- .../Wox.Plugin/Common/ShellLocalization.cs | 93 +++++++++++++++++++ 3 files changed, 106 insertions(+), 3 deletions(-) create mode 100644 src/modules/launcher/Wox.Plugin/Common/ShellLocalization.cs diff --git a/.github/actions/spell-check/expect.txt b/.github/actions/spell-check/expect.txt index 9e2ad31695..432ce51cf9 100644 --- a/.github/actions/spell-check/expect.txt +++ b/.github/actions/spell-check/expect.txt @@ -460,6 +460,7 @@ DNLEN Dns doctype DONOTROUND +DONTRESOLVEDLLREFERENCES DONTVALIDATEPATH dotnet DPICHANGED @@ -1081,6 +1082,7 @@ lmcons LMEM LMENU lnk +LOADLIBRARYASDATAFILE LOADSTRING LOBYTE LOCALAPPDATA diff --git a/src/modules/launcher/Plugins/Microsoft.Plugin.Program/Programs/Win32Program.cs b/src/modules/launcher/Plugins/Microsoft.Plugin.Program/Programs/Win32Program.cs index 5c3d9d361f..8ae2480235 100644 --- a/src/modules/launcher/Plugins/Microsoft.Plugin.Program/Programs/Win32Program.cs +++ b/src/modules/launcher/Plugins/Microsoft.Plugin.Program/Programs/Win32Program.cs @@ -19,6 +19,7 @@ using Microsoft.Win32; using Wox.Infrastructure; using Wox.Infrastructure.FileSystemHelper; using Wox.Plugin; +using Wox.Plugin.Common; using Wox.Plugin.Logger; using DirectoryWrapper = Wox.Infrastructure.FileSystemHelper.DirectoryWrapper; @@ -46,6 +47,9 @@ namespace Microsoft.Plugin.Program.Programs public string LnkResolvedExecutableName { get; set; } + // Localized name based on windows display language + public string LocalizedName { get; set; } = string.Empty; + public string ParentDirectory { get; set; } public string ExecutableName { get; set; } @@ -97,10 +101,11 @@ namespace Microsoft.Plugin.Program.Programs private int Score(string query) { var nameMatch = StringMatcher.FuzzySearch(query, Name); + var locNameMatch = StringMatcher.FuzzySearch(query, LocalizedName); var descriptionMatch = StringMatcher.FuzzySearch(query, Description); var executableNameMatch = StringMatcher.FuzzySearch(query, ExecutableName); var lnkResolvedExecutableNameMatch = StringMatcher.FuzzySearch(query, LnkResolvedExecutableName); - var score = new[] { nameMatch.Score, descriptionMatch.Score / 2, executableNameMatch.Score, lnkResolvedExecutableNameMatch.Score }.Max(); + var score = new[] { nameMatch.Score, locNameMatch.Score, descriptionMatch.Score / 2, executableNameMatch.Score, lnkResolvedExecutableNameMatch.Score }.Max(); return score; } @@ -221,7 +226,7 @@ namespace Microsoft.Plugin.Program.Programs var result = new Result { // To set the title for the result to always be the name of the application - Title = Name, + Title = !string.IsNullOrEmpty(LocalizedName) ? LocalizedName : Name, SubTitle = GetSubtitle(), IcoPath = IcoPath, Score = score, @@ -237,7 +242,7 @@ namespace Microsoft.Plugin.Program.Programs }, }; - result.TitleHighlightData = StringMatcher.FuzzySearch(query, Name).MatchData; + result.TitleHighlightData = StringMatcher.FuzzySearch(query, result.Title).MatchData; // Using CurrentCulture since this is user facing var toolTipTitle = string.Format(CultureInfo.CurrentCulture, "{0}: {1}", Properties.Resources.powertoys_run_plugin_program_file_name, result.Title); @@ -370,6 +375,9 @@ namespace Microsoft.Plugin.Program.Programs ExecutableName = Path.GetFileName(path), IcoPath = path, + // Localized name based on windows display language + LocalizedName = ShellLocalization.GetLocalizedName(path), + // Using InvariantCulture since this is user facing FullPath = path.ToLowerInvariant(), UniqueIdentifier = path, diff --git a/src/modules/launcher/Wox.Plugin/Common/ShellLocalization.cs b/src/modules/launcher/Wox.Plugin/Common/ShellLocalization.cs new file mode 100644 index 0000000000..4ce9e0cb83 --- /dev/null +++ b/src/modules/launcher/Wox.Plugin/Common/ShellLocalization.cs @@ -0,0 +1,93 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. +using System; +using System.IO; +using System.Runtime.InteropServices; +using System.Text; + +namespace Wox.Plugin.Common +{ + /// + /// Class to get localized name of shell items like 'My computer'. The localization is based on the 'windows display language'. + /// Reused code from https://stackoverflow.com/questions/41423491/how-to-get-localized-name-of-known-folder for the method + /// + public static class ShellLocalization + { + internal const uint DONTRESOLVEDLLREFERENCES = 0x00000001; + internal const uint LOADLIBRARYASDATAFILE = 0x00000002; + + [DllImport("shell32.dll", CallingConvention = CallingConvention.Winapi, CharSet = CharSet.Unicode)] + internal static extern int SHGetLocalizedName(string pszPath, StringBuilder pszResModule, ref int cch, out int pidsRes); + + [DllImport("user32.dll", EntryPoint = "LoadStringW", CallingConvention = CallingConvention.Winapi, CharSet = CharSet.Unicode)] + internal static extern int LoadString(IntPtr hModule, int resourceID, StringBuilder resourceValue, int len); + + [DllImport("kernel32.dll", CharSet = CharSet.Unicode, ExactSpelling = true, EntryPoint = "LoadLibraryExW")] + internal static extern IntPtr LoadLibraryEx(string lpFileName, IntPtr hFile, uint dwFlags); + + [DllImport("kernel32.dll", ExactSpelling = true)] + internal static extern int FreeLibrary(IntPtr hModule); + + [DllImport("kernel32.dll", EntryPoint = "ExpandEnvironmentStringsW", CharSet = CharSet.Unicode, ExactSpelling = true)] + internal static extern uint ExpandEnvironmentStrings(string lpSrc, StringBuilder lpDst, int nSize); + + /// + /// Returns the localized name of a shell item. + /// + /// Path to the shell item (e. g. shortcut 'File Explorer.lnk'). + /// The localized name as string or . + public static string GetLocalizedName(string path) + { + StringBuilder resourcePath = new StringBuilder(1024); + StringBuilder localizedName = new StringBuilder(1024); + int len, id; + len = resourcePath.Capacity; + + // If there is no resource to localize a file name the method returns a non zero value. + if (SHGetLocalizedName(path, resourcePath, ref len, out id) == 0) + { + _ = ExpandEnvironmentStrings(resourcePath.ToString(), resourcePath, resourcePath.Capacity); + IntPtr hMod = LoadLibraryEx(resourcePath.ToString(), IntPtr.Zero, DONTRESOLVEDLLREFERENCES | LOADLIBRARYASDATAFILE); + if (hMod != IntPtr.Zero) + { + if (LoadString(hMod, id, localizedName, localizedName.Capacity) != 0) + { + string lString = localizedName.ToString(); + _ = FreeLibrary(hMod); + return lString; + } + + _ = FreeLibrary(hMod); + } + } + + return string.Empty; + } + + /// + /// This method returns the localized path to a shell item (folder or file) + /// + /// The path to localize + /// The localized path or the original path if localized version is not available + public static string GetLocalizedPath(string path) + { + path = Environment.ExpandEnvironmentVariables(path); + string ext = Path.GetExtension(path); + var pathParts = path.Split("\\"); + string[] locPath = new string[pathParts.Length]; + + for (int i = 0; i < pathParts.Length; i++) + { + int iElements = i + 1; + string lName = GetLocalizedName(string.Join("\\", pathParts[..iElements])); + locPath[i] = !string.IsNullOrEmpty(lName) ? lName : pathParts[i]; + } + + string newPath = string.Join("\\", locPath); + newPath = !newPath.EndsWith(ext, StringComparison.InvariantCultureIgnoreCase) ? newPath + ext : newPath; + + return newPath; + } + } +}