Using resource files (resx) in Typescript HTML project

Topics: General
Feb 11, 2014 at 10:25 PM
Hi,

Am working on a web app using TypeScript on Visual Studio 2012. I am able to add a new resource file (resx) to the TypeScript HTML project using Visual Studio. However cannot figure out how to use the strings in the resource file in the .ts files.

Would appreciate some pointers towards using the resource files in TypeScript project.

Thanks,
Sesha
Feb 13, 2014 at 9:16 AM
@Sesha, you need to ask yourself: "How would I make this work with JavaScript?".

TypeScript is not a runtime or framework. It simply adds static typing to JavaScript and compiles into plain vanilla JavaScript.

If you are writing server-side code then you might have to read and parse the resource file. If you need the resource file in the browser then you need to get your HTTP server to serve the file up.
Feb 13, 2014 at 5:55 PM
Thanks. In that case should there be an “Add Resource File” option when one right clicks the project in Visual Studio?

Would be great if the TypeScript compiler can compile down the resource file (those that contain only strings) into JavaScript functions with the strings in arrays.

Thanks,
Sesha

Feb 13, 2014 at 9:57 PM
I have created a T4 Template that processes a resx file to create a TypeScript file containing the strings. Any 'format' strings (containing {0}, etc) are converted into functions taking the correct number or parameters. It's not bulletproof yet, but does the job in our projects. Let me know if you would like the source.
Feb 13, 2014 at 10:21 PM
I took the same approach - T4 template:

<#@ template debug="false" hostspecific="true" language="C#" #>
<#@ assembly name="System.Core" #>
<#@ assembly name="System.Xml" #>
<#@ import namespace="System.Linq" #>
<#@ import namespace="System.Text" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ import namespace="System.Xml" #>
<#@ import namespace="System.Xml.XPath" #>
<#@ output extension=".ts" #>
<#
    var doc = new XPathDocument(this.Host.ResolvePath(@"Strings.resx"));
    var root = doc.CreateNavigator();

    var ns = new XmlNamespaceManager(root.NameTable);
    ns.AddNamespace("xsd", "http://www.w3.org/2001/XMLSchema");

    this.Write("var strings = {");

foreach (XPathNavigator node in root.SelectDescendants("data", "", true))
{
    var name = node.GetAttribute("name", "");
    node.MoveToChild("value", "");
    this.Write("'{0}' : '{1}',", name, node.Value);
}
#>
};
export function getLocalizedString(val: string): string {
if (!val) return val;
var res = strings[val];
if (res == null) return val;
return res;
}

It converts strings from .RESX to something like this:

var strings = {'key1' : 'value1','key2' : 'value2'}; // and more ...
export function getLocalizedString(val: string): string {
if (!val) return val;
var res = strings[val];
if (res == null) return val;
return res;
}
Feb 13, 2014 at 10:32 PM
Yes, would be great if you can share the code.

Thanks,
Sesha
Feb 13, 2014 at 11:29 PM
I've tried to make this fairly generic, in that it can use a resx file specified in another project (just specify the relative path), at the cost of not 'automatically' generating the TS code. Just right click the tt file and "Run Custom Tool". Also, this should cope with single quotes being used in the string, and produces a function for parameterised strings (although format specifiers are not handled). It outputs the strings into a single, global variable, eg
var Strings = {
    string1: 'Hello World',
    string2: 'Goodbye World',
    paramString: (val1)=> { return 'Hello ' + val1 + ', how are you?'; }
}
Feel free to use what you want and modify or discard the rest.
<#@ template  debug="true" hostSpecific="true" #>
<#@ output extension=".ts" #>
<#@ Assembly Name="System.Core" #>
<#@ assembly name="System.Xml" #>
<#@ assembly name="System.Xml.Linq" #>
<#@ import namespace="System" #>
<#@ import namespace="System.IO" #>
<#@ import namespace="System.Linq" #>
<#@ import namespace="System.Xml" #>
<#@ import namespace="System.Xml.Linq" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ import namespace="System.Text.RegularExpressions" #>
<#
    var resxFileName = Host.ResolvePath(@"<path to>\ClientStrings.resx");

    if (!File.Exists(resxFileName)) {
        this.Error("Unable to find RESX file. Please check T4 variable. Current path is '" + resxFileName + "'");
    }
    else
    {
#>
var <#= Path.GetFileNameWithoutExtension(Host.TemplateFile) #> = {
<#
        this.PushIndent("    ");
        foreach (var el in GetElements(resxFileName, "data"))
        {
            OutputElement(this, el);
        }
        this.PopIndent();
#>
};
<#
    }
#>
<#+
    static void OutputElement(GeneratedTextTransformation tt, XElement el)
    {
        var name = el.Attribute("name").Value;
        var value = el.Element("value").Value;

        bool dblquote = false;
        if (value.Contains("'"))
        {
            dblquote = true;
        }

        var matches = Regex.Matches(value, @"\{[0-9]+\}");
        var variables = new List<string>();
        foreach (Match match in matches)
        {
            var param = match.Value;
            var paramIndex = Int32.Parse(param.Substring(1, param.Length - 2));
            var paramName = string.Format("val{0}", paramIndex);

            if (!variables.Contains(paramName))
            {
                variables.Add(paramName);
                value = value.Replace(param, string.Format("{0} + {1} + {0}", dblquote ? "\"" : "'", paramName));
            }
        }

        if (variables.Any())
        {
            tt.WriteLine("{0}: ({1})=> {{", name, string.Join(", ", variables.ToArray()));
            tt.PushIndent("    ");
            tt.WriteLine("return {0}{1}{0};", dblquote ? "\"" : "'", value);
            tt.PopIndent();
            tt.WriteLine("},");
        }
        else
        {
            tt.WriteLine("{0}: {1}{2}{1},", name, dblquote ? "\"" : "'", value);
        }
    }

    static IEnumerable<XElement> GetElements(string inputFile, string elementName)
    {
        using (var reader = XmlReader.Create(inputFile))
        {
            reader.MoveToContent();
            while (reader.Read())
            {
                if (reader.NodeType == XmlNodeType.Element)
                {
                    if (reader.Name == elementName)
                    {
                        XElement el = XNode.ReadFrom(reader) as XElement;
                        if (el != null)
                        {
                            yield return el;
                        }
                    }
                }
            }
        }
    }
#>
Marked as answer by seshaprasadv on 2/13/2014 at 7:18 PM
Feb 14, 2014 at 2:18 AM
Thanks all the help!

-Sesha