Set Your Sitecore Dictionary Structure According To The Helix Principles Using The Sitecore Dictionary API

High Score Labs News   •   June 21, 2018

Today, let’s review Helix principles for the Sitecore dictionary. In the Helix documentation for dictionaries (http://helix.sitecore.net/principles/language-support/dictionary.html ) it’s mentioned that the current dictionary implementation in the Sitecore is “rudimentary”, and as the main con against it, it was mentioned that it does not support Experience Editor. Unfortunately, this is true.

First of all, Sitecore developers and content authors have been using the default Sitecore dictionary (dictionary domains) functionality for years; however, one day Sitecore might decide to drop this functionality all together in the future. When considering any custom implementation in Sitecore, you now have to invest time and resources in this feature, but one day it may end up to be less advanced then the Sitecore developed replacement might be. Let’s review how we can use the Sitecore dictionary according to Helix principles.

First, it is recommended to use dictionary domains, in this way we can have the dictionary domain in our content “Globals” folder, and then we can set a different dictionary domain for each site. Next, each dictionary domain should specify a fallback domain to use if the dictionary domain does not contain a definition item for a key. So, the common dictionary domain is under the /sitecore/content/Globals, and then we can have the site specific dictionary domains with a fallback to the common dictionary domain for each site under /sitecore/content/SiteName/Globals.

According to the Helix principals, to increase modularity and discoverability in the dictionary, entries should be referenced in a hierarchical structure and it is recommended to use the following structure for the dictionary entries: [module]/[view]/[text].  Basically the [module] is our feature/foundation name, the [view] is our rendering name and [text] becomes our dictionary entry name. In our sample below, we have added an extra dictionary folder (“Folder”) on the [text] level to separate different types of text for the view, i.e., “Errors”, “Labels”. So in order to have an extra separation on the final [text] level:

For simplicity, let’s use just one common dictionary domain under /sitecore/content/Globals in our article. First of all, it is important to define the dictionary entry key format; it should never repeat. I recommend to use the dictionary entry item path relative to our dictionary domain path (so, for the dictionary key item /sitecore/content/Globals/Dictionary/Accounts/Registration/Errors/EmailRequired we can have a key like “Accounts/Registration/Errors/EmailRequired”).  In this case, the final code, i.e.,  Translate.Text(“Accounts/Registration/Errors/EmailRequired”), one can easily figure out where the dictionary entry is located, to which module and rendering it relates.

Next, it makes sense to implement the custom content editor context menu command to auto-generate dictionary entry keys, as this is this will speedup dictionary entries creation. In addition, we need to be sure that we have all the keys in the right format, so that any developer on the project will have a consistent naming convention to follow, leaving out any inconsistencies or guess work. We can then put our command on the project level under the common site. Below is the code for this command:

using Sitecore.Diagnostics;

using Sitecore.Foundation.SitecoreExtensions.Extensions;

using Sitecore.Shell.Framework.Commands;

using System.Linq;

namespace Sitecore.Common.Website.Commands

{

public class SetDictionaryKeys : Command

{

public override void Execute(CommandContext context)

{

Assert.ArgumentNotNull((object)Context.Items, “items”);

if (context.Items.Length > 0)

{

var itemDict = context.Items[0].GetAncestorOrSelfOfTemplate(Templates.Dictionary.DictionaryDomain_TemplateID);

foreach (var item in context.Items[0].GetDescendantsAndSelf().Where(x => x.TemplateID == Templates.Dictionary.DictionaryEntry_TemplateID))

{

var key = item.Paths.Path.Replace(StringUtil.EnsurePostfix(‘/’, itemDict.Paths.Path), string.Empty);

if (item[FieldIDs.DictionaryKey] != key)

{

item.Editing.BeginEdit();

item[FieldIDs.DictionaryKey] = key;

item.Editing.EndEdit();

}

}

}

}

public override CommandState QueryState(CommandContext context)

{

if (!this.IsAdvancedClient()

|| context.Items.Length != 1

|| !(context.Items[0].TemplateID == Templates.Dictionary.DictionaryDomain_TemplateID

|| context.Items[0].TemplateID == Templates.Dictionary.DictionaryFolder_TemplateID

|| context.Items[0].TemplateID == Templates.Dictionary.DictionaryEntry_TemplateID))

{

return CommandState.Hidden;

}

return base.QueryState(context);

}

}

}

Our item extension class on the foundation level:

using System;

using System.Collections.Generic;

using System.Linq;

using Sitecore.Data;

using Sitecore.Data.Items;

using Sitecore.Data.Managers;

namespace Sitecore.Foundation.SitecoreExtensions.Extensions

{

public static class ItemExtensions

{

public static Item GetAncestorOrSelfOfTemplate(this Item item, ID templateID)

{

if (item == null)

{

throw new ArgumentNullException(nameof(item));

}

return item.IsDerived(templateID) ? item : item.Axes.GetAncestors().Reverse().FirstOrDefault(i => i.IsDerived(templateID));

}

public static List<Item> GetDescendantsAndSelf(this Item item)

{

var items = item.Axes.GetDescendants().ToList();

items.Insert(0, item);

return items.ToList();

}

public static bool IsDerived(this Item item, ID templateId)

{

if (item == null)

{

return false;

}

return !templateId.IsNull && item.IsDerived(item.Database.Templates[templateId]);

}

private static bool IsDerived(this Item item, Item templateItem)

{

if (item == null)

{

return false;

}

if (templateItem == null)

{

return false;

}

var itemTemplate = TemplateManager.GetTemplate(item);

return itemTemplate != null && (itemTemplate.ID == templateItem.ID || itemTemplate.DescendsFrom(templateItem.ID));

}

}

}

Our structures for the dictionary template IDs:

using Sitecore.Data;

using System;

using System.Collections.Generic;

using System.Linq;

using System.Web;

namespace Sitecore.Common.Website

{

public struct Templates

{

public struct Dictionary

{

public static readonly ID DictionaryDomain_TemplateID = new ID(“{0A2847E6-9885-450B-B61E-F9E6528480EF}”);

public static readonly ID DictionaryEntry_TemplateID = new ID(“{6D1CD897-1936-4A3A-A511-289A94C2A7B1}”);

public static readonly ID DictionaryFolder_TemplateID = new ID(“{267D9AC7-5D85-4E9D-AF89-99AB296CC218}”);

}

}

}

So, in the “QueryState” method, we define how to show our command only in case of a dictionary domain, dictionary folder or dictionary entry is selected. In the “Execute” method, we search for the dictionary domain (we need it to set the key values relative to it) with the help of GetAncestorOrSelfOfTemplate extension method and then for each descendant or self of the selected item (GetDescendantsAndSelf extension method) with dictionary entry template we are setting up dictionary keys according to our format.

Next, lets add our command to context menu. We need to add it to the config Common.Website.Commands.config :

<?xml version=”1.0″?>

<configuration xmlns:patch=”http://www.sitecore.net/xmlconfig/”>

<sitecore>

<commands>

<command name=”item:common:set-dictionary-keys” type=”Sitecore.Common.Website.Commands.SetDictionaryKeys, Sitecore.Common.Website”/>

</commands>

</sitecore>

</configuration>

Next, in the core database under the “/sitecore/content/Applications/Content Editor/Context Menus/Default/Common” let’s add the “Common” menu item which is for our “Common” project context menu buttons:

Next, add “Set Dictionary Keys” menu item command inside of it:

The main thing here is to set the “Message” field value to the “item:common:set-dictionary-keys(id=$Target)”, where the “item:common:set-dictionary-keys” is our command that was defined in the config file.

Now, when we point in the content editor tree on any level of the dictionary domain, we can see our command available and we can set dictionary keys for selected item and it descendants according to our format.

In this article we have reviewed how to use standard Sitecore for the dictionaries on the helix based projects, also we reviewed an example of the custom context menu command (done with helix principles) to setup dictionary keys.