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

Commit

Permalink
Fix #111: Design changes to known/allowed culture list and cache
Browse files Browse the repository at this point in the history
  • Loading branch information
kirthik committed Oct 19, 2015
1 parent 90116dd commit 359f187
Show file tree
Hide file tree
Showing 18 changed files with 249 additions and 578 deletions.
7 changes: 0 additions & 7 deletions Localization.sln
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
global.json = global.json
EndProjectSection
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "CultureInfoGenerator", "src\CultureInfoGenerator\CultureInfoGenerator.xproj", "{BD22AE1C-6631-4DA6-874D-0DC0F803CEAB}"
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.Extensions.Globalization.CultureInfoCache", "src\Microsoft.Extensions.Globalization.CultureInfoCache\Microsoft.Extensions.Globalization.CultureInfoCache.xproj", "{F3988D3A-A4C8-4FD7-BAFE-13E0D0A1659A}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{B723DB83-A670-4BCB-95FB-195361331AD2}"
Expand Down Expand Up @@ -55,10 +53,6 @@ Global
{55D9501F-15B9-4339-A0AB-6082850E5FCE}.Debug|Any CPU.Build.0 = Debug|Any CPU
{55D9501F-15B9-4339-A0AB-6082850E5FCE}.Release|Any CPU.ActiveCfg = Release|Any CPU
{55D9501F-15B9-4339-A0AB-6082850E5FCE}.Release|Any CPU.Build.0 = Release|Any CPU
{BD22AE1C-6631-4DA6-874D-0DC0F803CEAB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{BD22AE1C-6631-4DA6-874D-0DC0F803CEAB}.Debug|Any CPU.Build.0 = Debug|Any CPU
{BD22AE1C-6631-4DA6-874D-0DC0F803CEAB}.Release|Any CPU.ActiveCfg = Release|Any CPU
{BD22AE1C-6631-4DA6-874D-0DC0F803CEAB}.Release|Any CPU.Build.0 = Release|Any CPU
{F3988D3A-A4C8-4FD7-BAFE-13E0D0A1659A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F3988D3A-A4C8-4FD7-BAFE-13E0D0A1659A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F3988D3A-A4C8-4FD7-BAFE-13E0D0A1659A}.Release|Any CPU.ActiveCfg = Release|Any CPU
Expand Down Expand Up @@ -88,7 +82,6 @@ Global
{23E3BC23-3464-4D9B-BF78-02CB2182BEF0} = {FB313677-BAB3-4E49-8CDB-4FA4A9564767}
{A1FCF259-70F6-4605-AA2D-E4B356BE771A} = {FB313677-BAB3-4E49-8CDB-4FA4A9564767}
{55D9501F-15B9-4339-A0AB-6082850E5FCE} = {79878809-8D1C-4BD4-BA99-F1F13FF96FD8}
{BD22AE1C-6631-4DA6-874D-0DC0F803CEAB} = {FB313677-BAB3-4E49-8CDB-4FA4A9564767}
{F3988D3A-A4C8-4FD7-BAFE-13E0D0A1659A} = {FB313677-BAB3-4E49-8CDB-4FA4A9564767}
{287AD58D-DF34-4F16-8616-FD78FA1CADF9} = {B723DB83-A670-4BCB-95FB-195361331AD2}
{19A2A931-5C60-47A0-816A-0DC9C4CE5736} = {B723DB83-A670-4BCB-95FB-195361331AD2}
Expand Down
2 changes: 1 addition & 1 deletion samples/LocalizationSample/Startup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ public void Configure(IApplicationBuilder app, IStringLocalizer<Startup> SR)

//}));

app.UseRequestLocalization(options);
app.UseRequestLocalization(options, new RequestCulture(new CultureInfo("en-US")));

app.Use(async (context, next) =>
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
// 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.Globalization;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNet.Http;
Expand Down Expand Up @@ -55,17 +57,10 @@ public override Task<RequestCulture> DetermineRequestCulture(HttpContext httpCon
// the CultureInfo ctor
if (language.Value != null)
{
var culture = CultureInfoCache.GetCultureInfo(language.Value);
var culture = CultureInfoCache.GetCultureInfo(language.Value, Options.SupportedCultures);
if (culture != null)
{
var requestCulture = new RequestCulture(culture);

requestCulture = ValidateRequestCulture(requestCulture);

if (requestCulture?.Culture == culture)
{
return Task.FromResult(requestCulture);
}
return Task.FromResult(new RequestCulture(culture));
}
}
}
Expand Down
26 changes: 22 additions & 4 deletions src/Microsoft.AspNet.Localization/ApplicationBuilderExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System;
using System.Globalization;
using Microsoft.AspNet.Localization;

namespace Microsoft.AspNet.Builder
Expand All @@ -16,17 +17,26 @@ public static class ApplicationBuilderExtensions
/// requests based on information provided by the client using the default options.
/// </summary>
/// <param name="builder">The <see cref="IApplicationBuilder"/>.</param>
/// <param name="defaultRequestCulture">The default <see cref="RequestCulture"/> to use if none of the
/// requested cultures match supported cultures.</param>
/// <returns>The <see cref="IApplicationBuilder"/>.</returns>
public static IApplicationBuilder UseRequestLocalization(this IApplicationBuilder builder)
public static IApplicationBuilder UseRequestLocalization(
this IApplicationBuilder builder,
RequestCulture defaultRequestCulture)
{
if (builder == null)
{
throw new ArgumentNullException(nameof(builder));
}

if (defaultRequestCulture == null)
{
throw new ArgumentNullException(nameof(defaultRequestCulture));
}

var options = new RequestLocalizationOptions();

return UseRequestLocalization(builder, options);
return UseRequestLocalization(builder, options, defaultRequestCulture);
}

/// <summary>
Expand All @@ -35,10 +45,13 @@ public static IApplicationBuilder UseRequestLocalization(this IApplicationBuilde
/// </summary>
/// <param name="builder">The <see cref="IApplicationBuilder"/>.</param>
/// <param name="options">The options to configure the middleware with.</param>
/// <param name="defaultRequestCulture">The default <see cref="RequestCulture"/> to use if none of the
/// requested cultures match supported cultures.</param>
/// <returns>The <see cref="IApplicationBuilder"/>.</returns>
public static IApplicationBuilder UseRequestLocalization(
this IApplicationBuilder builder,
RequestLocalizationOptions options)
RequestLocalizationOptions options,
RequestCulture defaultRequestCulture)
{
if (builder == null)
{
Expand All @@ -50,7 +63,12 @@ public static IApplicationBuilder UseRequestLocalization(
throw new ArgumentNullException(nameof(options));
}

return builder.UseMiddleware<RequestLocalizationMiddleware>(options);
if (defaultRequestCulture == null)
{
throw new ArgumentNullException(nameof(defaultRequestCulture));
}

return builder.UseMiddleware<RequestLocalizationMiddleware>(options, defaultRequestCulture);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
// 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.Globalization;
using System.Threading.Tasks;
using Microsoft.AspNet.Http;
using Microsoft.Extensions.Globalization;
Expand Down Expand Up @@ -43,9 +45,7 @@ public override Task<RequestCulture> DetermineRequestCulture(HttpContext httpCon
return Task.FromResult((RequestCulture)null);
}

var requestCulture = ParseCookieValue(cookie);

requestCulture = ValidateRequestCulture(requestCulture);
var requestCulture = ParseCookieValue(cookie, Options.SupportedCultures, Options.SupportedUICultures);

return Task.FromResult(requestCulture);
}
Expand Down Expand Up @@ -75,7 +75,10 @@ public static string MakeCookieValue(RequestCulture requestCulture)
/// </summary>
/// <param name="value">The cookie value to parse.</param>
/// <returns>The <see cref="RequestCulture"/> or <c>null</c> if parsing fails.</returns>
public static RequestCulture ParseCookieValue(string value)
public static RequestCulture ParseCookieValue(
string value,
IList<CultureInfo> supportedCultures,
IList<CultureInfo> supportedUICultures)
{
if (string.IsNullOrWhiteSpace(value))
{
Expand All @@ -100,8 +103,8 @@ public static RequestCulture ParseCookieValue(string value)
var cultureName = potentialCultureName.Substring(_culturePrefix.Length);
var uiCultureName = potentialUICultureName.Substring(_uiCulturePrefix.Length);

var culture = CultureInfoCache.GetCultureInfo(cultureName);
var uiCulture = CultureInfoCache.GetCultureInfo(uiCultureName);
var culture = CultureInfoCache.GetCultureInfo(cultureName, supportedCultures);
var uiCulture = CultureInfoCache.GetCultureInfo(uiCultureName, supportedUICultures);

if (culture == null || uiCulture == null)
{
Expand Down
2 changes: 2 additions & 0 deletions src/Microsoft.AspNet.Localization/IRequestCultureProvider.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
// 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.Collections.Generic;
using System.Globalization;
using System.Threading.Tasks;
using Microsoft.AspNet.Http;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,8 +71,8 @@ public override Task<RequestCulture> DetermineRequestCulture(HttpContext httpCon
queryCulture = queryUICulture;
}

var culture = CultureInfoCache.GetCultureInfo(queryCulture);
var uiCulture = CultureInfoCache.GetCultureInfo(queryUICulture);
var culture = CultureInfoCache.GetCultureInfo(queryCulture, Options.SupportedCultures);
var uiCulture = CultureInfoCache.GetCultureInfo(queryUICulture, Options.SupportedUICultures);

if (culture == null || uiCulture == null)
{
Expand All @@ -81,8 +81,6 @@ public override Task<RequestCulture> DetermineRequestCulture(HttpContext httpCon

var requestCulture = new RequestCulture(culture, uiCulture);

requestCulture = ValidateRequestCulture(requestCulture);

return Task.FromResult(requestCulture);
}
}
Expand Down
38 changes: 0 additions & 38 deletions src/Microsoft.AspNet.Localization/RequestCultureProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,43 +19,5 @@ public abstract class RequestCultureProvider : IRequestCultureProvider
/// <inheritdoc />
public abstract Task<RequestCulture> DetermineRequestCulture(HttpContext httpContext);

/// <summary>
/// Determines if the given <see cref="RequestCulture"/> is valid according to the currently configured.
/// <see cref="RequestLocalizationOptions"/>.
/// </summary>
/// <param name="requestCulture">The <see cref="RequestCulture"/> to validate.</param>
/// <returns>
/// The original <see cref="RequestCulture"/> if it was valid, otherwise a new <see cref="RequestCulture"/>
/// with values for <see cref="RequestCulture.Culture"/> and <see cref="RequestCulture.UICulture"/> that are
/// valid for the current configuration, or <c>null</c> if neither <see cref="RequestCulture.Culture"/> or
/// <see cref="RequestCulture.UICulture"/> were valid.
/// </returns>
protected RequestCulture ValidateRequestCulture(RequestCulture requestCulture)
{
if (requestCulture == null || Options == null)
{
return requestCulture;
}

var result = requestCulture;

if (Options.SupportedCultures != null && !Options.SupportedCultures.Contains(result.Culture))
{
result = new RequestCulture(Options.DefaultRequestCulture.Culture, result.UICulture);
}

if (Options.SupportedUICultures != null && !Options.SupportedUICultures.Contains(result.UICulture))
{
result = new RequestCulture(result.Culture, Options.DefaultRequestCulture.UICulture);
}

if (requestCulture.Culture != result.Culture && requestCulture.UICulture != result.UICulture)
{
// Both cultures were invalid, just return null
return null;
}

return result;
}
}
}
24 changes: 20 additions & 4 deletions src/Microsoft.AspNet.Localization/RequestLocalizationMiddleware.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,20 @@ public class RequestLocalizationMiddleware
{
private readonly RequestDelegate _next;
private readonly RequestLocalizationOptions _options;
private readonly RequestCulture _defaultRequestCulture;

/// <summary>
/// Creates a new <see cref="RequestLocalizationMiddleware"/>.
/// </summary>
/// <param name="next">The <see cref="RequestDelegate"/> representing the next middleware in the pipeline.</param>
/// <param name="options">The <see cref="RequestLocalizationOptions"/> representing the options for the <see cref="RequestLocalizationMiddleware"/>.</param>
public RequestLocalizationMiddleware(RequestDelegate next, RequestLocalizationOptions options)
/// <param name="options">The <see cref="RequestLocalizationOptions"/> representing the options for the
/// <see cref="RequestLocalizationMiddleware"/>.</param>
/// <param name="defaultRequestCulture">The default <see cref="RequestCulture"/> to use if none of the
/// requested cultures match supported cultures.</param>
public RequestLocalizationMiddleware(
RequestDelegate next,
RequestLocalizationOptions options,
RequestCulture defaultRequestCulture)
{
if (next == null)
{
Expand All @@ -37,8 +44,14 @@ public RequestLocalizationMiddleware(RequestDelegate next, RequestLocalizationOp
throw new ArgumentNullException(nameof(options));
}

if (defaultRequestCulture == null)
{
throw new ArgumentNullException(nameof(defaultRequestCulture));
}

_next = next;
_options = options;
_defaultRequestCulture = defaultRequestCulture;
}

/// <summary>
Expand All @@ -53,15 +66,18 @@ public async Task Invoke(HttpContext context)
throw new ArgumentNullException(nameof(context));
}

var requestCulture = _options.DefaultRequestCulture ??
new RequestCulture(CultureInfo.CurrentCulture, CultureInfo.CurrentUICulture);
var requestCulture = _defaultRequestCulture;

IRequestCultureProvider winningProvider = null;

if (_options.RequestCultureProviders != null)
{
foreach (var provider in _options.RequestCultureProviders)
{
if (provider is RequestCultureProvider)
{
((RequestCultureProvider)provider).Options = _options;
}
var result = await provider.DetermineRequestCulture(context);
if (result != null)
{
Expand Down
10 changes: 0 additions & 10 deletions src/Microsoft.AspNet.Localization/RequestLocalizationOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,6 @@ public class RequestLocalizationOptions
/// </summary>
public RequestLocalizationOptions()
{
DefaultRequestCulture = new RequestCulture(CultureInfo.CurrentCulture, CultureInfo.CurrentUICulture);

RequestCultureProviders = new List<IRequestCultureProvider>
{
new QueryStringRequestCultureProvider { Options = this },
Expand All @@ -27,14 +25,6 @@ public RequestLocalizationOptions()
};
}

/// <summary>
/// The default <see cref="RequestCulture"/> to use. This value will be used if none of the configured
/// <see cref="IRequestCultureProvider"/> options result in a non-<c>null</c> result.
/// Defaults to <see cref="RequestCulture.Culture"/> set to <see cref="CultureInfo.DefaultThreadCurrentCulture"/>
/// and <see cref="RequestCulture.UICulture"/> set to <see cref="CultureInfo.DefaultThreadCurrentUICulture"/>.
/// </summary>
public RequestCulture DefaultRequestCulture { get; set; }

/// <summary>
/// The cultures supported by the application. If this value is non-<c>null</c>, the
/// <see cref="RequestLocalizationMiddleware"/> will only set the current request culture to an entry in this
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,16 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;

namespace Microsoft.Extensions.Globalization
{
/// <summary>
/// Provides read-only cached instances of <see cref="CultureInfo"/>.
/// </summary>
public static partial class CultureInfoCache
public static class CultureInfoCache
{
private static readonly ConcurrentDictionary<string, CacheEntry> _cache = new ConcurrentDictionary<string, CacheEntry>();

Expand All @@ -22,11 +24,12 @@ public static partial class CultureInfoCache
/// A read-only cached <see cref="CultureInfo"/> or <c>null</c> a match wasn't found in
/// <see cref="KnownCultureNames"/>.
/// </returns>
public static CultureInfo GetCultureInfo(string name)
public static CultureInfo GetCultureInfo(string name, IList<CultureInfo> SupportedCultures)
{
// Allow only known culture names as this API is called with input from users (HTTP requests) and
// creating CultureInfo objects is expensive and we don't want it to throw either.
if (name == null || !KnownCultureNames.Contains(name))
if (name == null || SupportedCultures == null ||
SupportedCultures.Where( s => s.Name.ToLowerInvariant() == name.ToLowerInvariant()).FirstOrDefault() == null)
{
return null;
}
Expand Down
Loading

0 comments on commit 359f187

Please sign in to comment.