// 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.Collections.Concurrent;
using System.IO;
using Windows.Win32;
using Windows.Win32.UI.Shell;
namespace Microsoft.CmdPal.Ext.Apps.Utils;
///
/// Class to get localized name of shell items like 'My computer'. The localization is based on the 'windows display language'.
///
public class ShellLocalization
{
internal static readonly ShellLocalization Instance = new();
// Cache for already localized names. This makes localization of already localized string faster.
private ConcurrentDictionary _localizationCache = new ConcurrentDictionary();
///
/// 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 unsafe string GetLocalizedName(string path)
{
var lowerInvariantPath = path.ToLowerInvariant();
// Checking cache if path is already localized
if (_localizationCache.TryGetValue(lowerInvariantPath, out var value))
{
return value;
}
void* shellItemPtrVoid = null;
try
{
var retCode = PInvoke.SHCreateItemFromParsingName(path, null, typeof(IShellItem).GUID, out shellItemPtrVoid).ThrowOnFailure();
using var shellItemHandle = new SafeComHandle((IntPtr)shellItemPtrVoid);
IShellItem* shellItemPtr = (IShellItem*)shellItemPtrVoid;
var hr = shellItemPtr->GetDisplayName(SIGDN.SIGDN_NORMALDISPLAY, out var filenamePtr);
var filename = ComFreeHelper.GetStringAndFree(hr, filenamePtr);
if (filename == null)
{
return string.Empty;
}
_ = _localizationCache.TryAdd(lowerInvariantPath, filename);
return filename;
}
catch (Exception)
{
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 string GetLocalizedPath(string path)
{
path = Environment.ExpandEnvironmentVariables(path);
var ext = Path.GetExtension(path);
var pathParts = path.Split("\\");
var locPath = new string[pathParts.Length];
for (var i = 0; i < pathParts.Length; i++)
{
if (i == 0 && pathParts[i].EndsWith(':'))
{
// Skip the drive letter.
locPath[0] = pathParts[0];
continue;
}
// Localize path.
var iElements = i + 1;
var lName = GetLocalizedName(string.Join("\\", pathParts[..iElements]));
locPath[i] = !string.IsNullOrEmpty(lName) ? lName : pathParts[i];
}
var newPath = string.Join("\\", locPath);
newPath = !newPath.EndsWith(ext, StringComparison.InvariantCultureIgnoreCase) ? newPath + ext : newPath;
return newPath;
}
}