Sample: Localized widget

Overview

This project demonstrates how to create a localized widget. The Localized widget renders its content in the default language or in German, depending on which language version of the page is requested.
The project is based on the start template sample. For more information, see Sample: Starter template.

When the Renderer attempts to render a page, the culture for that page is passed along as information to the Renderer. The Renderer uses this culture to set the CultureInfo.CurrentUICulture and CultureInfo.CurrentCulture properties to match the value of the Sitefinity page.

The API for localized strings is based on the out-of-the-box API for developing ASP.NET Core applications. For more information, see Globalization and localization in ASP.NET Core.

PREREQUISITES: Before implementing the sample, you must:
  • Set up a Sitefinity renderer application and connect it to your Sitefinity CMS application.
    For more information, see Install Sitefinity in ASP.NET Core mode.
  • Create a German version of version of the page where you want to paste the Localized widget.
    For more information, see Translate a page.

NOTE: The instructions in this sample use Visual Studio 2022 and a Sitefinity renderer project named Renderer.

Create the folder structure

Under your Renderer project, you must create the following folders:

  • ViewComponents
  • Views/Shared/Components/Localized

Create a custom layout file

You must register the custom scripts or styles in the _Layout.cshtml file. You must then use this custom base layout for every page where the Localized widget is placed

Perform the following:

  1. In the context menu of folder Views/Shared, click Add » Class…
  2. Select Code File.
  3. In Name, enter _Layout.cshtml and click Add.
  4. In the file, paste the following code and save your changes:

    @model Progress.Sitefinity.AspNetCore.Models.PageModel
    @using Progress.Sitefinity.AspNetCore.Mvc.Rendering
    @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
    @addTagHelper *, Progress.Sitefinity.AspNetCore
    @inject Progress.Sitefinity.AspNetCore.Web.IRequestContext requestContext;
    <!DOCTYPE html>
    <html id="html" lang="@requestContext.Culture">
    <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <link rel="icon" type="image/x-icon" href="favicon.ico">
    @Html.RenderSeoMeta(this.Model)
    <environment include="Development">
    <link rel="stylesheet" href="@Progress.Sitefinity.AspNetCore.Constants.PrefixRendererUrl("Styles/bootstrap.css")" />
    </environment>
    <environment exclude="Development">
    <link rel="stylesheet" href="@Progress.Sitefinity.AspNetCore.Constants.PrefixRendererUrl("Styles/bootstrap.min.css")" />
    </environment>
    @* Scripts for the OOB Sitefinity widgets *@
    <link rel="stylesheet" href="@Progress.Sitefinity.AspNetCore.Constants.PrefixRendererUrl("styles/main.css")" type="text/css" />
    @* Custom styles *@
    <link rel="stylesheet" href="~/styles/styles.css" />
    @* Custom scripts *@
    <script src="~/scripts/scripts.js"></script>
    </head>
    <body class="container-fluid">
    @RenderSection("Scripts")
    </body>
    </html>
    view raw _Layout.cshtml hosted with ❤ by GitHub

NOTE: If you have any custom scripts or styles, place them in folders wwwroot/scripts/scripts.js and wwwroot/styles/styles.css.

Create the widget

In Visual Studio, open the Renderer application.

  • In the context menu of folder ViewComponents, click Add » Class…
  • In Name, enter LocalizedViewComponent.cs and click Add.
  • In the class, paste the following code and save your changes:

using System;
using Microsoft.AspNetCore.Mvc;
using Progress.Sitefinity.AspNetCore.ViewComponents;
using Microsoft.Extensions.Localization;
namespace Renderer.ViewComponents
{
/// <summary>
/// Test widget with different kind of restrictions for its properties.
/// </summary>
[SitefinityWidget]
public class LocalizedViewComponent : ViewComponent
{
private IStringLocalizer<LocalizedViewComponent> localizer;
public LocalizedViewComponent(IStringLocalizer<LocalizedViewComponent> localizer)
{
this.localizer = localizer;
}
/// <summary>
/// Invokes the view.
/// </summary>
/// <param name="context"></param>
/// <returns></returns>
public IViewComponentResult Invoke()
{
// when a Sitefinity page is executed the CultureInfo.CurrentUICulture is automatically populated
// with the culture the page is translated on, so the string here would be resolved automatically
var localizedString = this.localizer.GetString("Hello World !");
return this.View("Default", localizedString.Value);
}
}
}

  • In the context menu of folder ViewComponents, click Add » Class…
  • In Name, enter LocalizedViewComponent.de.resx and click Add.
  • In the class, paste the following code and save your changes:

<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="Hello World !" xml:space="preserve">
<value>Hallo Welt !</value>
</data>
</root>

  • In the context menu of folder Views/Shared/Components/Localized, click Add » Class…
  • Select Code File.
  • In Name, enter Default.cshtml and click Add.
  • In the class, paste the following code and save your changes:

@model string
@using Microsoft.AspNetCore.Mvc.Localization
@inject IViewLocalizer Localizer;
<h1>Localized string passed from view component: @Model</h1>
<h1>Localized string in view: @Localizer["Hello World !"]</h1>
view raw Default.cshtml hosted with ❤ by GitHub

  • In the context menu of folder Views/Shared/Components/Localized, click Add » Class…
  • Select Code File.
  • In Name, enter Default.de.resx and click Add.
  • In the class, paste the following code and save your changes:

<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="Hello World !" xml:space="preserve">
<value>Hallo Welt !</value>
</data>
</root>
view raw Default.de.resx hosted with ❤ by GitHub

Build your solution.

Result

When you open your Renderer application and open the New editor, you will see the Localized widget in the widget selector. When you add the widget on your page and click the Custom empty text link, you can enter a text string that will be displayed on the frontend.

Localization

Run the sample

This sample is available in Sitefinity’s GitHub repository. You can run and play with it.
To do this, perform the following:

  1. Go to Sitefinity’s GitHub repository Sitefinity ASP.NET Core samples.
  2. Expand Code and click Download ZIP.
  3. Extract the files on your computer.
  4. In the extracted folder, navigate to sitefinity-aspnetcore-mvc-samples-master/src/localization folder.
  5. Open the localization.sln in Visual Studio.
  6. Open the appsettings.json file.
  7. In section “Sitefinity”, change the “Url” property to the URL of your Sitefinity CMS site.
    If you have deployed Sitefinity CMS on the IIS, point to “https://localhost:<https_port>".
  8. In Visual Studio, in the context menu of localization project, click View in Browser.
  9. Log in to your Sitefinity CMS instance and place the widget on a page.

Want to learn more?

Increase your Sitefinity skills by signing up for our free trainings. Get Sitefinity-certified at Progress Education Community to boost your credentials.

Get started with Integration Hub | Sitefinity Cloud | Sitefinity SaaS

This free lesson teaches administrators, marketers, and other business professionals how to use the Integration hub service to create automated workflows between Sitefinity and other business systems.

Web Security for Sitefinity Administrators

This free lesson teaches administrators the basics about protecting yor Sitefinity instance and its sites from external threats. Configure HTTPS, SSL, allow lists for trusted sites, and cookie security, among others.

Foundations of Sitefinity ASP.NET Core Development

The free on-demand video course teaches developers how to use Sitefinity .NET Core and leverage its decoupled architecture and new way of coding against the platform.

Was this article helpful?