Frontend development

Sample: Language selector widget

Overview

You use the Language selector widget sample to create a language selector widget, which you can use to switch between different language versions of a page.

IRequestPreparation interface

This sample shows how two widgets can communicate with each other in the early stage of the pipeline.

PREREQUISITES: You must set up:

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:

  • Entities/LanguageSelector
  • Models/LanguageSelector
  • ViewModels/LanguageSelector
  • ViewComponents
  • Views/Shared/Components/LanguageSelector

Create the widgets

1. Create the LanguageSelector entity
  1. In the context menu of folder Entities/LanguageSelector, click Add » Class…
  2. In Name, enter LanguageSelectorEntity.cs and click Add.
  3. In the class, paste the following code and save your changes:
C#
namespace Renderer.Entities.LanguageSelector
{
    public class LanguageSelectorEntity
    {
    }
}
2. Create the LanguageSelector model
  1. In the context menu of folder Models/LanguageSelector, click Add » Class…
  2. In Name, enter LanguageSelectorModel.cs and click Add.
  3. In the class, paste the following code and save your changes:
C#
using System.Globalization;
using System.Threading.Tasks;
using System.Collections.Generic;
using Renderer.Entities.LanguageSelector;
using Renderer.ViewModels.LanguageSelector;
using Progress.Sitefinity.AspNetCore.Web;
using Progress.Sitefinity.RestSdk;
using Progress.Sitefinity.RestSdk.Clients.Pages.Dto;

namespace Renderer.Models.LanguageSelector
{
    public class LanguageSelectorModel
    {
        private IRequestContext requestContext;
        private IRestClient restClient;

        public LanguageSelectorModel(IRequestContext requestContext, IRestClient restClient)
        {
            this.requestContext = requestContext;
            this.restClient = restClient;
        }

        public async Task<LanguageSelectorViewModel> GetViewModel(LanguageSelectorEntity entity)
        {
            var cultures = this.requestContext.Site.Cultures;

            var viewModel = new LanguageSelectorViewModel();
            var culturePageMap = new Dictionary<string, Task<PageNodeDto>>();
            if (this.requestContext.PageNode != null)
            {
                var batchBuilder = this.restClient.StartBatch();
                foreach (var culture in cultures)
                {
                    var response = batchBuilder.GetItem<PageNodeDto>(new GetItemArgs()
                    {
                        Id = this.requestContext.PageNode.Id,
                        Provider = this.requestContext.PageNode.Provider,
                        Culture = culture.Name,
                    });
                    culturePageMap.Add(culture.Name, response);
                }

                await batchBuilder.Execute();
            }

            foreach (var culture in cultures)
            {
                var ci = CultureInfo.GetCultureInfo(culture.Name);
                var entry = new LanguageEntry()
                {
                    Name = ci.EnglishName,
                    Value = ci.Name,
                    Selected = ci.Name == this.requestContext.Culture.Name
                };

                if (culturePageMap.TryGetValue(culture.Name, out Task<PageNodeDto> task))
                {
                    entry.PageUrl = task.Result.ViewUrl;
                }

                viewModel.Languages.Add(entry);
            }

            return viewModel;
        }
    }
}
3. Create the LanguageSelector ViewModel
  1. In the context menu of folder ViewModels/LanguageSelector, click Add » Class…
  2. In Name, enter LanguageSelectorViewModel.cs and click Add.
  3. In the class, paste the following code and save your changes:
C#
using System.Collections.Generic;
using Progress.Sitefinity.AspNetCore.Web;

namespace Renderer.ViewModels.LanguageSelector
{
    public class LanguageSelectorViewModel
    {
        public IList<LanguageEntry> Languages { get; set; } = new List<LanguageEntry>();
    }
}
4. Create the Language entity
  1. In the context menu of folder ViewModels/LanguageSelector, click Add » Class…
  2. In Name, enter LanguageEntity.cs and click Add.
  3. In the class, paste the following code and save your changes:
C#
using System.Collections.Generic;
using Progress.Sitefinity.AspNetCore.Web;

namespace Renderer.ViewModels.LanguageSelector
{
    public class LanguageEntry
    {
        public string Name { get; set; }

        public string Value { get; set; }

        public bool Selected { get; set; }

        public string PageUrl { get; set; }
    }
}
5. Create the LanguageSelector view component
  1. In the context menu of folder ViewComponents, click Add » Class…
  2. In Name, enter LanguageSelectorViewComponent.cs and click Add.
  3. In the class, paste the following code and save your changes:
C#
using System;
using Microsoft.AspNetCore.Mvc;
using Renderer.Entities.LanguageSelector;
using Progress.Sitefinity.AspNetCore.ViewComponents;
using Renderer.Models.LanguageSelector;
using System.Threading.Tasks;

namespace Renderer.ViewComponents
{
    /// <summary>
    /// Language selector widget.
    /// </summary>
    [SitefinityWidget(Title="Language selector")]
    public class LanguageSelectorViewComponent : ViewComponent
    {
        private LanguageSelectorModel languageSelectorModel;

        public LanguageSelectorViewComponent(LanguageSelectorModel languageSelectorModel)
        {
            this.languageSelectorModel = languageSelectorModel;
        }

        /// <inheritdoc/>
        public async Task<IViewComponentResult> InvokeAsync(IViewComponentContext<LanguageSelectorEntity> context)
        {
            if (context == null)
            {
                throw new ArgumentNullException(nameof(context));
            }

            var viewModel = await this.languageSelectorModel.GetViewModel(context.Entity);
            return this.View(viewModel);
        }
    }
}
6. Create the LanguageSelector view
  1. In the context menu of folder Views/Shared/Components/LanguageSelector, click Add » Class…
  2. Select Code File.
  3. In Name, enter Default.cshtml and click Add.
  4. In the class, paste the following code and save your changes:
Razor
@using Progress.Sitefinity.AspNetCore.Mvc.Rendering;
@using language_selector.ViewModels.LanguageSelector;

@model LanguageSelectorViewModel

<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js"
        section-name="Top"
        asp-fallback-href="Scripts/bootstrap.bundle.min.js"
        integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM"
        crossorigin="anonymous"></script>

<div class="dropdown">
    <a class="nav-link dropdown-toggle" href="#" id="language_selector" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
        <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-globe" viewBox="0 0 16 16">
            <path d="M0 8a8 8 0 1 1 16 0A8 8 0 0 1 0 8zm7.5-6.923c-.67.204-1.335.82-1.887 1.855A7.97 7.97 0 0 0 5.145 4H7.5V1.077zM4.09 4a9.267 9.267 0 0 1 .64-1.539 6.7 6.7 0 0 1 .597-.933A7.025 7.025 0 0 0 2.255 4H4.09zm-.582 3.5c.03-.877.138-1.718.312-2.5H1.674a6.958 6.958 0 0 0-.656 2.5h2.49zM4.847 5a12.5 12.5 0 0 0-.338 2.5H7.5V5H4.847zM8.5 5v2.5h2.99a12.495 12.495 0 0 0-.337-2.5H8.5zM4.51 8.5a12.5 12.5 0 0 0 .337 2.5H7.5V8.5H4.51zm3.99 0V11h2.653c.187-.765.306-1.608.338-2.5H8.5zM5.145 12c.138.386.295.744.468 1.068.552 1.035 1.218 1.65 1.887 1.855V12H5.145zm.182 2.472a6.696 6.696 0 0 1-.597-.933A9.268 9.268 0 0 1 4.09 12H2.255a7.024 7.024 0 0 0 3.072 2.472zM3.82 11a13.652 13.652 0 0 1-.312-2.5h-2.49c.062.89.291 1.733.656 2.5H3.82zm6.853 3.472A7.024 7.024 0 0 0 13.745 12H11.91a9.27 9.27 0 0 1-.64 1.539 6.688 6.688 0 0 1-.597.933zM8.5 12v2.923c.67-.204 1.335-.82 1.887-1.855.173-.324.33-.682.468-1.068H8.5zm3.68-1h2.146c.365-.767.594-1.61.656-2.5h-2.49a13.65 13.65 0 0 1-.312 2.5zm2.802-3.5a6.959 6.959 0 0 0-.656-2.5H12.18c.174.782.282 1.623.312 2.5h2.49zM11.27 2.461c.247.464.462.98.64 1.539h1.835a7.024 7.024 0 0 0-3.072-2.472c.218.284.418.598.597.933zM10.855 4a7.966 7.966 0 0 0-.468-1.068C9.835 1.897 9.17 1.282 8.5 1.077V4h2.355z" />
        </svg>
    </a>
    <ul class="dropdown-menu" aria-labelledby="language_selector">
        @foreach (var item in Model.Languages)
        {
            <li class="nav-item">
                <a class="dropdown-item nav-link @GetClass(item.Selected)" href="@item.PageUrl" target="_self">@item.Name</a>
            </li>
        }
    </ul>
</div>

@*Resolves the active class for the selected culture*@
@{Microsoft.AspNetCore.Html.IHtmlContent GetClass(bool selected)
    {

        if (selected)
        {
            return Html.HtmlSanitize("active");
        }

        return null;
    }
}

Build your solution.

Result

When you open your Renderer application and open the New editor, you will see the Language selector widget in the widget selector. LanguageSelector

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/language-selector folder.
  5. Open the language-selector.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 language-selector project, click View in Browser.
  9. Log in to your Sitefinity CMS instance and place the widget on a page.
Want to learn more?
Enhance your Sitefinity skills by enrolling in free training sessions. Become Sitefinity certified through Progress Education Community to strengthen your professional credentials.