ブラウザ設定に基づくSitecore言語のリダイレクト

add-a-new-language

ユーザーは、ブラウザの設定で言語設定を行うことができます。Sitecoreソリューションにこの設定を反映させたい場合は、Language Resolverプロセッサをオーバーライドすることで可能になります。

ここで重要なことは、ブラウザの言語設定とSitecoreの言語設定の間に、潜在的な違いがあることです。

Sitecoreでは、言語を追加する際に、OOTBで利用可能なすべての言語に国コードが含まれていることに気づく。

add-a-new-language

そのため、コンテンツは言語と国の両方で修飾されることになります。

しかし、ブラウザでは、国を指定して設定できる言語がある一方で、国コードなしでしか利用できない言語もあります。

このようなケースのほとんどは、Sitecoreでも1つの国でしか利用できない言語の場合ですが(例:ロシア語/ポーランド語)、そうでない場合もあります。ポルトガル語を例にとると、ブラウザ上では次のようなオプションが利用できます:

Steps to add languages in Sitecore

Sitecoreの中では:

Add a new language Portuguese in Sitecore

この場合、ユーザーがブラウザの設定でポルトガル語だけを選択していて、Sitecoreサイトのポルトガル語コンテンツがポルトガルかブラジルのどちらかに特化している場合、どのようなコンテンツをユーザーに表示するのでしょうか?

この機能をサイトに実装する場合、最初に行うのは、サイトで利用可能なSitecoreの言語と、サイトの最新/互換性のあるブラウザの対応するオプションをマッピングすることです。このマッピングと上記のシナリオに基づいて、この機能を実装する前に特定のビジネス上の意思決定を行う必要があるかもしれません。

参考までに、ポイント3では、私たちがこのソリューションとして実装したものをご紹介します。

実際の実装については、LanguageResolverのオーバーライドにロジックを追加することになります。

今回の実装で注意すべき点は以下の通りです。

1. ウェブサイトの特定のセクションやページでのみ言語リダイレクトを有効にするロジックを追加しました。これにより、ユーザーが初めてホームページにアクセスしたときにリダイレクトされるだけでなく、ユーザーがGoogleの検索結果などから直接アクセスした場合にも、設定されたとおりに特定の重要なセクション/ページでリダイレクトが機能するようになります。(IsRedirectEnabledPatternMatch)

2. ユーザーはブラウザの設定で複数の言語を設定することができます。これらの言語設定はすべてHttpContextを介して行われ、サイトに設定されているSitecoreの言語に応じて、これらの言語を順にループして最初にマッチしたものを返します。

3. 前述のシナリオ(国コードがなくてもブラウザで利用できる言語がある)については、Sitecoreの言語テンプレートに拡張機能を追加して、国コードがない言語に対して言語リダイレクトが機能するかどうかをコンテンツ作成者が選択できるようにしました。以下の例を考えてみましょう。

a. ユーザーのブラウザでポーランド語が選択されている場合、利用可能なオプションは国コードなしのポーランド語のみです。この場合、Sitecoreにリダイレクトを実行させたいと考えています。このため、新しいフィールド「Enable Language Detection By Iso」にチェックを入れました。これは、言語リゾルバのオーバーライドコードで使用され、国コードなしで使用される言語のリダイレクトを可能にします。

Language Search

b. ユーザーのブラウザでポルトガル語が選択されている場合、Sitecoreではポルトガルやブラジルに特化したコンテンツが用意されているので、ユーザーをリダイレクトさせたくありませんでした。そのため、Sitecoreのpt-PTとpt-BRの両方で、このフィールド「Enable Language Detection By Iso」を無効にしました。こうすることで、ユーザーのブラウザに国別のポルトガル語が設定されている場合にのみ、リダイレクトが機能するようになりました。

4. 4. ブラウザの言語検出は、ブラウザの Cookie で言語が設定されていない場合にのみ行われます。これにより、ユーザーが別の言語でサイトを閲覧したいと思っても、私たちのコードはユーザーのブラウザ設定に基づいて強制的にリダイレクトさせることはありません。

5. 5. 上記のすべてに基づいて言語が一致し、ユーザーがリダイレクトされた場合、コードはコンテキスト言語を設定し、それによって言語クッキーも設定されます。それ以降のリクエストでは、言語クッキーが設定されているため、このコードは作動しません。

参考までにLanguageResolverをご紹介します:

using Sitecore.Data.Items;
using Sitecore.Data.Managers;
using Sitecore.Diagnostics;
using Sitecore.Globalization;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

namespace MySite.Core.IoC.Pipelines
{
    public class LanguageResolver : Sitecore.Pipelines.HttpRequest.LanguageResolver
    {
        public override void Process(Sitecore.Pipelines.HttpRequest.HttpRequestArgs args)
        {
            Assert.ArgumentNotNull(args, "args");

            string pathWithoutLanguage = StripLanguageFromPath(args.Context.Request.Url.PathAndQuery.ToLower());

            if (Sitecore.Context.Database != null
                && !pathWithoutLanguage.StartsWith("/-/media/")
                && !pathWithoutLanguage.StartsWith("/sitecore")
                && !pathWithoutLanguage.EndsWith(".js")
                && !pathWithoutLanguage.EndsWith(".css")
                && IsRedirectEnabledPatternMatch(pathWithoutLanguage))
            {
                Language lang = GetLanguageFromBrowser(args);
                if (lang != null)
                {
                    Sitecore.Context.Language = lang;

                    // if current language doesn't match with default site language, it will do a redirect
                    if (Sitecore.Context.Language.Name != Sitecore.Context.Site.Language)
                    {
                        // do redirect
                        UriBuilder uriBuilder = new UriBuilder(args.Context.Request.Url)
                        {
                            Path = Sitecore.Context.Language.Name.ToLower() + pathWithoutLanguage
                        };
                        HttpContext.Current.Response.Redirect(uriBuilder.Uri.ToString(), true);
                        args.AbortPipeline();
                    }
                }
                else
                {
                    base.Process(args);
                }
            }
            else
            {
                base.Process(args);
            }
        }

        private bool IsRedirectEnabledPatternMatch(string relativeUrl)
        {
            if (relativeUrl == "/")
                return true;

            List<string> redirectEnabledSections = Sitecore.Configuration.Settings
                .GetAppSetting("MySiteBrowserLanguageDetection.Paths.Sections")
                .Split('|').ToList();

            List<string> redirectEnabledPages = Sitecore.Configuration.Settings
                .GetAppSetting("MySiteBrowserLanguageDetection.Paths.Pages")
                .Split('|').ToList();

            relativeUrl = relativeUrl.Replace(' ', '-').Replace("%20", "").ToLower();

            if (redirectEnabledSections.Any(r => relativeUrl.Contains("/" + r.Replace(' ', '-').ToLower()))
            || redirectEnabledPages.Any(r => (r.StartsWith("/") ? r : "/" + r) == relativeUrl))
            {
                return true;
            }


            return false;
        }

        private string StripLanguageFromPath(string urlPathAndQuery)
        {
            if (Sitecore.Context.Database != null)
            {
                var languages = LanguageManager.GetLanguages(Sitecore.Context.Database);

                foreach (Language language in languages)
                {
                    if (urlPathAndQuery.StartsWith("/" + language.Name + "/"))
                    {
                        return urlPathAndQuery.Replace("/" + language.Name + "/", "/");
                    }

                    if (urlPathAndQuery == "/" + language.Name)
                    {
                        return urlPathAndQuery.Replace("/" + language.Name, "/");
                    }
                }
            }

            return urlPathAndQuery;
        }

        public Language GetLanguageFromBrowser(Sitecore.Pipelines.HttpRequest.HttpRequestArgs args)
        {
            if (IsBrowserLangDetectionAllowed(args) && Sitecore.Context.Database != null)
            {
                string[] userLanguages = args.Context.Request.UserLanguages;
                if (userLanguages != null && userLanguages.Any())
                {
                    var systemLanguages = LanguageManager.GetLanguages(Sitecore.Context.Database);

                    foreach (string userLanguage in userLanguages)
                    {
                        // gets first part where information about language is stored
                        string browserLanguage = userLanguage.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries).FirstOrDefault();
                        if (!string.IsNullOrWhiteSpace(browserLanguage))
                        {
                            foreach (Language systemLanguage in systemLanguages)
                            {
                                Item systemLanguageItem =
                                    LanguageManager.GetLanguageItem(systemLanguage, Sitecore.Context.Database);

                                if (systemLanguageItem != null)
                                {
                                    if (systemLanguageItem
                                            .Fields[Constants.Fields.SystemLanguage.EnableLanguageDetectionByIso]
                                            ?.Value != "1")
                                    {
                                        if (browserLanguage == systemLanguage.CultureInfo.Name)
                                        {
                                            return systemLanguage;
                                        }
                                    }
                                    else
                                    {
                                        if (browserLanguage == systemLanguage.CultureInfo.TwoLetterISOLanguageName)
                                        {
                                            return systemLanguage;
                                        }
                                    }
                                }
                            }
                        }
                    }

                    return LanguageManager.GetLanguage(Sitecore.Context.Site.Language);
                }
            }
            return null;
        }

        protected bool IsBrowserLangDetectionAllowed(Sitecore.Pipelines.HttpRequest.HttpRequestArgs args)
        {
            // site must be defined
            return Sitecore.Context.Site != null
                // lang cookie is not already set
                && !args.Context.Request.Cookies.AllKeys.Contains(Sitecore.Context.Site.GetCookieKey("lang"))
                // user agent is not robot
                && !Sitecore.Analytics.Configuration.AnalyticsSettings.Robots.ExcludeList.ContainsUserAgent(args.Context.Request.UserAgent);
        }
    }
}

設定については

<?xml version="1.0"?>
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
  <sitecore>
    <settings>
        <add key="MySiteBrowserLanguageDetection.Paths.Sections" value="services|careers|about-us|locations"/>
  	<add key="MySiteBrowserLanguageDetection.Paths.Pages" value=""/>
    </settings>
    <pipelines>
      <httpRequestBegin>
        <processor type="Sitecore.Pipelines.HttpRequest.LanguageResolver, Sitecore.Kernel">
          <patch:attribute name="type">MySite.Core.IoC.Pipelines.LanguageResolver, MySite.Core</patch:attribute>
        </processor>
      </httpRequestBegin>
    </pipelines>
  </sitecore>
</configuration>

►►►サービスについて