Skip to content
This repository has been archived by the owner on Dec 14, 2018. It is now read-only.

Commit

Permalink
Improve ViewLocalizer resource look-up semantics:
Browse files Browse the repository at this point in the history
- Always prepend with application name and let underlying `IStringLocalizerFactory` do the name gymnastics
- Use ExecutingFilePath instead of view name
- Trim off the file extension (so your resource doesn't have to have ".cshtml" in its name)
- Improved doc comments
- Added tests to cover ViewLocalizer behavior
- #3718
- #2767
  • Loading branch information
DamianEdwards committed Dec 21, 2015
1 parent f56cf97 commit 7b56ac7
Show file tree
Hide file tree
Showing 6 changed files with 61 additions and 7 deletions.
2 changes: 1 addition & 1 deletion src/Microsoft.AspNet.Mvc.Localization/IViewLocalizer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
namespace Microsoft.AspNet.Mvc.Localization
{
/// <summary>
/// A service that provides localized strings for views.
/// Represents a type that provides HTML-aware localization for views.
/// </summary>
public interface IViewLocalizer : IHtmlLocalizer
{
Expand Down
29 changes: 25 additions & 4 deletions src/Microsoft.AspNet.Mvc.Localization/ViewLocalizer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -12,7 +14,8 @@
namespace Microsoft.AspNet.Mvc.Localization
{
/// <summary>
/// A <see cref="IHtmlLocalizer"/> implementation that provides localized strings for views.
/// An <see cref="IViewLocalizer"/> implementation that derives the resource location from the executing view's
/// file path.
/// </summary>
public class ViewLocalizer : IViewLocalizer, ICanHasViewContext
{
Expand Down Expand Up @@ -93,12 +96,30 @@ public void Contextualize(ViewContext viewContext)
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))
{
path = viewContext.View.Path;
}

// Trim the file extension from the end of the path
if (!string.IsNullOrEmpty(path) && Path.HasExtension(path))
{
baseName = baseName.Substring(1);
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);
}
}
Expand Down
37 changes: 35 additions & 2 deletions test/Microsoft.AspNet.Mvc.Localization.Test/ViewLocalizerTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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<IApplicationEnvironment>();
applicationEnvironment.Setup(a => a.ApplicationName).Returns(appName);
var htmlLocalizerFactory = new Mock<IHtmlLocalizerFactory>(MockBehavior.Loose);
var view = new Mock<IView>();
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<string>(baseName => baseName == expectedBaseName),
It.Is<string>(location => location == appName)
));
}

[Fact]
public void ViewLocalizer_UseIndexer_ReturnsLocalizedHtmlString()
{
Expand All @@ -29,7 +62,7 @@ public void ViewLocalizer_UseIndexer_ReturnsLocalizedHtmlString()
htmlLocalizer.Setup(h => h["Hello"]).Returns(localizedString);

var htmlLocalizerFactory = new Mock<IHtmlLocalizerFactory>();
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);
Expand Down Expand Up @@ -62,7 +95,7 @@ public void ViewLocalizer_UseIndexerWithArguments_ReturnsLocalizedHtmlString()

var htmlLocalizerFactory = new Mock<IHtmlLocalizerFactory>();
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);

Expand Down

0 comments on commit 7b56ac7

Please sign in to comment.