Home » Translation

Category Archives: Translation

Inject Dependencies Into Sitecore MVC Razor Views Using a SitecoreHelper Extension Method

Last week before the Christmas holiday break, I came up with a solution similar to what I am going to show here except that I had used the Simple Injector Dependency Injection framework in that solution (if you would like me to add a blog post on that approach, please let me know in a comment).

I wanted a “quick and dirty” solution — well, I guess this is really in the eye of the beholder on whether the following is a good idea or not, but I’m throwing it out there just in case it helps out someone — where I could inject a dependency into an MVC Razor view via an HtmlHelper extension method, and in that solution, I used this approach for injecting an object instance which grabs values from a Sitecore Dictionary Domain. I am going to recreate this solution here though using the Sitecore Configuration Factory (we’re all not using Simple Injector in our solutions but the Sitecore Configuration Factory is available to all of us via the Sitecore API).

I first defined the following interface for class instances which return a value for a given dictionary entry key:

using Sitecore.Globalization;

namespace Sitecore.Sandbox.Dictionaries
{
    public interface ITranslator
    {
        string Text(string key, params object[] parameters);

        string Text(TranslateOptions options, string key, params object[] parameters);
    }
}

The following class implements the interface above:

using Sitecore.Diagnostics;
using Sitecore.Globalization;

namespace Sitecore.Sandbox.Dictionaries
{
    public class DomainDictionaryTranslator : ITranslator
    {
        public string Domain { get; set; }

        public virtual string Text(string key, params object[] parameters)
        {
            AssertDomainDictionary();
            Assert.ArgumentNotNullOrEmpty(key, "key");
            return Translate.TextByDomain(Domain, key, parameters);
        }

        public virtual string Text(TranslateOptions options, string key, params object[] parameters)
        {
            AssertDomainDictionary();
            Assert.ArgumentNotNullOrEmpty(key, "key");
            return Translate.TextByDomain(Domain, options, key, parameters);
        }

        protected virtual void AssertDomainDictionary()
        {
            Assert.IsNotNullOrEmpty(Domain, "The Domain must be set!");
        }
    }
}

Client code of the class above must supply the Domain name for the Sitecore Dictionary via the Domain property on the class.

Client code can then use either Text method by supplying a key for the lookup — both methods just delegate to the corresponding static methods defined on the Sitecore.Globalization.Translate class.

We now need classes that serve as Factories. I created the following interface for such classes:

namespace Sitecore.Sandbox.Helpers.Sitecore
{
    public interface IFactory
    {
        T CreateObject<T>(string configPath, bool assert) where T : class;
    }
}

The following class implements the above interface, and basically has one method that delegates to the static CreateObject() method on the Sitecore.Configuration.Factory class:

using Sitecore.Configuration;

namespace Sitecore.Sandbox.Helpers.Sitecore
{
    public class ConfigurationFactory : IFactory
    {
        public T CreateObject<T>(string configPath, bool assert) where T : class
        {
            return Factory.CreateObject(configPath, assert) as T;
        }
    }
}

Now, we need an extension method on the Sitecore.Mvc.Helpers.SitecoreHelper class — this lives in Sitecore.Mvc.dll — so that we can leverage the code defined above (check out Sitecore MVP Kevin Brechbühl’s blog post where he talks about this approach as well as another):

using Sitecore.Configuration;
using Sitecore.Diagnostics;
using Sitecore.Mvc.Helpers;

namespace Sitecore.Sandbox.Helpers.Sitecore
{
    public static class ConfigurationFactoryHelper
    {
        private static IFactory ConfigurationFactory { get; set; }

        static ConfigurationFactoryHelper()
        {
            ConfigurationFactory = CreateFactory();
        }

        public static T CreateObject<T>(this SitecoreHelper sitecoreHelper, string configPath, bool assert) where T : class
        {
            return ConfigurationFactory.CreateObject<T>(configPath, assert);
        }

        private static IFactory CreateFactory()
        {
            IFactory factory = Factory.CreateObject("configurationFactory", true) as IFactory;
            Assert.IsNotNull(factory, "The configurationFactory must be defined in configuration!");
            return factory;
        }
    }
}

We are instantiating an instance of the ConfigurationFactory class defined above via the Sitecore Configuration Factory, and then setting it on a static property in this class — all members and methods on this class must be static since it contains extension methods.

The CreateObject() method just delegates to the ConfigurationFactory class instance’s CreateObject() method and returns its value.

I then glued everything together using the following Sitecore patch configuration file:

<?xml version="1.0" encoding="utf-8" ?>
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
  <sitecore>
    <configurationFactory type="Sitecore.Sandbox.Helpers.Sitecore.ConfigurationFactory, Sitecore.Sandbox" singleInstance="true" />
    <domainDictionaryTranslator type="Sitecore.Sandbox.Dictionaries.DomainDictionaryTranslator, Sitecore.Sandbox" singleInstance="true">
      <Domain>MyCoolDictionary</Domain>
    </domainDictionaryTranslator>
  </sitecore>
</configuration>

Let’s see this in action!

First, we need a Dictionary Domain. I created one with the following folder with entries:

label-one-dictionary

label-two-dictionary

Now, we new a Razor view to make this work. I created the following, and mapped it to my home page’s presentation details:

@using Sitecore.Mvc;
@using Sitecore.Sandbox.Helpers.Sitecore
@using Sitecore.Sandbox.Dictionaries

@{
    var translator = Html.Sitecore().CreateObject<ITranslator>("domainDictionaryTranslator", true);
    if (translator == null)
    {
        return;
    }

    var labelOne = translator.Text("mycooldictionary.somelabels.labelone");
    var labelTwo = translator.Text("mycooldictionary.somelabels.labeltwo");
    if (string.IsNullOrWhiteSpace(labelOne) && string.IsNullOrWhiteSpace(labelTwo))
    {
        return;
    }
}

<div>
    @if (!string.IsNullOrWhiteSpace(labelOne))
    {
        <h2>@labelOne</h2>
    }

    @if (!string.IsNullOrWhiteSpace(labelTwo))
    {
        <h2>@labelTwo</h2>
    }
</div>

After building and deploying, I navigated to my home page. As you can see, the dictionary entry values display:

dictionary-values-displayed

If you have any thoughts on this, please drop a comment.

Advertisement

Prevent Sitecore Dictionary Entry Keys From Appearing When Their Phrase Field is Empty

Earlier today when doing research for another blog post around on-demand language translation in Sitecore, I remembered I wanted to blog about an issue I saw a while back when using the Sitecore Dictionary, but before I dive into that issue — and a possible approach for resolving it — let me give you a little information on what the Sitecore Dictionary is, and why you might want to use it — actually you probably should use it!

The Sitecore Dictionary is a place in Sitecore where you can store multilingual content for labels or string literals in your code (this could be front-end code, or even content displayed in the Sitecore shell). I’ve created the following Dictionary entry as an example:

coffee-dictionary-entry-set

The “coffee” item above is the Dictionary entry, and its parent item “beverage types” is a Dictionary folder.

You could use a sublayout like the following to display the text stored in the Phrase field on the front-end of your website:

<%@ Control Language="C#" AutoEventWireup="true" CodeBehind="Translate Test.ascx.cs" Inherits="Sandbox.layouts.sublayouts.Translate_Test" %>
<h2>Dictionary Test</h2>
Key => <asp:Literal ID="litKey" runat="server" /><br />
Phrase => <asp:Literal ID="litTranslateTest" runat="server" />

The code-behind of the sublayout:

using System;

using Sitecore.Globalization;

namespace Sandbox.layouts.sublayouts
{
    public partial class Translate_Test : System.Web.UI.UserControl
    {
        protected void Page_Load(object sender, EventArgs e)
        {
            string key = "beveragetypes.coffee";
            litKey.Text = key;
            litTranslateTest.Text = Translate.Text(key);
        }
    }
}

In the Page_Load method above, I’ve invoked Sitecore.Globalization.Translate.Text() to grab the value out of the Phrase field of the “coffee” Dictionary entry using its key. The Sitecore.Globalization.Translate.Text() method uses Sitecore.Context.Language to ascertain which language version of the Dictionary entry to use.

When I navigated to the page that has the sublayout above mapped to its presentation, I see the “coffee” entry’s Phrase appear:

coffee-dictionary-entry-set-front-end

Let’s see how this works using another language version of this Dictionary entry. I added a Danish version for our “coffee” entry:

coffee-dictionary-entry-set-danish

I navigated to my page again after embedding the Danish language code in its URL to get the Danish version of this Dictionary entry:

coffee-dictionary-entry-set-front-end-danish

As you can see the Danish version appeared, and I did not have to write any additional code to make this happen.

Well, this is great and all until someone forgets to include a phrase for a Dictionary entry:

coffee-dictionary-entry-not-set

When we go to the front-end, we see that the Dictionary entry’s key appears instead of its phrase:

coffee-dictionary-entry-empty-front-end-problem

As a fix for this, I created the following class to serve as a processor for the <getTranslation> pipeline (this pipeline was introduced in Sitecore 6.6):

using System.Collections.Generic;

using Sitecore.Diagnostics;
using Sitecore.Pipelines.GetTranslation;

namespace Sitecore.Sandbox.Pipelines.GetTranslation
{
    public class SetAsEmpty
    {
        private IList<string> _KeyPrefixes;
        private IList<string> KeyPrefixes 
        {
            get
            {
                if (_KeyPrefixes == null)
                {
                    _KeyPrefixes = new List<string>();
                }

                return _KeyPrefixes;
            }
        }

        public void Process(GetTranslationArgs args)
        {
            if (!ShouldSetAsEmpty(args))
            {
                return;
            }

            args.Result = string.Empty;
        }

        protected virtual bool ShouldSetAsEmpty(GetTranslationArgs args)
        {
            Assert.ArgumentNotNull(args, "args");
            return args.Result == null && HasKeyPrefix(args.Key);
        }

        protected virtual bool HasKeyPrefix(string key)
        {
            if (string.IsNullOrWhiteSpace(key))
            {
                return false;
            }

            foreach (string keyPrefix in KeyPrefixes)
            {
                if (key.StartsWith(keyPrefix))
                {
                    return true;
                }
            }

            return false;
        }

        protected virtual void AddKeyPrefix(string keyPrefix)
        {
            if(string.IsNullOrWhiteSpace(keyPrefix))
            {
                return;
            }

            KeyPrefixes.Add(keyPrefix);
        }
    }
}

The idea here is to check to see if the Dictionary entry’s key starts with a configuration defined prefix, and if it does, set the GetTranslationArgs instance’s Result property to the empty string when it’s null.

The reason why we check for a specific prefix is to ensure we don’t impact other parts of Sitecore that use methods that leverage the <getTranslation> pipeline (I learned this the hard way when virtually all labels in my instance’s Content Editor disappeared before adding the logic above to check whether a Dictionary entry’s key started with a config defined prefix).

I then wired this up in Sitecore using the following configuration file:

<?xml version="1.0" encoding="utf-8"?>
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
  <sitecore>
    <pipelines>
      <getTranslation>
        <processor type="Sitecore.Sandbox.Pipelines.GetTranslation.SetAsEmpty, Sitecore.Sandbox">
          <keyPrefixes hint="list:AddKeyPrefix">
            <prefix>beveragetypes.</prefix>
          </keyPrefixes>
        </processor>
      </getTranslation>
    </pipelines>
  </sitecore>
</configuration>

When I navigated back to my page, I see that nothing appears for the Dictionary entry’s phrase since it was set to the empty string by our pipeline processor above.

coffee-dictionary-entry-empty-front-end

One thing I should note: I have only tested this in Sitecore 6.6, and I’m not aware if this Dictionary entry issue exists in Sitecore 7. If this issue was fixed in Sitecore 7, please share in a comment.

Plus, if you have any comments on this, or other ideas for solving this problem, please leave a comment.