Saturday, June 09, 2007

Using satellite assemblies in SharePoint applications (with a twist)

What I like about the way SharePoint stores localization resource files is that all strings are stored in central location. This makes the maintenance a bit easier (you don't need to hunt down all files that need translation) and allows ad-hoc changes without the need to recompile. On the other hand it's kind of hard to make strongly-typed notation (Resources.[file prefix].[resource name].) work and the time when the resources are auto-compiled can be a bit unpredictable.

To find a solution, which allows me to create centralized language assembly for all strings and still be able to use strongly-typed notation I looked at another feature of the .Net framework - the satellite assemblies.

Create a resource file in an assembly (e.g. Strings.resx), then add language specific version (Strings.en-us.resx), and in the build folder the compiler will create a sub folder with the locale id to store language a specific extension of the assembly. The limitation of this approach is that the automatically generated class is declared as private thus we cannot reference the resources from different modules in our SharePoint application.

Trying to find a solution to this limitation I noticed one feature of the tool ResGen.exe, which compiles the resources. The command line parameter /publicClass allows us to generate the resources class as a public class.

To make this work I added the following line to my pre-build commands:

ResGen.exe /str:cs,<namespace> /publicClass "$(ProjectDir)Strings.resx"

Then I added the main assembly and all satellite assemblies to the SharePoint solution manifest. In addition the rest of the application modules can use the localization assembly simply by adding a reference to it.

<Assembly DeploymentTarget="WebApplication" Location="Localization.dll" />
<Assembly DeploymentTarget="WebApplication" Location="en-USLocalization.resources.dll" />

Compared to the use of global resources this approach does not allow the use of explicit expressions <%$ Resources:[filename prefix,]resource-key %> in ASPX pages, but I found that this is not a big limitation, especially if the development style of your team is more server side oriented.

Dovizhdane!

5 comments:

Philipp Schumann said...

Doesn't work for me under VS 2008 and VSeWSS 1.2 with a DeploymentTarget of GlobalAssemblyCache: "Cannot find this file specified in the manifest file: de\MyAssembly.resources.dll".

Assuming that maybe the base path here isn't bin\debug but bin\debug\solution, I tried ..\de\MyAssembly.resources.dll, which yielded the same error. Since bin\debug\solution is being recreated during a build\deploy, no use trying to manually copy the resource assembly there either.

I won't switch to a WebApplication deployment target here, the presence of the assemblies in the GAC is crucial in this instance.

The only other content on this topic I found was on http://my.safaribooksonline.com/9780735623200/ch09lev1sec6 where they suggest that another way of deploying resource DLLs is to created another solution package acting as a "language pack" and only containing the localized additions to the base, language-neutral solution package. Will give it a go.

Mikhail Dikov said...

Not sure how this works in VSeWSS, I personally don't use it because it has too many issues.

The issue in your case might be that you are putting the satellite assembly in the wrong sub folder. It should go in sub folder "de-De" not "de" if you deploy in the bin. However I am not sure how this works with GAC deployment.

Philipp Schumann said...

Well, I'm not *putting* the .resources.dll in the "de" folder, VS does -- because I have a Resources.de.resx file. That said, in *theory* de should be just as valid as de-de, being a base locale that covers all de-* users (ie. de-de, de-at, de-ch). Then again, in *practice* you may well be right.

Given the fact that apparently a .resources.dll file apparently can be gac-installed just like any other assembly, that's sufficient now for my needs. At some point, I might create a "language pack" WSP, for now, telling people where to stick the .resources.dll will be sufficient.

admin said...

Did you find any way to localize a feature using a satellite assembly? I prefer to have all the text resources in one place and therefore I used an resource file deployed on the 14/Resource folder so far. But I think that with satellite assemblies it would work faster.

Mikhail Dikov said...

There is no difference than any other satellite assembly deployment. Create your resources for each language and deploy all of them to the corresponding language sub folder in \bin.