Rico Suter's blog.
 


In WinRT/C# projects the .resx resource file is not available anymore. The new resource type is .resw which is directly supported by the XAML framework. The drawback of this new file format is that it does not automatically generate a code-behind C# class with a property for each key-value resource pair. The problem is that it is not possible to access these strings in a strongly typed manner. The only way to load resource strings is to load them by specifying the resource key as a string. The problem with this solution is that typing errors are not detected by the compiler but on application runtime.

A solution to this problem is to generate the code-behind file manually. To automate this process I wrote a .tt template file for Visual Studio which generates the C# class for an existing .resw file. The only problem of this solution is that the generation has to be triggered manually; the class is not generated automatically when the resource file changes.

To use this solution you have to create a new .tt file and copy the following code. Now simply change the “reswPath” variable in the code to the location of your .resw file. The language of the resource file is irrelevant as the system automatically chooses the correct language at runtime - the resw. file is only used to retrieve the resource keywords. The template will not generate a property for resources with a dot in the keyword because they are usually used in XAML and C# does not allow dots in property names.

<#@ template debug="false" hostspecific="true" language="C#" #>
<#@ assembly name="System.Core" #>
<#@ assembly name="System.Xml" #>
<#@ assembly name="System.Xml.Linq" #>
<#@ import namespace="System.IO" #>
<#@ import namespace="System.Linq" #>
<#@ import namespace="System.Xml.Linq" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ import namespace="Microsoft.CSharp" #>
<#@ output extension=".cs" #>
<#
    var reswPath = @"../Resources/Resources.lang-en.resw";

    var provider = new CSharpCodeProvider();
    var className = provider.CreateEscapedIdentifier(
        Path.GetFileNameWithoutExtension(Host.TemplateFile));

    Directory.SetCurrentDirectory(Host.ResolvePath(""));
    if (File.Exists(reswPath))
    { 
#>
using Windows.ApplicationModel.Resources; 

namespace <#= GetNamespace() #> 
{
    public static class <#= className #> 
    {
        private static readonly ResourceLoader resourceLoader; 

        static <#= className #>() 
        {
            resourceLoader = new ResourceLoader();
        }
<#
        foreach (string name in GetResourceKeys(reswPath).Where(n => !n.Contains(".")))
        {
#>

        public static string <#= provider.CreateEscapedIdentifier(name) #> 
        {
            get { return resourceLoader.GetString("<#= name #>"); }
        }
<#
        }
#>
    }
}
<#
    }
    else
        throw new FileNotFoundException(); 
#>
<#+
    private string GetNamespace()
    {
        return Host.ResolveParameterValue("directiveId", "namespaceDirectiveProcessor", "namespaceHint");
    }

    private static IEnumerable<string> GetResourceKeys(string filePath)
    {
        var doc = XDocument.Load(filePath);
        return doc.Root.Elements("data").Select(e => e.Attribute("name").Value);
    }
#>

The generated class will look like the following code assuming the template file is called “Strings.tt” and can be found in the “Localization” directory of the project.

using Windows.ApplicationModel.Resources; 

namespace MyProject.Localization 
{
    public static class Strings 
    {
        private static readonly ResourceLoader resourceLoader; 

        static Strings() 
        {
            resourceLoader = new ResourceLoader();
        }

        public static string DeleteMessage 
        {
            get { return resourceLoader.GetString("DeleteMessage"); }
        }

        ... 

If you are excessively writing T4 templates, I recommend using tangible T4 editor which adds IntelliSense & Syntax-Highlighting to Visual Studio’s tt-editor.



Discussion