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;
}
|
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?
|
|
|
|
Bo Skjoett | |
|
Commented on 9.October 2010 |
Is it possible to have design time support? I would like to see the default text (en-US locale) during design time in Visual Studio instead of the "!TEXT_KEY!" syntax.
|
|
|
|
Bo Skjoett | |
|
Commented on 9.October 2010 |
Is it possible to have design time support? I would like to see the default text (en-US locale) during design time in Visual Studio instead of the "!TEXT_KEY!" syntax.
|
|
|
|
Gnought | |
|
Commented on 18.October 2010 |
@Sebastian, did you try?
Closing and destroying a form will call Dispose() method which calls Disposing(true) so the listener will still listen 1 event.
|
|
|
|
Steven | |
|
Commented on 8.November 2010 |
Why is this better than simply using something like
xmlns:resx="clr-namespace:MyApp.Properties"
Text="{x:Static resx:Resources.Title}
|
|
|
|
Doc | |
|
Commented on 23.December 2010 |
Great article, thanks for that!
But I still wonder, is there an easy way to create this {Translator Resource Binding} dynamically in the code?
|
|
|
|
Steve G. | |
|
Commented on 9.January 2011 |
Awesome article! I was struggling to get a dynamic way to switch languages and did not want to deal with LocBaml. Your classes work great.
|
|
|
|
Griz | |
|
Commented on 26.January 2011 |
Wonderful article.
FYI, From code I was able to use
TextBox.Text = TranslationManager.Instance.Translate(tranlationKey) as string;
|
|
|
|
Griz | |
|
Commented on 26.January 2011 |
Continuing... to get the value in the current culture.
And to bind from code, this worked
var binding = new Binding("Value")
{
Source = new TranslationData(tranlationKey)
};
BindingOperations.SetBinding(TextBox, TextBox.TextProperty, binding);
which both methods were taken from the Translate classes in the sample.
|
|
|
|
Spook | |
|
Commented on 11.February 2011 |
Can you tell more about the memory management in this example? Why do you use WeakEventListener instead of simple delegation? And why one shall implement both Finalize and Dispose methods? I am not familiar with such resource allocation and collection issues :(
|
|
|
|
Flo | |
|
Commented on 11.February 2011 |
Do I have to do any aditional linking to get the .resource files or the .dll they are in linked to the assembly? My app continiosly tells me it can't find <Namespace>.Resource.resources but the files are created in the bin directory and then correctly packed into a dll which is found in the debug directory.
lg flo
|
|
|
|
Wom | |
|
Commented on 24.February 2011 |
Is there a reason why in your sample project the languages are displayed in the combobox as 'German' and 'Dutch' while in my own project it just stays 'nl' and 'de'?
|
|
|
|
Klaus | |
|
Commented on 24.March 2011 |
Hi
Is there a way to use this extension in a multibinding?
I want to have a label that consists of a text from the markup extension followed by a text that comes from a second control (im my example: datagrid.items.count.
The resulting label should be "Number of records: 123" where "Number of records:" comes from the extension and "123" is the number of records in a datagrid on the same window
Regards
|
|
|
|
Christian Moser | |
|
Commented on 24.March 2011 |
Hi Klaus,
since the TranslationExtension returns a binding, you can use it like a binding in a multibinding. If you are using .NET 4.0 you can profit of the new bindable <Run> within a <TextBlock> to combine fragments to one string.
Greetings
Christian
|
|
|
|
Stefan | |
|
Commented on 6.April 2011 |
I like that very much - Small, lean straight forward. Thanks a lot for sharing - learned some new concepts on your code!
|
|
|
|
James Hurst | |
|
Commented on 26.April 2011 |
Nice work Christian. Can you tell me, though, why the languages in my sample-project display in the ComboBox as "en" and "de" while they display in your project as "English" and "German"? I've been digging and searching and can't figure out why! I would expect the binding (to that ComboBox) to use the ToString() method on the CultureInfo objects by default, which yields the "de" and "en" values. The solution that comes to my mind is to add a ValueConverter. But I would like to know how you got yours to automatically show the EnglishName property in that ComboBox?!
Thank you for your advice, James Hurst
|
|
|
|
Manuel Strausz | |
|
Commented on 3.May 2011 |
Hey James,
I'm not sure if this will help you but I did it with a ValueConverter which gets applied on each item like so in xaml notation:
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Converter={StaticResource TranslationConverter}}"/>
</DataTemplate>
</ComboBox.ItemTemplate>
Where TranslationConverter is a simple converter which will translate the values given by the combobox by using the TranslationManager directly. Could be useful for other things as well, I suppose.
|
|
|
|
Hari | |
|
Commented on 20.May 2011 |
A great article.
I have one question on the use of this markup extension with binding.
For ex.
We often have situtations to bind Text property to view model property
<TextBlock Text="{Binding CustomerType}" />
Assuming we need translation for CustomerType
<TextBlock Text="{l:Translate {Binding CustomerType}}" /> does not work.
I could use a value converter to do this. However, I would like keep the option open as the value converter may be used for converting from a specific object to string. Also, if we use value converters, dynamic language switching will not work
|
|
|
|
jones | |
|
Commented on 14.June 2011 |
I observed that if you choose the .Net Framework 4 instead of 3.5, as in the example code, the combobox for the language selection shows only the short names of the culture (de, en) instead of the complete name (Deutsch, English).
Can you please show, how to display the full name when using .net Framework 4?
|
|
|
|
ا... | |
|
Commented on 20.July 2011 |
تاناتناتن
|
|
|
|
apo | |
|
Commented on 12.September 2011 |
Hi Christian,
great job! is your code restricted to any license?
|
|
|
|
apo | |
|
Commented on 12.September 2011 |
Hi Christian,
great job! is your code restricted to any license?
|
|
|
|
Great article. | |
|
Commented on 17.September 2011 |
How about if i want to use the translation manager in different assemblies (which consists of different resource files?
Example:
Assembly.exe (resource.de.resx:MyString="Foo A")
AssemblyA.dll (resource.de.resx:MyString="Foo B")
AssemblyB.dll (resource.de.resx:MyString="Foo C")
|
|
|
|
Beck | |
|
Commented on 20.September 2011 |
Great post!
Do you know of a way to show a default value on the designer window, currently it is littered with the key values?
Thanks in advance
|
|
|