A simple and effective way to localize application resources is to write a custom MarkupExtension that provides a localized value. The extension takes a parameter in the constructor that is the unique resource key. When the DepdendencyProperty asks for the value, the markup extension looks up the value from a generic resource provider. This gives you the flexibility to reuse the resource management and translation process that is already established within your company.
The idea of using a markup extension for localization is not unique by me. There are also other similar implementations out there. Here you find some links:
The usage of the markup extension is very simple. Just replace the string you want to localize by {l:Translate resourceKey}.
public class TranslationData : IWeakEventListener,
INotifyPropertyChanged, IDisposable
{
private string _key;
public TranslationData( string key)
{
_key = key;
LanguageChangedEventManager.AddListener(
TranslationManager.Instance, this);
}
~TranslationData()
{
Dispose(false);
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
LanguageChangedEventManager.RemoveListener(
TranslationManager.Instance, this);
}
}
public object Value
{
get
{
return TranslationManager.Instance.Translate(_key);
}
}
public bool ReceiveWeakEvent(Type managerType,
object sender, EventArgs e)
{
if (managerType == typeof(LanguageChangedEventManager))
{
OnLanguageChanged(sender, e);
return true;
}
return false;
}
private void OnLanguageChanged(object sender, EventArgs e)
{
if( PropertyChanged != null )
{
PropertyChanged( this, new PropertyChangedEventArgs("Value"));
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
 |
| Christian Moser | |
|
| Commented on 12.May 2009 |
Hi Simon,
you can use it in any WPF elements - also for ContextMenus. Just get the Header of a MenuItem from the TranslateExtension.
|
|
|
 |
| John | |
|
| Commented on 12.May 2009 |
| I cannot download your sample project. Using IE7.
|
|
|
 |
| Simon | |
|
| Commented on 14.May 2009 |
| The problem with e.g. context menu is that datacontext is not flowed or something like that, and thus I have been unable to get it to work.
|
|
|
 |
| Greg | |
|
| Commented on 14.May 2009 |
Exactly what I was looking for. End to locbaml woes.
The sample seems to be wired directly into Localize. Not what you have in the article above, making it much more developer friendly.
Great job, Christian. Thanks
|
|
|
 |
| Boris | |
|
| Commented on 20.May 2009 |
| Correct me if I'm wrong but this will give you a nice memeory leak...
|
|
|
 |
| Christian Moser | |
|
| Commented on 20.May 2009 |
Hi Boris,
you are right. You talk about the event registration in the markup extension? I have to find a solution for this. Probably a weak event can solve the issue.
|
|
|
 |
| Chris Sullivan | |
|
| Commented on 27.July 2009 |
| Good solution, one of the better one's I've seen mainly for it's simplicity. Also like the fact the "resource" is held as a seperate project which makes it a more realistic solution. Thanks for taking the time and effort to create the solution and share with others.
|
|
|
 |
| SeriousM | |
|
| Commented on 17.August 2009 |
Hello!
This is a nice approach that i have developed some months ago (4. May 08).
Have a look at this please: http://wpflocalizeextension.codeplex.com
|
|
|
 |
| Stephen | |
|
| Commented on 14.September 2009 |
| Hi Christian, I have a question. I have downloaded the example app. When I ran it the Hebrew text was squares, but when viewing the resx I see the Hebrew text. This seed strange. Cah you clue me in on what might be going on here? Thanks. Nice site by the way. Thanks for that as well.
|
|
|
 |
| Alex | |
|
| Commented on 16.September 2009 |
| Is it only me or doing the simplest thing with wpf suposses to turn the down the whole world !? Those things were simpler before !!
|
|
|
 |
| Vytas | |
|
| Commented on 21.October 2009 |
What is "Localize" in 'Title="{Localize ...'
That's the only thing I can not understand and I can't apply this example to my application. Besides, the article is Totaly different from the example, but I think you know that...
|
|
|
 |
| Raul | |
|
| Commented on 13.November 2009 |
Hi Christian,
I downloaded your project and it works fine, but when i added another culture SupportedCultures it doesn't work. I have also made the respective resource files in 'LocalizationResources'. Any idea what I might be doing wrong?
|
|
|
 |
| MGA | |
|
| Commented on 23.November 2009 |
| This is the best sample for localization! Thanks so much!
|
|
|
 |
| Spock | |
|
| Commented on 24.November 2009 |
I cannot figure out how the localize markup extension is being called by
What is "Localize" in 'Title="{Localize ...'
It works in the example, but not being able to call the extension in my app is frustrating. Can Christian or someone else clarify, please? Gred, how is the sample wired into Localize? Thanks and live long and prosper
|
|
|
 |
| Prabu P | |
|
| Commented on 14.December 2009 |
Hello Christian
Excellent solution for WPF localization using Resources. Keep posting such nice articles and happy coding :)
|
|
|
 |
| Prabu P | |
|
| Commented on 14.December 2009 |
Hi Spock,
"What is "Localize" in 'Title="{Localize ...' "
"Localize" represents "LocalizeExtension" class name. You may or maynot use "Extension" keyword in Markup representation.
|
|
|
 |
| Gil | |
|
| Commented on 18.February 2010 |
Nice article Christian.
This method is very good for Xaml, but is there still a way to use strongly-typed resources FROM CODE e.g.:
myTitle = LocalizationResources.ExampleResources.ApplicationTitle;
?
|
|
|
 |
| linsong | |
|
| Commented on 16.March 2010 |
Hi Christian,
Thanks for the article. However, there is an error on the dtor of TranslationData when attempting to remove the listener from the source when the line "var manager = WeakEventManager.GetCurrentManager(managerType);" is executed. To fix the problem, I suggest adding a GC.Suppress to the TranslationData ctor as the following:
// fix the error that "Handle is not initialised" when
// calling LanguageChangedEventManager.CurrentManager
GC.SuppressFinalize(this);
|
|
|
 |
| Gnought | |
|
| Commented on 9.April 2010 |
@linsong: Yes, you're right. The code will generate a "Handler is not initialized" error when the window is closed.
This error is generated from the finalizer in the TranslationData class. The faulty line is as follows:
LanguageChangedEventManager.RemoveListener(
TranslationManager.Instance, this);
The finalizer called by GC collects unmanaged resources, but I think that the above code is an unmanaged resource, and it should be called under Dispose() method.
I rewrite it with a Finalizer-Dispose pattern as follows to solve perfectly:
/// <summary>
/// Releases unmanaged resources and performs other cleanup operations before the
/// <see cref="TranslationData"/> is reclaimed by garbage collection.
/// </summary>
~TranslationData()
{
Dispose(false);
}
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
// Code to dispose the managed resources of the class
LanguageChangedEventManager.RemoveListener(TranslationManager.Instance, this);
}
// Code to dispose the un-managed resources of the class
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
|
|
|
 |
| Gnought | |
|
| Commented on 9.April 2010 |
| P.S.: the TranslationData class should implement IDisposable interface
|
|
|
 |
| Christian Moser | |
|
| Commented on 9.April 2010 |
@Gnought: Thank you for the solution. I added it to the code sample
@SeriousM: I added a link to your codeplex site.
|
|
|
 |
| SeriousM | |
|
| Commented on 14.April 2010 |
Hello Christian,
thank you for adding my project link to your article!
just one thing: my name is "Bernhard Millauer" :)
thank you!
|
|
|
 |
| Juliana | |
|
| Commented on 21.May 2010 |
I created a translator project and have generated a dll.
I created another one and am trying to use the translation.
Could tell me if I have to do some configuration, so the project recognizes the project resource translator?
|
|
|
 |
| Juliana | |
|
| Commented on 21.May 2010 |
I created a translator project and have generated a dll.
I created another one and am trying to use the translation.
Could tell me if I have to do some configuration, so the project recognizes the project resource translator?
|
|
|
 |
| Sebastian | |
|
| Commented on 20.August 2010 |
@Gnought: Your solution is working, because the "removeListener" isn't executed, isn't it?
in your solution it will be called, if dispose is called with "disposing=true"
if (disposing)
{
// Code to dispose the managed resources of the class
LanguageChangedEventManager.RemoveListener(TranslationManager.Instance, this);
}
but the finalizer calls the dispose with "false".
another question:
shouldn't the "removeListener" function be called, after closing and destroying a form that uses the specific MarkupExtension. If you create the same Window 3 times. The Listener will be added 3 times in a row. And will only be removed after closing the application. It this intended?
|
|
|