Translate Categories in Episerver using DbLocalizationProvider
Tags: episerver, localization, translation, optimizely
Published: 2021-01-21 20:52:53

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](https://github.com/valdisiljuconoks/localization-provider-epi/blob/master/docs/working-with-resources-epi.md#translating-episerver-categories).

```csharp
[LocalizedCategory]
public class SampleCategory : Category
{
    public SampleCategory()
    {
        Name = "This is sample cat. from code";
    }
}
```

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](https://github.com/valdisiljuconoks/localization-provider-epi/blob/master/README.md) has a way to [manually create resources](https://github.com/valdisiljuconoks/LocalizationProvider/blob/master/docs/sync-net.md#register-manually-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.

```csharp
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}");
            }
        }
    }
}


```

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

![DbLocalizationProvider](https://dev-to-uploads.s3.amazonaws.com/i/knqhifult9weu0oghnht.PNG)

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.

```csharp

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);
            }

        }
    }
}
```

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

```csharp
...
DbLocalizationProvider.LocalizationProvider.Current.GetString(category.GetResourceKey())
...
```

I would like to thank the [Valdis Iljuconoks](https://twitter.com/tech_fellow) for helping me out and answering some questions I had along the way.