DEV Community

Cover image for Translate Categories in Episerver using DbLocalizationProvider
Paul McGann
Paul McGann

Posted on • Updated on

Translate Categories in Episerver using DbLocalizationProvider

While doing some investigation into localization with Episerver I came across a useful library that helps content editors easily translate resources that are normally stored in xml, by storing them within a database.

One of the areas that I investigated was translating the built in categories within Episerver. This is possible to do using a strongly-typed category definition.

[LocalizedCategory]
public class SampleCategory : Category
{
    public SampleCategory()
    {
        Name = "This is sample cat. from code";
    }
}
Enter fullscreen mode Exit fullscreen mode

This may be suitable in some cases, however in some scenarios where the categories already exist this may not be the best course of action.

The DbLocalizationProvider has a way to manually create resources. Using this I set about creating a resource manually for each category.

Each resource needs a unique resource key, the translation /value and the language the translation is for.

In order to create each resource I used a stored procedure that would iterate over the categories and build out a unique resource key, whilst assigning the value and language.

using DbLocalizationProvider.Sync;
using EPiServer.DataAbstraction;
using EPiServer.PlugIn;
using EPiServer.Scheduler;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;

namespace LangTransaltion.Business.Localization
{
    [ScheduledPlugIn(DisplayName = "Category Localization Scheduled Job")]
    public class CategoryLocalizationScheduledJob : ScheduledJobBase
    {
        private readonly CategoryRepository _categoryRepository;
        private readonly ISynchronizer _synchronizer;

        private bool _stopSignaled;
        private int _categoryCounterTotal = 0;
        private IList<ManualResource> _allCategories;

        public CategoryLocalizationScheduledJob(CategoryRepository categoryRepository, ISynchronizer synchronizer)
        {
            IsStoppable = true;
            _categoryRepository = categoryRepository;
            _synchronizer = synchronizer;
        }

        /// <summary>
        /// Called when a user clicks on Stop for a manually started job, or when ASP.NET shuts down.
        /// </summary>
        public override void Stop()
        {
            _stopSignaled = true;
        }

        /// <summary>
        /// Called when a scheduled job executes
        /// </summary>
        /// <returns>A status message to be stored in the database log and visible from admin mode</returns>
        public override string Execute()
        {
            OnStatusChanged($"Starting execution of {this.GetType()}");

            _allCategories = new List<ManualResource>();

            var root = _categoryRepository.GetRoot();

            IterateCategories(root, $"Categories.{root.Name}");

            _synchronizer.RegisterManually(_allCategories);

            return $"{_categoryCounterTotal} categories checked";
        }

        /// <summary>
        /// Iterates over categories build a unique key
        /// </summary>
        /// <param name="currentParent"></param>
        /// <param name="path"></param>
        private void IterateCategories(Category currentParent, string path)
        {
            var categories = currentParent.Categories.ToList();

            foreach (var category in categories)
            {
                if (_stopSignaled) break;

                _categoryCounterTotal++;

                OnStatusChanged($"Working on category {category.Name}, child of: {currentParent.Name}");

                _allCategories.Add(new ManualResource($"{path}.{category.Name}", $"{category.Description}", new CultureInfo("en")));

                IterateCategories(category, $"{path}.{category.Name}");
            }
        }
    }
}


Enter fullscreen mode Exit fullscreen mode

After running the scheduled job the new resources will be added to the database.

DbLocalizationProvider

The final piece of the puzzle was to retrieve the resource key whenever we use the category on the site. In order to do this I created an extension method to return the resource key based on the current category.


using EPiServer.DataAbstraction;
using System.Collections.Generic;

namespace LangTransaltion.Helpers
{
    public static class CategoryHelper
    {
        /// <summary>
        /// Gets the category resource key.
        /// </summary>
        /// <param name="category"></param>
        /// <returns></returns>
        public static string GetResourceKey(this Category category)
        {
            var categoryList = new List<string>();

            GetParentCategories(category, categoryList);

            categoryList.Reverse();

            var resourceKey = $"Categories.{string.Join(".", categoryList)}";

            return resourceKey;
        }

        /// <summary>
        /// Gets the parent categories, starting a from a root category.
        /// </summary>
        /// <param name="currentCategory"></param>
        /// <param name="categoryList"></param>
        private static void GetParentCategories(Category currentCategory, List<string> categoryList)
        {
            categoryList.Add(currentCategory.Name);

            if (currentCategory.Parent != null)
            {
                var parent = currentCategory.Parent;

                GetParentCategories(parent, categoryList);
            }

        }
    }
}
Enter fullscreen mode Exit fullscreen mode

Now when you are using a category and wish to retrieve the translation you can simply use

...
DbLocalizationProvider.LocalizationProvider.Current.GetString(category.GetResourceKey())
...
Enter fullscreen mode Exit fullscreen mode

I would like to thank the Valdis Iljuconoks for helping me out and answering some questions I had along the way.

Top comments (0)