Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Strongly Typed Localized Strings In Universal Apps

DZone's Guide to

Strongly Typed Localized Strings In Universal Apps

· DevOps Zone
Free Resource

Best practices for getting to continuous deployment faster and with dramatic results in reduced outage minutes, development costs, and QA testing cycles. Brought to you by Rainforest QA.

Back in the old days when the need for localized string arose, I would add a new string and use AppResources to access the correct string in code.

In Windows Phone application (known today as Windows Phone Silverlight 8.0/8.1) I would do something like this:

public void SomeMethod()
{
    var localizedString1 = AppResources.MyFirstSring;
    var localizedString2 = AppResources.AnotherString;
}

In the new universal apps the story is a bit different:

  • Only strings are allowed in the resource files (which are .resw instead of .resx)
  • Using a directory for each supported language (i.e. Strings\en-US\)
  • Access to localized strings is string based

I can live with the first two (I actually prefer it that way) but I have huge problems withStringly Typed API calls – it’s easy to get it wrong, and even worse a trivial rename can cause problems that would only show during application run.

Luckily a co-worker of mine has put together a T4 template that fixes this problem (thanks Noam):

<#@ template debug="false" hostspecific="true" language="C#" #>
<#@ assembly name="System.Core" #>
<#@ assembly name="System.Xml" #>
<#@ assembly name="System.Xml.Linq" #>
<#@ import namespace="System.Linq" #>
<#@ import namespace="System.Xml.Linq" #>
<#@ import namespace="System.Text" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ output extension=".cs" #>
 
<# string str = this.Host.ResolvePath("strings\\en-us\\Resources.resw");
    System.Xml.Linq.XElement resXml = System.Xml.Linq.XElement.Load(str); // Read a data file here. "%aaa%/string/en-us/Resources.resw"
   IEnumerable<XElement> appElement = (from dataElements in resXml.Descendants("data") select dataElements); #>
 
using Windows.ApplicationModel.Resources;
 
namespace UniversalApp
{
    public class StringsResourceManager
    {
        private static readonly ResourceLoader _resourceLoader = new ResourceLoader();
        public StringsResourceManager()
        {
        }
 
<# foreach (var element in appElement) { #>  
        public static string <#= element.Attribute("name").Value.Replace(".","_")#>  
        {
            get
            {
                return _resourceLoader.GetString("<#= element.Attribute("name").Value#>");
            }
        } 
<# } #>
    }
}

Using the class created from this template I can get from this:

public void SomeMethod()
{
    var resourceLoader = new ResourceLoader();
 
    var string1 = resourceLoader.GetString("MyFirstSring");
    var string2 = resourceLoader.GetString("AnotherString");
}

To this:

public void SomeMethod() 
{ 
    var string1 = StringsResourceManager.MyFirstSring; 
    var string2 = StringsResourceManager.AnotherString; 
}

Happy coding...


Discover how to optimize your DevOps workflows with our on-demand QA solution, brought to you in partnership with Rainforest QA.

Topics:

Published at DZone with permission of Dror Helper, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}