Use the existing Resource file structure (*.resx files), which can be maintained in Visual Studio

Keep design-time support for localized elements working in Expression Blend (and hopefully other XAML design applications)

The example application has a few localized strings to show it working, but since there's only so many ways to demonstrate localized strings, I've kept it brief.

The original implementation presented in this article required a wrapper class around the set of properties, which was a hassle to either manually or auto-generate. This update removes the need for this wrapper, making this much easier to use.

There have been a number of other articles on this subject, including Localizing WPF Applications using Locbaml which covered different methods of localizing XAML files, each with their own advantages and disadvantages. The first approach in that article (Targeted Localization Without Using LocBaml.exe) got me thinking, but I had to diverge from that approach in order to trigger runtime auto-updating (for all elements, even on existing Windows).

Another earlier project is WPF Multi-Lingual at Runtime, which does provide a solution for runtime auto-updating, but to my mind introduces complexity in the management of the language resource files. By maintaining support for existing *.resx files in this article, we can still use existing applications to easily translate applications to new cultures.

I thoroughly recommend reading and understanding both these articles, there is a lot of information there, as well as useful tricks that have helped me a lot in other projects.

In order to get automatic updating when we change the current Culture, I am exploiting the characteristics of the ObjectDataProvider. The little gem of information from the MSDN page is:

"This class is also useful when you want to replace your current binding source object with another object and have all the associated bindings updated."

So all we need to do is replace (or refresh) the ObjectDataProvider object instance, and any bindings on the ODP properties will be updated automagically.

Here is where this solution has been improved. While it is possible to directly bind to the properties of the auto-generated RESX designer class (Resources.Designer.cs), we also need to get an instance of this class for the ODP. All ResXFileCodeGenerators (both default and custom) mark the Resources constructor as internal, meaning it can only be accessed from within the same assembly. (This also means the constructor cannot be accessed from XAML - i.e. using an ODP ObjectType.) To solve this, we can use the MethodName property on the ODP. (An alternative might be to extend an existing custom ResXFileCodeGenerator to mark the constructor public, but there is no need for that.)...

Use of MethodName means that the ODP will become the object returned by the method, allowing us to bind an instance of the Resources class. We can create this instance because the call to the internal constructor above is coming from within the same assembly, not directly from XAML. Works like a charm.

One constraint for this to work is that the Resources class must be public, as we cannot return an instance of an internal class using a public method (this gives a lovely compiler error). This means we could use either the Extended Strongly Typed Resource Generator[^] which works in both Visual Studio 2005 & 2008, or the PublicResXFileCodeGenerator tool that ships with Visual Studio 2008. I like the Extended Code Generator as it generates super helpful string formatting methods as a bonus.

publicstaticvoid ChangeCulture(CultureInfo culture)
{
//remain on the current culture if the desired culture cannot be found// - otherwise it would revert to the default resources set,// which may or may not be desired.if (pSupportedCultures.Contains(culture))
{
Properties.Resources.Culture = culture;
ResourceProvider.Refresh();
}
else
Debug.WriteLine("Culture [{0}] not available", culture);
}

Updating the current culture is quite simple, I have added a method to the CultureResources class that updates the current Resources Culture and triggers an update on the ObjectDataProvider, causing it to call the GetResourceInstance method, updating the ODP ObjectInstance, which refreshes any bindings on the ODP - which are updated to the new resource values.

At design-time, Properties.Resources.Culture is initially set to the Neutral Language set in the project, or the Culture of the current thread if a Neutral Language has not been set. Either way, any bindings will default to the strings in the default Resources file (Resources.resx).

All strings that you want to localize need to be defined in all Resource files for localization to work, so it is generally easier to add more cultures after you have set everything up with the default Resources.resx file. Otherwise you would need to add each new string to all existing RESX files.

And if we have added this resource string to the default Resources RESX file, after recompiling the project this default string value should now show up in the designer, and also of course when you run the application.

If you see the case that a string added for a Resources file other than the default seems to always show the default value, check that the resource string name in each RESX file is correct. If you have binding errors, then the Path set in the Binding does not match any strings in any of the RESX files, and it cannot even fall back to the default RESX value as in the previous case.

A simple way to add another culture to your project is to Copy & Paste the default Resources.resx file in Visual Studio to create a new file. Select a suitable culture code from the list on the MSDN CultureInfo reference page. Add the culture code in the extension as in Resources.Fr-fr.resx, which Visual Studio will use to create the localized DLL when the application is built. Now that you have a new RESX file, you can change the resource values for the new culture and you're done.

With a number of cultures added to this project, the code used to enumerate those we have implemented can be demonstrated. I have made this dynamic to avoid any need to rebuild the application when a new culture is added. For an existing installed copy, you just need to create a folder with the new culture name and put the new correctly named resources DLL inside. Restart the application and it will be available (or if you are importing a culture from within the application, this method could be modified to search the install directory again).

The above is a relatively quick way of checking the applications bin directory for any folders that match Culture names. The CultureInfo.GetCultureInfo method will fail as desired if the string argument does not match any of the defined CultureInfo types.

A problem arose for me in the veiled form of the UserControl. If the properties that you want to localize are accessible external to the user control (added to the code-behind file as dependency properties), then there is no problem, you can localize them as described above. However, if the properties you want to localize are not externally accessible, such as a Label Content property, then the solution is a little trickier.

When you add a binding to a label inside a UserControl as above, it will be rendered correctly at runtime, and also at design-time (e.g. in Blend) when it is loaded up by itself. Unfortunately it will fail to render correctly when you load up a window that contains the UserControl. (This seems to only be a problem in Expression Blend, the Visual Studio 2008 Designer will render correctly in this situation.)

I understand that the problem when loading a UserControl as a child of a Window is that the designer creates an instance of the control and then adds it to the Window. The Resources that would be available at runtime are not present because the instance is not created from within the window, and so the above binding fails and the control cannot be rendered. After many failures trying to work around this situation, I eventually came up with the following:

public UserControl2()
{
#if DEBUG
//only perform the following fix if we are in the designer// - the default ctor is not executed when editing the usercontrol,// but is executed when usercontrol has been added to a window/page// NB. The Visual Studio designer might return null for Application.Current// http://msdn.microsoft.com/en-us/library/bb546934.aspxif (DesignerProperties.GetIsInDesignMode(this) && Application.Current != null)
{
Uri resourceLocater =
new System.Uri("/WPFLocalize;component/ResourceDictionary1.xaml",
UriKind.Relative);
ResourceDictionary dictionary =
(ResourceDictionary)Application.LoadComponent(resourceLocater);
//add the resourcedictionary containing our Resources ODP to//App.Current (which is the Designer / Blend)if (!Application.Current.Resources.MergedDictionaries.Contains(dictionary))
Application.Current.Resources.MergedDictionaries.Add(dictionary);
}
#endif
this.InitializeComponent();
}

Using the DesignerProperties.GetIsInDesignMode() means this code is only executed at design-time, and all it's doing is adding the ResourceDictionary that contains our Resources ObjectDataProvider to the designer itself, so that they will be available when the UserControl is initialized. This will actually be a second instance of the ODP, which would be bad at runtime (as only the first one included in App.xaml would be updated), but fine at design-time as we will not be updating the culture. Problem solved.

In this example I am using WPF Bindings, which require Dependency Properties to bind to. There are other cases where you might want to access these properties, but adding a binding is not appropriate or easy to accomplish. An example of this is when you want to access a localized value direct from code. In order to keep auto-updating working in this case, you can hook an eventhandler on the ObjectDataProvider DataChanged event, which is triggered after we update the ODP. So, when values are re-fetched in the eventhandler the updated resource values are available. Alternatively you can just make sure you re-fetch the localized value after you know the ODP has been updated, there is little difference.

Hello Andrew,
I am currently developing a WPF desktop application that needs to be localized. I found your solution very nice and I implemented it successfully. My application is a little different from yours. It contains several specialized projects: one for the main application, written in VB.Net and several others for the realization of UserControls Libraries WPF, in C #.
This is the main application that instantiates the UsersControls and each manages its location under the control of the main application that calls each of its UserControls. Everything works perfectly, however ...
I'm using VS 2017 Community version 15.9.5 to develop the solution and it always says, for each UserControl: "Can not find part of the C: \ Program Files (x86) path \ Microsoft Visual Studio \ 2017 \ WpfControlLib \ Common7 \ IDE" and, in design mode, none of the UserControl can be viewed in the application. In their place appears a message "Can not create an instance". Note that here my UserControl is called WpfControlLib.
Do you have an explanation for this defect? For my part I think it is due to the removal of the Resource.resx file initially generated by VS when creating each UserControl and replaced by the resources in the Cultures directory. Do you know how to improve the situation?
Thank you again for your work.

Hi Peter,
Sorry my reply is beyond late - don't think I ever saw the notification?
At any rate, I have updated the sample code to work out of the box in VS2017.
I was relieved to find after only a few minor tweaks the method still works 10 years later

You are absolutely right, I pulled down the source and found the same problem.
Tried updating both the Demo and the Satellite projects to .Net 4.5 and it works again.
I'm sure it used to work with the older .Net version (3.5), but perhaps something more recent affects that.
I'll update the sample with this change.

I think this is possible, you will need to make another ObjectDataProvider and GetResourcesInstance() method for each Resources file. GetResourcesInstance() needs to return an instance of each different Resources file.

If I remember correctly, the Visual Studio designer doesn't load resources from App.xaml at design time, only at runtime. So when you eg. load up a Window xaml file, you only see resources that the window can loads directly - either included in the window resources or in resources included by custom/user controls on the window.

Perhaps some code can be put in the window constructor to include the resources from App.xaml, but only to run at design time - similar to the code in the article for handling User controls at design time?

At runtime resources in App.xaml are loaded first, so it should all be working correctly already?

Yes, at runtime it's working perfect. The problem comes with the design mode, but I also read that if I load a ResourceDictionary from App.xaml, for Visual Studio 2010 it's not solved yet to show styles on design time.

My real problem comes:

My UserControls, at runtime, don't change language... If I'm debugging at Debug or Release mode, it works fine. But if I execute the App, the UserControls I created ignore the language I chose. Any idea on why?

As mentioned, for design-time to work with usercontrols you would need to add code similar to section in the article under "Design-time solution for usercontrols". This uses GetIsInDesignMode so that the code only runs at design-time, then loads the Resource Dictionaries needed for the usercontrol manually.

I'm not sure if it's practical to try to build something that does this automatically (eg. perhaps at design-time, before InitializeCompoent is called, try to automatically load all resource dictionaries from App.xaml), I'm sure there would be cases where this still wouldn't work. Might have a play with this sometime, but not sure when that would be.

As for not working when you run the app outside visual studio - are you running the same exe from the VS output folder, or are you copying the exe somewhere else? If you are copying the exe somewhere else, make sure you copy all the resources folders as well (eg. "fr-FR" for french, the resources dlls are inside these folders). If these do not exist in the same location as the exe when you run it, the culturew ould not change as you are seeing.

One idea to check if this doesn't help - does it work when you run the exe directly from the Debug/Release output folder?

Some ideas:
- Try disabling the Visual Studio Hosting process - exe project settings, debug tab. Am curious whether this would lead to the same behaviour in the debugger as when running exe directly.
- Are you using Debugger.IsAttached anywhere? Could be leading to the behaviour you are seeing.

First of all thank you for a great post even after three years (more or less).

This has shed some light over me since I was not managing to get it to work. I ended up doing some changes and some points where not very clear like:
- Changing the resources file to a custom folder but leaving the namespace pointing to Properties
- The necessity of the ComboBox handler being added on the begining before setting the default values (allows values to filled in first, without it, no values will be found)

Anyway I will set the code straight and I can send it to you for an update.

You are right, it really doesn't clearly explain what to do if you move the resource files to a different folder. As you have probably found you need to set the Custom Tool Namespace on the resources files to correct this. The sample code I does show an example of this though, where the resource files are all in the Cultures folder.

I'm not totally sure what you mean about the Combobox handler - do you mean that the binding needs to have been added before setting the default selected value? I would be interested to see an example of this, a code snippet here would be fine.

Just to say that I managed to make it work on WPF with VS2010 SP1 and Windows 7. There where some tricky parts though like having to set the resources namespace and using the event handler to detect changes and doing nothing with the handler. Great help and thank you for a great post (even after three years)

I am not sure what sort of problem you seem to be having. Does the sample code work ok? If so then there might be a typo - make sure the Binding matches the string you are trying to bind to, it must match exactly (uppercase characters too). Visual Studio should give you a pointer as to which Binding is affected.

If you are still having problems perhaps you could post some code snippets here - the Binding string, the resource name and any other description about how you are using this code would be helpful.