ILearnable .Net

August 6, 2010

rework of strongly typed access to language files in EPiServer

Filed under: Uncategorized — andreakn @ 12:34

I showed my strongly typed language handling in EPiServer to a guy at work who immediately pointed out a weakness: ReSharper will harass you and try to get you to add using statements all over the code, which in turn would reduce readability of the language strings.

This is because the hierarchical information of the language files was encoded into namespaces. I remodelled it to use nested public static classes instead and it works just the same, here’s the code:

<#@ template debug="true" hostSpecific="true" #><#@ assembly name="System.Xml"#><#@ assembly name="System.Xml.Linq" #><#@ assembly name="System.Core" #><#@ import namespace="System.Xml.Linq"#><#@ import namespace="System.Linq"#><#@ import namespace="System.Collections.Generic"#><#
int skipLevels = 1; //depends on xml file, for standard episerver use 2
string xmlFilePath = @"\lang.xml";
string baseCodeGenNamespace = "Lang";
XDocument xdoc = XDocument.Load(System.IO.Path.GetDirectoryName(this.Host.TemplateFile)+xmlFilePath);
GenerateLanguageAccessors(xdoc.Root, baseCodeGenNamespace,skipLevels);
#><#+
   void GenerateLanguageAccessors(XNode xNode, string baseNS, int skipLevels)
        {
            this.WriteLine("public static class " + baseNS + "{");
            GenerateLanguageAccessorsRecursive(xNode, new List<string>(), skipLevels, new List<string>());
            this.WriteLine("}");
           
        }

         void GenerateLanguageAccessorsRecursive(XNode xNode, List<string> ns, int skipLevels, List<string> cache)
        {
            string indenting = Times("\t", ns.Count - skipLevels);

            if (xNode is XText)
            {
                if (ns.Count < skipLevels) return;

                string theKey = "/" + string.Join("/", ns.Skip(skipLevels+1).ToArray());
                string s = indenting + "public static string Text{ get{ return Utils.LanguageUtil.Translate(\"<KEY>\");} }\n";
                s += indenting + "public static string TextOrDefault(string defaultString){ return Utils.LanguageUtil.TranslateOrDefault(\"<KEY>\",defaultString);}";
                s = s.Replace("<KEY>", theKey);
                this.WriteLine(s);
                
                return;
            }
            if (xNode is XElement)
            {
                var xElement = xNode as XElement;
                var newNameSpace = ns.ToArray().ToList();
                newNameSpace.Add(xElement.Name.ToString());
                var pathKey = string.Join(".", newNameSpace);
                var shouldWriteClass = (ns.Count > skipLevels && !cache.Contains(pathKey));
                if (shouldWriteClass) this.WriteLine(indenting + "public static class " + newNameSpace.Last() + "{");
                cache.Add(pathKey);
                foreach (var subNode in xElement.Nodes())
                {
                    GenerateLanguageAccessorsRecursive(subNode, newNameSpace, skipLevels, cache);
                }
                if (shouldWriteClass) this.WriteLine(indenting + "}");
            }
        }

         string Times(string input, int times)
        {
            string ret = "";
            for(int i = 0; i<times; i++)
            {
                ret += input;
            }
            return ret;
        }
#>

the reason everything is squashed together in the top is to avoid getting a lot of linebreaks in the start of the file.

Using nested classes comes with two small limitations: you cannot have the language string contain two identical names in a row (for example: “/strings/pages/frontpage/frontpage/heading”) and you should never have it end with Text:
(“/strings/pages/frontpage/heading/Text”)

(the C# compiler refuses to compile the generated code in both these two cases with the error “member names cannot be the same as their enclosing type”)

Advertisements

1 Comment »

  1. […] while back I wrote about how to get strongly typed globalization handling for EPiServer […]

    Pingback by Introducing EPiLang « ILearnable .Net — May 22, 2011 @ 18:28 | Reply


RSS feed for comments on this post. TrackBack URI

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Blog at WordPress.com.

%d bloggers like this: