Localization, FubuMVC Style Part 1 of 2

After several years of having life easy, I re-entered the world of web development a couple years ago when Chad and I joined Dovetail and suddenly I can’t just hard code user messages anywhere in the user interface.  Besides the obvious need to make our web application usable for non-English speakers, we’ll need to customize header names and text messages for those pesky customers.  Fine and dandy, we have to do it.  All over the place – and every single time you forget it’s going to be a bug. 

We’ve had a more or less workable solution for the past couple years, and I’d like to put a (streamlined) version of the Dovetail solution out there as the integrated FubuMVC localization strategy.  Localization by itself isn’t a very exciting feature for a web development framework, but it’s a foundational piece for a lot of Html generation and the user messages generated by the validation subsystem.  I’m actually working on the localization now as a prerequisite to replacing our creaky old validation code from ShadeTree with something cleaner that would live in FubuMVC with full integration.  Before I go too far with this, I’d really like some feedback on the basic approach.  What don’t you like, what am I missing, what other suggestions do you have?  I’m mostly concerned about retrieving the proper strings for a given culture, but I’ll address things like numbers and dates in a later post.

Before I go any farther, my spike code for localization just to is on GitHub here.  This isn’t much more than a teaser trailer, so I’ll follow along tomorrow with some technical details.

Goals

  1. FubuMVC should make no hard assumptions about how the localized text data is stored and retrieved. 
  2. I don’t want to have to think about localization (very much) as I’m working.  As much as possible, I want FubuMVC to just know when and how to do the localization for me. 
  3. When I do have to explicitly say “display this localized string here,” I want to express that as quickly as possible without having to break my stride. 
  4. FubuMVC should be able to use the type system to retrieve localized strings in some scenarios

 

Use Case #1:  Placeholder for a named string (StringToken)

The simplest case is when I simply need to say “give me this named string for whatever the current culture is.”  You might be writing out a string into a view being rendered or setting the inner text of an html tag being generated on the server side or even just bundling up a response from an Ajax service.  In that scenario, we use a class called the StringToken as a placeholder for the localized text:

    public class StringToken

    {

        // Notice that there’s no public ctor

        // We use StringToken as a Java style

        // strongly typed enumeration

        protected StringToken(string defaultText)

        {

            DefaultText = defaultText;

        }

 

        public string DefaultText { get; set; }

 

        // I’m thinking about a way that the Key

        // gets automatically assigned to the field name 

        // of the holding class

        public string Key { get; protected set; }

 

        public static StringToken FromKeyString(string key)

        {

            return new StringToken(null)

            {

                DefaultText = null

            };

        }

 

        // ToString() on StringToken delegates to a

        // static Localizer class to look up the

        // localized text for this StringToken.

        // Putting the lookup in ToString() made

        // our code shrink quite a bit.

        public override string ToString()

        {

            return Localizer.TextFor(this);

        }

    }

 

StringToken values are designated on static holder classes like this one (pulled from our code):

    public class WidgetKeys : StringToken

    {

        public static readonly StringToken WEBSITE = new WidgetKeys( “Website”);

        public static readonly StringToken REPORT = new WidgetKeys(“Report”);

        public static readonly StringToken RSS = new WidgetKeys(“RSS Feed”);

 

        protected WidgetKeys(string defaultValue)

            : base(defaultValue)

        {

        }

    }

 

In usage, I can use the StringToken:

  • Directly in a view page:  <% = WidgetKeys.WEBSITE %>
  • As part of a custom Html expression:  <% = this.SelectorFor(x => x.SomeProperty).Label(UserMessageKeys.SOME_PROPERTY) %>
  • To construct the response from an Ajax service:  return new Response(Message = CoreMessageKeys.CASE_NOT_FOUND);

 

Use Case #2:  Header text and information for a given Property

One of my personal favorite features of FubuMVC is the InputFor() / DisplayFor() / LabelFor() html builders.  Once we have the localization shims put into FubuMVC, we can change the out of the box convention for LabelFor(x => x.SomeProperty) to this:

 

        public DefaultHtmlConventions()

        {

 

            Labels.Always.BuildBy(req =>

            {

                // Return a single <label> element with

                // the inner text as the heading name retrieved

                // from the localization subsystem for the

                // property being rendered

                return new HtmlTag(“label”)

                    .Text(Localizer.HeaderTextFor(req.Accessor.InnerProperty));

            });

 

        }

With this above configuration, a call to LabelFor(x => x.SomeProperty) would be rendered as “<label>the localized header for SomeProperty</label>.”  LabelFor() by itself is useful, but it gets even cooler when you start to take the fine grained Html convention pieces and put them together into some sort of form builder DSL like this (from Dovetail code, but the DSL support is in FubuMVC itself):

 

    x.Show(m => m.User.Username);
    x.Edit(m => m.User.Status).EditableForRole(“admin”);
    x.Edit(m => m.User.IsAdmin).EditableForRole(“admin”);
    x.Edit(m => m.User.Workgroup);
    x.Edit(m => m.User.Calendar);
    x.Edit(m => m.User.TimeZone);
    x.Edit(m=>m.User.Email).Label(SharedMessageKeys.USER_EMAIL_KEY); // Label text can be overridden with our friend the StringToken

The calls to Show(expression) and Edit(expression) render both a label and the editing or display tags for each field.  It’s a fast way to put together simple forms while retaining a modicum of control over the layout, but it’s not possible unless Fubu has its own way to determine the label text for a property by itself.

 

Tomorrow I’ll post more on the innards of how this all works and explain why it’s in this shape.

About Jeremy Miller

Jeremy is the Chief Software Architect at Dovetail Software, the coolest ISV in Austin. Jeremy began his IT career writing "Shadow IT" applications to automate his engineering documentation, then wandered into software development because it looked like more fun. Jeremy is the author of the open source StructureMap tool for Dependency Injection with .Net, StoryTeller for supercharged acceptance testing in .Net, and one of the principal developers behind FubuMVC. Jeremy's thoughts on all things software can be found at The Shade Tree Developer at http://codebetter.com/jeremymiller.
This entry was posted in FubuMVC. Bookmark the permalink. Follow any comments here with the RSS feed for this post.
  • http://codebetter.com/members/jmiller/default.aspx Jeremy D. Miller

    @Venugopal,

    Magic Strings are a no no for me, and that defeats part of the purpose for the StringToken’s in the first place (being able to scan the assembly, avoiding mistakes, asserting on the StringToken, embedding a default value).

    @fschwiet,

    I don’t think the perf optimization is worth it (and might not actually be any faster). Mostly though, I really don’t want to have to invoke service location everywhere to get at the localized strings.

  • Venugopal

    Why not use something like < % = "Website".Localize() %> instead of < % = WidgetKeys.WEBSITE %>, where Localize() is an extension method on string. This way the error messages or strings are *there* inline in code and makes code more readable?

  • fschwiet

    The WidgetKeys class is similar to how I manage string constants now without localization, so it would be a smooth transition to use that. It seems though when a page is rendered you’re going to need to do a localization lookup once per localized string.

    I wonder if rather then have WidgetKeys be a static class with multiple WidgetKeys, have it be a nonstatic class exposing simple string properties. Then achieve localization by extracting interface, so to localize the whole class can be replaced. Then there is only 1 localization lookup per web request. And you can leverage you’re IoC container to find the right instance of the class.

    Maybe its premature optimization. Just a thought.

  • http://www.markdavidrogers.com Mark Rogers

    Awesome article, I was going to have to tackle this very problem with Fubu. This article will definitely save me some time, thanks!