SharePoint 2010 resource files have to be complete

by fkollmann 6/17/2010 12:29:00 PM

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