Problem: Localize text displayed in the web parts based on user's selected language.
Background:
For a recent implementation of global collaboration and content management platform, we supported multiple languages. Content was displayed on the pages through custom DataFormWebParts that relied on underlying services that pulled data (xml) from variety of sources and transformed it using xslt. Content displayed on the SharePoint pages can come from a number of places:
- For XML web parts, some strings (for buttons, titles etc) are embedded within the xsl files.
- For server controls, content is build in server side code.
- Any branding elements embedded within master pages + page layouts + css + javascript + jquery.
Goal:
Use a common set of resource file(s) for maintaining localized strings. Content should be displayed from these resource files irrespective of where it is being displayed from (server side, client side)
Utilize an approach that minimizes changing every web part and coupling them tightly to the user interface. We had over 30 custom web parts and we need a design pattern that did not require us to maintain a copy of these web part for each instance needed on the site.
Here are the options we came up with during our design stage.
Option 1: Pass the appropriate resource file reference to xslt
Based on the language setting, the appropriate resource file could be sent as a parameter to the xslt. The xslt can then read the resource file and retrieve localized strings as needed during the translation.
Implementation Details:
In the XSLT, define a parameter to accept the URL to the resource XML document for the selected language. Use the XPath 'document' function to read the XML with the translated strings. Define a parameter for each string used in the XSLT. Using parameters rather than variables allows the values to be overridden when the XSLT is transformed. Use xsl:value-of to display the translated text.
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:param name="resourcefile" select="'myresourcefile_en-US.xml'"/>
<xsl:variable name="resourceXml" select="document($resourcefile)/*" />
<xsl:param name="MyCustomText" select="$resourceXml/data[@name='MyCustomText']/value/text()"/>
:
<xsl:value-of select="$MyCustomText"/>
:
When the transform is processed, pass the URL to the resource XML document for the desired language.
System.Xml.Xsl.XsltArgumentList objXsltArgs = new System.Xml.Xsl.XsltArgumentList();
objXsltArgs.AddParam("resourcefile", "", "myresourcefile_sp-SP.xml");
Feasibility: Yes
The Document function is disabled by default and can be turned on by using the XsltSettings.EnableDocumentFunction Property.
Recommended: No
While technically feasible, this approach loads the resource xml as a document and reads the localized string value from the resource xml. This will consume additional memory for each xslt transformation as well as adding additional computational overhead.
Option 2: Using special xPath syntax @Resources
Resource strings can also be made available to XSLT transforms as XPath expressions that are used in <xsl:value-of> tags. To retrieve resources, SharePoint Foundation uses a special XPath syntax that is interpreted by the XPathNavigator object that is used by the XsltListViewWebPart object when the transform runs.
Implementation Details:
The syntax is specified as follows:
<xsl:value-of select=”@Resources. resfile . resname ” />
The following example retrieves the “noitemsinview_doclibrary” resource from the wss.resx file.
<xsl:value-of select=”@Resources.wss.noitemsinview_doclibrary” />
The “resource” attributes are actually something like pseudo-attributes of the source XML. There is no source XML at least in the form of a XML DOM container class like XmlDocument or XDocument. The trick is possible because the XLV uses a custom XPathNavigator (overriding the DataFormWebPart.GetXPathNavigator virtual method). This custom XPathNavigator queries directly the SPListItem data that the XLV web part retrieves from SharePoint and as an extra provides the “resource” pseudo-attribute support.
Feasibility: No
While this would be the simplest and an efficient way of consuming resources from the xslt, this option is only available for web parts based on XSLTListViewrWebPart. Unfortunately the “resource” pseudo-attribute “shortcut” is not available in the custom DataFormWebPart.
Recommended: n/a
Option 3: Use ParamaterBindings and Style sheet Params
Resource strings can be made available to XSLT transforms via style sheet parameters (<xsl:param>) that are retrieved through <ParameterBinding> tags in the Web Part.
This approach requires updating each web part to include a ParameterBinding element to pass in every localized string value needed by the xslt.
The ParameterBinding element includes a Location attribute that specifies resource types. The syntax for this element is:
<ParameterBinding Name=” parameterName ” Location=”Resource( resourceFile , resourceName )” />
resourceFile
The base name of a SharePoint Foundation resource file, without the extension. For example: wss, or core.
resourceName
The name of the resource string. For example: string1.
Implementation Details:
You can use the server object model to add a parameter binding to the collection of bindings for an XsltListViewWebPart object through the ParameterBindings property of the Web Part, or through the ParameterBindings property of the SPView object that is associated with the Web Part. You can also declaratively add a <ParameterBinding> tag to the <ParameterBindings> section of a View element in a list’s Schema.xml file to declare a parameter binding for use in XSLT. For example, the following tag makes a string available for display in document library list views when there are no items:
<ParameterBinding Name=”NoAnnouncements” Location=”Resource(wss,noitemsinview_doclibrary)” />
In the example, NoAnnouncements is the name of the resource for use in XSLT, wss is the name of the file that contains the resource minus the file name extension, and noitemsinview_doclibrary is the name of the resource as represented in the resource file.
After the <ParameterBinding> tag is added to the <ParameterBindings> collection on the Web Part, it becomes available to the XSLT style sheet by defining a top-level <xsl:param> tag of the same name:
<xsl:param name=”NoAnnouncements”/>
You can then consume the resource by using an XPath expression anywhere in the style sheet, as in the following:
<xsl:value-of select=”$NoAnnouncements” />
Feasibility: Yes
Recommended: No
While the approach is technically feasible, it requires a change to every single web part to pass in the UI specific parameters. While this can still be achieved by setting the parameterbindings declaratively in the .webpart files, any changes (like using new resource keys within xslt) would require development effort and a new code build. In the absence of a better approach, this would be our fallback.
Option 4: Calling an external function from Xslt
Resource strings can be made available to XSLT transforms via an external function call. The Xslt will use a static method to read the localized strings from the resource files. This will allow us to utilize the recommended SharePoint approach (SPUtility.GetLocalizedString) for reading localized strings in the server side static method.
Note: SPUtility.GetLocalizedStringmethod can retrieve a string value from a resource file located in the Resources folder that is just below the SharePoint Foundation installation root. The token that Visual Studio uses for this folder is {SharePointRoot}\Resources. The full path is %ProgramFiles%\Common Files\Microsoft Shared\web server extensions\14\Resources.
.NET XSL transformations implementation allows us to define a .NET class whose methods (rather static methods) can become available and be used from inside the XSL. Such “extension” class can also be used in a web part derived from the DataForm web part. The ModifyXsltArgumentList virtual method will have to be overridden to hook the custom extension object.
Implementation Details:
1. Implement a static method for reading the localized string values from the resource files. This method will read the mycustomresourcefile.resx file from the {SharePointRoot}\Resources\MyResources folder.
public class PGLLocalizationResourceManager
{
public static string GetLocalizedValue(string name)
{
try
{
// sample code below the language code is being read from the current thread. In practice you could read the Thread Culture or cookie/session value where you are persisting the user’s language selection. int intLCID = System.Threading.Thread.CurrentThread.CurrentUICulture.LCID;
return SPUtility.GetLocalizedString("$Resources: " + name, "Myresources\\mycustomeresourcefile", (uint)intLCID);
}
catch (Exception ex)
{
//TODO:
throw ex;
}
}
}
2. Override the ModifyXsltArgumentList method in the custom web part class.
protected override void ModifyXsltArgumentList(ArgumentClassWrapper argList)
{
argList.AddExtensionObject("EMC:ResourceManager", new PGLLocalizationResourceManager());
base.ModifyXsltArgumentList(argList);
}
where EMC:ResourceManager' is the namespace URI to associate with the object and PGLocalizationResourceManager() is the object that provides the static method.
3. Within the xslt, add a namespace for this extension object.
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:EMCRM="EMC:ResourceManager">
4. Within the xslt, use the static method to get to the localized string.
<xsl:variable name ="localizedtextstring" select ="EMCRM:GetLocalizedValue('Sample_Text_String')"></xsl:variable>
<xsl:value-of select="$localizedtextstring"/>
Or
<xsl:value-of select="EMCRM:GetLocalizedValue(''Sample_Text_String)"/>
5. Any server side code can use the same static library method to retrieve strings as needed.
Feasibility: Yes
Recommended: Yes (PREFERRED OPTION)
Based on our analysis, this seemed to be the option that met our goals. Last week we successfully rolled out a solution based on this approach.
References: