Introduction
This article is the next step in a Catharsis documented tutorial. Catharsis is Web-application framework gathering best-practices, using ASP.NET MVC (preview 5), NHibernate 2.0. You can find on all the needed source code on http://www.codeplex.com/Catharsis/.
Version 0.9.5 news
Catharsis in version 0.9.5 provides new - sometimes fundamental - extensions and improvements. To let you quickly know I've created 7 snapshots which are (as I do believe) self-explanatory.
The main localization improvement is in fact the possibility to order by any language used in the application (what's a bit complicated then it could look like) and searching for localized phrase.
Quick story about Catharsis 0.9.5
You can add new languages in runtime - when application is deployed.

Newly added language can be immediately selected by user. Default Action for every controller is the List. The not localized Keys are decorated with '$'. 
Translator entity management allows to add new phrases for every language in the system.
Phrases are stored in the static cache. That results in a very high performance. But every change (new phrase or updated one) is replaced immediately.

Default order is based on the DB clustered keys

As said above, Catharsis now provides the simple way how to order by any language in the application.

Whatever was localized, it can be searched out now.

Localization
There is a strong tool for localizing the user UI built-in Catharsis. You will use it even if you are developing mono-language application, because Catharsis provides run-time 'Phrases management' for your application users. They won't 'disturb you' to change your some misspellings or incorrectness - they will be able to adjust whatever 'text' they want.
But firstly small review of the approach used in ASP.NET
Localization is not only translation!
We are not talking about translation only! The main purpose of localization is adjusting. You (as programmer) use words Like 'SearchForResults' but use prefer 'Search for results'. That's the added value you gain from localization. Every rendered text can be sent through Catharsis localization and adjusted to be as user-friendly as needed (and even translated)
ASP.NET localization
ASP.NET provides among others features - strong support for localization. Usually used are Resources (local or global) stored in special XML files with suffix .resx
. They are not bad and can do a lot of things for you. The real disadvantage of such solution comes when your application grows and the amount of your *.resx file is starting to be unmanageable. The same must be applied for every new language; every file (with ‘lang’ suffix) must be added. Do not forget, that before you can add them, you have to create them and distribute to translator, who must be able to edit ‘sophisticated’ xml files …
ASP.NET provides (correct would be ‘can be provided’) with database storage for all resources. You can implement only few classes (ResourcesReader
etc.) and the localization than can be completely stored in database. I would say, this is the real improvement. Anything stored in DB can be “simply” converted to ‘translator-user-friendly’ output, which can be subsequently (after translating) converted as an input back to DB. Really good approach!
Catharsis localization
Catharsis does not use ASP.NET ResourceManager
, she has its own. You can find it as a static ResourceManager
object in a Common
project (which every upper layer is referencing). ResourceManager
has GetLocalized()
method with two overloads. The first takes only one string as the ‘key’ for searching; the second is more precious and takes two: ‘key’ and adjusting second one: ‘area’.
The ‘key’ could be anything, action name (Search, List…), controller Name (Language, AppUser) and even Error message (CannotDelete). The ‘area’ can be used for better translation granularity, on one place the word “Search” could be explained as “Search” on other as “Searching”. And at that point the ‘area’ identifier comes into play.
Catharsis on UI uses ResourceMangar.GetLocalized()
in a smart way. There are base classes for pages and web-controls, which provide GetLocalized()
method with one attribute ‘key’ – the second ‘area’ is provided behind, the current controller name is used. If you call GetLocalized(
“Search”)
on Person controller, then your call is translated into GetLocalized(
“Search”, “Person”);
Of course, you can avoid this default behavior, only just using the GetLocalized(
“Search”,
“yourArea”)
– simple as you can expect. Catharsis is very kind, she will never force you … She just tries to help you)
How is GetLocalized evaluated?
It’s a kind of waterfall based on user selected language, provided ‘key’ and ‘area’.
If user selected ‘en-GB
’ and is looking for a key ‘Brainy’ in area ‘Entity’: the first look is for match of all three attributes
If not found the ‘en-GB
’ with key ‘Brainy’ is evaluated (area is not used)
If not found the ‘en
’ ‘Brainy’ ‘Entity’ comes to play
Next is ‘en
’ ‘Brainy’
Next step is the default language – so if default is Czech than ‘cs
’ ‘Brainy’ with ‘Entity’ and next without is evaluated
Still nothing? Then at least the ‘key’ is returned == ‘Brainy’
You do not like this behavior? Adjust it as you wish, no *.dll - pure source code is provided with Catharsis. (And let me know!)
Language object
Catharsis can be localized to as many languages as you want, and what is really cool – in a run-time. (Application User can add new language if (s)he has needed access rights). Languages can be general ‘en
’ or specific ‘en-GB
’.
public class Language : Persistent
{
public virtual string LanguageName { get; set; }
public virtual string EnglishName { get; set; }
public virtual string NativeName { get; set; }
public override string ToDisplay()
{
return LanguageName + "(" + EnglishName + ")";
}
protected override string GetDomainObjectSignature()
{
return LanguageName;
}
}
Object Language
has three properties:
- For system needed
LanguageName
equal to abbr. ‘en’ or ‘cs-CZ’ - For UI needed
NativeName
e.g. ‘Ceština’ for Czech - For better understanding the
EnglishName
Translator object
Translator has two properties ‘Key’ and ‘Area’ - for searching. Phrases for application languages are stored in a IDictionary
with key equal to language abbr. If new language is added to application, new item to IDictionary
is added. That’s all.
public class Translator : Persistent
{
IDictionary<string, string> _translations = new Dictionary<string, string>();
public virtual IDictionary<string, string> Translations
{
get { return _translations; }
set { _translations = value; }
}
public virtual string Key { get; set; }
public virtual string Area { get; set; }
public override string ToDisplay()
{
return Key;
}
protected override string GetDomainObjectSignature()
{
return Key + Area;
}
}
Cache
Translations are stored in application cache. They are loaded lazily – when they are needed. If there are any changes to language or translator objects made, cache is cleared.
For accessing storage Catharsis uses business Facades. Nothing surprising, this is the essence of framework communication (Façade – Dao – Storage).
Language switching
Catharsis allows users to change UI language in a few ways.
Default language
The first important place handling localization is the default language in application web.config
. There is magical element <globalization>
which can determine the UI language:
<globalization uiCulture='cs' culture='cs-CZ' />
This is working but a little bit brutal. The user's browser settings are now out of the play.
With a more gentle approach, we can reach similar result, but more comfortable for our user:
<globalization enableClientBasedCulture='true'
uiCulture='auto:cs' culture='auto:cs-CZ' />
This is what we want. We did set default language, which is as spoken above crucial for translations. But we (user) gained the ability to set application language via browser setting.
Browser language setting
Because of gentle setting in web.config
user can switch among languages using browser, selecting the language in options.
The translated phrases are changed after first postback (the same goes for numbers, dates … formats).
Settings in Firefox
But sometimes users cannot adjust settings on their PCs. Some restrictions can forbid some changes like 'Internet options' in Internet Explorer. Sad for user but reality in some organizations.
Settings in Internet Explorer
Language switcher on Catharsis UI
Luckily, Catharsis provides the second way how to set the language - just by clicking the language name in the upper right corner (if you are using Catharsis default layout).
This way of switching languages is a little bit different. Firstly, user clicks on a link, it means there is a response. Catharsis therefore does two things:
- Immediately changes the UI language (every translation or date, number etc. format is changed to asked culture)
- Transfers user to the page, from which the request was done. User is not transferred to some dummy page, what sometimes could be confusing.
But there is more. User selection is done once (opposite to browser language setting, which is appended to every request). Catharsis must store that clicked value. For that purposes the session is used as the storage. And also HttpModule
s starts to work – they are evaluated before every user request is handled, and set the CurrentCulture
to user selected value.
That behavior (and also the implementation) is simple. Now you know how it works.
But I guess that you will just:
Consume the fruits of Catharsis by calling GetLocalized()
in your code.
Insert knew ‘keys’ using Catharsis TranslatorController
UI
Grant access for ‘User or Translator’ to manage phrases, they want to see…
Enjoy Catharsis!
Sources
You can find all the needed source code on http://www.codeplex.com/Catharsis/.