The missing ResWFileCodeGenerator custom tool for Visual Studio 2012

I’ve been doing quite a lot of Windows Store Apps on Windows 8 lately. Ever since I started I always seemed to miss the ResXFileCodeGenerator custom tool. This is because in WinRT when you want to load a localized string in code then you will have to do something like this:

var resources = new ResourceLoader();
var localizedString = resources.GetString("SomeResourceName");

This of course can easily lead to a lot of errors. In my case I got a little to eager while refactoring and forgot to notice that I was also changing hard coded strings. This broke the code quite a lot. Because of this frustration I decided to implement my own custom tool since Microsoft didn’t provide one

The project is open source and is available at Github. Here’s a preview of the description which I took directly from my CodePlex project site.

Download from Visual Studio Marketplace

Features

  • Define custom namespace for the generated file
  • Auto-updating of generated code file when changes are made to the .ResW Resource file
  • XML documentation style comments like “Localized resource similar to ‘[the value]’”
  • Supports Visual Studio 2015, 2017, 2019, and 2022
  • Supports dotted keys - Replaces . with _ (e.g. Something.Awesome = Something_Awesome)

Custom Tools

  • ReswFileCodeGenerator - Generates a public class
  • InternalReswFileCodeGenerator - Generates an internal (C#) / friend (VB) class

Supported Languages

  • C#
  • Visual Basic

Screenshots

ReswFileCodeGenerator Custom Tool

InternalReswFileCodeGenerator Custom Tool

C# Usage

string test1, test2, test3;

void LoadLocalizedStrings()
{
    test1 = App1.LocalizedResources.Resources.Test1;
    test2 = App1.LocalizedResources.Resources.Test2;
    test3 = App1.LocalizedResources.Resources.Test3;
    test4 = App1.LocalizedResources.Resources.Test_With_Dotted_Keys;
}

Visual Basic Usage

Dim test1, test2, test3

Private Sub LoadLocalizedStrings()
    test1 = AppVb.LocalizedStrings.Resources.Test1
    test2 = AppVb.LocalizedStrings.Resources.Test2
    test3 = AppVb.LocalizedStrings.Resources.Test3
    test4 = AppVb.LocalizedStrings.Resources.Test_With_Dotted_Keys;
End Sub

Generated C# Code

//------------------------------------------------------------------------------
// <auto-generated>
//     This code was generated by a tool.
//     Runtime Version:4.0.30319.42000
//
//     Changes to this file may cause incorrect behavior and will be lost if
//     the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------

// --------------------------------------------------------------------------------------------------
// <auto-generatedInfo>
// 	This code was generated by ResW File Code Generator (http://bit.ly/reswcodegen)
// 	ResW File Code Generator was written by Christian Resma Helle
// 	and is under GNU General Public License version 2 (GPLv2)
// 
// 	This code contains a helper class exposing property representations
// 	of the string resources defined in the specified .ResW file
// 
// 	Generated: 05/20/2019 15:47:37
// </auto-generatedInfo>
// --------------------------------------------------------------------------------------------------
namespace App2
{
    using Windows.ApplicationModel.Resources;


    public sealed partial class Resources
    {
    
        private static ResourceLoader resourceLoader;
    
        /// <summary>
        /// Get or set ResourceLoader implementation
        /// </summary>
        public static ResourceLoader Resource
        {
            get
            {
                if ((resourceLoader == null))
                {
                    Resources.Initialize();
                }
                return resourceLoader;
            }
            set
            {
                resourceLoader = value;
            }
        }
    
        /// <summary>
        /// Localized resource similar to "test"
        /// </summary>
        public static string Test
        {
            get
            {
                return Resource.GetString("Test");
            }
        }
    
        /// <summary>
        /// Localized resource similar to "test"
        /// </summary>
        public static string Test2
        {
            get
            {
                return Resource.GetString("Test2");
            }
        }
    
        /// <summary>
        /// Localized resource similar to "test"
        /// </summary>
        public static string Test3
        {
            get
            {
                return Resource.GetString("Test3");
            }
        }
    
        /// <summary>
        /// Localized resource similar to "test"
        /// </summary>
        public static string Test_With_Dotted_Keys
        {
            get
            {
                return Resource.GetString("Test/With/Dotted/Keys");
            }
        }
    
        public static void Initialize()
        {
            string executingAssemblyName;
            executingAssemblyName = Windows.UI.Xaml.Application.Current.GetType().AssemblyQualifiedName;
            string[] executingAssemblySplit;
            executingAssemblySplit = executingAssemblyName.Split(',');
            executingAssemblyName = executingAssemblySplit[1];
            string currentAssemblyName;
            currentAssemblyName = typeof(Resources).AssemblyQualifiedName;
            string[] currentAssemblySplit;
            currentAssemblySplit = currentAssemblyName.Split(',');
            currentAssemblyName = currentAssemblySplit[1];
            if (executingAssemblyName.Equals(currentAssemblyName))
            {
                resourceLoader = ResourceLoader.GetForCurrentView("Resources");
            }
            else
            {
                resourceLoader = ResourceLoader.GetForCurrentView(currentAssemblyName + "/Resources");
            }
        }
    }
}

Generated Visual Basic Code

'------------------------------------------------------------------------------
' <auto-generated>
'     This code was generated by a tool.
'     Runtime Version:4.0.30319.42000
'
'     Changes to this file may cause incorrect behavior and will be lost if
'     the code is regenerated.
' </auto-generated>
'------------------------------------------------------------------------------

Option Strict Off
Option Explicit On

Imports Windows.ApplicationModel.Resources

'--------------------------------------------------------------------------------------------------
'<auto-generatedInfo>
'	This code was generated by ResW File Code Generator (http://bit.ly/reswcodegen)
'	ResW File Code Generator was written by Christian Resma Helle
'	and is under GNU General Public License version 2 (GPLv2)
'
'	This code contains a helper class exposing property representations
'	of the string resources defined in the specified .ResW file
'
'	Generated: 05/20/2019 15:48:18
'</auto-generatedInfo>
'--------------------------------------------------------------------------------------------------

Partial Public NotInheritable Class Resources

    Private Shared resourceLoader As ResourceLoader

    '''<summary>
    '''Get or set ResourceLoader implementation
    '''</summary>
    Public Shared Property Resource() As ResourceLoader
        Get
            If (resourceLoader Is Nothing) Then
                Resources.Initialize
            End If
            Return resourceLoader
        End Get
        Set
            resourceLoader = value
        End Set
    End Property

    '''<summary>
    '''Localized resource similar to "test"
    '''</summary>
    Public Shared ReadOnly Property Test() As String
        Get
            Return Resource.GetString("Test")
        End Get
    End Property

    '''<summary>
    '''Localized resource similar to "test"
    '''</summary>
    Public Shared ReadOnly Property Test2() As String
        Get
            Return Resource.GetString("Test2")
        End Get
    End Property

    '''<summary>
    '''Localized resource similar to "test"
    '''</summary>
    Public Shared ReadOnly Property Test3() As String
        Get
            Return Resource.GetString("Test3")
        End Get
    End Property

    '''<summary>
    '''Localized resource similar to "test"
    '''</summary>
    Public Shared ReadOnly Property Test_With_Dotted_Keys() As String
        Get
            Return Resource.GetString("Test/With/Dotted/Keys")
        End Get
    End Property

    Public Shared Sub Initialize()
        Dim executingAssemblyName As String
        executingAssemblyName = Windows.UI.Xaml.Application.Current.GetType().AssemblyQualifiedName
        Dim executingAssemblySplit() As String
        executingAssemblySplit = executingAssemblyName.Split(Global.Microsoft.VisualBasic.ChrW(44))
        executingAssemblyName = executingAssemblySplit(1)
        Dim currentAssemblyName As String
        currentAssemblyName = GetType(Resources).AssemblyQualifiedName
        Dim currentAssemblySplit() As String
        currentAssemblySplit = currentAssemblyName.Split(Global.Microsoft.VisualBasic.ChrW(44))
        currentAssemblyName = currentAssemblySplit(1)
        If executingAssemblyName.Equals(currentAssemblyName) Then
            resourceLoader = ResourceLoader.GetForCurrentView("Resources")
        Else
            resourceLoader = ResourceLoader.GetForCurrentView(currentAssemblyName + "/Resources")
        End If
    End Sub
End Class



Parenthood

Once again I have taken a break from blogging. Parenting and my new family has been my absolute top priority and I’ve been having a hard time finding the time to write articles. I really enjoy spending time with my son and wife and I take advantage of every possible moment to do so.

I ended up at a schedule that starts around 5:30 AM in which my son wakes up, and wakes me up. I go and start preparing some breakfast for him. We then have a cozy morning playing around the house. I drop him off at day care as late possible (in my case 9:00 AM) and then drive to the office, or to a customer, or back home if I’m working from home. I pick my son up at between 3:00-4:00 PM and spend time with him and my wife until around 6:30 PM (this is his bed time and I’m very lucky that he has no trouble sleeping). From around 6:30 to 8:00-9:00 PM I spend some time with my wife discussing how our days went and do some planning for the following days. My wife goes to bed a bit early, and when that happens I go back to work. Since I drastically shortened by day, I need to do quite a bit of catching up at night. I usually work an extra 2-3 hours more at night, then I have an hour or 2 to give my mind a rest or work on a hobby project. My day usually ends between 12:30 AM and 2:00 AM. I need at least 4 hours of sleep to recharge for the next day, otherwise I won’t be able to last a full week on my schedule.

Hence the break from blogging :)



Windows Phone and HTML5 - Danish Developer Conference 2012 Session Recording

Here’s the recording of my session on Windows Phone and HTML5 for the Danish Developer Conference 2012, for those who missed it…

You can also watch it on YouTube