Added configurable option for LanguageViewLocationExpander to use subfolder or suffix

This commit is contained in:
Kirthi Krishnamraju 2015-06-04 14:55:33 -07:00
parent 67d0bf880a
commit 74b193b15d
23 changed files with 327 additions and 6 deletions

17
Mvc.sln
View File

@ -1,7 +1,7 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 14
VisualStudioVersion = 14.0.22808.1
VisualStudioVersion = 14.0.22823.1
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "samples", "samples", "{DAAE4C74-D06F-4874-A166-33305D2643CE}"
EndProject
@ -168,6 +168,8 @@ Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.AspNet.Mvc.ApiExp
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.AspNet.Mvc.Abstractions.Test", "test\Microsoft.AspNet.Mvc.Abstractions.Test\Microsoft.AspNet.Mvc.Abstractions.Test.xproj", "{DA000953-7532-4DF5-8DB9-8143DF98D999}"
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "LocalizationWebSite", "test\WebSites\LocalizationWebSite\LocalizationWebSite.xproj", "{FCFE6024-2720-49B4-8257-9DBC6114F0F1}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -1010,6 +1012,18 @@ Global
{DA000953-7532-4DF5-8DB9-8143DF98D999}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{DA000953-7532-4DF5-8DB9-8143DF98D999}.Release|x86.ActiveCfg = Release|Any CPU
{DA000953-7532-4DF5-8DB9-8143DF98D999}.Release|x86.Build.0 = Release|Any CPU
{FCFE6024-2720-49B4-8257-9DBC6114F0F1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{FCFE6024-2720-49B4-8257-9DBC6114F0F1}.Debug|Any CPU.Build.0 = Debug|Any CPU
{FCFE6024-2720-49B4-8257-9DBC6114F0F1}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
{FCFE6024-2720-49B4-8257-9DBC6114F0F1}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
{FCFE6024-2720-49B4-8257-9DBC6114F0F1}.Debug|x86.ActiveCfg = Debug|Any CPU
{FCFE6024-2720-49B4-8257-9DBC6114F0F1}.Debug|x86.Build.0 = Debug|Any CPU
{FCFE6024-2720-49B4-8257-9DBC6114F0F1}.Release|Any CPU.ActiveCfg = Release|Any CPU
{FCFE6024-2720-49B4-8257-9DBC6114F0F1}.Release|Any CPU.Build.0 = Release|Any CPU
{FCFE6024-2720-49B4-8257-9DBC6114F0F1}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{FCFE6024-2720-49B4-8257-9DBC6114F0F1}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{FCFE6024-2720-49B4-8257-9DBC6114F0F1}.Release|x86.ActiveCfg = Release|Any CPU
{FCFE6024-2720-49B4-8257-9DBC6114F0F1}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -1092,5 +1106,6 @@ Global
{A2B72833-5D70-4C42-AE85-E0319926FB8A} = {32285FA4-6B46-4D6B-A840-2B13E4C8B58E}
{4C2AD8AB-8AC0-46C4-80C6-C5577C7255F6} = {3BA657BF-28B1-42DA-B5B0-1C4601FCF7B1}
{DA000953-7532-4DF5-8DB9-8143DF98D999} = {3BA657BF-28B1-42DA-B5B0-1C4601FCF7B1}
{FCFE6024-2720-49B4-8257-9DBC6114F0F1} = {16703B76-C9F7-4C75-AE6C-53D92E308E3C}
EndGlobalSection
EndGlobal

View File

@ -46,7 +46,7 @@ namespace MvcSample.Web
options.Filters.Add(new FormatFilterAttribute());
});
services.AddMvcLocalization();
services.AddMvcLocalization(LanguageViewLocationExpanderOption.SubFolder);
#if DNX451
// Fully-qualify configuration path to avoid issues in functional tests. Just "config.json" would be fine
@ -59,7 +59,9 @@ namespace MvcSample.Web
var configBuilder = new ConfigurationBuilder()
.AddJsonFile(configurationPath)
.AddEnvironmentVariables();
var configuration = configBuilder.Build();
string diSystem;
if (configuration.TryGet("DependencyInjection", out diSystem) &&
diSystem.Equals("AutoFac", StringComparison.OrdinalIgnoreCase))

View File

@ -25,6 +25,24 @@ namespace Microsoft.AspNet.Mvc.Razor
public class LanguageViewLocationExpander : IViewLocationExpander
{
private const string ValueKey = "language";
private LanguageViewLocationExpanderOption _option;
/// <summary>
/// Instantiates a new <see cref="LanguageViewLocationExpander"/> instance.
/// </summary>
public LanguageViewLocationExpander()
: this(LanguageViewLocationExpanderOption.Suffix)
{
}
/// <summary>
/// Instantiates a new <see cref="DefaultTagHelperActivator"/> instance.
/// </summary>
/// <param name="option">The <see cref="LanguageViewLocationExpanderOption"/>.</param>
public LanguageViewLocationExpander(LanguageViewLocationExpanderOption option)
{
_option = option;
}
/// <inheritdoc />
public void PopulateValues([NotNull] ViewLocationExpanderContext context)
@ -71,7 +89,14 @@ namespace Microsoft.AspNet.Mvc.Razor
while (temporaryCultureInfo != temporaryCultureInfo.Parent)
{
yield return location.Replace("{0}", temporaryCultureInfo.Name + "/{0}");
if (_option == LanguageViewLocationExpanderOption.SubFolder)
{
yield return location.Replace("{0}", temporaryCultureInfo.Name + "/{0}");
}
else
{
yield return location.Replace("{0}", "{0}." + temporaryCultureInfo.Name);
}
temporaryCultureInfo = temporaryCultureInfo.Parent;
}

View File

@ -0,0 +1,27 @@
// 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.
namespace Microsoft.AspNet.Mvc.Razor
{
/// <summary>
/// Specifies the localized view format for <see cref="LanguageViewLocationExpander"/>.
/// </summary>
public enum LanguageViewLocationExpanderOption
{
/// <summary>
/// Locale is a subfolder under which the view exisits.
/// </summary>
/// <example>
/// Home/Views/en-US/Index.chtml
/// </example>
SubFolder,
/// <summary>
/// Locale is part of the view name as a suffix.
/// </summary>
/// <example>
/// Home/Views/Index.en-US.chtml
/// </example>
Suffix
}
}

View File

@ -263,11 +263,29 @@ namespace Microsoft.Framework.DependencyInjection
services.TryAdd(ServiceDescriptor.Singleton<ITempDataProvider, SessionStateTempDataProvider>());
}
/// <summary>
/// Adds Mvc localization to the application.
/// </summary>
/// <param name="services">The <see cref="IServiceCollection"/>.</param>
/// <returns>The <see cref="IServiceCollection"/>.</returns>
public static IServiceCollection AddMvcLocalization([NotNull] this IServiceCollection services)
{
return AddMvcLocalization(services, LanguageViewLocationExpanderOption.Suffix);
}
/// <summary>
/// Adds Mvc localization to the application.
/// </summary>
/// <param name="services">The <see cref="IServiceCollection"/>.</param>
/// <param name="option">The view format for localized views.</param>
/// <returns>The <see cref="IServiceCollection"/>.</returns>
public static IServiceCollection AddMvcLocalization(
[NotNull] this IServiceCollection services,
LanguageViewLocationExpanderOption option)
{
services.ConfigureRazorViewEngine(options =>
{
options.ViewLocationExpanders.Add(new LanguageViewLocationExpander());
options.ViewLocationExpanders.Add(new LanguageViewLocationExpander(option));
});
return services;

View File

@ -0,0 +1,75 @@
// 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.
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Threading.Tasks;
using LocalizationWebSite;
using Microsoft.AspNet.Builder;
using Microsoft.Framework.DependencyInjection;
using Microsoft.Net.Http.Headers;
using Xunit;
namespace Microsoft.AspNet.Mvc.FunctionalTests
{
public class LocalizationTest
{
private const string SiteName = nameof(LocalizationWebSite);
private static readonly Assembly _assembly = typeof(LocalizationTest).GetTypeInfo().Assembly;
private readonly Action<IApplicationBuilder> _app = new Startup().Configure;
private readonly Action<IServiceCollection> _configureServices = new Startup().ConfigureServices;
public static IEnumerable<object[]> LocalizationData
{
get
{
var expected1 =
@"<language-layout>
en-gb-index
partial
mypartial
</language-layout>";
yield return new[] { "en-GB", expected1 };
var expected2 =
@"<fr-language-layout>
fr-index
fr-partial
mypartial
</fr-language-layout>";
yield return new[] { "fr", expected2 };
var expected3 =
@"<language-layout>
index
partial
mypartial
</language-layout>";
yield return new[] { "na", expected3 };
}
}
[Theory]
[MemberData(nameof(LocalizationData))]
public async Task Localization_SuffixViewName(string value, string expected)
{
// Arrange
var server = TestHelper.CreateServer(_app, SiteName, _configureServices);
var client = server.CreateClient();
var cultureCookie = "c=" + value + "|uic=" + value;
client.DefaultRequestHeaders.Add(
"Cookie",
new CookieHeaderValue("ASPNET_CULTURE", cultureCookie).ToString());
// Act
var body = await client.GetStringAsync("http://localhost/");
// Assert
Assert.Equal(expected, body.Trim());
}
}
}

View File

@ -31,6 +31,7 @@
"HtmlGenerationWebSite": "1.0.0",
"InlineConstraintsWebSite": "1.0.0",
"JsonPatchWebSite": "1.0.0",
"LocalizationWebSite": "1.0.0",
"LoggingWebSite": "1.0.0",
"LowercaseUrlsWebSite": "1.0.0-*",
"Microsoft.AspNet.Mvc": "6.0.0-*",

View File

@ -14,6 +14,26 @@ namespace Microsoft.AspNet.Mvc.Razor
{
yield return new object[]
{
LanguageViewLocationExpanderOption.Suffix,
new[]
{
"/Views/{1}/{0}.cshtml",
"/Views/Shared/{0}.cshtml"
},
new[]
{
"/Views/{1}/{0}.en-GB.cshtml",
"/Views/{1}/{0}.en.cshtml",
"/Views/{1}/{0}.cshtml",
"/Views/Shared/{0}.en-GB.cshtml",
"/Views/Shared/{0}.en.cshtml",
"/Views/Shared/{0}.cshtml"
}
};
yield return new object[]
{
LanguageViewLocationExpanderOption.SubFolder,
new[]
{
"/Views/{1}/{0}.cshtml",
@ -32,6 +52,30 @@ namespace Microsoft.AspNet.Mvc.Razor
yield return new object[]
{
LanguageViewLocationExpanderOption.Suffix,
new[]
{
"/Areas/{2}/Views/{1}/{0}.cshtml",
"/Areas/{2}/Views/Shared/{0}.cshtml",
"/Views/Shared/{0}.cshtml"
},
new[]
{
"/Areas/{2}/Views/{1}/{0}.en-GB.cshtml",
"/Areas/{2}/Views/{1}/{0}.en.cshtml",
"/Areas/{2}/Views/{1}/{0}.cshtml",
"/Areas/{2}/Views/Shared/{0}.en-GB.cshtml",
"/Areas/{2}/Views/Shared/{0}.en.cshtml",
"/Areas/{2}/Views/Shared/{0}.cshtml",
"/Views/Shared/{0}.en-GB.cshtml",
"/Views/Shared/{0}.en.cshtml",
"/Views/Shared/{0}.cshtml"
}
};
yield return new object[]
{
LanguageViewLocationExpanderOption.SubFolder,
new[]
{
"/Areas/{2}/Views/{1}/{0}.cshtml",
@ -82,12 +126,13 @@ namespace Microsoft.AspNet.Mvc.Razor
[Theory]
[MemberData(nameof(ViewLocationExpanderTestDataWithExpectedValues))]
public void ExpandViewLocations_SpecificLocale(
LanguageViewLocationExpanderOption option,
IEnumerable<string> viewLocations,
IEnumerable<string> expectedViewLocations)
{
// Arrange
var viewLocationExpanderContext = new ViewLocationExpanderContext(new ActionContext(),"testView", false);
var languageViewLocationExpander = new LanguageViewLocationExpander();
var languageViewLocationExpander = new LanguageViewLocationExpander(option);
viewLocationExpanderContext.Values = new Dictionary<string, string>();
viewLocationExpanderContext.Values["language"] = "en-GB";

View File

@ -0,0 +1,16 @@
// 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.
using Microsoft.AspNet.Mvc;
namespace LocalizationWebSite.Controllers
{
public class HomeController : Controller
{
// GET: /<controller>/
public IActionResult Index()
{
return View();
}
}
}

View File

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">14.0</VisualStudioVersion>
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
</PropertyGroup>
<Import Project="$(VSToolsPath)\DNX\Microsoft.DNX.Props" Condition="'$(VSToolsPath)' != ''" />
<PropertyGroup Label="Globals">
<ProjectGuid>fcfe6024-2720-49b4-8257-9dbc6114f0f1</ProjectGuid>
<RootNamespace>LocalizationWebSite</RootNamespace>
<BaseIntermediateOutputPath Condition="'$(BaseIntermediateOutputPath)'=='' ">..\..\..\artifacts\obj\$(MSBuildProjectName)</BaseIntermediateOutputPath>
<OutputPath Condition="'$(OutputPath)'=='' ">..\..\..\artifacts\bin\$(MSBuildProjectName)\</OutputPath>
</PropertyGroup>
<PropertyGroup>
<SchemaVersion>2.0</SchemaVersion>
</PropertyGroup>
<Import Project="$(VSToolsPath)\DNX\Microsoft.DNX.targets" Condition="'$(VSToolsPath)' != ''" />
</Project>

View File

@ -0,0 +1,29 @@
// 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.
using Microsoft.AspNet.Builder;
using Microsoft.Framework.DependencyInjection;
namespace LocalizationWebSite
{
public class Startup
{
// Set up application services
public void ConfigureServices(IServiceCollection services)
{
// Add MVC services to the services container
services.AddMvc();
services.AddMvcLocalization();
}
public void Configure(IApplicationBuilder app)
{
app.UseCultureReplacer();
app.UseRequestLocalization();
// Add MVC to the request pipeline
app.UseMvcWithDefaultRoute();
}
}
}

View File

@ -0,0 +1,6 @@
@{
Layout = "_MyLayout";
}
index
@await Html.PartialAsync("_Partial")
@await Html.PartialAsync("_MyPartial")

View File

@ -0,0 +1,6 @@
@{
Layout = "_MyLayout";
}
en-gb-index
@await Html.PartialAsync("_Partial")
@await Html.PartialAsync("_MyPartial")

View File

@ -0,0 +1,6 @@
@{
Layout = "_MyLayout";
}
fr-index
@await Html.PartialAsync("_Partial")
@await Html.PartialAsync("_MyPartial")

View File

@ -0,0 +1 @@
mypartial

View File

@ -0,0 +1 @@
partial

View File

@ -0,0 +1 @@
fr-partial

View File

@ -0,0 +1 @@
<language-layout>@RenderBody()</language-layout>

View File

@ -0,0 +1 @@
<fr-language-layout>@RenderBody()</fr-language-layout>

View File

@ -0,0 +1 @@
mypartial-en-gb-shared

View File

@ -0,0 +1,20 @@
{
"commands": {
"web": "Microsoft.AspNet.Hosting server=Microsoft.AspNet.Server.WebListener server.urls=http://localhost:5001",
"kestrel": "Microsoft.AspNet.Hosting --server Kestrel --server.urls http://localhost:5000"
},
"dependencies": {
"Kestrel": "1.0.0-*",
"Microsoft.AspNet.Localization": "1.0.0-*",
"Microsoft.AspNet.Mvc": "6.0.0-*",
"Microsoft.AspNet.Mvc.TestConfiguration": "1.0.0",
"Microsoft.AspNet.Server.IIS": "1.0.0-*",
"Microsoft.AspNet.Server.WebListener": "1.0.0-*",
"Microsoft.AspNet.StaticFiles": "1.0.0-*"
},
"frameworks": {
"dnx451": { },
"dnxcore50": { }
},
"webroot": "wwwroot"
}

View File

@ -0,0 +1,4 @@
LocalizationWebSite
===
This web site illustrates use cases for Mvc localization.

View File

@ -32,7 +32,7 @@ namespace RazorWebSite
options.HtmlHelperOptions.ValidationMessageElement = "validationMessageElement";
options.HtmlHelperOptions.ValidationSummaryMessageElement = "validationSummaryElement";
});
services.AddMvcLocalization();
services.AddMvcLocalization(LanguageViewLocationExpanderOption.SubFolder);
}
public void Configure(IApplicationBuilder app)