Switching authoring language in Sitecore

There are different approaches that can be taken when managing multi-lingual web sites in Sitecore. Sometimes your authors might have to manage content in multiple languages and the might need to switch between site trees that are built on different languages.

Updated 26 September 2016: The OpenExperienceEditor command needs to be replaced in order to make this work properly. Code for that added below.

In that scenario, editor often faces the message that there is no version on the current language, and they have to find the right one in a long list of languages. This isn’t very convenient. Using the new Experience Editor it’s even worse, because authors are not given this information at all, so they might start editing a page in the wrong language by mistake.

default-content-editor-message

I wanted to make this easier for the authors, so I decided to change the built-in Content Editor warning, and add an equivalent function to the Experience Editor.

The Content Editor uses the getContentEditorWarnings pipeline, and it contains a HasNoVersions processor. This particular processor is a little special, because it suppresses the other processors in the pipeline. So the easiest way to achieve the goal is to override it, like this:

public class ContentEditorMissingLanguageWarning
{
	public void Process(GetContentEditorWarningsArgs args)
	{
		var item = args.Item;
		if (item == null)
			return;

		var versionNumbers = item.Versions.GetVersionNumbers(false);
		if(versionNumbers != null && versionNumbers.Length > 0)
			return;

		var languages = item.Versions.GetVersions(true)
			.Select(i => i.Language)
			.Distinct(new LanguageComparer())
			.OrderBy(l => l.Name)
			.ToList();
			
		var contentEditorWarning = args.Add();
		contentEditorWarning.Title = string.Format("The current item does not have a version in \"{0}\".", item.Language.CultureInfo.DisplayName);
		contentEditorWarning.Text = "You may jump to any of the languages below";
		foreach (var lang in languages)
		{
			var message = string.Format("Go to {0} {1}", lang.Name, lang.CultureInfo.DisplayName);
			var command = string.Format("item:load(id={0},language={1})", item.ID, lang.Name);
			contentEditorWarning.AddOption(message, command);
		}

		if (item.Access.CanWriteLanguage() && item.Access.CanWrite())
		{
			contentEditorWarning.Text += ", or add a new version";
			contentEditorWarning.AddOption(string.Format("Add a new version on {0}", item.Language.CultureInfo.DisplayName), "item:addversion");
			contentEditorWarning.IsExclusive = true;
		}
		else
			contentEditorWarning.IsExclusive = false;

		contentEditorWarning.HideFields = true;
		contentEditorWarning.Key = "ContentEditorMissingLanguageWarning";
	}
}

public class LanguageComparer : IEqualityComparer<Language>
{
	public bool Equals(Language x, Language y)
	{
		if (x == null && y == null)
			return true;
		if (x == null || y == null)
			return false;
		return string.Equals(x.Name, y.Name);
	}

	public int GetHashCode(Language obj)
	{
		if (obj == null)
			return 0;
		return obj.Name.GetHashCode();
	}
}

With this, we will get the following result in the Content Editor instead, that is much more convenient:

go-to-language-content-editor

Adding the same function to the Experience Editor is about the same, but with a few adaptations to make it fit into the getPageEditorNotifications pipeline:

public class ExperienceEditorMissingLanguageWarning : GetPageEditorNotificationsProcessor
{
	public override void Process(GetPageEditorNotificationsArgs args)
	{
		Assert.ArgumentNotNull(args, "arguments");
		var item = args.ContextItem;
		if (item == null)
			return;

		var versionNumbers = item.Versions.GetVersionNumbers(false);
		if (versionNumbers != null && versionNumbers.Length > 0)
			return;

		var languages = item.Versions.GetVersions(true)
			.Select(i => i.Language)
			.Distinct(new LanguageComparer())
			.OrderBy(l => l.Name)
			.ToList();

		var message = string.Format("This page does not have a version in {0}", item.Language.CultureInfo.DisplayName);

		var options = new List<PageEditorNotificationOption>();
		foreach (var lang in languages)
		{
			var title = string.Format("Go to {0} {1}", lang.Name, lang.CultureInfo.DisplayName);
			var cb = new CommandBuilder("webedit:setlanguage");
			cb.Add("language", lang.Name);
			var option = new PageEditorNotificationOption(title, cb.ToString());
			options.Add(option);
		}

		var notification = new PageEditorNotification(message, PageEditorNotificationType.Error);
		notification.Options.AddRange(options);
		args.Notifications.Add(notification);
	}
}

After some testing, I’ve found that opening the Experience Editor for a page with no versions, switching to a language that has more than one version, would make the first version active instead of the latest version. We can correct this by reflecting and replacing the OpenExperienceEditor command and just add an if-statement so that the sc_version parameter isn’t sent when opening the editor for an item with no versions. Credits to Sitecore support for helping finding how to resolve this issue.

public class CustomOpenExperienceEditor : Command
{
	public override void Execute(CommandContext context)
	{
		// ... insert from reflected file here
		Item item = context.Items[0];
		parameters["uri"] = item.Uri.ToString();
		parameters.Add("sc_lang", item.Language.ToString());
				
		// Don't include sc_version if the current item doesn't have any versions
		if (item.Versions != null && item.Versions.Count > 0)
		{
			parameters.Add("sc_version", item.Version.Number.ToString(CultureInfo.InvariantCulture));
		}
		if (HasPresentationPipeline.Run(item))
			parameters.Add("sc_itemid", item.ID.ToString());
		else
			hasNoPresentation = true;
		// ... insert from reflected file here
	}
	// ... insert from reflected file here
}

And we’ll get an Experience Editor that looks like this:

go-to-language-experience-editor

And here’s how to patch it to the config:

<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
	<sitecore>
		<pipelines>
			<getContentEditorWarnings>
				<processor type="Stendahls.Sc.EditorExtensions.Pipelines.ContentEditorMissingLanguageWarning, Stendahls.Sc.EditorExtensions" patch:instead="processor[@type='Sitecore.Pipelines.GetContentEditorWarnings.HasNoVersions, Sitecore.Kernel']"/>
			</getContentEditorWarnings>
			<getPageEditorNotifications>
				<processor type="Stendahls.Sc.EditorExtensions.Pipelines.ExperienceEditorMissingLanguageWarning, Stendahls.Sc.EditorExtensions" />
			</getPageEditorNotifications>
		</pipelines>
		<commands>
			<command name="webedit:openexperienceeditor">
				<patch:attribute name="type">Stendahls.Sc.EditorExtensions.CustomOpenExperienceEditor, Stendahls.Sc.EditorExtensions</patch:attribute>
			</command>
		</commands>
	</sitecore>
</configuration>

Enjoy!

One thought on “Switching authoring language in Sitecore

Leave a Reply