diff --git a/samples/LocalizationSample.Web/Resources/Views.Home.Locpage.cshtml.en-GB.resx b/samples/LocalizationSample.Web/Resources/Views.Home.Locpage.en-GB.resx similarity index 100% rename from samples/LocalizationSample.Web/Resources/Views.Home.Locpage.cshtml.en-GB.resx rename to samples/LocalizationSample.Web/Resources/Views.Home.Locpage.en-GB.resx diff --git a/samples/LocalizationSample.Web/Resources/Views.Home.Locpage.cshtml.fr.resx b/samples/LocalizationSample.Web/Resources/Views.Home.Locpage.fr.resx similarity index 100% rename from samples/LocalizationSample.Web/Resources/Views.Home.Locpage.cshtml.fr.resx rename to samples/LocalizationSample.Web/Resources/Views.Home.Locpage.fr.resx diff --git a/samples/LocalizationSample.Web/Resources/Views.Shared._LocalizationLayout.cshtml.fr.resx b/samples/LocalizationSample.Web/Resources/Views.Shared._LocalizationLayout.fr.resx similarity index 100% rename from samples/LocalizationSample.Web/Resources/Views.Shared._LocalizationLayout.cshtml.fr.resx rename to samples/LocalizationSample.Web/Resources/Views.Shared._LocalizationLayout.fr.resx diff --git a/src/Microsoft.AspNet.Mvc.Localization/IViewLocalizer.cs b/src/Microsoft.AspNet.Mvc.Localization/IViewLocalizer.cs index 8204e3205a..9f586d7f52 100644 --- a/src/Microsoft.AspNet.Mvc.Localization/IViewLocalizer.cs +++ b/src/Microsoft.AspNet.Mvc.Localization/IViewLocalizer.cs @@ -4,7 +4,7 @@ namespace Microsoft.AspNet.Mvc.Localization { /// - /// A service that provides localized strings for views. + /// Represents a type that provides HTML-aware localization for views. /// public interface IViewLocalizer : IHtmlLocalizer { diff --git a/src/Microsoft.AspNet.Mvc.Localization/ViewLocalizer.cs b/src/Microsoft.AspNet.Mvc.Localization/ViewLocalizer.cs index c45b038d38..657a98623d 100644 --- a/src/Microsoft.AspNet.Mvc.Localization/ViewLocalizer.cs +++ b/src/Microsoft.AspNet.Mvc.Localization/ViewLocalizer.cs @@ -3,7 +3,9 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.Globalization; +using System.IO; using Microsoft.AspNet.Mvc.Rendering; using Microsoft.AspNet.Mvc.ViewFeatures.Internal; using Microsoft.Extensions.Localization; @@ -12,7 +14,8 @@ using Microsoft.Extensions.PlatformAbstractions; namespace Microsoft.AspNet.Mvc.Localization { /// - /// A implementation that provides localized strings for views. + /// An implementation that derives the resource location from the executing view's + /// file path. /// public class ViewLocalizer : IViewLocalizer, ICanHasViewContext { @@ -93,12 +96,30 @@ namespace Microsoft.AspNet.Mvc.Localization throw new ArgumentNullException(nameof(viewContext)); } - var baseName = viewContext.View.Path.Replace('/', '.').Replace('\\', '.'); - if (baseName.StartsWith(".", StringComparison.OrdinalIgnoreCase)) + // 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)) { - baseName = baseName.Substring(1); + path = viewContext.View.Path; } + // Trim the file extension from the end of the path + if (!string.IsNullOrEmpty(path) && Path.HasExtension(path)) + { + var extension = Path.GetExtension(path); + path = path.Substring(0, path.Length - extension.Length); + } + + Debug.Assert(!string.IsNullOrEmpty(path), "Couldn't determine a path for the view"); + + var baseName = path.Replace('/', '.').Replace('\\', '.'); + baseName = baseName.TrimStart('.'); + + // Prepend the application name + baseName = _applicationName + "." + baseName; + _localizer = _localizerFactory.Create(baseName, _applicationName); } } diff --git a/test/Microsoft.AspNet.Mvc.Localization.Test/ViewLocalizerTest.cs b/test/Microsoft.AspNet.Mvc.Localization.Test/ViewLocalizerTest.cs index 5b30ad9752..d695cd0b06 100644 --- a/test/Microsoft.AspNet.Mvc.Localization.Test/ViewLocalizerTest.cs +++ b/test/Microsoft.AspNet.Mvc.Localization.Test/ViewLocalizerTest.cs @@ -16,6 +16,39 @@ namespace Microsoft.AspNet.Mvc.Localization.Test { public class ViewLocalizerTest { + [Theory] + [InlineData("TestApplication", "Views/Home/Index.cshtml", "Views/Home/Index.cshtml", "TestApplication.Views.Home.Index")] + [InlineData("TestApplication.Web", "Views/Home/Index.cshtml", "Views/Home/Index.cshtml", "TestApplication.Web.Views.Home.Index")] + [InlineData("TestApplication", "Views/Home/Index.cshtml", "Views/Shared/_Layout.cshtml", "TestApplication.Views.Shared._Layout")] + [InlineData("TestApplication", "Views/Home/Index.cshtml", "Views/Shared/_MyPartial.cshtml", "TestApplication.Views.Shared._MyPartial")] + [InlineData("TestApplication", "Views/Home/Index.cshtml", "Views/Home/_HomePartial.cshtml", "TestApplication.Views.Home._HomePartial")] + [InlineData("TestApplication", "Views/Home/Index.cshtml", null, "TestApplication.Views.Home.Index")] + [InlineData("TestApplication", "Views/Home/Index.txt", null, "TestApplication.Views.Home.Index")] + [InlineData("TestApplication", "Views/Home/Index.cshtml", "", "TestApplication.Views.Home.Index")] + [InlineData("TestApplication", "Views/Home/Index.txt", "", "TestApplication.Views.Home.Index")] + public void ViewLocalizer_LooksForCorrectResourceBaseNameLocation(string appName, string viewPath, string executingPath, string expectedBaseName) + { + // Arrange + var applicationEnvironment = new Mock(); + applicationEnvironment.Setup(a => a.ApplicationName).Returns(appName); + var htmlLocalizerFactory = new Mock(MockBehavior.Loose); + var view = new Mock(); + view.Setup(v => v.Path).Returns(viewPath); + var viewContext = new ViewContext(); + viewContext.ExecutingFilePath = executingPath; + viewContext.View = view.Object; + var viewLocalizer = new ViewLocalizer(htmlLocalizerFactory.Object, applicationEnvironment.Object); + + // Act + viewLocalizer.Contextualize(viewContext); + + // Assert + htmlLocalizerFactory.Verify(h => h.Create( + It.Is(baseName => baseName == expectedBaseName), + It.Is(location => location == appName) + )); + } + [Fact] public void ViewLocalizer_UseIndexer_ReturnsLocalizedHtmlString() { @@ -29,7 +62,7 @@ namespace Microsoft.AspNet.Mvc.Localization.Test htmlLocalizer.Setup(h => h["Hello"]).Returns(localizedString); var htmlLocalizerFactory = new Mock(); - htmlLocalizerFactory.Setup(h => h.Create("example", "TestApplication")) + htmlLocalizerFactory.Setup(h => h.Create("TestApplication.example", "TestApplication")) .Returns(htmlLocalizer.Object); var viewLocalizer = new ViewLocalizer(htmlLocalizerFactory.Object, applicationEnvironment.Object); @@ -62,7 +95,7 @@ namespace Microsoft.AspNet.Mvc.Localization.Test var htmlLocalizerFactory = new Mock(); htmlLocalizerFactory.Setup( - h => h.Create("example", "TestApplication")).Returns(htmlLocalizer.Object); + h => h.Create("TestApplication.example", "TestApplication")).Returns(htmlLocalizer.Object); var viewLocalizer = new ViewLocalizer(htmlLocalizerFactory.Object, applicationEnvironment.Object);