1

私は多くのローカリゼーションアプローチを検索して試しましたが、すべてが私が望んでいるものではありません。基本的に、このようなURLが欲しいです

  • www.myweb.com <==デフォルトの言語(英語)
  • www.myweb.com/promotion
  • www.myweb.com/th/promotion <==現地語(タイ語)
  • www.myweb.com/cn/promotion <==現地の言語(中国語)

次に、これらのURLを以下のように異なるビュー構造でマップします

/Views
    /_Localization
        /cn
            /Home
                /About.cshtml
                /Index.cshtml
            /Shared
                /_Layout.cshtml
                /Error.cshtml
        /th
            /Home
                /About.cshtml
            /Shared
    /Home
        /About.cshtml
        /Index.cshtml
    /Shared
        /_Layout.cshtml
        /_LogOnPartial.cshtml
        /Error.cshtml
    _ViewStart.cshtml
    Web.config

Index.cshtmlご覧のとおり、タイには独自の、、が_Layout.cshtmlありませんError.cshtml。したがって、代わりにデフォルトを使用するようにフォールバックしたいと思います。しかし、中国人はそれを独自に使用します。

MapRoute私はこれを好きにしようとしました

routes.MapRoute(
    "DefaultLocal",
    "{lang}/{controller}/{action}/{id}",
    new { lang = "th", controller = "Home", action = "Index", id = UrlParameter.Optional }
);

routes.MapRoute(
    "Default",
    "{controller}/{action}/{id}",
    new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);

しかし、別のビュー構造を指す方法がわかりません。そして、この例では、Brian ReiterCookieは、 URLを使用していません。

では、どうすればこれを達成できますか。RazorViewEngineを使用していることに注意してください。 助けと考えをありがとう。

4

1 に答える 1

3

大量のコードが必要なため、それを行う方法のアイデアのみを示します。

RazorViewEngine次のようにサブクラス化できます。

public class I18NRazorViewEngine : RazorViewEngine
{
    public I18NRazorViewEngine() : this(null)
    { }

    protected string[] I18NAreaViewLocationFormats;
    protected string[] I18NAreaMasterLocationFormats;
    protected string[] I18NAreaPartialViewLocationFormats;
    protected string[] I18NViewLocationFormats;
    protected string[] I18NMasterLocationFormats;
    protected string[] I18NPartialViewLocationFormats;

    public I18NRazorViewEngine(IViewPageActivator viewPageActivator)
        : base(viewPageActivator)
    {
        this.I18NAreaViewLocationFormats = new string[]
        {
            "~/Areas/{3}/{2}/Views/{1}/{0}.cshtml",
            "~/Areas/{3}/{2}/Views/{1}/{0}.vbhtml",
            "~/Areas/{3}/{2}/Views/Shared/{0}.cshtml",
            "~/Areas/{3}/{2}/Views/Shared/{0}.vbhtml"
        };

        this.I18NAreaMasterLocationFormats = new string[]
        {
            "~/Areas/{3}/{2}/Views/{1}/{0}.cshtml",
            "~/Areas/{3}/{2}/Views/{1}/{0}.vbhtml",
            "~/Areas/{3}/{2}/Views/Shared/{0}.cshtml",
            "~/Areas/{3}/{2}/Views/Shared/{0}.vbhtml"
        };

        this.I18NAreaPartialViewLocationFormats = new string[]
        {
            "~/Areas/{3}/{2}/Views/{1}/{0}.cshtml",
            "~/Areas/{3}/{2}/Views/{1}/{0}.vbhtml",
            "~/Areas/{3}/{2}/Views/Shared/{0}.cshtml",
            "~/Areas/{3}/{2}/Views/Shared/{0}.vbhtml"
        };

        this.I18NViewLocationFormats = new string[]
        {
            "~/Views/{2}/{1}/{0}.cshtml",
            "~/Views/{2}/{1}/{0}.vbhtml",
            "~/Views/{2}/Shared/{0}.cshtml",
            "~/Views/{2}/Shared/{0}.vbhtml"
        };

        this.I18NMasterLocationFormats = new string[]
        {
            "~/Views/{2}/{1}/{0}.cshtml",
            "~/Views/{2}/{1}/{0}.vbhtml",
            "~/Views/{2}/Shared/{0}.cshtml",
            "~/Views/{2}/Shared/{0}.vbhtml"
        };

        this.I18NPartialViewLocationFormats = new string[]
        {
            "~/Views/{2}/{1}/{0}.cshtml",
            "~/Views/{2}/{1}/{0}.vbhtml",
            "~/Views/{2}/Shared/{0}.cshtml",
            "~/Views/{2}/Shared/{0}.vbhtml"
        };
    }

    public override ViewEngineResult FindView(ControllerContext controllerContext, string viewName, string masterName, bool useCache)
    {
        var langValue = controllerContext.Controller.ValueProvider.GetValue("lang");
        if (langValue == null || String.IsNullOrEmpty(langValue.AttemptedValue))
            return base.FindView(controllerContext, viewName, masterName, useCache);

        //Code here
    }

    public override ViewEngineResult FindPartialView(ControllerContext controllerContext, string partialViewName, bool useCache)
    {
        var langValue = controllerContext.Controller.ValueProvider.GetValue("lang");
        if (langValue == null || String.IsNullOrEmpty(langValue.AttemptedValue))
            return base.FindPartialView(controllerContext, partialViewName, useCache);

        //Code here
    }        
}

次に行うべきことはVirtualPathProviderViewEngine、FindView と実装の内部を調べることFindPartialViewです。反映されたコードは次のようになります。

public virtual ViewEngineResult FindPartialView(ControllerContext controllerContext, string partialViewName, bool useCache)
{
    if (controllerContext == null)
    {
        throw new ArgumentNullException("controllerContext");
    }
    if (string.IsNullOrEmpty(partialViewName))
    {
        throw new ArgumentException(MvcResources.Common_NullOrEmpty, "partialViewName");
    }
    string requiredString = controllerContext.RouteData.GetRequiredString("controller");
    string[] searchedLocations;
    string path = this.GetPath(controllerContext, this.PartialViewLocationFormats, this.AreaPartialViewLocationFormats, "PartialViewLocationFormats", partialViewName, requiredString, "Partial", useCache, out searchedLocations);
    if (string.IsNullOrEmpty(path))
    {
        return new ViewEngineResult(searchedLocations);
    }
    return new ViewEngineResult(this.CreatePartialView(controllerContext, path), this);
}

public virtual ViewEngineResult FindView(ControllerContext controllerContext, string viewName, string masterName, bool useCache)
{
    if (controllerContext == null)
    {
        throw new ArgumentNullException("controllerContext");
    }
    if (string.IsNullOrEmpty(viewName))
    {
        throw new ArgumentException(MvcResources.Common_NullOrEmpty, "viewName");
    }
    string requiredString = controllerContext.RouteData.GetRequiredString("controller");
    string[] first;
    string path = this.GetPath(controllerContext, this.ViewLocationFormats, this.AreaViewLocationFormats, "ViewLocationFormats", viewName, requiredString, "View", useCache, out first);
    string[] second;
    string path2 = this.GetPath(controllerContext, this.MasterLocationFormats, this.AreaMasterLocationFormats, "MasterLocationFormats", masterName, requiredString, "Master", useCache, out second);
    if (string.IsNullOrEmpty(path) || (string.IsNullOrEmpty(path2) && !string.IsNullOrEmpty(masterName)))
    {
        return new ViewEngineResult(first.Union(second));
    }
    return new ViewEngineResult(this.CreateView(controllerContext, path, path2), this);
}

どちらの方法もプライベート メソッドに依存していますGetPath

private string GetPath(ControllerContext controllerContext, string[] locations, string[] areaLocations, string locationsPropertyName, string name, string controllerName, string cacheKeyPrefix, bool useCache, out string[] searchedLocations)
{
    searchedLocations = VirtualPathProviderViewEngine._emptyLocations;
    if (string.IsNullOrEmpty(name))
    {
        return string.Empty;
    }
    string areaName = AreaHelpers.GetAreaName(controllerContext.RouteData);
    List<VirtualPathProviderViewEngine.ViewLocation> viewLocations = VirtualPathProviderViewEngine.GetViewLocations(locations, (!string.IsNullOrEmpty(areaName)) ? areaLocations : null);
    if (viewLocations.Count == 0)
    {
        throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, MvcResources.Common_PropertyCannotBeNullOrEmpty, new object[]
        {
            locationsPropertyName
        }));
    }
    bool flag = VirtualPathProviderViewEngine.IsSpecificPath(name);
    string text = this.CreateCacheKey(cacheKeyPrefix, name, flag ? string.Empty : controllerName, areaName);
    if (useCache)
    {
        return this.ViewLocationCache.GetViewLocation(controllerContext.HttpContext, text);
    }
    if (!flag)
    {
        return this.GetPathFromGeneralName(controllerContext, viewLocations, name, controllerName, areaName, text, ref searchedLocations);
    }
    return this.GetPathFromSpecificName(controllerContext, name, text, ref searchedLocations);
}

あなたがすべきことは、それを再実装することです。ほとんどのコードは再利用できますが、代わりに独自のメソッドを作成する必要がありますVirtualPathProviderViewEngine.GetViewLocations。ここに反映されたコード:

private static List<VirtualPathProviderViewEngine.ViewLocation> GetViewLocations(string[] viewLocationFormats, string[] areaViewLocationFormats)
{
    List<VirtualPathProviderViewEngine.ViewLocation> list = new List<VirtualPathProviderViewEngine.ViewLocation>();
    if (areaViewLocationFormats != null)
    {
        for (int i = 0; i < areaViewLocationFormats.Length; i++)
        {
            string virtualPathFormatString = areaViewLocationFormats[i];
            list.Add(new VirtualPathProviderViewEngine.AreaAwareViewLocation(virtualPathFormatString));
        }
    }
    if (viewLocationFormats != null)
    {
        for (int j = 0; j < viewLocationFormats.Length; j++)
        {
            string virtualPathFormatString2 = viewLocationFormats[j];
            list.Add(new VirtualPathProviderViewEngine.ViewLocation(virtualPathFormatString2));
        }
    }
    return list;
}

ほとんどのコードを再利用することもできますが、VirtualPathProviderViewEngine.ViewLocation と VirtualPathProviderViewEngine.AreaAwareViewLocation の代わりに独自のクラスを使用する必要があります。それらは次のようになります。

class ViewLocation
{
    protected string _virtualPathFormatString;
    public ViewLocation(string virtualPathFormatString)
    {
        this._virtualPathFormatString = virtualPathFormatString;
    }
    public virtual string Format(string viewName, string controllerName, string areaName, string lang)
    {
        return string.Format(CultureInfo.InvariantCulture, this._virtualPathFormatString, new object[]
        {
            viewName,
            controllerName,
                    lang
        });
    }
}

と:

class AreaAwareViewLocation : VirtualPathProviderViewEngine.ViewLocation
{
    public AreaAwareViewLocation(string virtualPathFormatString) : base(virtualPathFormatString)
    {
    }
    public override string Format(string viewName, string controllerName, string areaName, string lang)
    {
        return string.Format(CultureInfo.InvariantCulture, this._virtualPathFormatString, new object[]
        {
            viewName,
            controllerName,
            areaName,
                    lang
        });
    }
}

次に、メソッドを呼び出すときに(スコープから、最初のコード ブロックにある) lang パラメータにFormat渡す必要があります。通常は in で呼び出されます。langValue.AttemptedValueFindViewFindPartialViewVirtualPathProviderViewEngine.GetPathFromGeneralName

主なアドバイスは、ILSpy または別の逆アセンブラーを使用して、System.Web.Mvc のコードを調査することです (または、そのソースをダウンロードすることをお勧めします)。目標は、FindViewおよびを再実装することFindPartialViewです。提供される残りのコードは、mvc フレームワークで既に行われている方法を示すためのものです。

すでに存在し、デフォルトのクラスで使用される I18N プレフィックスのない配列ではなく、新しいビュー エンジンで宣言された配列をシークすることも重要です。

答えが間接的であるにもかかわらず、それが役立つことを願っています。問題が発生した場合は、追加の質問をすることができます。

PS ビューエンジンは、開発後に global.asax.cs に登録することを忘れないでください。

protected virtual void Application_Start()
{
   ViewEngines.Engines.Clear();
   ViewEngines.Engines.Add(new I18NRazorViewEngine());
}
于 2012-06-05T07:44:59.610 に答える