Search code examples
c#windows-store-appstranslationt4globalization

Is there a way to avoid using hard-coded resw keys with the windows store app ResourceLoader?


Is there a way to avoid using hard-coded keys for the globalized strings contained in the resw file within a windows store app? i.e.

// I have to use a hard-coded string key, which may violate the
// DRY principle to gain access to the translation from C#:
var translation =
    Windows.ApplicationModel.Resources.ResourceLoader.GetString("MyStringKey/Text")

Solution

  • You can use the T4 template below to automatically generate a C# wrapper around your resw file. This means you have no need to litter your code with magic-string resource keys to access translations from C# - all your keys are then known to be valid at compile-time, rather than run-time. The template generates the following code:

    internal static class Translation
    {
        public static ResourceLoader ResourceLoader { get; private set; }
    
        static Translation()
        {
            ResourceLoader = new ResourceLoader();
        }
    
        public static string MyFirstString
        {
            get { return ResourceLoader.GetString("MyFirstString/Text"); }
        }
    }
    

    This is generated by the following T4 template:

    <#@ template debug="false" hostspecific="true" language="C#" #>
    <#@ assembly name="System.Core" #>
    <#@ assembly name="System.Xml" #>
    <#@ assembly name="System.Xml.Linq" #>
    <#@ assembly name="EnvDTE" #>
    <#@ import namespace="System.IO" #>
    <#@ import namespace="System.Linq" #>
    <#@ import namespace="System.Xml.Linq" #>
    <#@ import namespace="System.Collections.Generic" #>
    <#@ import namespace="Microsoft.CSharp" #>
    <#@ import namespace="EnvDTE" #>
    <#@ output extension=".cs" #>
    <#
        DTE env = GetVSEnvironment();
        var inputFilePath = @"en-GB\Resources.resw";
        var provider = new CSharpCodeProvider();
        string className = CreateClassName(provider);
    
        SetCurrentDirectory();
        if (File.Exists(inputFilePath)) {
    #>
    //------------------------------------------------------------------------------
    // <auto-generated>
    // This code was generated by a resource generator.
    // </auto-generated>
    //------------------------------------------------------------------------------
    using Windows.ApplicationModel.Resources;
    
    namespace <#= GetNamespace() #> {
        internal static class <#= className #> {
            public static ResourceLoader ResourceLoader { get; private set; }
    
            static <#= className #>() {
                ResourceLoader = new ResourceLoader();
            }
    <#
            foreach (string name in GetResourceKeys(inputFilePath)) {
    #>
            public static string <#= provider.CreateEscapedIdentifier(name.Replace(".Text", String.Empty).Replace(".Content", String.Empty).Replace(".FlowDirection", string.Empty)) #> {
                get { return ResourceLoader.GetString("<#= name.Replace(".Text", "/Text").Replace(".Content", "/Content").Replace(".FlowDirection", "/FlowDirection") #>"); }
            }
    <#
            }
    #>
        }
    }
    <#
        } else {
            throw new FileNotFoundException(String.Format("Unable to find Resource file: {0}", inputFilePath)); 
        } 
    #>
    <#+
        private DTE GetVSEnvironment() {
            DTE env = null;
            var provider = Host as IServiceProvider;
            if (provider != null) {
                env = provider.GetService(typeof(DTE)) as DTE;
            }
    
            if (env == null) {
                throw new InvalidOperationException("Template must be executed from Visual Studio");
            }
    
            return env;
        }
    
        private void SetCurrentDirectory() {
            Directory.SetCurrentDirectory(Host.ResolvePath(""));
        }
    
        private string CreateClassName(CSharpCodeProvider provider) {
            string name = Path.GetFileNameWithoutExtension(Host.TemplateFile);
            return provider.CreateEscapedIdentifier(name);
        }
    
        private string GetNamespace() {
            return Host.ResolveParameterValue("directiveId", "namespaceDirectiveProcessor", "namespaceHint");
        }
    
        private static IEnumerable<string> GetResourceKeys(string filePath) {
            XDocument doc = XDocument.Load(filePath);
            var rootElement = doc.Root.Elements().SingleOrDefault(x => x.Name == "root");
            if (rootElement == null)
            {
                throw new InvalidOperationException("No root element was found");
            }
            var dataElements = rootElement.Elements("data").ToList();
            var resourceKeys = dataElements.Select(e => e.Attribute("name").Value).ToList();
            return resourceKeys;
        }
    #>