Sitecore display name url does not work without language code

2.8k views Asked by At

On this Sitecore website (6.5.0 rev. 120706), I have this sitecore item called XYZ. So I have http://example.com/XYZ/.

I've added french localization, so using display names I now have:

http://example.com/XYZ-en/ http://example.com/XYZ-fr/

The english version works well, but the french does not and resolves to 404 unless I go to the english first, click on my language switcher button first. When I click on it, I'm redirected to http://example.com/fr-CA/XYZ-fr/, which works. From then on, the english url stops working, the french one works. As I switch languages like that, I always only have one of the two that work.

That button runs this code:

protected void LanguageLinkClick(object sender, EventArgs e)
{
    var lang = (Sitecore.Context.Language.Name == "en") ? "fr-ca" : "en";
    Tools.RedirectToLanguage(lang, Response);
}

That Tools function runs the following code:

public static void RedirectToLanguage(string pStrLangToSet, HttpResponse pResponse)
{
    if (!string.IsNullOrEmpty(pStrLangToSet))
    {
        var newLang = Language.Parse(pStrLangToSet);
        if (newLang != null)
        {
            Sitecore.Context.SetLanguage(newLang, true);
            var itm = Sitecore.Context.Item;
            if (Sitecore.Context.Item != null)
            {
                var itemInLang = Sitecore.Context.Database.Items[itm.ID, newLang];
                if (itemInLang != null)
                {
                    pResponse.Redirect(BuildUrl(itemInLang));
                }
            }
        }
    }
}

This is somewhat old code that is in this old project.

Is there anything I should look for that would intercept default display name behavior? Or isthis behavior with display names something that's not managed out of the box?

Thanks for any help!

2

There are 2 answers

0
RvanDalen On

So if I understand correctly you'd like these urls to resolve the language for you: http://example.com/XYZ-en/
http://example.com/XYZ-fr/

The LanguageResolver will resolve the language in this order:
1. language can be distilled from the url (cannot be done with above urls)
2. a language cookie is available for this site (sitename#lang)
3. fall back to default configured language

When you switch, using your switcher, the url paths start with the language code, then the resolver is able to resolve the language by url and persists the language in the language cookie. This is why you experience this behavior of one working language at a time.

The best you can do is always use language in urls (linkprovider configuration), otherwise you'd have to hook in the languageresolver and do some funky check on displayname for each language, but that will probably get very expensive.

2
jammykam On

This is the expected behaviour. This article from John West describes the steps involved in the language resolving process and details of the ItemResolver process can be found here.

What I assume you have set in the LinkProvider for your site is useDisplayName=true and maybe languageEmbedding=false, something like the following:

<add name="sitecore" type="Sitecore.Links.LinkProvider, Sitecore.Kernel" 
  alwaysIncludeServerUrl="false" addAspxExtension="true" encodeNames="true"
  languageLocation="filePath" lowercaseUrls="false" shortenUrls="true" 
  languageEmbedding="never" useDisplayName="true" />

This tells the LinkManager to build URLs using the display name of the Item, hence you have multilingual URLs. Your RedirectToLanguage method switches the context language, from EN to FR-CA and vice versa, which puts Sitecore into that specific language mode for the user.

The following pipeline in <httpRequestBegin> tries to resolve an Item from your requested URL:

<processor type="Sitecore.Pipelines.HttpRequest.ItemResolver, Sitecore.Kernel" />

And one of the attempts in that method is to ResolveUsingDisplayName(args). So it will be trying to resolve an Item with an EN display name, but the Context Language is set to FR-CA so in fact it will never find the item, hence the 404.

You have 2 options here:

  1. Set languageEmbedding="always" in your LinkProvider, which mean your URLs will be formatted like "/en/Nice-Product-With-Lots-Of-Options" and "/fr-ca/Mon-Produit-Avec-Plusieurs-Options".
  2. Add a custom pipeline processor after the default ItemResolver which tries to resolve using the alternate language, and if it finds a match sets the Context Item and switches language.

The following will call the default Sitecore ItemResolver after you switch to the alternate language and then attempt to resolve, which will try to find the Item using display name:

public class AlternateLanguageItemResolver : HttpRequestProcessor
{
    public override void Process(HttpRequestArgs args)
    {
        Assert.ArgumentNotNull(args, "args");
        if (Context.Item != null || Context.Database == null || args.Url.ItemPath.Length == 0)
          return;

        var origLanguage = Sitecore.Context.Language;
        Sitecore.Context.Language = AltLanguage;

        // try to find it using default ItemResolver with alternate language
        var itemResolver = new Sitecore.Pipelines.HttpRequest.ItemResolver();
        itemResolver.Process(args);

        if (Context.Item == null)
        {
            // well we didn't find it, so switch the context back so everyting can continue as normal
            Sitecore.Context.Language = origLanguage;
            return;
        }

        // We found the Item! Switch the User Language for future requests
        Sitecore.Context.SetLanguage(AltLanguage, true);
    }

    private Language _altLanguage = null;
    private Language AltLanguage
    {
        get
        {
            if (_altLanguage == null)
            {
                var altLang = (Sitecore.Context.Language.Name == "en") ? "fr-ca" : "en";
                _altLanguage = Language.Parse(altLang);
            }
            return _altLanguage;
        }
    }
}

And patch it in after the default ItemResolver:

<processor type="Sitecore.Sample.AlternateLanguageItemResolver, Sitecore.Sample"
           patch:after="processor[@type='Sitecore.Pipelines.HttpRequest.ItemResolver, Sitecore.Kernel']"/>

This is an untested prototype, check that subsequent requests are returned in the correct switched language. Otherwise redirect back to itself like you in your original code.