diff --git a/Microsoft.Toolkit.Uwp.SampleApp/Assets/Strings/en-US/Resources.resw b/Microsoft.Toolkit.Uwp.SampleApp/Assets/Strings/en-US/Resources.resw new file mode 100644 index 00000000000..7c909c61815 --- /dev/null +++ b/Microsoft.Toolkit.Uwp.SampleApp/Assets/Strings/en-US/Resources.resw @@ -0,0 +1,126 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Hello! + + + Resource String Markup Extension + + \ No newline at end of file diff --git a/Microsoft.Toolkit.Uwp.SampleApp/Assets/Strings/es-ES/Resources.resw b/Microsoft.Toolkit.Uwp.SampleApp/Assets/Strings/es-ES/Resources.resw new file mode 100644 index 00000000000..531bd774c7e --- /dev/null +++ b/Microsoft.Toolkit.Uwp.SampleApp/Assets/Strings/es-ES/Resources.resw @@ -0,0 +1,126 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Hola! + + + Resource String Markup Extension + + \ No newline at end of file diff --git a/Microsoft.Toolkit.Uwp.SampleApp/Microsoft.Toolkit.Uwp.SampleApp.csproj b/Microsoft.Toolkit.Uwp.SampleApp/Microsoft.Toolkit.Uwp.SampleApp.csproj index 0a9bf08f523..bab9877f54b 100644 --- a/Microsoft.Toolkit.Uwp.SampleApp/Microsoft.Toolkit.Uwp.SampleApp.csproj +++ b/Microsoft.Toolkit.Uwp.SampleApp/Microsoft.Toolkit.Uwp.SampleApp.csproj @@ -276,6 +276,7 @@ + @@ -389,6 +390,8 @@ PreserveNewest + + @@ -512,6 +515,9 @@ MetadataControlPage.xaml + + ResourceStringPage.xaml + AttachedDropShadowPage.xaml @@ -644,6 +650,7 @@ + @@ -999,6 +1006,10 @@ Designer + + MSBuild:Compile + Designer + MSBuild:Compile Designer diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/ResourceString/ResourceString.png b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/ResourceString/ResourceString.png new file mode 100644 index 00000000000..ceb19f77980 Binary files /dev/null and b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/ResourceString/ResourceString.png differ diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/ResourceString/ResourceStringPage.xaml b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/ResourceString/ResourceStringPage.xaml new file mode 100644 index 00000000000..e4dd6f8eebd --- /dev/null +++ b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/ResourceString/ResourceStringPage.xaml @@ -0,0 +1,8 @@ + + + \ No newline at end of file diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/ResourceString/ResourceStringPage.xaml.cs b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/ResourceString/ResourceStringPage.xaml.cs new file mode 100644 index 00000000000..d31dc3b797c --- /dev/null +++ b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/ResourceString/ResourceStringPage.xaml.cs @@ -0,0 +1,26 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Windows.UI.Xaml; + +namespace Microsoft.Toolkit.Uwp.SampleApp.SamplePages +{ + /// + /// A page that shows how to use the offset behavior. + /// + public sealed partial class ResourceStringPage : IXamlRenderListener + { + /// + /// Initializes a new instance of the class. + /// + public ResourceStringPage() + { + InitializeComponent(); + } + + public void OnXamlRendered(FrameworkElement control) + { + } + } +} \ No newline at end of file diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/ResourceString/ResourceStringXaml.bind b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/ResourceString/ResourceStringXaml.bind new file mode 100644 index 00000000000..100516a97fa --- /dev/null +++ b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/ResourceString/ResourceStringXaml.bind @@ -0,0 +1,67 @@ + + + + + + + + + Get a greating from the English resource context: + + + + Or get a greeting from the Spanish resource context: + + + + + You can alternatively get a greeting using x:Bind for optimized compiled bind: + + + {x:Bind ui:ResourceStringExtension.GetValue('HelloText')} + + + {x:Bind ui:ResourceStringExtension.GetValue('HelloText', 'es-ES')} + + + + + See our blog post on #ifdef Windows: + Use a Custom Resource Markup Extension to Succeed at UI String Globalization! + + + + \ No newline at end of file diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/XamlOnlyPage.xaml b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/XamlOnlyPage.xaml index a3a001645f7..78d31dc5f06 100644 --- a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/XamlOnlyPage.xaml +++ b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/XamlOnlyPage.xaml @@ -39,6 +39,8 @@ + diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/samples.json b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/samples.json index 8cdfcc3a619..ef72da0f55f 100644 --- a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/samples.json +++ b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/samples.json @@ -1266,11 +1266,20 @@ "Name": "OnDevice", "Type": "OnDevicePage", "About": "The OnDevice markup extension allows you to customize UI appearance on a per-DeviceFamily basis.", - "CodeUrl" : "https://github.com/CommunityToolkit/WindowsCommunityToolkit/blob/main/Microsoft.Toolkit.Uwp.UI/Extensions/Markup/OnDeviceExtension.cs", + "CodeUrl": "https://github.com/CommunityToolkit/WindowsCommunityToolkit/blob/main/Microsoft.Toolkit.Uwp.UI/Extensions/Markup/OnDeviceExtension.cs", "Icon": "/SamplePages/OnDevice/OnDevice.png", "XamlCodeFile": "OnDeviceXaml.bind", "DocumentationUrl": "https://raw.githubusercontent.com/MicrosoftDocs/WindowsCommunityToolkitDocs/master/docs/extensions/OnDeviceMarkup.md" }, + { + "Name": "ResourceString", + "Type": "ResourceStringPage", + "About": "The ResourceString markup extension allows you to easily access resource strings through bindings instead of Uid.", + "CodeUrl": "https://github.com/CommunityToolkit/WindowsCommunityToolkit/blob/main/Microsoft.Toolkit.Uwp.UI/Extensions/Markup/ResourceStringExtension.cs", + "Icon": "/SamplePages/ResourceString/ResourceString.png", + "XamlCodeFile": "ResourceStringXaml.bind", + "DocumentationUrl": "https://raw.githubusercontent.com/MicrosoftDocs/WindowsCommunityToolkitDocs/master/docs/extensions/ResourceStringMarkup.md" + }, { "Name": "IconExtensions", "Type": "IconExtensionsPage", @@ -1284,7 +1293,7 @@ "Name": "EnumValuesExtension", "Type": "EnumValuesExtensionPage", "About": "The EnumValuesExtension markup extension allows you to easily retrieve a collection of all values from an enum type.", - "CodeUrl" : "https://github.com/CommunityToolkit/WindowsCommunityToolkit/blob/main/Microsoft.Toolkit.Uwp.UI/Extensions/Markup/EnumValuesExtension.cs", + "CodeUrl": "https://github.com/CommunityToolkit/WindowsCommunityToolkit/blob/main/Microsoft.Toolkit.Uwp.UI/Extensions/Markup/EnumValuesExtension.cs", "Icon": "/Assets/Helpers.png", "XamlCodeFile": "EnumValuesExtensionXaml.bind", "CodeFile": "EnumValuesExtensionCode.bind", diff --git a/Microsoft.Toolkit.Uwp.UI/Extensions/Markup/ResourceStringExtension.cs b/Microsoft.Toolkit.Uwp.UI/Extensions/Markup/ResourceStringExtension.cs new file mode 100644 index 00000000000..be877b32952 --- /dev/null +++ b/Microsoft.Toolkit.Uwp.UI/Extensions/Markup/ResourceStringExtension.cs @@ -0,0 +1,70 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Windows.ApplicationModel.Resources; +using Windows.ApplicationModel.Resources.Core; +using Windows.UI.Xaml.Markup; + +namespace Microsoft.Toolkit.Uwp.UI +{ + /// + /// Xaml extension to return a value from resource file associated with a resource key + /// + [MarkupExtensionReturnType(ReturnType = typeof(string))] + public sealed class ResourceStringExtension : MarkupExtension + { + private static readonly ResourceLoader resourceLoader = ResourceLoader.GetForViewIndependentUse(); + + /// + /// Gets or sets associated ID from resource strings. + /// + public string Name { get; set; } + + /// + /// Gets or sets the associated language resource to use (ie: "es-ES"). + /// Defaults to the OS language of current view. + /// + public string Language { get; set; } + + /// + protected override object ProvideValue() + { + return GetValue(this.Name, this.Language); + } + + /// + /// Gets a string value from resource file associated with a resource key. + /// + /// Resource key name. + /// A string value from resource file associated with a resource key. + public static string GetValue(string name) + { + // This function is needed to accomodate compiled function usage without second paramater, + // which doesn't work with optional values. + return resourceLoader.GetString(name); + } + + /// + /// Gets a string value from resource file associated with a resource key. + /// + /// Resource key name. + /// Optional language of the associated resource to use (ie: "es-ES"). + /// default is the OS language of current view. + /// A string value from resource file associated with a resource key. + public static string GetValue(string name, string language = "") + { + if (string.IsNullOrEmpty(language)) + { + return GetValue(name); + } + + // Not using ResourceContext.GetForCurrentView nor GetForViewIndependentUse + ResourceContext resourceContext = new() { Languages = new[] { language } }; + var resourceMap = ResourceManager.Current.MainResourceMap.GetSubtree("Resources"); + + return resourceMap.GetValue(name, resourceContext).ValueAsString; + } + + } +} diff --git a/Windows Community Toolkit.sln b/Windows Community Toolkit.sln index c09b6446a9a..21f6aa5ef03 100644 --- a/Windows Community Toolkit.sln +++ b/Windows Community Toolkit.sln @@ -205,6 +205,8 @@ Global EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {719C43C6-8753-4395-ADAA-2FCC70F76BF3}.Debug|Any CPU.ActiveCfg = Debug|x86 + {719C43C6-8753-4395-ADAA-2FCC70F76BF3}.Debug|Any CPU.Build.0 = Debug|x86 + {719C43C6-8753-4395-ADAA-2FCC70F76BF3}.Debug|Any CPU.Deploy.0 = Debug|x86 {719C43C6-8753-4395-ADAA-2FCC70F76BF3}.Debug|ARM.ActiveCfg = Debug|ARM {719C43C6-8753-4395-ADAA-2FCC70F76BF3}.Debug|ARM.Build.0 = Debug|ARM {719C43C6-8753-4395-ADAA-2FCC70F76BF3}.Debug|ARM.Deploy.0 = Debug|ARM @@ -387,6 +389,7 @@ Global {BAB1CAF4-C400-4A7F-A987-C576DE63CFFD}.Release|x86.Build.0 = Release|x86 {BAB1CAF4-C400-4A7F-A987-C576DE63CFFD}.Release|x86.Deploy.0 = Release|x86 {1AE2CB5C-58A0-4F12-8E6F-2CD4AAADB34C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1AE2CB5C-58A0-4F12-8E6F-2CD4AAADB34C}.Debug|Any CPU.Build.0 = Debug|Any CPU {1AE2CB5C-58A0-4F12-8E6F-2CD4AAADB34C}.Debug|ARM.ActiveCfg = Debug|ARM {1AE2CB5C-58A0-4F12-8E6F-2CD4AAADB34C}.Debug|ARM.Build.0 = Debug|ARM {1AE2CB5C-58A0-4F12-8E6F-2CD4AAADB34C}.Debug|ARM64.ActiveCfg = Debug|ARM64