CmdPal: Clipboard history - localize metadata strings (#45506)

## Summary of the Pull Request

This PR enables the localization of strings in metadata providers
(section titles and keys) and other unlocalized strings in Clipboard
History built-in extension.

## PR Checklist

- [x] Closes: #42185
<!-- - [ ] Closes: #yyy (add separate lines for additional resolved
issues) -->
- [ ] **Communication:** I've discussed this with core contributors
already. If the work hasn't been agreed, this work might be rejected
- [ ] **Tests:** Added/updated and all pass
- [ ] **Localization:** All end-user-facing strings can be localized
- [ ] **Dev docs:** Added/updated
- [ ] **New binaries:** Added on the required places
- [ ] [JSON for
signing](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ESRPSigning_core.json)
for new binaries
- [ ] [WXS for
installer](https://github.com/microsoft/PowerToys/blob/main/installer/PowerToysSetup/Product.wxs)
for new binaries and localization folder
- [ ] [YML for CI
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ci/templates/build-powertoys-steps.yml)
for new test projects
- [ ] [YML for signed
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/release.yml)
- [ ] **Documentation updated:** If checked, please file a pull request
on [our docs
repo](https://github.com/MicrosoftDocs/windows-uwp/tree/docs/hub/powertoys)
and link it here: #xxx

<!-- Provide a more detailed description of the PR, other things fixed,
or any additional comments/features here -->
## Detailed Description of the Pull Request / Additional comments

<!-- Describe how you validated the behavior. Add automated tests
wherever possible, but list manual validation steps taken as well -->
## Validation Steps Performed
This commit is contained in:
Jiří Polášek
2026-02-12 16:34:48 +01:00
committed by GitHub
parent f88a4908ac
commit aca0b9c747
7 changed files with 387 additions and 35 deletions

View File

@@ -6,13 +6,14 @@ using System;
using System.Collections.Generic;
using ManagedCommon;
using Microsoft.CmdPal.Ext.ClipboardHistory.Models;
using Microsoft.CmdPal.Ext.ClipboardHistory.Properties;
using Microsoft.CommandPalette.Extensions.Toolkit;
namespace Microsoft.CmdPal.Ext.ClipboardHistory.Helpers.Analyzers;
internal sealed class ImageMetadataProvider : IClipboardMetadataProvider
{
public string SectionTitle => "Image metadata";
public string SectionTitle => Resources.metadata_image_section_title;
public bool CanHandle(ClipboardItem item) => item.IsImage;
@@ -30,12 +31,12 @@ internal sealed class ImageMetadataProvider : IClipboardMetadataProvider
result.Add(new DetailsElement
{
Key = "Dimensions",
Key = Resources.metadata_image_dimensions_key,
Data = new DetailsLink($"{metadata.Width} x {metadata.Height}"),
});
result.Add(new DetailsElement
{
Key = "DPI",
Key = Resources.metadata_image_dpi_key,
Data = new DetailsLink($"{metadata.DpiX:0.###} x {metadata.DpiY:0.###}"),
});
@@ -43,7 +44,7 @@ internal sealed class ImageMetadataProvider : IClipboardMetadataProvider
{
result.Add(new DetailsElement
{
Key = "Storage size",
Key = Resources.metadata_image_storage_size_key,
Data = new DetailsLink(SizeFormatter.FormatSize(metadata.StorageSize.Value)),
});
}

View File

@@ -9,6 +9,7 @@ using System.IO;
using ManagedCommon;
using Microsoft.CmdPal.Core.Common.Helpers;
using Microsoft.CmdPal.Ext.ClipboardHistory.Models;
using Microsoft.CmdPal.Ext.ClipboardHistory.Properties;
using Microsoft.CommandPalette.Extensions.Toolkit;
namespace Microsoft.CmdPal.Ext.ClipboardHistory.Helpers.Analyzers;
@@ -18,7 +19,7 @@ namespace Microsoft.CmdPal.Ext.ClipboardHistory.Helpers.Analyzers;
/// </summary>
internal sealed class TextFileSystemMetadataProvider : IClipboardMetadataProvider
{
public string SectionTitle => "File";
public string SectionTitle => Resources.metadata_file_system_section_title;
public bool CanHandle(ClipboardItem item)
{
@@ -47,8 +48,8 @@ internal sealed class TextFileSystemMetadataProvider : IClipboardMetadataProvide
if (PathHelper.IsSlow(path) || !PathHelper.Exists(path, out var isDirectory))
{
result.Add(new DetailsElement { Key = "Name", Data = new DetailsLink(Path.GetFileName(path)) });
result.Add(new DetailsElement { Key = "Location", Data = new DetailsLink(UrlHelper.NormalizeUrl(path), path) });
result.Add(new DetailsElement { Key = Resources.metadata_file_system_name_key, Data = new DetailsLink(Path.GetFileName(path)) });
result.Add(new DetailsElement { Key = Resources.metadata_file_system_location_key, Data = new DetailsLink(UrlHelper.NormalizeUrl(path), path) });
return result;
}
@@ -57,21 +58,21 @@ internal sealed class TextFileSystemMetadataProvider : IClipboardMetadataProvide
if (!isDirectory)
{
var fi = new FileInfo(path);
result.Add(new DetailsElement { Key = "Name", Data = new DetailsLink(fi.Name) });
result.Add(new DetailsElement { Key = "Location", Data = new DetailsLink(UrlHelper.NormalizeUrl(fi.FullName), fi.FullName) });
result.Add(new DetailsElement { Key = "Type", Data = new DetailsLink(fi.Extension) });
result.Add(new DetailsElement { Key = "Size", Data = new DetailsLink(SizeFormatter.FormatSize(fi.Length)) });
result.Add(new DetailsElement { Key = "Modified", Data = new DetailsLink(fi.LastWriteTime.ToString(CultureInfo.CurrentCulture)) });
result.Add(new DetailsElement { Key = "Created", Data = new DetailsLink(fi.CreationTime.ToString(CultureInfo.CurrentCulture)) });
result.Add(new DetailsElement { Key = Resources.metadata_file_system_name_key, Data = new DetailsLink(fi.Name) });
result.Add(new DetailsElement { Key = Resources.metadata_file_system_location_key, Data = new DetailsLink(UrlHelper.NormalizeUrl(fi.FullName), fi.FullName) });
result.Add(new DetailsElement { Key = Resources.metadata_file_system_type_key, Data = new DetailsLink(fi.Extension) });
result.Add(new DetailsElement { Key = Resources.metadata_file_system_size_key, Data = new DetailsLink(SizeFormatter.FormatSize(fi.Length)) });
result.Add(new DetailsElement { Key = Resources.metadata_file_system_modified_key, Data = new DetailsLink(fi.LastWriteTime.ToString(CultureInfo.CurrentCulture)) });
result.Add(new DetailsElement { Key = Resources.metadata_file_system_created_key, Data = new DetailsLink(fi.CreationTime.ToString(CultureInfo.CurrentCulture)) });
}
else
{
var di = new DirectoryInfo(path);
result.Add(new DetailsElement { Key = "Name", Data = new DetailsLink(di.Name) });
result.Add(new DetailsElement { Key = "Location", Data = new DetailsLink(UrlHelper.NormalizeUrl(di.FullName), di.FullName) });
result.Add(new DetailsElement { Key = "Type", Data = new DetailsLink("Folder") });
result.Add(new DetailsElement { Key = "Modified", Data = new DetailsLink(di.LastWriteTime.ToString(CultureInfo.CurrentCulture)) });
result.Add(new DetailsElement { Key = "Created", Data = new DetailsLink(di.CreationTime.ToString(CultureInfo.CurrentCulture)) });
result.Add(new DetailsElement { Key = Resources.metadata_file_system_name_key, Data = new DetailsLink(di.Name) });
result.Add(new DetailsElement { Key = Resources.metadata_file_system_location_key, Data = new DetailsLink(UrlHelper.NormalizeUrl(di.FullName), di.FullName) });
result.Add(new DetailsElement { Key = Resources.metadata_file_system_type_key, Data = new DetailsLink(Resources.metadata_file_system_folder_value) });
result.Add(new DetailsElement { Key = Resources.metadata_file_system_modified_key, Data = new DetailsLink(di.LastWriteTime.ToString(CultureInfo.CurrentCulture)) });
result.Add(new DetailsElement { Key = Resources.metadata_file_system_created_key, Data = new DetailsLink(di.CreationTime.ToString(CultureInfo.CurrentCulture)) });
}
}
catch (Exception ex)

View File

@@ -5,13 +5,14 @@
using System.Collections.Generic;
using System.Globalization;
using Microsoft.CmdPal.Ext.ClipboardHistory.Models;
using Microsoft.CmdPal.Ext.ClipboardHistory.Properties;
using Microsoft.CommandPalette.Extensions.Toolkit;
namespace Microsoft.CmdPal.Ext.ClipboardHistory.Helpers.Analyzers;
internal sealed class TextMetadataProvider : IClipboardMetadataProvider
{
public string SectionTitle => "Text statistics";
public string SectionTitle => Resources.metadata_text_section_title;
public bool CanHandle(ClipboardItem item) => item.IsText;
@@ -27,32 +28,32 @@ internal sealed class TextMetadataProvider : IClipboardMetadataProvider
result.Add(new DetailsElement
{
Key = "Characters",
Key = Resources.metadata_text_characters_key,
Data = new DetailsLink(r.CharacterCount.ToString(CultureInfo.CurrentCulture)),
});
result.Add(new DetailsElement
{
Key = "Words",
Key = Resources.metadata_text_words_key,
Data = new DetailsLink(r.WordCount.ToString(CultureInfo.CurrentCulture)),
});
result.Add(new DetailsElement
{
Key = "Sentences",
Key = Resources.metadata_text_sentences_key,
Data = new DetailsLink(r.SentenceCount.ToString(CultureInfo.CurrentCulture)),
});
result.Add(new DetailsElement
{
Key = "Lines",
Key = Resources.metadata_text_lines_key,
Data = new DetailsLink(r.LineCount.ToString(CultureInfo.CurrentCulture)),
});
result.Add(new DetailsElement
{
Key = "Paragraphs",
Key = Resources.metadata_text_paragraphs_key,
Data = new DetailsLink(r.ParagraphCount.ToString(CultureInfo.CurrentCulture)),
});
result.Add(new DetailsElement
{
Key = "Line Ending",
Key = Resources.metadata_text_line_ending_key,
Data = new DetailsLink(r.LineEnding.ToString()),
});

View File

@@ -7,6 +7,7 @@ using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using Microsoft.CmdPal.Ext.ClipboardHistory.Models;
using Microsoft.CmdPal.Ext.ClipboardHistory.Properties;
using Microsoft.CommandPalette.Extensions.Toolkit;
namespace Microsoft.CmdPal.Ext.ClipboardHistory.Helpers.Analyzers;
@@ -16,7 +17,7 @@ namespace Microsoft.CmdPal.Ext.ClipboardHistory.Helpers.Analyzers;
/// </summary>
internal sealed class WebLinkMetadataProvider : IClipboardMetadataProvider
{
public string SectionTitle => "Link";
public string SectionTitle => Resources.metadata_web_link_section_title;
public bool CanHandle(ClipboardItem item)
{
@@ -62,29 +63,29 @@ internal sealed class WebLinkMetadataProvider : IClipboardMetadataProvider
return result;
}
result.Add(new DetailsElement { Key = "URL", Data = new DetailsLink(normalized) });
result.Add(new DetailsElement { Key = "Host", Data = new DetailsLink(uri.Host) });
result.Add(new DetailsElement { Key = Resources.metadata_web_link_url_key, Data = new DetailsLink(normalized) });
result.Add(new DetailsElement { Key = Resources.metadata_web_link_host_key, Data = new DetailsLink(uri.Host) });
if (!uri.IsDefaultPort)
{
result.Add(new DetailsElement { Key = "Port", Data = new DetailsLink(uri.Port.ToString(CultureInfo.CurrentCulture)) });
result.Add(new DetailsElement { Key = Resources.metadata_web_link_port_key, Data = new DetailsLink(uri.Port.ToString(CultureInfo.CurrentCulture)) });
}
if (!string.IsNullOrEmpty(uri.AbsolutePath) && uri.AbsolutePath != "/")
{
result.Add(new DetailsElement { Key = "Path", Data = new DetailsLink(uri.AbsolutePath) });
result.Add(new DetailsElement { Key = Resources.metadata_web_link_path_key, Data = new DetailsLink(uri.AbsolutePath) });
}
if (!string.IsNullOrEmpty(uri.Query))
{
var q = uri.Query;
var count = q.Count(static c => c == '&') + (q.Length > 1 ? 1 : 0);
result.Add(new DetailsElement { Key = "Query params", Data = new DetailsLink(count.ToString(CultureInfo.CurrentCulture)) });
result.Add(new DetailsElement { Key = Resources.metadata_web_link_query_params_key, Data = new DetailsLink(count.ToString(CultureInfo.CurrentCulture)) });
}
if (!string.IsNullOrEmpty(uri.Fragment))
{
result.Add(new DetailsElement { Key = "Fragment", Data = new DetailsLink(uri.Fragment) });
result.Add(new DetailsElement { Key = Resources.metadata_web_link_fragment_key, Data = new DetailsLink(uri.Fragment) });
}
}
catch

View File

@@ -68,7 +68,7 @@ internal sealed partial class ClipboardListItem : ListItem
if (item.IsImage)
{
Title = "Image";
Title = Properties.Resources.clipboard_item_image_title;
_pasteCommand = new CommandContextItem(new PasteCommand(_item, ClipboardFormat.Image, _settingsManager));
_copyCommand = new CommandContextItem(new CopyCommand(_item, ClipboardFormat.Image));
@@ -220,12 +220,12 @@ internal sealed partial class ClipboardListItem : ListItem
metadata.Add(new DetailsElement
{
Key = "General",
Key = Properties.Resources.metadata_general_section_title,
Data = new DetailsSeparator(),
});
metadata.Add(new DetailsElement
{
Key = "Copied",
Key = Properties.Resources.metadata_copied_key,
Data = new DetailsLink(_item.Timestamp.DateTime.ToString(DateTimeFormatInfo.CurrentInfo)),
});

View File

@@ -78,6 +78,15 @@ namespace Microsoft.CmdPal.Ext.ClipboardHistory.Properties {
}
}
/// <summary>
/// Looks up a localized string similar to Image.
/// </summary>
public static string clipboard_item_image_title {
get {
return ResourceManager.GetString("clipboard_item_image_title", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Copied to clipboard.
/// </summary>
@@ -150,6 +159,258 @@ namespace Microsoft.CmdPal.Ext.ClipboardHistory.Properties {
}
}
/// <summary>
/// Looks up a localized string similar to Copied.
/// </summary>
public static string metadata_copied_key {
get {
return ResourceManager.GetString("metadata_copied_key", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Created.
/// </summary>
public static string metadata_file_system_created_key {
get {
return ResourceManager.GetString("metadata_file_system_created_key", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Folder.
/// </summary>
public static string metadata_file_system_folder_value {
get {
return ResourceManager.GetString("metadata_file_system_folder_value", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Location.
/// </summary>
public static string metadata_file_system_location_key {
get {
return ResourceManager.GetString("metadata_file_system_location_key", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Modified.
/// </summary>
public static string metadata_file_system_modified_key {
get {
return ResourceManager.GetString("metadata_file_system_modified_key", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Name.
/// </summary>
public static string metadata_file_system_name_key {
get {
return ResourceManager.GetString("metadata_file_system_name_key", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to File.
/// </summary>
public static string metadata_file_system_section_title {
get {
return ResourceManager.GetString("metadata_file_system_section_title", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Size.
/// </summary>
public static string metadata_file_system_size_key {
get {
return ResourceManager.GetString("metadata_file_system_size_key", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Type.
/// </summary>
public static string metadata_file_system_type_key {
get {
return ResourceManager.GetString("metadata_file_system_type_key", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to General.
/// </summary>
public static string metadata_general_section_title {
get {
return ResourceManager.GetString("metadata_general_section_title", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Dimensions.
/// </summary>
public static string metadata_image_dimensions_key {
get {
return ResourceManager.GetString("metadata_image_dimensions_key", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to DPI.
/// </summary>
public static string metadata_image_dpi_key {
get {
return ResourceManager.GetString("metadata_image_dpi_key", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Image metadata.
/// </summary>
public static string metadata_image_section_title {
get {
return ResourceManager.GetString("metadata_image_section_title", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Storage size.
/// </summary>
public static string metadata_image_storage_size_key {
get {
return ResourceManager.GetString("metadata_image_storage_size_key", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Characters.
/// </summary>
public static string metadata_text_characters_key {
get {
return ResourceManager.GetString("metadata_text_characters_key", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Line ending.
/// </summary>
public static string metadata_text_line_ending_key {
get {
return ResourceManager.GetString("metadata_text_line_ending_key", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Lines.
/// </summary>
public static string metadata_text_lines_key {
get {
return ResourceManager.GetString("metadata_text_lines_key", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Paragraphs.
/// </summary>
public static string metadata_text_paragraphs_key {
get {
return ResourceManager.GetString("metadata_text_paragraphs_key", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Text statistics.
/// </summary>
public static string metadata_text_section_title {
get {
return ResourceManager.GetString("metadata_text_section_title", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Sentences.
/// </summary>
public static string metadata_text_sentences_key {
get {
return ResourceManager.GetString("metadata_text_sentences_key", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Words.
/// </summary>
public static string metadata_text_words_key {
get {
return ResourceManager.GetString("metadata_text_words_key", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Fragment.
/// </summary>
public static string metadata_web_link_fragment_key {
get {
return ResourceManager.GetString("metadata_web_link_fragment_key", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Host.
/// </summary>
public static string metadata_web_link_host_key {
get {
return ResourceManager.GetString("metadata_web_link_host_key", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Path.
/// </summary>
public static string metadata_web_link_path_key {
get {
return ResourceManager.GetString("metadata_web_link_path_key", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Port.
/// </summary>
public static string metadata_web_link_port_key {
get {
return ResourceManager.GetString("metadata_web_link_port_key", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Query params.
/// </summary>
public static string metadata_web_link_query_params_key {
get {
return ResourceManager.GetString("metadata_web_link_query_params_key", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Web link.
/// </summary>
public static string metadata_web_link_section_title {
get {
return ResourceManager.GetString("metadata_web_link_section_title", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to URL.
/// </summary>
public static string metadata_web_link_url_key {
get {
return ResourceManager.GetString("metadata_web_link_url_key", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Open URL.
/// </summary>

View File

@@ -186,4 +186,91 @@
<data name="open_url_command_name" xml:space="preserve">
<value>Open URL</value>
</data>
<data name="metadata_image_section_title" xml:space="preserve">
<value>Image metadata</value>
</data>
<data name="metadata_image_dimensions_key" xml:space="preserve">
<value>Dimensions</value>
</data>
<data name="metadata_image_dpi_key" xml:space="preserve">
<value>DPI</value>
</data>
<data name="metadata_image_storage_size_key" xml:space="preserve">
<value>Storage size</value>
</data>
<data name="metadata_text_section_title" xml:space="preserve">
<value>Text statistics</value>
</data>
<data name="metadata_text_characters_key" xml:space="preserve">
<value>Characters</value>
</data>
<data name="metadata_text_words_key" xml:space="preserve">
<value>Words</value>
</data>
<data name="metadata_text_sentences_key" xml:space="preserve">
<value>Sentences</value>
</data>
<data name="metadata_text_lines_key" xml:space="preserve">
<value>Lines</value>
</data>
<data name="metadata_text_paragraphs_key" xml:space="preserve">
<value>Paragraphs</value>
</data>
<data name="metadata_text_line_ending_key" xml:space="preserve">
<value>Line ending</value>
</data>
<data name="metadata_file_system_section_title" xml:space="preserve">
<value>File</value>
</data>
<data name="metadata_file_system_name_key" xml:space="preserve">
<value>Name</value>
</data>
<data name="metadata_file_system_location_key" xml:space="preserve">
<value>Location</value>
</data>
<data name="metadata_file_system_type_key" xml:space="preserve">
<value>Type</value>
</data>
<data name="metadata_file_system_size_key" xml:space="preserve">
<value>Size</value>
</data>
<data name="metadata_file_system_modified_key" xml:space="preserve">
<value>Modified</value>
</data>
<data name="metadata_file_system_created_key" xml:space="preserve">
<value>Created</value>
</data>
<data name="metadata_web_link_section_title" xml:space="preserve">
<value>Web link</value>
</data>
<data name="metadata_web_link_url_key" xml:space="preserve">
<value>URL</value>
</data>
<data name="metadata_web_link_host_key" xml:space="preserve">
<value>Host</value>
</data>
<data name="metadata_web_link_port_key" xml:space="preserve">
<value>Port</value>
</data>
<data name="metadata_web_link_path_key" xml:space="preserve">
<value>Path</value>
</data>
<data name="metadata_web_link_query_params_key" xml:space="preserve">
<value>Query params</value>
</data>
<data name="metadata_web_link_fragment_key" xml:space="preserve">
<value>Fragment</value>
</data>
<data name="clipboard_item_image_title" xml:space="preserve">
<value>Image</value>
</data>
<data name="metadata_general_section_title" xml:space="preserve">
<value>General</value>
</data>
<data name="metadata_copied_key" xml:space="preserve">
<value>Copied</value>
</data>
<data name="metadata_file_system_folder_value" xml:space="preserve">
<value>Folder</value>
</data>
</root>