If there’s one thing that’s confusing as heck about Localization in WPF it’s how resources are loaded. The two major approaches for localization in WPF – using LocBaml BAML resource localization or standard Resx Localization with MarkupExtensions – stand at odds in what they expect for their resource loading requirements.
BAML Resources
When using the LocBaml approach that creates localized BAML resources it’s necessary to specify the <UICulture> key in the project file:
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="3.5" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<UICulture>en-US</UICulture>
…
You also need to specify at the assembly level what the neutral culture is and that ALL resources need to be stored in external satellite resources including the neutral culture:
[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)]
This setting tells the runtime to look for the final fallback not in the MainAssembly, but in a satellite assembly.For BAML localization both of these settings must be made or localized BAML content won’t load. Effectively these two settings result in no resources being stored in the main assembly.
When both settings are made the compiler emits the default BAML resources – basically those XAML documents you’ve created – into an en-US folder in the target output folder. When enabled WPF will expect resources to be loaded from these resource directories including the default ‘neutral’ resources.
If localization is enabled and you try to set the UltimateFallbackLocation.MainAssembly you’ll run into the unfortunate result that your BAML Resources could not be found even for the neutral/default language. MainAssembly is the default setting for the flag, but it will only work if localization is not enabled and the <UICulture> flag is not set in the project file.
Bottom line: If you are localizing BAML resources both settings are required and you have to use satellite assemblies for everything including the neutral culture.
Resx Resources and BAML Resources in the same Project: Resource Hell
LocBaml is a pretty unstable solution that has lots of issues, but fortunately Resx resources also still work in WPF. You can use Resx resources through code, by binding static resource values ( Content=”{x:static res:Resources.HelloWorld}”) to strongly typed resources or by using one of the many custom localization markup extensions that are available.
Here’s where things get confusing though – if you are mixing approaches with BAML and Resx localization you need to be real careful how to set up your Resx resources because of the required UltimateResourceFallbackLocation.Satellite setting used.
Specifically at design time you need to create a non-culture specific .resx file like Resources.resx. You can then create your culture specific versions as well (Resources.de.resx). Normally you’d expect that’s enough, but because of the Satellite export of resources even the neutral culture gets exported to an external resource assembly in the en-US folder. This means in addition to Resources.resx you also need to have Resources.en-US.resx in your project! At runtime .NET only looks for the en-US version for that particular culture as well as the fallback culture when a specific or non-specific culture can’t be mapped.
Assuming my neutral culture is en-US here’s what this looks like in the VS Project:
Notice both ResxResources.resx and ResxResource.en-US.resx. The former is never used at runtime, but required in order to provide the strongly typed class.
You might think that why can’t we just skip the ResxResource.resx file and JUST create the the ResxResource.en-US.resx file. While that works just fine for compilation at runtime, if you want strongly typed resources the non-culture version of the resources is the only one that generates the strongly typed resource class.
Bottom Line: You’ll want to work with Resource.resx and then also copy that file to Resources.en-US.resx when keys change or use a build task to automate this process.
That’s pretty annoying, but it gets even worse: Microsoft Blend doesn’t load resources from satellite assemblies and since this approach effectively requires that resources are loaded from satellite assemblies any binding against resources or use of markup extensions that retrieve even neutral resources in Blend will fail with an error that Resources could not be loaded. Now that’s a big bummer especially after building a markup extension and having it work in the Visual Studio Cider designer.
NOTE: This tedious copy process is required only if you use both localized BAML and Resx resources in the same project and you have the <UICulture> key set in your project file.
Running only Resx Resources
Things are much easier if you don’t localize BAML resources and only work with Resx resources. In this scenario don’t specify the <UICulture> key in your project file or remove it if it was there before. Then set your assembly NeutralResourcesLanguage attribute to compile the neutral resources into the main assembly:
[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.MainAssembly)]
In this set up you can work with resources the way you always have in WinForms and classlibrary projects where neutral resources are retrieved from the main assembly and any localized resources are retrieved from satellite assemblies. This means you just create your neutral resources in Resources.resx (or whatever other file) and create only your translated cultures in Resources.de.resx and so on. No copying for the explicitly fallback culture. Nice and logical the way you would expect it to work.
With the neutral fallback resources stored in the main assembly Expression Blend is also happy and now loads static resources and markup extension bound resources without issues.
Confused?
If you think all of this sounds confusing as hell you’re not alone. While working on this I found dozens of questions regarding resource load failures. This stuff isn’t real obvious and worst of all the behavior that BAML resources introduces are partially incompatible with the standard Resx resources. It also doesn’t help that Microsoft has practically no guidance on this matter. Oh wait – I’m supposed to be writing that :-}
Hopefully this will help some of you out. I know I wish had seen something along these lines a couple of weeks back when I was ramming my head against the wall wondering why BAML resources were working and Resx Resources continued to fail. The more I see the more I wonder who should be strapped to a wheel for throwing LocBaml into the localization mix for WPF. I’ll have more on that in my next post.
Other Posts you might also like