Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Main assembly versus Satellite assembly problems when fetching both XAML and RESX resources in C# .Net

In a localized program containing a culture neutral XAML file, some culture neutral icons and localized strings, how do you organize these different resource types so they can all be found? No matter what arrangement I try, I find that one of my resource types is inaccessible.

The problem that I'm having is:

  • The MainWindow.xaml file always builds into the en-US satellite assembly. If the UltimateResourceFallbackLocation is set to MainAssembly, it will never find the Window's BAML and I get an exception in the InitializeComponent() call. So I feel forced to set UltimateResourceFallbackLocation to "Satellite".

  • The culture neutral icon resources contained in the Resources.resx file always builds into the main EXE assembly and cannot be found if the UltimateResourceFallbackLocation is set to Satellite. This would appear to be totally incompatible with the requirements of the MainWindow.xaml file.

  • If I remove the UICulture and NeutralResourcesLanguage entirely – which forces the XAML and RESX data to both build into the MainAssembly, then my culture-specific strings don't work.

The question is: what am I doing wrong? How am I supposed to build the project so that these three types of resources are all accessible.

Edit (working solution but it seems wrong):

I have managed to get my culture neutral RESX file, Resources.resx, to build into the Satellite assembly by duplicating it entirely, renaming the duplicate Resources.en-US.resx and setting Resources.resx to Build Action:None (so Resources.resx is only used for generating the Resources.Designer.cs file but doesn't insert data into the Main EXE assembly anymore).

The program does now work for all three cases (localized strings, non-localized data from resx and non-localized data from XAML) since all my culture neutral resources are now in the en-US assembly – but duplicating the Resources.resx file to achieve this seems pretty silly.

Is it silly? Is there a smarter way to do this?

like image 760
Matt Gallagher Avatar asked Dec 10 '11 05:12

Matt Gallagher


2 Answers

Localization with WPF is very troublesome due to the bad documentation. I spent hours for some insights.

The MainWindow.xaml file always builds into the en-US satellite assembly. If the UltimateResourceFallbackLocation is set to MainAssembly, it will never find the Window's BAML and I get an exception in the InitializeComponent() call. So I feel forced to set UltimateResourceFallbackLocation to "Satellite".

This is due to the fact that your project.vcproj file probably contains <UICulture>en-US</UICulture>. This essentially tells VS to generate stellite assemblies (you see the folder en-US is being created with the according satellite in it).

UltimateResourceFallbackLocation.MainAssembly now tells .net to speed up finding the localization info by using the embedded one instead of the one in the matching directory. If your systems culture is en-US it would explain this behaviour perfectly.

quoting Kim Hamilton:

... then the ResourceManager looks for "en-US" resources directly in the main assembly, instead of searching first in an “en-US” folder. Since resource probes can be expensive, this attribute can help improve perf.

Since you've found a solution for your second bulletpoint there is only one left:

If I remove the UICulture and NeutralResourcesLanguage entirely – which forces the XAML and RESX data to both build into the MainAssembly, then my culture-specific strings don't work.

Actually WPF still does localization: When you don't have an entry like <UICulture>en-US</UICulture> in the .vcproj file wpf still tries to find a satellite in a directory having a name matching to System.Threading.Thread.CurrentThread.CurrentUICulture. It it is found, it is being used. Except, of course, if your neutrallanguage is set to your system language and you use the main assembly fallback strategy. For you to test the localization on your en-US system you should pick some other culture, e.g. ja-JP as the neutral language. Then provide the en-US folder with the according satellite in it.

So what I am using for localization and recommend is:

  1. dont have <UICulture>en-US</UICulture> in the .vcproj file
  2. use [assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.MainAssembly)]
  3. provide all target-culture directories with matching satellites.
like image 151
codingdave Avatar answered Nov 09 '22 16:11

codingdave


I believe an arbitrary "smarter" alternative is to use the Assembly Linker (Al.exe) to compile .resources files into satellite assemblies as outlined in this article.

** Update **

Examples used in that article:

This Assembly Linker (Al.exe) command creates a satellite assembly for the application MyApp from the file strings.de.resources.

al /t:lib /embed:strings.de.resources /culture:de /out:MyApp.resources.dll

This Assembly Linker (Al.exe) command also creates a satellite assembly for the application MyApp from the file strings.de.resources. The /template option causes the satellite assembly to inherit assembly metadata from the parent assembly MyApp.dll.

al /t:lib /embed:strings.de.resources /culture:de /out:MyApp.resources.dll
/template:MyApp.dll
like image 2
chridam Avatar answered Nov 09 '22 18:11

chridam