An Agreeable Procrastination – and the blog of Niels Kühnel

Code, the universe and everything

An 101 on the Localization framework in Umbraco 5

with 7 comments

Yes! Umbraco 5 is out, and I remember talking with sir Alex Norcliffe about having one assembly even non-Umbraco projects could use to leverage some of its awesomeness.

That’s “Umbraco.Framework.dll”.

So, you can use Umbraco 5 localization in Umbraco 5 projects, and also in any other project. Just reference “Umbraco.Framework”.

In this blog post I’ll run through:

  • How to use localized texts in views and code
  • How to define your texts
  • MVC conventions (how to set texts for view model labels, validation errors etc. without attributes)
  • How ITextSource can be used to keep your texts in a Google Spreadsheet.
  • How to define custom languages and fallbacks

How to print texts

In views you import the namespace Umbraco.Framework and then you can write


<div>@Html.GetText("Hello")</div>

If you need parameters for your text you’ll write


<div>@HtmlGetText("Hello", new {UserName="Bob", NewMessages = 4, Weight=158})</div>

If you write the above and have defined a text “Hello” with the pattern


Hello {UserName}. You have #NewMessage{0: no new messages | 1: one new message | {#} new messages}. You weigh {Weight:N2} kg.

your view would print “Hello Bob. You have 4 new messages. You weigh 158.00 kg.”

In your controllers and other code the easiest way to localize is to import the namespace “Umbraco.Framework.Localization” and use the extension method on string called Localize. This method assumes that the string is a text key, so in code you would write:


var message = "Hello".Localize(this, new {UserName="Bob", NewMessages = 4, Weight=158})

You have to write “this” as the first parameter because texts are “namespaced”, but I’ll cover that in an even later blog post. (Basically, you can embed texts in assemblies and these texts won’t interfere with your other texts even if they have the same key. And you can also override assemblies texts with your own, it’s awesome! But not covered here).

Defining texts
If you’re in an Umbraco 5 project it’s pretty simple. You just define your texts in ~/App_Data/Umbraco/LocalizationEntries.xml and start coding. NOTE: You’ll have to add Namespace=”” to the root tag, so your file should like this:

 <?xml version="1.0" encoding="utf-8" ?>
 <Translations Namespace="">
 <Text Key="Welcome">
 <Translation Language="*">Hello {UserName}.</Translation>
 <Translation Language="da-DK">Hej {UserName}.</Translation>
 </Text>
 </Localization>

Note that if a text is missing, the framework will look for a text in the language “*”. This is the “default” language all other languages will fall back to if a text is missing. If your site’s default language is English you should define your English texts with “*”.

Standalone

If you’re not in an Umbraco 5 project you only have to add this to Application_Start in global.asax.cs:

LocalizationWebConfig.SetupDefaultManager<MvcApplication>();
LocalizationWebConfig.SetupMvcDefaults();

And then you’re ready to go.

Standalone, the path for LocalizationEntries.xml is ~/App_Config/LocalizationEntries.xml

MVC conventions

The localization framework provides a ModelMetaDataProvider and ModelBinder that automatically binds texts to view model labels and validation messages following the conventions described here.
Assume you have a view model like


public class FooModel
{
 [Required]
 public string Name {get;set;}
 [Range(0,250)]
 public int Age{get;set}
}

Properties
If you would like your views to print something other than “Name” and “Age” when you write Html.LabelFor(m=>m.Name) you simply define texts with the keys “FooModel.Name” and “FooModel.Age”.

You can also just define keys called “Name” and “Age”. In that case those would be used for all view models with those properties. Also, if you want to be really specific you can prefix the view model’s class name with its namespace.

The framework consider these keys in this order:

  1. Namespace.ClassName.PropertyName
  2. ClassName.PropertyName
  3. PropertyName

Validation messages
Validation messages are also localized, and opposite .NETs localization with resource strings the messages can use parameters.
You add keys named ClassName.PropertyName.[Name of validation attribute]. (Same patterns as above, so Name.Required will do and including the namespace is more specific)
For instance, if you add a key with the name FooModel.Name.Required and text “This is so required” this will be used if the Name field is left blank.

You can also add general validation texts for all properties by adding keys named “Validation.Required”, “Validation.Range” etc. If you define a text for “Validation.Required” all required fields will show this validation message if you have not defined specific texts for them.

The texts for validation messages can use the special parameter “Value” and all public properties of the validation attribute. For example, for [Range] you can define your text as “{Value} is not between {Minimum} and {Maximum}” and then you have a nice validation message that can easily be translated and state the limits (standard .NET resource texts are not great for that).

Text sources
If you don’t like the XML format or have texts from an external source you can create your own provider that implements ITextSource.
You’ll only have to implement the Get method that returns an IEnumerable of LocalizedText. When creating those you shouldn’t normally bother with more than Key, Language and Pattern. (The rest are “advanced topic material”)
In the demo project (see link at bottom) I have implemented a CSV file source and another one that reads texts from a Google Spreadsheet. If you have your own spreadsheet just click File->Publish and copy the link from the “Get a link to the published data” after you have picked CSV, and insert that link instead of mine. I (think I) have shared my spreadsheet so that you can read it.

Custom languages
The localization framework is not bound to CultureInfos. It is the standard though, so if you don’t specify anything else it will look up the language code from the current CultureInfo (e.g. “en-US”, “ar-SY”, “da-DK” etc.).
If you choose to define custom languages you can define “fallback chains” for languages so, for example, if a Danish text isn’t found a Norwegian one is better than English. You can see a simple example of how to do this in the demo project.

The CultureInfo used for formatting dates and numbers is inferred from the key. This means DO follow .NETs names (“en-US” etc.). You are allowed to add more dashes, so if you need a few texts different for Mac and PC users you can define “en-US-mac” and “en-US-pc”. Both will use “en-US” locale and fall back to “en-US” if no specific text is found. It’s a design choice because it’s easier if you stick with standardized language keys. If you really, really need something else you can subclass LanguageInfo and define your own logic (also “advanced topic”)

Demo

I’ve created a small demo solution for the purpose, and it can be found here: https://bitbucket.org/nielskuhnel/localization101
It demonstrates the concepts described in this post.

Happy localizing!

Written by niels.kuhnel

March 11, 2012 at 4:43 am

Posted in Uncategorized

7 Responses

Subscribe to comments with RSS.

  1. Hi there,

    Thanks for your lights. Really useful.

    Quick question, does that LocalizationEntries.xml set work for title or placeholder attributes ?

    I can tough set them on the view side using Umbraco.GetDictionaryItem() but that implies using two distincts dictionaries 😦

    Thanks.

    Nicolas

    March 28, 2012 at 12:45 pm

    • Yes, you should definitely also be able to use that there.
      You can print localized texts anywhere in your view so both

      and
      should work like a charm.

      Are you in Umbraco 4.7 WebPages or Umbraco 5 MVC views?
      Umbraco.Framework doesn’t support WebPages, but it’s quite easy to add support. Let me know if you’re in 4.7

      niels.kuhnel

      March 29, 2012 at 10:34 pm

  2. First, thanks for the great article!

    I want to use Html.GetText() in an Umbraco project. How can I tell Umbraco to embed the language code in the URL and set DefaultTextManager.CurrentLanguage based on the language code in the URL?

    Bassem Mohsen

    April 5, 2012 at 3:03 am

  3. Thanks for the great article!

    I also want to know how to configure the language in URL umbraco. like http://www.text.com/topics/en-us/Topic1 can be seen in multiple languages (www.text.com/topics/Fr/Topic1)

    usamakhalil

    April 17, 2012 at 4:13 am

  4. Hi! In your CG11 talk on Umbraco localization, you briefly mentioned a special area that could generate a Javascript for client side localization. I believe the URL was something like /Localization/Script/CG11.Localization.Web, but I seem to be missing some vital clue as how to get this working in Umbraco 5.1. Could you possibly elaborate on this? Thanks 🙂

    hccode

    May 9, 2012 at 4:35 pm

  5. Hi, this is a really useful article. I’ve just used this technique in one of our development projects.

    I was wondering something though, I’ve used the EmailAttribute from the DataAnnotationsExtensions library to validate an email address. Can this be localized through the LocalizationEntries.xml file at all? I can’t seem to work out the correct name convention…

    Any help would be greatly appreciated, thank you 🙂

    Paul

    May 18, 2012 at 5:41 pm

  6. It’s amazing in support of me to have a site, which is helpful designed for my know-how. thanks admin

    http://TW6Qr.wordpress.com/

    August 1, 2013 at 2:16 pm


Leave a reply to niels.kuhnel Cancel reply