Search code examples
sitecoresitecore-mvcsitecore8

Language Fallback / Setting the language in Sitecore.Globalization.Translate.TextByLanguage(key, language);?


I am utilizing the Language Fallback provider. This does not appear to have any influence on items stored in System > Dictionary.

I would like it to fallback to the language specified in the "Fallback" attribute of the Language item.

Worse case, I would like to fallback to English.

My method for getting text out of the Dictionary can call this.

Globalization.Translate.TextByLanguage(key, ????);

I am stuck on:

  1. How do I find the value of the "Fallback" attribute for the currently specified language
  2. How do I pass that to TextByLanguage. It appears to be expecting a Language attribute, but Language doesn't appear to have any constructors.

HISTORICAL: Following @Jammykam 's advice I took a look at the Verndale example but I am having zero luck with it. It does not appear to ever fire.

I added the following class to my solution:

using Sitecore.Diagnostics;
using Sitecore.Globalization;
using Sitecore.Pipelines.GetTranslation;
using Sitecore.SharedSource.PartialLanguageFallback.Extensions;
using System;
using System.Collections.Generic;

namespace Sitecore.Web.Pipelines
{
    public class DictionaryFallback : TryGetFromFallbackDomains
    {
        public static bool EnableFallback
        {
            get
            {
                return Sitecore.Context.Site != null &&
                       Sitecore.Context.Site.SiteInfo.Properties["enableFallback"] != null &&
                       Sitecore.Context.Site.SiteInfo.Properties["enableFallback"].Equals("true", StringComparison.InvariantCultureIgnoreCase);
            }
        }

        /// <summary>
        /// Runs the processor.
        /// 
        /// </summary>
        /// <param name="args">The arguments.
        ///             </param>
        public void Process(GetTranslationArgs args)
        {
            Assert.ArgumentNotNull((object)args, "args");
            List<string> processedDomains = new List<string>();
            if (args.HasResult || Context.Site == null || string.IsNullOrEmpty(Context.Site.DictionaryDomain))
                return;
            this.Args = args;
            this.Database = args.Options.Database ?? args.ContentDatabase;
            DictionaryDomain domain;
            if (!DictionaryDomain.TryParse(Context.Site.DictionaryDomain, this.Database, out domain) || domain == null) {
                Log.Info("Error Parsing Dictionary: " + domain.FullyQualifiedName, this);
                return;
            }


            string result;
            if (this.TryGetTranslation(domain, processedDomains, out result) && result != null)
            {
                Log.Info("Normal Translation: " + domain.FullyQualifiedName + ": " + result, this);
                args.Result = result;
            }
            else if (EnableFallback)
            {
                if (this.TryTranslateTextByFallbackLanguage(args, domain, out result) && result != null)
                {
                    Log.Info("Fallback Translation: " + domain.FullyQualifiedName + ": " + result, this);
                    args.Result = result;
                }
            }

        }

        protected virtual bool TryTranslateTextByFallbackLanguage(GetTranslationArgs args, DictionaryDomain domain, out string result)
        {
            result = null;
            List<string> processedDomains = new List<string>();

            // check if the the language passed in with the args has fallback assigned
            // if so, then get that fallback language
            // must try to get the translation based on that language
            var languageFallsBack = args.Language.HasFallbackAssigned(args.ContentDatabase);
            if (languageFallsBack)
            {
                Language fallbackLanguage = args.Language.GetFallbackLanguage(args.ContentDatabase);

                // the following cannot be called from here, because it is an internal method to the Sitecore.Kernel library
                //Translate.TryTranslateTextByLanguage(args.Key, domain, fallbackLanguage, out result, args.Parameters);

                // therefore, we set Args.Language to the fallbacklanguage
                // this.Args is the Args object in TryGetFromFallbackDomains processor
                // then we call this.TryGetTranslation, which is a method in the TryGetFromFallbackDomains processor, 
                // which IS in the Sitecore.Kernel library and therefore can make the call to TryTranslateTextByLanguage

                this.Args.Language = fallbackLanguage;

                if (this.TryGetTranslation(domain, processedDomains, out result) && result != null)
                {
                    return true;
                }
                else
                {
                    // if no results if found, try to see if this fallback language falls back itself to another language
                    // and then if so, try the translation with that
                    // pass into the recursive call this.Args (instead of args), since the language has been updated in this.Args
                    if (result == null)
                    {
                        var isSuccess = TryTranslateTextByFallbackLanguage(this.Args, domain, out result);
                        return isSuccess;
                    }
                    else
                        return false;
                }
            }
            else
            {
                return false;
            }

        }


    }
}

I added the following code to my patch file:

<pipelines>
            <getTranslation>
                <processor patch:after="*[@type='Sitecore.Pipelines.GetTranslation.TryGetFromCoreDatabase, Sitecore.Kernel']" type="Sitecore.Web.Pipelines.DictionaryFallback, Sitecore.Web" />
            </getTranslation>
        </pipelines>

I can see it using showconfig.aspx:

<getTranslation>
<processor type="Sitecore.Pipelines.GetTranslation.ResolveContentDatabase, Sitecore.Kernel"/>
<processor type="Sitecore.Pipelines.GetTranslation.TryGetFromDomain, Sitecore.Kernel"/>
<processor type="Sitecore.Pipelines.GetTranslation.TryGetFromFallbackDomains, Sitecore.Kernel"/>
<processor type="Sitecore.Pipelines.GetTranslation.TryGetFromSiteDomain, Sitecore.Kernel"/>
<processor type="Sitecore.Pipelines.GetTranslation.TryGetFromContextDatabase, Sitecore.Kernel"/>
<processor type="Sitecore.Pipelines.GetTranslation.TryGetFromCoreDatabase, Sitecore.Kernel"/>
<processor type="Sitecore.Web.Pipelines.DictionaryFallback, Sitecore.Web" patch:source="SitecoreSettings.config"/>
</getTranslation>

You will see in my class that I threw in some logging, but I never see an item logged. Items that have a version in the selected language render correctly. But if they don't have a version it's displaying the key instead of falling back.


Here is the section as displayed via showconfig.aspx (my real domain changed here to example.com):

<sites><site name="shell" virtualFolder="/sitecore/shell" physicalFolder="/sitecore/shell" rootPath="/sitecore/content" startItem="/home" language="en" database="core" domain="sitecore" loginPage="/sitecore/login" content="master" contentStartItem="/Home" enableWorkflow="true" enableAnalytics="false" analyticsDefinitions="content" xmlControlPage="/sitecore/shell/default.aspx" browserTitle="Sitecore" htmlCacheSize="10MB" registryCacheSize="15MB" viewStateCacheSize="1MB" xslCacheSize="25MB" disableBrowserCaching="true" itemwebapi.mode="StandardSecurity" itemwebapi.access="ReadWrite" itemwebapi.allowanonymousaccess="false" /><site name="login" virtualFolder="/sitecore/login" physicalFolder="/sitecore/login" enableAnalytics="false" database="core" domain="sitecore" disableXmlControls="true" /><site name="admin" virtualFolder="/sitecore/admin" physicalFolder="/sitecore/admin" enableAnalytics="false" enableWorkflow="true" domain="sitecore" loginPage="/sitecore/admin/login.aspx" /><site name="service" virtualFolder="/sitecore/service" physicalFolder="/sitecore/service" /><site name="modules_shell" virtualFolder="/sitecore modules/shell" physicalFolder="/sitecore modules/shell" rootPath="/sitecore/content" startItem="/home" language="en" database="core" domain="sitecore" content="master" enableAnalytics="false" enableWorkflow="true" /><site name="modules_website" virtualFolder="/sitecore modules/web" physicalFolder="/sitecore modules/web" rootPath="/sitecore/content" startItem="/home" language="en" database="web" domain="extranet" allowDebug="true" cacheHtml="true" /><!-- ITEM WEB API SETTINGS FOR A SITE
           Supported attributes (first is default):
             itemwebapi.mode: [Off|StandardSecurity|AdvancedSecurity]
               If set to Off, Item Web API is turned off.
               If set to StandardSecurity, Item Web API is turned on. Default Sitecore security model is used.
               If set to AdvancedSecurity, Item Web API is turned on. Default Sitecore security model is extended with a requirement to explicitely set the 'remote:fieldread' access right for content fields.
             itemwebapi.access: [ReadOnly|ReadWrite]
               If set to ReadOnly, then only READ operation is allowed.
               If set to ReadWrite, then CREATE, READ, UPDATE, and DELETE operations are allowed.
             itemwebapi.allowanonymousaccess: [false|true].
               Defines if access is allowed for non-authenticated user.
      --><!--<site name="mysite" patch:before="site[@name='website']"
            virtualFolder="/"
            physicalFolder="/"
            rootPath="/sitecore/content"
            startItem="/home"
            database="web"
            domain="extranet"
            allowDebug="true"
            cacheHtml="true"
            htmlCacheSize="50MB"
            enablePreview="true"
            enableWebEdit="true"
            enableDebugger="true"
            disableClientData="false"/>--><site name="english" virtualFolder="/" physicalFolder="/" rootPath="/sitecore/content" startItem="/home" hostName="stage.example.com|127.0.0.1|localhost" language="en" database="web" domain="extranet" allowDebug="true" cacheHtml="true" htmlCacheSize="1GB" enablePreview="true" enableWebEdit="true" enableDebugger="true" disableClientData="false" enableFallback="true" patch:source="SiteDefinition.config" /><site name="chinese" virtualFolder="/" physicalFolder="/" rootPath="/sitecore/content" startItem="/home" hostName="chinesestage.example.com" language="zh-CN" database="web" domain="extranet" allowDebug="true" cacheHtml="true" htmlCacheSize="500MB" enablePreview="true" enableWebEdit="true" enableDebugger="true" disableClientData="false" enableFallback="true" patch:source="SiteDefinition.config" /><site name="german" virtualFolder="/" physicalFolder="/" rootPath="/sitecore/content" startItem="/home" hostName="germanstage.example.com" language="de-DE" database="web" domain="extranet" allowDebug="true" cacheHtml="true" htmlCacheSize="500MB" enablePreview="true" enableWebEdit="true" enableDebugger="true" disableClientData="false" enableFallback="true" patch:source="SiteDefinition.config" /><site name="spanish" virtualFolder="/" physicalFolder="/" rootPath="/sitecore/content" startItem="/home" hostName="spanishstage.example.com" language="es-ES" database="web" domain="extranet" allowDebug="true" cacheHtml="true" htmlCacheSize="500MB" enablePreview="true" enableWebEdit="true" enableDebugger="true" disableClientData="false" enableFallback="true" patch:source="SiteDefinition.config" /><site name="french" virtualFolder="/" physicalFolder="/" rootPath="/sitecore/content" startItem="/home" hostName="frenchstage.example.com" language="fr-FR" database="web" domain="extranet" allowDebug="true" cacheHtml="true" htmlCacheSize="500MB" enablePreview="true" enableWebEdit="true" enableDebugger="true" disableClientData="false" enableFallback="true" patch:source="SiteDefinition.config" /><site name="italian" virtualFolder="/" physicalFolder="/" rootPath="/sitecore/content" startItem="/home" hostName="italianstage.example.com" language="it-IT" database="web" domain="extranet" allowDebug="true" cacheHtml="true" htmlCacheSize="500MB" enablePreview="true" enableWebEdit="true" enableDebugger="true" disableClientData="false" enableFallback="true" patch:source="SiteDefinition.config" /><site name="japanese" virtualFolder="/" physicalFolder="/" rootPath="/sitecore/content" startItem="/home" hostName="japanesestage.example.com" language="ja-JP" database="web" domain="extranet" allowDebug="true" cacheHtml="true" htmlCacheSize="500MB" enablePreview="true" enableWebEdit="true" enableDebugger="true" disableClientData="false" enableFallback="true" patch:source="SiteDefinition.config" /><site name="portuguese" virtualFolder="/" physicalFolder="/" rootPath="/sitecore/content" startItem="/home" hostName="portuguesestage.example.com" language="pt-BR" database="web" domain="extranet" allowDebug="true" cacheHtml="true" htmlCacheSize="500MB" enablePreview="true" enableWebEdit="true" enableDebugger="true" disableClientData="false" enableFallback="true" patch:source="SiteDefinition.config" /><site name="russian" virtualFolder="/" physicalFolder="/" rootPath="/sitecore/content" startItem="/home" hostName="russianstage.example.com" language="ru-RU" database="web" domain="extranet" allowDebug="true" cacheHtml="true" htmlCacheSize="500MB" enablePreview="true" enableWebEdit="true" enableDebugger="true" disableClientData="false" enableFallback="true" patch:source="SiteDefinition.config" /><site name="website" virtualFolder="/" physicalFolder="/" rootPath="/sitecore/content" startItem="/home" database="web" domain="extranet" allowDebug="true" cacheHtml="true" registryCacheSize="0" viewStateCacheSize="0" xslCacheSize="25MB" filteredItemsCacheSize="10MB" enablePreview="true" enableWebEdit="true" enableDebugger="true" disableClientData="false" cacheRenderingParameters="true" renderingParametersCacheSize="10MB" itemwebapi.mode="Off" itemwebapi.access="ReadOnly" itemwebapi.allowanonymousaccess="false" htmlCacheSize="1GB" /><site name="scheduler" enableAnalytics="false" domain="sitecore" /><site name="system" enableAnalytics="false" domain="sitecore" /><site name="publisher" domain="sitecore" enableAnalytics="false" enableWorkflow="true" /></sites>

CLOSING UPDATE: I used the accepted answer below and modified it to work for me: https://gist.github.com/eat-sleep-code/0ea3f1bd2be19bbf9fc6


Solution

  • I already solved this before, Please check the following blog post, the section related to dictionary items:

    Dictionary Items Language Fallback

    I also built a module for this, uploaded to sitecore still waiting review.