Today a friend woke me up by phone wanting to ask me how to best do internationalization (i18n) in .NET. He already had read some guides that involved compiling satellite assemblies with global resource files etc, and that didn’t work all that well.
My first thought went to that article I had read half a year before when I was tasked with i18n, and I tried to remember how I did it back then.
I remembered: I didn’t.
At no point in time was the scope of the application so big that it had warranted to jump through the hoops Microsoft hat set up to get to real internationalization.
If you Google for the thing or ask on StackOverflow, you realize rather fast that you don’t want to have any of this i18n “goo” in your application logic, so the smartest thing is to simply abstract everything away into a separate class that handles all those things.
And once you have it abstracted from your application, nobody forces you any more to use the .NET i18n stuff any more.
Simple switch statements will do if you only need 2 languages for a couple of strings, even reading something on the Globalization stuff from .NET takes longer than simply writing something similar to this:
public class LocalizationManager : ILocalizationManager { private IDictionary<CultureInfo, IDictionary<string, string>> dictionary;public LocalizationManager(IDictionary<CultureInfo, IDictionary<string, string>> dictionary) { this.dictionary = dictionary; }
public string Translate(CultureInfo culture, string name) { var translationDictionary = dictionary[culture]; if (!translationDictionary.ContainsKey(name)) throw new Exception("No translation found"); return translationDictionary[name]; } }
Now, the only trouble here is to fill that dictionary with values, and that can even be simply hardcoded into the application.
To further facilitate the use of this you can now write Facades for your languages like this:
public class Translation : ITranslation { private readonly ILocalizationManager localizationManager; private readonly CultureInfo culture;public Translation(ILocalizationManager localizationManager, CultureInfo culture) { this.localizationManager = localizationManager; this.culture = culture; }
public string Translate(string name) { return localizationManager.Translate(culture, name); } }
And you can go even further (if you like hardcoding as much as I do) and write another facade like this to have intellisense on your translations:
public class SpecializedTranslation { private readonly ITranslation translation; public SpecializedTranslation(ITranslation translation) { this.translation = translation; }public string GetLoginMessage() { return translation.Translate("LoginMessage"); } }
Also mind that since everything follows a rather simple Interface I can easily change these implementations later if I need to. But for something simple, this will do just fine.
Also I advise to use this through an IoC container like Windsor so you could even go as far as to swap hardcoded translations through the configuration.