After trying a lot of stuff on how to translate/localize XML declarations and code in SharePoint 2010, I came across a bad topic: translated resource files have to be complete!
Assuming that the following structure exists:
- Foo.resx
- HelloWorld –> “Hello World!”
- ExampleTitle –> “Example”
- Foo.de-DE.resx
- HelloWorld –> “Hallo Welt!”
Requesting ‘ExampleTitle’ in a German web will not return the expected “Example” (as it would using compiled resources) but “$Resources:Example”.
There are two ways how resources are being requested by SharePoint:
Requesting localized string by XML declaration
These are the resource references: $Resources:<resx file base name>,<resx key>;
This results in the following error in the SharePoint log:
06/15/2010 16:57:20.06 vssphost4.exe (0x0F98) 0x06C8 SharePoint Foundation General 8e25 Medium Failed to look up string with key "<resx key>", keyfile <resx file>.
06/15/2010 16:57:20.06 vssphost4.exe (0x0F98) 0x06C8 SharePoint Foundation General 8l3c Medium Localized resource for token '<resx key>' could not be found for file with path: "C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\14\Template\Features\<feature name>\feature.xml".
Requesting localized string using code
I use the following method to query translations from code:
UPDATE: Due to the fact that the SPContext is not set in feature receivers when the feature is being activated using the commandline, I extended the function to be able to pass ((SPSite)properties.Feature.Parent).RootWeb:
public static class LocalizationHelpers
{
public const string DefaultResourceFile = "<default resource name, without extension>";
public static string GetLocalizedString(string str)
{
if (string.IsNullOrEmpty(str))
throw new ArgumentException("null or empty", "str");
return GetLocalizedStringInternal(str, DefaultResourceFile, null);
}
public static string GetLocalizedString(string str, SPWeb web)
{
if (string.IsNullOrEmpty(str))
throw new ArgumentException("null or empty", "str");
if (web == null)
throw new ArgumentNullException("web");
return GetLocalizedStringInternal(str, DefaultResourceFile, web);
}
public static string GetLocalizedString(string str, string file)
{
if (string.IsNullOrEmpty(str))
throw new ArgumentException("null or empty", "str");
if (string.IsNullOrEmpty(file))
throw new ArgumentException("null or empty", "file");
return GetLocalizedStringInternal(str, file, null);
}
public static string GetLocalizedString(string str, string file, SPWeb web)
{
if (string.IsNullOrEmpty(str))
throw new ArgumentException("null or empty", "str");
if (string.IsNullOrEmpty(file))
throw new ArgumentException("null or empty", "file");
if (web == null)
throw new ArgumentNullException("web");
return GetLocalizedStringInternal(str, file, web);
}
private static string GetLocalizedStringInternal(string str, string file, SPWeb web)
{
// determine language
var lang = 1033u; // english
if (web != null)
lang = web.Language;
else if ((SPContext.Current != null) && (SPContext.Current.Web != null))
lang = SPContext.Current.Web.Language;
// request string
var r = SPUtility.GetLocalizedString("$Resources:" + str, file, lang);
// enrich error string
if (r.StartsWith("$Resources:"))
r = string.Format("$Resources:{0},{1},{2};", file, str, lang);
return r;
}
}
More details: http://blogs.msdn.com/b/johnwpowell/archive/2009/11/29/sharepoint-2010-localization-with-visual-studio-2010.aspx