Added configurable option for LanguageViewLocationExpander to use subfolder or suffix
This commit is contained in:
parent
67d0bf880a
commit
74b193b15d
17
Mvc.sln
17
Mvc.sln
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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-*",
|
||||
|
|
|
|||
|
|
@ -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";
|
||||
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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>
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
@{
|
||||
Layout = "_MyLayout";
|
||||
}
|
||||
index
|
||||
@await Html.PartialAsync("_Partial")
|
||||
@await Html.PartialAsync("_MyPartial")
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
@{
|
||||
Layout = "_MyLayout";
|
||||
}
|
||||
en-gb-index
|
||||
@await Html.PartialAsync("_Partial")
|
||||
@await Html.PartialAsync("_MyPartial")
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
@{
|
||||
Layout = "_MyLayout";
|
||||
}
|
||||
fr-index
|
||||
@await Html.PartialAsync("_Partial")
|
||||
@await Html.PartialAsync("_MyPartial")
|
||||
|
|
@ -0,0 +1 @@
|
|||
mypartial
|
||||
|
|
@ -0,0 +1 @@
|
|||
partial
|
||||
|
|
@ -0,0 +1 @@
|
|||
fr-partial
|
||||
|
|
@ -0,0 +1 @@
|
|||
<language-layout>@RenderBody()</language-layout>
|
||||
|
|
@ -0,0 +1 @@
|
|||
<fr-language-layout>@RenderBody()</fr-language-layout>
|
||||
|
|
@ -0,0 +1 @@
|
|||
mypartial-en-gb-shared
|
||||
|
|
@ -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"
|
||||
}
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
LocalizationWebSite
|
||||
===
|
||||
|
||||
This web site illustrates use cases for Mvc localization.
|
||||
|
|
@ -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)
|
||||
|
|
|
|||
Loading…
Reference in New Issue