Walkthrough: Localizing a Web Part

Applies to: SharePoint Foundation 2010

This walkthrough shows you how to localize a Web Part for deployment on Microsoft SharePoint Foundation 2010. The techniques used in the walkthrough are specific to SharePoint Foundation 2010. They do not work for a Web Part that is deployed on Microsoft ASP.NET.

In the walkthrough, you create a simple Web Part that can display the text "Hello, world" in several languages. In addition, the walkthrough shows you how to make all display text that is associated with the Web Part ready for localization, including display text for:

  • The Feature that provisions the Web Part.

  • The Web Part's entry in the Web Part Gallery.

  • The title in the Web Part chrome.

  • A custom action on the Web Part menu.

  • A custom property in the Web Part editor.

  • A child control hosted by the Web Part.

Prerequisites

The procedures in the walkthrough are written for a developer who is using Microsoft Visual Studio 2010. For prerequisites, see Requirements for Developing SharePoint Solutions.

To complete the walkthrough you need:

For an introduction to key concepts for localizing solutions in SharePoint Foundation 2010, see Understanding the Multilingual User Interface (MUI).

Creating an Empty SharePoint Project

Begin by creating an empty SharePoint project in Visual Studio 2010. Name the project Wingtip.WebParts. Configure it for deployment as a farm solution.

Note

The Web Part that you create in this walkthrough could deploy as a sandboxed solution. All of the code for the Web Part can run with partial trust. However, in addition to the Web Part, the walkthrough also deploys language resource files to a root folder in the SharePoint installation, and this part of the project requires full trust. In a production environment, you might want to use two separate solutions: a sandboxed solution to deploy the Web Part, and a farm solution to deploy the resource files. For simplicity, the walkthrough uses only one, a farm solution.

To create an empty SharePoint project

  1. Start Visual Studio 2010 as an administrator.

  2. Open the New Project dialog by pointing to New on the File menu, and then clicking New Project.

  3. Expand the SharePoint node under either Visual C# or Visual Basic, and then click 2010.

  4. In the Templates pane, click Empty SharePoint Project, change the name of the project to Wingtip.WebParts, and then click OK.

    The SharePoint Customization Wizard appears.

  5. On the Specify the site and security level for debugging page, enter the URL for the SharePoint site to which you want to add the new Web Part, or use the default location (http://< system name>/).

  6. In the What is the trust level for this SharePoint solution? section, select Deploy as a farm solution.

  7. Click Finish.

    The project appears in Solution Explorer.

Creating the Web Part

The next task is to add a Web Part to the project. You do this by creating a new class that derives from the WebPart class in the System.Web.UI.WebControls.WebParts namespace. The example Web Part overrides the CreateChildControls method to add a child control, an instance of the Label class. Later in the walkthrough, you learn how to localize the text that this control displays. To give you an opportunity to localize all the different types of user interface (UI) that a Web Part can have, the example code creates a custom property that is decorated with the WebBrowsable and Personalizable attributes. These attributes cause the property to appear in the tool pane when a user edits the Web Part. Finally, the example adds an item to the Web Part options menu by creating a custom WebPartVerb.

To add a Web Part to the project

  1. In Solution Explorer, select the Wingtip.WebParts node. On the Project menu, click Add New Item.

  2. In the Add New Item dialog box, select Web Part. In the Name field, type LocalizedWebPart. Then click Add.

    The LocalizedWebPart class opens in the code editor.

  3. In the code editor, press Ctrl+A to select all. Then copy and paste the following code:

    using System;
    using System.ComponentModel;
    using System.Globalization;
    using System.Web.UI.WebControls;
    using System.Web.UI.WebControls.WebParts;
    using Microsoft.SharePoint.WebControls;
    
    namespace Wingtip.WebParts.LocalizedWebPart
    {
        [ToolboxItemAttribute(false)]
        public class LocalizedWebPart : WebPart
        {
    
            // Constants for display strings.
            const string CATEGORY_TEXT = "Text";
            const string LABEL_TEXT = "Hello, world!";
            const string TOOLPANE_BOLD = "Bold";
            const string TOOLPANE_BOLD_DESC = "Make the text bold";
            const string VERB_UNDERLINE = "Underline";
            const string VERB_UNDERLINE_DESC = "Draw a line under the text";
            const string WEB_PART_TITLE = "Localized Web Part";
            const string WEB_PART_DESC = "An example localized Web Part";
    
            // Member variable for the child control.
            private Label m_textLabel;
    
            // Instantiates the Web Part.
            public LocalizedWebPart()
            {
    
                // Add a handler for the PreRender event.
                PreRender += LocalizedWebPart_PreRender;
            }
    
            // Creates the control tree.
            protected override void CreateChildControls()
            {
    
                // Add a Label control to display content.
                m_textLabel = new Label();
                m_textLabel.Text = Text;
                Controls.Add(m_textLabel);
            }
    
            // The PreRender event is raised after the Web Part is edited,
            // so this is a good time to update the child control.
            void LocalizedWebPart_PreRender(object sender, EventArgs e)
            {
    
                // Make sure that CreateChildControls has been called.
                EnsureChildControls();
    
                // Apply current Web Part settings to the child control.
                m_textLabel.Font.Bold = Bold;
            }
    
    
            // Indicates whether the text is bold.
            // This property is exposed in the Web Part Editor.
            [WebBrowsable]
            [Personalizable(PersonalizationScope.Shared)]
            [Category(CATEGORY_TEXT)]
            [WebDisplayName(TOOLPANE_BOLD)]
            [WebDescription(TOOLPANE_BOLD_DESC)]
            public bool Bold { get; set; }
    
            // Overrides the Description property.
            [WebBrowsable(false), Personalizable(false)]
            public override string Description
            {
                get { return WEB_PART_DESC; }
                set { ; }
            }
    
            // Gets the text to display.
            private string Text
            {
                get { return LABEL_TEXT; }
            }
    
            // Overrides the Title property.
            [WebBrowsable(false), Personalizable(false)]
            public override string Title
            {
                get { return WEB_PART_TITLE; }
                set { ; }
            }
    
            // Gets a collection of custom verbs that provide items
            // on the options menu for the Web Part.
            public override WebPartVerbCollection Verbs
            {
                get
                {
    
                    // Add a custom verb to toggle underlining for the text.
                    WebPartVerb verb = new WebPartVerb(this.ID + "_UnderlineVerb",
                     (sender, args) =>
                     {
                         EnsureChildControls();
                         m_textLabel.Font.Underline = !m_textLabel.Font.Underline;
                     });
    
                    verb.Checked = m_textLabel.Font.Underline;
                    verb.Text = VERB_UNDERLINE;
                    verb.Description = VERB_UNDERLINE_DESC;
    
                    WebPartVerb[] newVerbs = new WebPartVerb[] { verb };
                    return new WebPartVerbCollection(base.Verbs, newVerbs);
                }
            }
        }
    }
    
    Imports System
    Imports System.ComponentModel
    Imports System.Globalization
    Imports System.Web.UI.WebControls
    Imports System.Web.UI.WebControls.WebParts
    Imports Microsoft.SharePoint.WebControls
    
    <ToolboxItemAttribute(false)> _
    Public Class LocalizedWebPart
        Inherits WebPart
    
        ' Constants for display strings.
        Const CATEGORY_TEXT As String = "Text"
        Const LABEL_TEXT As String = "Hello, world!"
        Const TOOLPANE_BOLD As String = "Bold"
        Const TOOLPANE_BOLD_DESC As String = "Make the text bold"
        Const VERB_UNDERLINE As String = "Underline"
        Const VERB_UNDERLINE_DESC As String = "Draw a line under the text"
        Const WEB_PART_TITLE As String = "Localized Web Part"
        Const WEB_PART_DESC As String = "An example localized Web Part"
    
        ' Member variable for the child control.
        Private m_textLabel As Label
    
        ' Instantiates the Web Part.
        Public Sub New()
            ' Add a handler for the PreRender event.
            AddHandler PreRender, AddressOf LocalizedWebPart_PreRender
        End Sub
    
        ' Creates the control tree.
        Protected Overrides Sub CreateChildControls()
            ' Add a Label control to display content.
            m_textLabel = New Label()
            m_textLabel.Text = Text
            Controls.Add(m_textLabel)
        End Sub
    
        ' The PreRender event is raised after the Web Part is edited,
        ' so this is a good time to update the child control.
        Private Sub LocalizedWebPart_PreRender(ByVal sender As Object, ByVal e As EventArgs)
    
            ' Make sure that CreateChildControls has been called.
            EnsureChildControls()
    
            ' Apply current Web Part settings to the child control.
            m_textLabel.Font.Bold = Bold
        End Sub
    
        Private m_bold As Boolean
    
        ' Indicates whether the text is bold.
        ' This property is exposed in the Web Part Editor.
        <WebBrowsable()> _
        <Personalizable(PersonalizationScope.[Shared])> _
        <Category(CATEGORY_TEXT)> _
        <WebDisplayName(TOOLPANE_BOLD)> _
        <WebDescription(TOOLPANE_BOLD_DESC)> _
        Public Property Bold() As Boolean
            Get
                Return m_bold
            End Get
            Set(ByVal value As Boolean)
                m_bold = value
            End Set
        End Property
    
        ' Overrides the Description property.
        <WebBrowsable(False)> _
        <Personalizable(False)> _
        Public Overrides Property Description As String
            Get
                Return WEB_PART_DESC
            End Get
            Set(ByVal value As String)
            End Set
        End Property
    
        ' Gets the text to display.
        ReadOnly Property Text As String
            Get
                Return LABEL_TEXT
            End Get
        End Property
    
        ' Overrides the Title property.
        <WebBrowsable(False)> _
        <Personalizable(False)> _
        Public Overrides Property Title As String
            Get
                Return WEB_PART_TITLE
            End Get
            Set(ByVal value As String)
            End Set
        End Property
    
        ' Gets a collection of custom verbs that provide items
        ' on the options menu for the Web Part.
        Public Overrides ReadOnly Property Verbs() As WebPartVerbCollection
            Get
    
                ' Add a custom verb to toggle underlining for the text.
                Dim verb As New WebPartVerb(Me.ID + "_UnderlineVerb", AddressOf UnderlineVerbHandler)
    
                verb.Checked = m_textLabel.Font.Underline
                verb.Text = VERB_UNDERLINE
                verb.Description = VERB_UNDERLINE_DESC
    
                Dim newVerbs As WebPartVerb() = New WebPartVerb() {verb}
                Return New WebPartVerbCollection(MyBase.Verbs, newVerbs)
            End Get
        End Property
    
        ' Toggles underlining.
        Private Sub UnderlineVerbHandler(ByVal sender As Object, ByVal args As EventArgs)
            EnsureChildControls()
            m_textLabel.Font.Underline = Not m_textLabel.Font.Underline
        End Sub
    
    End Class
    
  4. Press Ctrl+Shift+S to save all.

Now that you have a working Web Part, test it. Press F5 to build and deploy the solution. When your website opens, create a new Web Part page and add your Web Part to it. While the page is in edit mode, check the options menu in the upper-right corner of the Web Part chrome. Click Underline several times to verify that the custom verb is working. Then click Edit Web Part. When the tool pane appears, look for the custom property at the bottom of the pane. Select Bold, and then click Apply to verify that the property sets correctly.

Adding Language Resources

The first step in localizing the Web Part is to add language resource files to the project. For this walkthrough, you need only two resource files: one file for the default language of the website, and another file for one of the alternate languages supported by the website. You can use whichever languages you prefer. The examples use English and Spanish.

Tip

To find out which languages are supported by your development website, open its home page in your browser. Click Site Actions, and then click Site Settings. Under Site Administration, click Language settings. The default language is listed at the top of the page. Alternate languages are listed below the default language.

You must deploy the resource files that you create to a location in the file system of your web server that can be accessed by all components of the Web Part that generate display text, including:

  • The Feature that activates the Web Part.

  • The Web Part Control Description File that provides information for the Web Part Gallery.

  • The code that executes in the compiled assembly.

You have several alternatives for provisioning language resources on the target web servers. The simplest approach, and the one that offers the most flexibility for adding support for other languages in the future, is to provision language resource files 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. This folder is where SharePoint Foundation installs language resources that are consumed by built-in Web Parts.

To add resources to the {SharePointRoot}\Resources folder

  1. In Solution Explorer, select the Wingtip.Webparts project node. On the Project menu, click Add SharePoint Mapped Folder.

  2. In the Add SharePoint Mapped Folder dialog box, select Resources, and then click OK.

    A Resources folder is added to the project. Check the properties of this folder. Notice that the DeploymentLocation property is set to {SharePointRoot}\Resources.

  3. In Solution Explorer, select the Resources folder. On the Project menu, click Add New Item.

  4. In the Add New Item dialog box, expand the node for Visual C# if your development language is C#, or Common Items if you are working in Visual Basic. Select General. In the template pane, select Resources File. Name the file Wingtip.WebParts.resx, and then click Add.

    The resource file is added to the project and opened in the Managed Resources Editor.

    Tip

    Whenever you provision files in the global Resources folder, it is a good practice to choose unique file names to avoid naming conflicts with resources that are provisioned by other applications. One convention is to prefix the file name with your company name. For example, if your company is Wingtip Toys, the names of resource files that you deploy can all begin with "Wingtip."

  5. In Solution Explorer, right-click the resource file that you just created, and then click Open With. In the Open With dialog box, select XML (Text) Editor, and then click OK.

    The resource file opens in the editor.

  6. Scroll down to the bottom of the file. Insert a blank line immediately above the </root> tag. Copy the following markup and paste it into the file at the insertion point.

      <data name="Bold" xml:space="preserve">
        <value>Bold (invariant)</value>
      </data>
      <data name="BoldDesc" xml:space="preserve">
        <value>Make the text bold (invariant)</value>
      </data>
      <data name="FeatureDesc" xml:space="preserve">
        <value>An example localized Web Part (invariant)</value>
      </data>
      <data name="FeatureTitle" xml:space="preserve">
        <value>Localized Web Part (invariant)</value>
      </data>
      <data name="LabelText" xml:space="preserve">
        <value>Hello, world! (invariant)</value>
      </data>
      <data name="TextCategory" xml:space="preserve">
        <value>Text (invariant)</value>
      </data>
      <data name="Underline" xml:space="preserve">
        <value>Underline (invariant)</value>
      </data>
      <data name="UnderlineDesc" xml:space="preserve">
        <value>Draw a line under the text (invariant)</value>
      </data>
      <data name="WebPartDesc" xml:space="preserve">
        <value>An example localized Web Part (invariant)</value>
      </data>
      <data name="WebPartTitle" xml:space="preserve">
        <value>Localized Web Part (invariant)</value>
      </data>
    
  7. Press Ctrl+S to save the file. Then on the File menu, click Close to close the file.

  8. In Solution Explorer, select the file Wingtip.WebParts.resx. On the Edit menu, click Copy. Then click Paste.

    A file that is named Copy of Wingtip.WebParts.resx is added to the Resources folder.

  9. Right-click the file that is named Copy of Wingtip.WebParts.resx, and then click Rename. Rename the file to Wingtip.WebParts.language-COUNTRY/REGION.resx where language is a two-letter code for the default language of your website and COUNTRY/REGION is a two-letter code for the subculture.

    For example, if the default language is English (United States), the resource file name is Wingtip.WebParts**.en-US**.resx.

    For more information about language identifiers, see the System.Globalization.CultureInfo class.

    Important

    As is demonstrated later in the walkthrough, you reference a resource file by using its base file name, such as "Wingtip.WebParts." When SharePoint Foundation retrieves a resource value, it looks for a resource file that has this base file name followed by a language identifier that corresponds to the value of the CurrentUICulture property of the currently executing thread. SharePoint Foundation expects to find an exact match. For example, if the language identifier for the current thread is "pt-BR", SharePoint Foundation looks for a resource file that has the name "BaseFilename.pt-BR.resx". It ignores files that have the same language code but not the same subculture, such as "BaseFilename.pt.resx" and "BaseFilename.pt-PT.resx".

    If SharePoint Foundation does not find a resource file to match the language identifier on the executing thread, it looks for a fallback resource file. The first choice for a fallback is a resource file for the invariant culture--that is, a resource file that has no language identifier at the end of the file name. For example, in the current project the resource file for the invariant culture is Wingtip.WebParts.resx. If SharePoint Foundation cannot find a resource file for the invariant culture, it tries to fall back to the default language. If it does not find a resource file for the default language, it displays the resource name.

  10. Repeat steps 8 and 9, this time appending the language identifier for an alternate language that is enabled on your website.

  11. Open both of the culture-specific language resources files that you have just created, and replace the word "invariant" with the language identifier for the file. For example, in Wingtip.WebParts.en-US.resx, "Bold (invariant)" becomes "Bold (en-US)".

    For this walkthrough, it is not necessary to translate string values. Instead, it should be sufficient to append a language identifier to an untranslated string. When you test the Web Part, the language identifier indicates the language that is displayed.

  12. Press Ctrl+Shift+S to save all.

Localizing the Feature and the Web Part Control Description File

The next major task is to localize display strings that occur in two key XML files: the Feature element file, and the Web Part Control Description file. Both files contain title and description strings that you can replace with ASP.NET expressions that conform to the following pattern:

$Resources:BaseFileName,ResourceName

The dollar sign ($) indicates that an expression follows. The prefix "Resources" indicates the type of expression. The suffix after the colon (:) is the expression value to resolve. The first part of the value is the base file name for a resource file. The part that follows the comma is the name of the localized string to retrieve from the file. No spaces are allowed anywhere in the expression.

Note

You might see resource expressions that end with a semicolon (;). This delimiter is acceptable but is not required by SharePoint Foundation 2010.

To localize the Feature

  1. In Solution Explorer, expand the Features folder, and then double-click Feature1.

    Feature1.feature opens in the Feature Designer.

  2. In the Feature Designer, delete the text in the Title box, and replace it with the following resource expression:

    $Resources:Wingtip.WebParts,FeatureTitle
    
  3. Paste the following expression in the Description box:

    $Resources:Wingtip.WebParts,FeatureDesc
    
  4. Press Ctrl+Shift+S to save all.

To localize the Web Part Control Description

  1. In Solution Explorer, expand the LocalizedWebPart folder, and then double-click LocalizedWebPart.webpart.

    LocalizedWebPart.webpart opens in the XML editor.

  2. Delete the value for the Title property ("LocalizedWebPart"), and replace it with the following resource expression:

    $Resources:Wingtip.WebParts,WebPartTitle
    
  3. Delete the value for the Description property ("My WebPart"), and replace it with the following resource expression:

    $Resources:Wingtip.WebParts,WebPartDesc
    
  4. Press Ctrl+Shift+S to save all.

Writing Code to Retrieve Localized Strings

You localize code in an ordinary ASP.NET application by embedding language resources in a satellite assembly that is deployed with the main assembly in the global assembly cache. When your code needs to retrieve a resource value, you call the HttpContext.GetGlobalResourceObject method.

Although you can certainly call this method from code that runs on SharePoint Foundation, an alternative method is available to SharePoint Foundation developers, a method that does not require you to deploy your language resources in satellite assemblies. The SPUtility class includes a static method, GetLocalizedString, that can retrieve a string value from a resource file located in the {SharePointRoot}\Resources folder on the web server. You will use the GetLocalizedString method in this walkthrough.

The GetLocalizedString method expects a resource expression in the first argument and the base file name of a resource file in the second argument. To simplify method calls from your Web Part code, you wrap GetLocalizedString in a method of your own design.

To create a method that retrieves resource strings

  1. In Solution Explorer, select the Wingtip.Webparts project node. On the Project menu, click Add Class.

    The Add New Item dialog box appears.

  2. In the Name field, type Utility.cs or Utility.vb, depending on your language. Then click Add.

  3. Press Ctrl+A to select all. Then copy and paste the following code, replacing the current content of the file.

    using Microsoft.SharePoint.Utilities;
    
    namespace Wingtip.WebParts.LocalizedWebPart
    {
        public static class Utility
        {
    
            // Wraps the SPUtility method of the same name.
            public static string GetLocalizedString(string resourceName, int LCID)
            {
                if (string.IsNullOrEmpty(resourceName))
                    return string.Empty;
    
                // SPUtility.GetLocalized string needs a resource expression as the first argument.
                string resourceExpression = string.Format("$Resources:{0}", resourceName);
    
                string resourceFile = "Wingtip.WebParts";
    
                // Note: If the named resource does not have a value for the specified language, 
                // SPUtility.GetLocalizedString returns the value for the invariant language.
                // If the named resource does not exist, it returns the original expression.
               return SPUtility.GetLocalizedString(resourceExpression, resourceFile, (uint)LCID);
            }
        }
    }
    
    Imports Microsoft.SharePoint.Utilities
    
    Public NotInheritable Class Utility
    
        ' Wraps the SPUtility method of the same name.
        Public Shared Function GetLocalizedString(ByVal resourceName As String, ByVal LCID As Integer) As String
    
            If String.IsNullOrEmpty(resourceName) Then
                Return String.Empty
            End If
    
            Dim resourceFile As String = "Wingtip.WebParts"
    
            ' SPUtility.GetLocalized string needs a resource expression as the first argument.
            Dim resourceExpression As String = String.Format("$Resources:{0}", resourceName)
    
            ' Note: If the named resource does not have a value for the specified language, 
            ' SPUtility.GetLocalizedString returns the value for the invariant language.
            ' If the named resource does not exist, it returns the original expression.
            Return SPUtility.GetLocalizedString(resourceExpression, resourceFile, CUInt(LCID))
    
        End Function
    
    End Class
    
  4. Press Ctrl+Shift+S to save all.

Localizing Web Part Properties

The next task is to localize the code for the Web Part properties that return display text. In each case, you can replace a hard-coded string with a call to the utility method GetLocalizedString, passing a resource name and the Locale ID (LCID) for the language of the current thread. You can get the LCID for the current language by accessing the static property CultureInfo.CurrentUICulture.

To localize properties of the Web Part

  1. In Solution Explorer, double-click either LocalizedWebPart.cs or LocalizedWebPart.vb to open the source file.

  2. At the top of the file, just below the declaration for the LocalizedWebPart class, are declarations for several string constants. Delete these declarations and replace them with the following code:

    // Translate resource keys to string constants.
    const string CATEGORY_TEXT = "TextCategory";
    const string LABEL_TEXT = "LabelText";
    const string TOOLPANE_BOLD = "Bold";
    const string TOOLPANE_BOLD_DESC = "BoldDesc";
    const string VERB_UNDERLINE = "Underline";
    const string VERB_UNDERLINE_DESC = "UnderlineDesc";
    const string WEB_PART_TITLE = "WebPartTitle";
    const string WEB_PART_DESC = "WebPartDesc";
    
    ' Translate resource keys to string constants.
    Const CATEGORY_TEXT As String = "TextCategory"
    Const LABEL_TEXT As String = "LabelText"
    Const TOOLPANE_BOLD As String = "Bold"
    Const TOOLPANE_BOLD_DESC As String = "BoldDesc"
    Const VERB_UNDERLINE As String = "Underline"
    Const VERB_UNDERLINE_DESC As String = "UnderlineDesc"
    Const WEB_PART_TITLE As String = "WebPartTitle"
    Const WEB_PART_DESC As String = "WebPartDesc"
    
  3. Navigate to the Description property. Replace the code for the get accessor with the following code:

    get { return Utility.GetLocalizedString(WEB_PART_DESC, CultureInfo.CurrentUICulture.LCID); }
    
    Get
        Return Utility.GetLocalizedString(WEB_PART_DESC, CultureInfo.CurrentUICulture.LCID)
    End Get
    
  4. Navigate to the Text property. Replace the code for the get accessor with the following code:

    get { return Utility.GetLocalizedString(LABEL_TEXT, CultureInfo.CurrentUICulture.LCID); }
    
    Get
        Return Utility.GetLocalizedString(LABEL_TEXT, CultureInfo.CurrentUICulture.LCID)
    End Get
    
  5. Navigate to the Title property. Replace the code for the get accessor with the following code:

    get { return Utility.GetLocalizedString(WEB_PART_TITLE, CultureInfo.CurrentUICulture.LCID); }
    
    Get
        Return Utility.GetLocalizedString(WEB_PART_TITLE, CultureInfo.CurrentUICulture.LCID)
    End Get
    
  6. Navigate to the Verbs property. Replace the two lines of code that set the verb.Text and verb.Description properties with the following lines:

    verb.Text = Utility.GetLocalizedString(VERB_UNDERLINE, CultureInfo.CurrentUICulture.LCID);
    verb.Description = Utility.GetLocalizedString(VERB_UNDERLINE_DESC, CultureInfo.CurrentUICulture.LCID);
    
    verb.Text = Utility.GetLocalizedString(VERB_UNDERLINE, CultureInfo.CurrentUICulture.LCID)
    verb.Description = Utility.GetLocalizedString(VERB_UNDERLINE_DESC, CultureInfo.CurrentUICulture.LCID)
    
  7. Press Ctrl+Shift+S to save all.

Localizing Attributes

The code for the Web Part defines a custom Bold property that is decorated with several attributes, as shown in the following snippet:

[WebBrowsable]
[Personalizable(PersonalizationScope.Shared)]
[Category(CATEGORY_TEXT)]
[WebDisplayName(TOOLPANE_BOLD)]
[WebDescription(TOOLPANE_BOLD_DESC)]
public bool Bold { get; set; }
<WebBrowsable()> _
<Personalizable(PersonalizationScope.[Shared])> _
<Category(CATEGORY_TEXT)> _
<WebDisplayName(TOOLPANE_BOLD)> _
<WebDescription(TOOLPANE_BOLD_DESC)> _
Public Property Bold() As Boolean
    Get
        Return m_bold
    End Get
    Set(ByVal value As Boolean)
        m_bold = value
    End Set
End Property

The WebBrowsable and Personalizable attributes make the property appear in the editing user UI when a user edits the Web Part. Three additional attributes provide display text for the editing UI:

  • Category

    Provides the display name of a custom category for the property.

  • WebDisplayName

    Provides the display name of the property.

  • WebDescription

    Provides the text of the tooltip for the property.

To localize the text for these attributes, you must write custom attribute classes that derive from the CategoryAttribute class, the WebDisplayNameAttribute class, and the WebDescriptionAttribute class.

To localize the Category, WebDisplayName, and WebDescription attributes

  1. In Solution Explorer, double-click either LocalizedWebPart.cs or LocalizedWebPart.vb to open the source file.

  2. At the bottom of the LocalizedWebPart class, but still within the class, insert code for the following subclasses:

    public sealed class LocalizedCategoryAttribute : CategoryAttribute
    {
        public LocalizedCategoryAttribute(string category)
            : base(category)
        { }
    
        // Override this method to return values from the webpart's resource file.
        protected override string GetLocalizedString(string value)
        {
            return Utility.GetLocalizedString(value, CultureInfo.CurrentUICulture.LCID);
        }
    }
    
    public sealed class LocalizedWebDisplayNameAttribute : WebDisplayNameAttribute
    {
        bool m_isLocalized;
    
        public LocalizedWebDisplayNameAttribute(string displayName)
            : base(displayName)
        { }
    
        // Override this property to return values from the webpart's resource file.
        public override string DisplayName
        {
            get
            {
                if (!m_isLocalized)
                {
                    this.DisplayNameValue = Utility.GetLocalizedString(base.DisplayName, CultureInfo.CurrentUICulture.LCID);
                    m_isLocalized = true;
                }
                return base.DisplayName;
            }
        }
    }
    
    public sealed class LocalizedWebDescriptionAttribute : WebDescriptionAttribute
    {
        bool m_isLocalized;
    
        public LocalizedWebDescriptionAttribute(string description)
            : base(description)
        { }
    
        // Override this property to return values from the webpart's resource file.
        public override string Description
        {
            get
            {
                if (!m_isLocalized)
                {
                    this.DescriptionValue = Utility.GetLocalizedString(base.Description, CultureInfo.CurrentUICulture.LCID);
                    m_isLocalized = true;
                }
                return base.Description;
            }
        }
    }
    
    Public NotInheritable Class LocalizedCategoryAttribute
        Inherits CategoryAttribute
        Public Sub New(ByVal category As String)
            MyBase.New(category)
        End Sub
    
        ' Override this method to return values from the webpart's resource file.
        Protected Overrides Function GetLocalizedString(ByVal value As String) As String
            Return Utility.GetLocalizedString(value, CultureInfo.CurrentUICulture.LCID)
        End Function
    End Class
    
    Public NotInheritable Class LocalizedWebDisplayNameAttribute
        Inherits WebDisplayNameAttribute
        Private m_isLocalized As Boolean
    
        Public Sub New(ByVal displayName As String)
            MyBase.New(displayName)
        End Sub
    
        ' Override this property to return values from the webpart's resource file.
        Public Overrides ReadOnly Property DisplayName() As String
            Get
                If Not m_isLocalized Then
                    Me.DisplayNameValue = Utility.GetLocalizedString(MyBase.DisplayName, CultureInfo.CurrentUICulture.LCID)
                    m_isLocalized = True
                End If
                Return MyBase.DisplayName
            End Get
        End Property
    End Class
    
    Public NotInheritable Class LocalizedWebDescriptionAttribute
        Inherits WebDescriptionAttribute
        Private m_isLocalized As Boolean
    
        Public Sub New(ByVal description As String)
            MyBase.New(description)
        End Sub
    
        ' Override this property to return values from the webpart's resource file.
        Public Overrides ReadOnly Property Description() As String
            Get
                If Not m_isLocalized Then
                    Me.DescriptionValue = Utility.GetLocalizedString(MyBase.Description, CultureInfo.CurrentUICulture.LCID)
                    m_isLocalized = True
                End If
                Return MyBase.Description
            End Get
        End Property
    End Class
    
  3. Navigate to the Bold property. Then change the names of the Category, WebDisplayName, and WebDescription attributes to LocalizedCategory, LocalizedWebDisplayName, and LocalizedWebDescription.

    Do not change the attribute parameters. The revised code is as follows:

    [LocalizedCategory(CATEGORY_TEXT)]
    [LocalizedWebDisplayName(TOOLPANE_BOLD)]
    [LocalizedWebDescription(TOOLPANE_BOLD_DESC)]
    
    <LocalizedCategory(CATEGORY_TEXT)> _
    <LocalizedWebDisplayName(TOOLPANE_BOLD)> _
    <LocalizedWebDescription(TOOLPANE_BOLD_DESC)> _
    
  4. Press Ctrl+Shift+S to save all.

Testing the Web Part

Test the Web Part to make sure that it works correctly.

To test the Web Part

  1. In Visual Studio, press F5 to start debugging.

    The Output pane reports the build and deployment process. Eventually the website appears in the default browser.

  2. Click Site Actions, and then click Site Settings. Under Site Collection Administration, click Site collection features. Verify that the Localized Web Part feature displays the title and description in the default language.

  3. Click your name in the upper-right corner of the page. Point to Select Display Language, and then click the alternate language that you used for localizing the Web Part. Verify that the title and description of the Localized Web Part feature display in the alternate language.

  4. (Optional) Repeat step 3, but select a language for which you did not deploy a language resource file. Verify that the title and description of the Localized Web Part feature display in the invariant language.

  5. Repeat step 3, but return the language to the website default.

  6. Click Site Actions, and then click Site Settings. Under Galleries, click Web parts. Then click LocalizedWebPart.webpart. Verify that the title, description, and example display in the default language.

  7. Repeat step 3 and optionally step 4. Verify that the language changes as expected. Then return the display to the default language.

  8. Click Site Actions, and then click More options. Click Page, select Web Part Page, and then click Create. Type a name for the page. Then click Create.

    The new Web Part page appears.

  9. In Middle Column, click Add a Web Part. Under Categories, click Custom. Select LocalizedWebPart, and then click Add.

    Note

    In this area of the user interface, the Web Part title and example always appear in the website default language. Switching to an alternate language does not affect the text that is displayed by the Web Part.

    The Web Part is added to the page.

  10. Click Stop Editing. Switch to the alternate language to verify that the Web Part title and the label text change as expected. Then return to the default language.

  11. Move the cursor to the far right of the page. When the down arrow appears, click it. Verify that the Underline action appears on the menu in the default language.

  12. Switch to the alternate language. Then repeat step 11. Verify that the Underline action displays in the alternate language. Return the display to the default language.

  13. Move the cursor to the far right side of the page. When the down arrow appears, click it. Then click Edit Web Part. At the bottom of the tool pane, look for the Text category. Verify that the name appears in the default language. Expand the category. Verify that the Bold check box and its tooltip appear in the default language.

  14. Switch to the alternate language. Then repeat step 13. Verify that display text changes to the alternate language.

See Also

Tasks

Walkthrough: Localizing Columns, Content Types, and Lists

Reference

CultureInfo

GetLocalizedString

WebBrowsableAttribute

PersonalizableAttribute

CategoryAttribute

WebDisplayNameAttribute

WebDescriptionAttribute

Concepts

Understanding the Multilingual User Interface (MUI)

Other Resources

Make multiple languages available for your site's user interface

How to: Add and Remove Mapped Folders