diff --git a/League/Views/ViewLocalizer.cs b/League/Views/ViewLocalizer.cs new file mode 100644 index 00000000..adaa9433 --- /dev/null +++ b/League/Views/ViewLocalizer.cs @@ -0,0 +1,154 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +// 2021-02-07: Modifications by axuno + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Globalization; +using System.IO; +using System.Reflection; +using System.Text; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Mvc.Controllers; +using Microsoft.AspNetCore.Mvc.Localization; +using Microsoft.AspNetCore.Mvc.Rendering; +using Microsoft.AspNetCore.Mvc.ViewFeatures; +using Microsoft.Extensions.Localization; + +namespace League.Views +{ + /// + /// An implementation that derives the resource location from the + /// assembly name the belongs to. + /// + /// + /// See more details on this topic: + /// https://terryaney.wordpress.com/2021/01/04/migrating-to-net-core-overridable-localization-in-razor-class-libraries/ + /// + public class ViewLocalizer : IViewLocalizer, IViewContextAware + { + private readonly IHtmlLocalizerFactory _localizerFactory; + private string _applicationName; + private IHtmlLocalizer _localizer; + + /// + /// Creates a new . + /// + /// The . + /// The . + public ViewLocalizer(IHtmlLocalizerFactory localizerFactory, IWebHostEnvironment hostingEnvironment) + { + if (hostingEnvironment == null) + { + throw new ArgumentNullException(nameof(hostingEnvironment)); + } + + _applicationName = hostingEnvironment.ApplicationName; + + _localizerFactory = localizerFactory ?? throw new ArgumentNullException(nameof(localizerFactory)); + } + + /// + public virtual LocalizedHtmlString this[string key] + { + get + { + if (key == null) + { + throw new ArgumentNullException(nameof(key)); + } + + return _localizer[key]; + } + } + + /// + public virtual LocalizedHtmlString this[string key, params object[] arguments] + { + get + { + if (key == null) + { + throw new ArgumentNullException(nameof(key)); + } + + return _localizer[key, arguments]; + } + } + + /// + public LocalizedString GetString(string name) => _localizer.GetString(name); + + /// + public LocalizedString GetString(string name, params object[] values) => _localizer.GetString(name, values); + + /// + public IEnumerable GetAllStrings(bool includeParentCultures) => + _localizer.GetAllStrings(includeParentCultures); + + /// + /// Apply the specified . + /// + /// The . + public void Contextualize(ViewContext viewContext) + { + if (viewContext == null) + { + throw new ArgumentNullException(nameof(viewContext)); + } + + // Given a view path "/Views/Home/Index.cshtml" we want a baseName like "MyApplication.Views.Home.Index" + var path = viewContext.ExecutingFilePath; + + if (string.IsNullOrEmpty(path)) + { + path = viewContext.View.Path; + } + + Debug.Assert(!string.IsNullOrEmpty(path), "Couldn't determine a path for the view"); + + #region ** Modification 2021-02-07 by axuno ** + // To generate the path to the localization resource, the original ViewLocalizer + // is using the hostingEnvironment.ApplicationName. + // If the view is loaded from a different assembly (e.g. from a Razor Class Library), + // the generated path is wrong for the _localizerFactory. + // This is the workaround to set the applicationName to the Razor Class Library assembly: + if (viewContext.ActionDescriptor is ControllerActionDescriptor controllerActionDescriptor) + { + if (controllerActionDescriptor.ControllerTypeInfo.Assembly.FullName != null) + { + var appName = new AssemblyName(controllerActionDescriptor.ControllerTypeInfo.Assembly.FullName).Name; + if (appName != null) _applicationName = appName; + } + } + #endregion + + _localizer = _localizerFactory.Create(BuildBaseName(path), _applicationName); + } + + private string BuildBaseName(string path) + { + var extension = Path.GetExtension(path); + var startIndex = path[0] == '/' || path[0] == '\\' ? 1 : 0; + var length = path.Length - startIndex - extension.Length; + var capacity = length + _applicationName.Length + 1; + var builder = new StringBuilder(path, startIndex, length, capacity); + + builder.Replace('/', '.').Replace('\\', '.'); + + // Prepend the application name + builder.Insert(0, '.'); + builder.Insert(0, _applicationName); + + return builder.ToString(); + } + + /// + [Obsolete("ViewLocalizer.WithCulture is no longer supported", true)] + public IHtmlLocalizer WithCulture(CultureInfo culture) + { + throw new NotImplementedException(); + } + } +}