Module: Multilingual content

3 of 6 Pages

Implement multilingual support for your site

Although creating the language versions of content is a task for editors, as a developer, you must take steps to ensure a consistent experience for your website visitors.

Retrieve preferred language

The first important building block is the preferred language retriever. Its Get() method returns the language of the request (if defined) or the primary language of the current website channel. You can simply inject IPreferredLanguageRetriever anywhere you need, using dependency injection. For example:

C#


using Kentico.Content.Web.Mvc.Routing;

public class MyClass
{
    private readonly IPreferredLanguageRetriever preferredLanguageRetriever;

    public MyClass(IPreferredLanguageRetriever preferredLanguageRetriever)
    {
        this.preferredLanguageRetriever = preferredLanguageRetriever;
    }

    public string DoThings()
    {
        // Retrieve the preferred language
        var currentLanguage = preferredLanguageRetriever.Get();
        ...
    }
}

The value returned is a Code name that has been defined for the language in the Xperience administration interface. 

Code name of a language in Xperience administration

Retrieve localized content items

To retrieve a specific language variant of a content item (website channel pages or content items stored in content hub), you simply use the InLanguage extension method of ContentItemQueryBuilder, passing it a language Code name (see picture above) as a parameter.

By passing in the preferred language, you will get your content item(s) in a language variant consistent with your current context.

Take a look at the place in your code where you call the ContentItemQueryBuilder. If you have been working off the main branch of our training guides repository or are following along looking at the finished branch, one example is located in the TrainingGuides.Web/Features/Shared/Services/ContentItemRetrieverService.cs

C#
ContentItemRetrieverService.cs

...
public async Task<IEnumerable<T>> RetrieveWebPageContentItems(
        string contentTypeName,
        Func<ContentTypesQueryParameters, ContentTypesQueryParameters> innerQueryFilter,
        Action<ContentQueryParameters> outerParams,
        string? languageName = null)
    {
        var builder = new ContentItemQueryBuilder()
                            .ForContentTypes(
                                config => innerQueryFilter(config)
                                .OfContentType(contentTypeName)
                                .WithWebPageData()
                            )
                            .Parameters(outerParams)
                            .InLanguage(languageName ?? preferredLanguageRetriever.Get());

        var queryExecutorOptions = new ContentQueryExecutionOptions
        {
            ForPreview = webSiteChannelContext.IsPreview
        };

        var pages = await contentQueryExecutor.GetMappedWebPageResult<T>(builder, queryExecutorOptions);

        return pages;
    }
...

Referenced content items

In case you need to retrieve a deeper structure of content items (e.g., a page that references another content item, which also references another content item), call WithLinkedItems method as a part of your queryFilter.

Here’s an example calling the RetrieveWebPageContentItem method from above:

C#
ContentItemRetrieverService.cs


...
var pages = await RetrieveWebPageContentItems(
                contentTypeName,
                config => config
                    .Where(where => where.WhereEquals(nameof(WebPageFields.WebPageItemID), webPageItemId))
                    .WithLinkedItems(depth));
...

Because RetrieveWebPageContentItem is using the InLanguage extension method, this call will return the whole structure of content items localized.

Read more about WithLinkedItems and other methods of ContentItemBuilder.ForContentType

Editor’s task (optional): Display Home page in Spanish

Now, if your implementation is correct, your application is able to retrieve and display content items in different languages. You can test this, for example, by creating a new language version of one of your pages in the Xperience administration.

  1. Navigate to Channels → Training guides pages and translate the Home page into the new language.

  2. Remember to translate and publish the reusable content item referenced by the Home page (Landing content) as well.

    • Welcome to Training guides! in Spanish is ¡Bienvenidos a las guías de formación!.
    • Hello world! translates into ¡Hola Mundo!.
  3. Visit the root of your website and add /es to the end of the URL (e.g., http://localhost:53415/es). You should see your home page content in Spanish, like in the screenshot below.
    Image of the home page displaying Spanish content

Localizing consent texts looks very similar to localizing content items - you utilize the IPreferredLanguageRetriever. Simply pass the preferred language to the GetConsentTextAsync method as a parameter:

C#


await consent.GetConsentTextAsync(preferredLanguageRetriever.Get()))

A good example of this in practice is a cookie preferences widget or a tracking consent banner. If you have previously followed our Create a cookie preferences widget and Build a tracking consent banner guides, your code likely already handles multilingual situations, but let’s take a closer look.

  1. Cookie preferences widget

    Navigate to CookiePreferencesWidgetViewComponent.cs in TrainingGuides.Web/Features/DataProtection/Widgets/CookiePreferences:

    C#
    CookiePreferencesWidgetViewComponent.cs
    
    
     ...
     public async Task<ViewViewComponentResult> InvokeAsync(CookiePreferencesWidgetProperties properties)
     {
         ...
         return View("~/Features/DataProtection/Widgets/CookiePreferences/CookiePreferencesWidget.cshtml", new CookiePreferencesWidgetViewModel
         {
             // In this example Essential header and description are defined in the cookies widget (not as a separate consent in Data protection).
             // The correct language version will come back together with the page content.
             EssentialHeader = properties.EssentialHeader,
             EssentialDescription = properties.EssentialDescription,
    
             PreferenceHeader = preferenceCookiesConsent.ConsentDisplayName ?? CONSENT_MISSING_HEADER,
             // Retrieve consent text in preferred language and display its FullText property value as PreferenceDescriptionHtml
             PreferenceDescriptionHtml = new HtmlString((await preferenceCookiesConsent.GetConsentTextAsync(preferredLanguageRetriever.Get())).FullText) ?? consentMissingDescriptionHtml,
    
             AnalyticalHeader = analyticalCookiesConsent.ConsentDisplayName ?? CONSENT_MISSING_HEADER,
             // Retrieve consent text in preferred language and display its FullText property value as  AnalyticalDescriptionHtml
             AnalyticalDescriptionHtml = new HtmlString((await analyticalCookiesConsent.GetConsentTextAsync(preferredLanguageRetriever.Get())).FullText) ?? consentMissingDescriptionHtml,
    
             MarketingHeader = marketingCookiesConsent.ConsentDisplayName ?? CONSENT_MISSING_HEADER,
             // Retrieve consent text in preferred language and display its FullText property value as MarketingDescriptionHtml
             MarketingDescriptionHtml = new HtmlString((await marketingCookiesConsent.GetConsentTextAsync(preferredLanguageRetriever.Get())).FullText) ?? consentMissingDescriptionHtml,
    
             ...
         });
     }
     ...
    
     

    If you are showing the ConsentDisplayName to the visitor (e.g., as a consent header), be advised, that ConsentDisplayName does not support multiple language versions at this time. We recommend utilizing stringLocalizer and Shared resources in your .Net project to handle this, as you will see in the next section.

    See this guide to view the code in context.

  2. Tracking consent banner

    Navigate to TrackingConsentViewComponent.cs in TrainingGuides.Web/Features/DataProtection/ViewComponents/TrackingConsent:

    C#
    TrackingConsentViewComponent.cs
    
    
     ...
     public async Task<IViewComponentResult> InvokeAsync()
     {
         ...
    
         if (consents.Count() > 0)
         {
             var currentContactInfo = ContactManagementContext.GetCurrentContact(false);
    
             string text = "<ul>";
             List<string> codenames = [];
             bool isAgreed = false;
             foreach (var consent in consents)
             {
                 ...
    
                 // When iterating through the list of consents to render a list of short texts:
                 // Retrieve consent text in preferred language and access its ShortText property
                 text += $"<li>{(await consent.GetConsentTextAsync(preferredLanguageRetriever.Get())).ShortText}</li>";
    
                 ...
             }
             text += "</ul>";
    
             ...
             // Displays a view with tracking consent information and actions
             return View("~/Features/DataProtection/ViewComponents/TrackingConsent/TrackingConsent.cshtml", consentModel);
         }
    
         return Content(string.Empty);
     }
     ...
    
     

    Feel free to look over this code in the context of its guide.

Editor’s task (optional): Display cookie texts in Spanish

  1. Navigate to Data protection in your Xperience administration interface and follow the documentation to translate your consent texts into Spanish (Español).

  2. Navigate to Training guides pages website channel (Channels → Training guides pages), translate the Cookie policy page, including the Cookie preferences widget, and publish.

  3. Visit the Spanish version of your Cookie policy page by inserting the ‘es’ language code after the base URL (e.g., http://localhost:53415/es/cookie-policy). You should see your translated Cookie preferences banner content.
    Image of the cookie policy page displaying consent texts in Spanish, but not consent names

  4. Open your site in a new browser incognito window, appending /es to the base URL. You should see your Tracking consent banner, looking similar to the picture below:

    Screenshot of the tracking consent banner showing consent texts in Spanish, but not other texts

Translate other application texts

Your application can now display localized consent texts. However, there are still some visitor-facing strings in your application that are not part of content items or consents. For example, the title in the header, the texts in the footer, cookie headers, or the Configure cookies link in the Tracking consent banner.

We recommend utilizing ASP .NET Core IStringLocalizer and Shared resources for translating strings like these. If you are following along and working off the Training guides repository, you can see the TrainingGuides.Web project already contains a Resources folder with a SharedResources class. Let’s translate your texts into Spanish. Start with the “Training guides” title in the Header view component. 

  1. Locate HeaderViewComponent.cs file under TrainingGuides.Web/Features/Header.

  2. Inject IStringLocalizer using constructor injection and translate the Heading property as you see in the code below:

    C#
    HeaderViewComponent.cs
    
    
     using Microsoft.AspNetCore.Mvc;
     using Microsoft.Extensions.Localization;
    
     namespace TrainingGuides.Web.Features.Header;
     public class HeaderViewComponent : ViewComponent
     {
    
         // Inject the IStringLocalizer passing in the SharedResources type.
         private readonly IStringLocalizer<SharedResources> stringLocalizer;
         public HeaderViewComponent(IStringLocalizer<SharedResources> stringLocalizer)
         {
             this.stringLocalizer = stringLocalizer;
         }
    
         public IViewComponentResult Invoke()
         {
             var model = new HeaderViewModel()
             {
                 // Localize the "Training guides" string.
                 Heading = stringLocalizer["Training guides"]
             };
             return View("~/Features/Header/Header.cshtml", model);
         }
     }
    
     
  3. In the Resources folder, create a SharedResources.es.resx file.

    It is a good practice to use the two-letter ISO language name, based on IETF or one of the ISO-639 standards - “es” for Spanish.

    The example implementation in this series requires the language name to match the Code name defined for your language in Xperience administration.

    IStringLocalizer doesn’t require storing the default language strings in a resource file. Simply call the localizer on the default strings (like in the code sample above).

    While the older approach of creating SharedResources.resx file and defining a code name for each string is still valid and works, Microsoft documentation of the IStringLocalizer refers to the newer practice as preferred.

  4. Add the Spanish translation:

    • name: Training guides (the text in primary language)

    • value: Guías de formación

      Visual Studio will allow you to modify the file in a table view. If you use a different editor (e.g., Visual Studio Code), you may have to manually edit the XML inside the file. To add a text translation, add a <data> tag inside the root:

      XML
      
      
        <data name="Training guides" xml:space="preserve">
            <value>Guías de formación</value>
        </data>
      
        
  5. Find other strings in the project that are “hardcoded” in the application, rather than stored in Xperience by Kentico, and repeat the same steps.

    To localize strings inside views, simply @inject IStringLocalizer<SharedResources> StringLocalizer in the view or in your _ViewImports.cshtml file. Then, you can call StringLocalizer just like in the example above. For instance, @StringLocalizer["Xperience by Kentico training guide samples."] to translate one of the footer texts.

    Here is a link to the full SharedResources.es.resx file in our repository, for your reference. It contains texts from TrainingGuides.Web project translated into Spanish.

You can see this IStringLocalizer approach applied to the cookie preferences widget and the tracking consent banner in the finished branch of our Training guides repository. We’ll use this approach to make some improvements later in this series:

Your website visitors can now view individual pages’ content in English (the default language) and Spanish (by typing ‘/es’ after the base URL in the browser navigation bar):