With the recent release of Dynamics 365 v9, you can now upload RESX files that contain localized strings.  These files are used by the Xrm.Utility.getResourceString() function to provide localized strings on forms through custom scripts.

How can you leverage the resource files on a custom web resource?  You might be thinking that you can use window.opener.Xrm or parent.Xrm to reference the Xrm namespace.  And you would be right...in most situations.  However, what if you have a custom HTML web resource that the user bookmarks and comes back to after a period of time.  In that case, the page won't have an opener or a parent.  So what can we do?

One way to solve this is to use a custom action with a plugin.  We can setup an action which behaves exactly like the getResourceString function - it takes in a parameter of the webResourceName, and returns all of the localized strings.

First, we will create the Action.  (Yes, I know I created this in the Default Solution...ideally you would create it in your own solution so it doesn't have the "new" prefix.)

Localized_Process.png

Next, we will setup some models which we can use to deserialize the RESX files.  We cannot use the standard System.Resources.ResXResourceReader since it requires full trust, so we will deserialize into these classes instead.

using System.Collections.Generic;
using System.Xml.Serialization;

namespace BGuidinger.Samples
{
    [XmlRoot("root")]
    public class Root
    {
        [XmlElement("data")]
        public List Data { get; set; }
    }
    public class Data
    {
        [XmlAttribute("name")]
        public string Name { get; set; }
        [XmlElement("value")]
        public string Value { get; set; }
    }
}

Next, we create our plugin.  I am using my base plugin class, so there are some details hidden here, but this should give you an idea of what's necessary.

namespace BGuidinger.Samples
{
    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.Linq;
    using System.Runtime.Serialization.Json;
    using System.Text;
    using System.Xml.Serialization;

    public class GetResourceStrings : Plugin
    {
        public override void OnExecute(IPluginProvider context)
        {
            var resource = context.OrganizationService.GetWebResource("new_Resources", "1033");

            var content = resource.GetAttributeValue("content");
            var contentBytes = Convert.FromBase64String(content);

            Dictionary<string, string> strings = null;

            using (var stream = new MemoryStream(contentBytes))
            {
                var serializer = new XmlSerializer(typeof(Root));
                var root = serializer.Deserialize(stream) as Root;

                strings = root.Data.ToDictionary(x => x.Name, x => x.Value);
            }

            using (var stream = new MemoryStream())
            {
                var settings = new DataContractJsonSerializerSettings
                {
                    UseSimpleDictionaryFormat = true
                };
                var serializer = new DataContractJsonSerializer(typeof(Dictionary<string, string>), settings);
                serializer.WriteObject(stream, strings);

                context.ExecutionContext.OutputParameters["resourceStrings"] = Encoding.UTF8.GetString(stream.ToArray());
            }
        }
    }
}

From here, all we have to do is register the step using the Plugin Registration Tool.  The step must be registerd PostOperation.

Localized_RegisterStep.png

Now we can call the action via the API and it will return our localized strings!

Comments