diff --git a/Localization.sln b/Localization.sln index 903a81a931..d38677c13c 100644 --- a/Localization.sln +++ b/Localization.sln @@ -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}" @@ -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 @@ -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} diff --git a/samples/LocalizationSample/My/Resources/Startup.es-ES.resx b/samples/LocalizationSample/My/Resources/LocalizationSample.Startup.es-ES.resx similarity index 100% rename from samples/LocalizationSample/My/Resources/Startup.es-ES.resx rename to samples/LocalizationSample/My/Resources/LocalizationSample.Startup.es-ES.resx diff --git a/samples/LocalizationSample/My/Resources/Startup.fr-FR.resx b/samples/LocalizationSample/My/Resources/LocalizationSample.Startup.fr-FR.resx similarity index 100% rename from samples/LocalizationSample/My/Resources/Startup.fr-FR.resx rename to samples/LocalizationSample/My/Resources/LocalizationSample.Startup.fr-FR.resx diff --git a/samples/LocalizationSample/My/Resources/Startup.ja-JP.resx b/samples/LocalizationSample/My/Resources/LocalizationSample.Startup.ja-JP.resx similarity index 100% rename from samples/LocalizationSample/My/Resources/Startup.ja-JP.resx rename to samples/LocalizationSample/My/Resources/LocalizationSample.Startup.ja-JP.resx diff --git a/samples/LocalizationSample/My/Resources/Startup.zh-CN.resx b/samples/LocalizationSample/My/Resources/LocalizationSample.Startup.zh-CN.resx similarity index 100% rename from samples/LocalizationSample/My/Resources/Startup.zh-CN.resx rename to samples/LocalizationSample/My/Resources/LocalizationSample.Startup.zh-CN.resx diff --git a/samples/LocalizationSample/My/Resources/Startup.zh.resx b/samples/LocalizationSample/My/Resources/LocalizationSample.Startup.zh.resx similarity index 100% rename from samples/LocalizationSample/My/Resources/Startup.zh.resx rename to samples/LocalizationSample/My/Resources/LocalizationSample.Startup.zh.resx diff --git a/samples/LocalizationSample/Startup.cs b/samples/LocalizationSample/Startup.cs index a3545f6622..41fbedb378 100644 --- a/samples/LocalizationSample/Startup.cs +++ b/samples/LocalizationSample/Startup.cs @@ -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.Collections.Generic; using System.Globalization; using Microsoft.AspNet.Builder; using Microsoft.AspNet.Http; @@ -24,16 +25,30 @@ namespace LocalizationSample var options = new RequestLocalizationOptions { // Set options here to change middleware behavior - //SupportedCultures = new List - //{ - // new CultureInfo("en-US"), - // new CultureInfo("en-AU") - //}, - //SupportedUICultures = new List - //{ - // new CultureInfo("en-US"), - // new CultureInfo("en-AU") - //} + SupportedCultures = new List + { + new CultureInfo("en-US"), + new CultureInfo("en-AU"), + new CultureInfo("en-GB"), + new CultureInfo("es-ES"), + new CultureInfo("ja-JP"), + new CultureInfo("fr-FR"), + new CultureInfo("zh"), + new CultureInfo("zh-CN"), + new CultureInfo("zh-CHT") + }, + SupportedUICultures = new List + { + new CultureInfo("en-US"), + new CultureInfo("en-AU"), + new CultureInfo("en-GB"), + new CultureInfo("es-ES"), + new CultureInfo("ja-JP"), + new CultureInfo("fr-FR"), + new CultureInfo("zh"), + new CultureInfo("zh-CN"), + new CultureInfo("zh-CHT") + } }; // Optionally create an app-specific provider with just a delegate, e.g. look up user preference from DB. @@ -43,7 +58,7 @@ namespace LocalizationSample //})); - app.UseRequestLocalization(options); + app.UseRequestLocalization(options, defaultRequestCulture: new RequestCulture("en-US")); app.Use(async (context, next) => { diff --git a/src/Microsoft.AspNet.Localization/AcceptLanguageHeaderRequestCultureProvider.cs b/src/Microsoft.AspNet.Localization/AcceptLanguageHeaderRequestCultureProvider.cs index adf41adc9a..8ce131d6f4 100644 --- a/src/Microsoft.AspNet.Localization/AcceptLanguageHeaderRequestCultureProvider.cs +++ b/src/Microsoft.AspNet.Localization/AcceptLanguageHeaderRequestCultureProvider.cs @@ -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; @@ -23,7 +25,7 @@ namespace Microsoft.AspNet.Localization public int MaximumAcceptLanguageHeaderValuesToTry { get; set; } = 3; /// - public override Task DetermineRequestCulture(HttpContext httpContext) + public override Task DetermineProviderCultureResult(HttpContext httpContext) { if (httpContext == null) { @@ -34,7 +36,7 @@ namespace Microsoft.AspNet.Localization if (acceptLanguageHeader == null || acceptLanguageHeader.Count == 0) { - return Task.FromResult((RequestCulture)null); + return Task.FromResult((ProviderCultureResult)null); } var languages = acceptLanguageHeader.AsEnumerable(); @@ -47,30 +49,14 @@ namespace Microsoft.AspNet.Localization } var orderedLanguages = languages.OrderByDescending(h => h, StringWithQualityHeaderValueComparer.QualityComparer) - .ToList(); + .Select(x => x.Value).ToList(); - foreach (var language in orderedLanguages) + if (orderedLanguages.Any()) { - // Allow empty string values as they map to InvariantCulture, whereas null culture values will throw in - // the CultureInfo ctor - if (language.Value != null) - { - var culture = CultureInfoCache.GetCultureInfo(language.Value); - if (culture != null) - { - var requestCulture = new RequestCulture(culture); - - requestCulture = ValidateRequestCulture(requestCulture); - - if (requestCulture?.Culture == culture) - { - return Task.FromResult(requestCulture); - } - } - } + return Task.FromResult(new ProviderCultureResult(orderedLanguages)); } - return Task.FromResult((RequestCulture)null); + return Task.FromResult((ProviderCultureResult)null); } } } diff --git a/src/Microsoft.AspNet.Localization/ApplicationBuilderExtensions.cs b/src/Microsoft.AspNet.Localization/ApplicationBuilderExtensions.cs index 6d49afbb66..0076668faf 100644 --- a/src/Microsoft.AspNet.Localization/ApplicationBuilderExtensions.cs +++ b/src/Microsoft.AspNet.Localization/ApplicationBuilderExtensions.cs @@ -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 @@ -15,34 +16,46 @@ namespace Microsoft.AspNet.Builder /// Adds the to automatically set culture information for /// requests based on information provided by the client using the default options. /// - /// The . + /// The . + /// The default to use if none of the + /// requested cultures match supported cultures. /// The . - public static IApplicationBuilder UseRequestLocalization(this IApplicationBuilder builder) + public static IApplicationBuilder UseRequestLocalization( + this IApplicationBuilder app, + RequestCulture defaultRequestCulture) { - if (builder == null) + if (app == null) { - throw new ArgumentNullException(nameof(builder)); + throw new ArgumentNullException(nameof(app)); + } + + if (defaultRequestCulture == null) + { + throw new ArgumentNullException(nameof(defaultRequestCulture)); } var options = new RequestLocalizationOptions(); - return UseRequestLocalization(builder, options); + return UseRequestLocalization(app, options, defaultRequestCulture); } /// /// Adds the to automatically set culture information for /// requests based on information provided by the client. /// - /// The . + /// The . /// The options to configure the middleware with. + /// The default to use if none of the + /// requested cultures match supported cultures. /// The . public static IApplicationBuilder UseRequestLocalization( - this IApplicationBuilder builder, - RequestLocalizationOptions options) + this IApplicationBuilder app, + RequestLocalizationOptions options, + RequestCulture defaultRequestCulture) { - if (builder == null) + if (app == null) { - throw new ArgumentNullException(nameof(builder)); + throw new ArgumentNullException(nameof(app)); } if (options == null) @@ -50,7 +63,12 @@ namespace Microsoft.AspNet.Builder throw new ArgumentNullException(nameof(options)); } - return builder.UseMiddleware(options); + if (defaultRequestCulture == null) + { + throw new ArgumentNullException(nameof(defaultRequestCulture)); + } + + return app.UseMiddleware(options, defaultRequestCulture); } } } \ No newline at end of file diff --git a/src/Microsoft.AspNet.Localization/CookieRequestCultureProvider.cs b/src/Microsoft.AspNet.Localization/CookieRequestCultureProvider.cs index 22c98c1e15..46bb7ebbfa 100644 --- a/src/Microsoft.AspNet.Localization/CookieRequestCultureProvider.cs +++ b/src/Microsoft.AspNet.Localization/CookieRequestCultureProvider.cs @@ -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; @@ -29,7 +31,7 @@ namespace Microsoft.AspNet.Localization public string CookieName { get; set; } = DefaultCookieName; /// - public override Task DetermineRequestCulture(HttpContext httpContext) + public override Task DetermineProviderCultureResult(HttpContext httpContext) { if (httpContext == null) { @@ -40,14 +42,12 @@ namespace Microsoft.AspNet.Localization if (cookie == null) { - return Task.FromResult((RequestCulture)null); + return Task.FromResult((ProviderCultureResult)null); } - var requestCulture = ParseCookieValue(cookie); + var providerResultCulture = ParseCookieValue(cookie); - requestCulture = ValidateRequestCulture(requestCulture); - - return Task.FromResult(requestCulture); + return Task.FromResult(providerResultCulture); } /// @@ -75,7 +75,7 @@ namespace Microsoft.AspNet.Localization /// /// The cookie value to parse. /// The or null if parsing fails. - public static RequestCulture ParseCookieValue(string value) + public static ProviderCultureResult ParseCookieValue(string value) { if (string.IsNullOrWhiteSpace(value)) { @@ -100,15 +100,25 @@ namespace Microsoft.AspNet.Localization var cultureName = potentialCultureName.Substring(_culturePrefix.Length); var uiCultureName = potentialUICultureName.Substring(_uiCulturePrefix.Length); - var culture = CultureInfoCache.GetCultureInfo(cultureName); - var uiCulture = CultureInfoCache.GetCultureInfo(uiCultureName); - - if (culture == null || uiCulture == null) + if (cultureName == null && uiCultureName == null) { + // No values specified for either so no match return null; } - return new RequestCulture(culture, uiCulture); + if (cultureName != null && uiCultureName == null) + { + // Value for culture but not for UI culture so default to culture value for both + uiCultureName = cultureName; + } + + if (cultureName == null && uiCultureName != null) + { + // Value for UI culture but not for culture so default to UI culture value for both + cultureName = uiCultureName; + } + + return new ProviderCultureResult(cultureName, uiCultureName); } } } diff --git a/src/Microsoft.AspNet.Localization/CustomRequestCultureProvider.cs b/src/Microsoft.AspNet.Localization/CustomRequestCultureProvider.cs index 4af0faabda..7676b85b04 100644 --- a/src/Microsoft.AspNet.Localization/CustomRequestCultureProvider.cs +++ b/src/Microsoft.AspNet.Localization/CustomRequestCultureProvider.cs @@ -12,13 +12,13 @@ namespace Microsoft.AspNet.Localization /// public class CustomRequestCultureProvider : RequestCultureProvider { - private readonly Func> _provider; + private readonly Func> _provider; /// /// Creates a new using the specified delegate. /// /// The provider delegate. - public CustomRequestCultureProvider(Func> provider) + public CustomRequestCultureProvider(Func> provider) { if (provider == null) { @@ -29,7 +29,7 @@ namespace Microsoft.AspNet.Localization } /// - public override Task DetermineRequestCulture(HttpContext httpContext) + public override Task DetermineProviderCultureResult(HttpContext httpContext) { if (httpContext == null) { diff --git a/src/Microsoft.AspNet.Localization/IRequestCultureProvider.cs b/src/Microsoft.AspNet.Localization/IRequestCultureProvider.cs index 4af4908b2e..c2bb3810d0 100644 --- a/src/Microsoft.AspNet.Localization/IRequestCultureProvider.cs +++ b/src/Microsoft.AspNet.Localization/IRequestCultureProvider.cs @@ -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; @@ -16,9 +18,9 @@ namespace Microsoft.AspNet.Localization /// /// The for the request. /// - /// The determined . - /// Returns null if the provider couldn't determine a . + /// The determined . + /// Returns null if the provider couldn't determine a . /// - Task DetermineRequestCulture(HttpContext httpContext); + Task DetermineProviderCultureResult(HttpContext httpContext); } } diff --git a/src/Microsoft.AspNet.Localization/ProviderCultureResult.cs b/src/Microsoft.AspNet.Localization/ProviderCultureResult.cs new file mode 100644 index 0000000000..f397855f92 --- /dev/null +++ b/src/Microsoft.AspNet.Localization/ProviderCultureResult.cs @@ -0,0 +1,66 @@ +// 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; + +namespace Microsoft.AspNet.Localization +{ + /// + /// Details about the cultures obtained from . + /// + public class ProviderCultureResult + { + /// + /// Creates a new object that has its and + /// properties set to the same culture value. + /// + /// The name of the culture to be used for formatting, text, i.e. language. + public ProviderCultureResult(string culture) + : this(new List { culture }, new List { culture }) + { + } + + /// + /// Creates a new object has its and + /// properties set to the respective culture values provided. + /// + /// The name of the culture to be used for formatting. + /// The name of the ui culture to be used for text, i.e. language. + public ProviderCultureResult(string culture, string uiCulture) + : this(new List { culture }, new List { uiCulture }) + { + } + + /// + /// Creates a new object that has its and + /// properties set to the same culture value. + /// + /// The list of cultures to be used for formatting, text, i.e. language. + public ProviderCultureResult(IList cultures) + : this(cultures, cultures) + { + } + + /// + /// Creates a new object has its and + /// properties set to the respective culture values provided. + /// + /// The list of cultures to be used for formatting. + /// The list of ui cultures to be used for text, i.e. language. + public ProviderCultureResult(IList cultures, IList uiCultures) + { + Cultures = cultures; + UICultures = uiCultures; + } + + /// + /// Gets the list of cultures to be used for formatting. + /// + public IList Cultures { get; } + + /// + /// Gets the list of ui cultures to be used for text, i.e. language; + /// + public IList UICultures { get; } + } +} diff --git a/src/Microsoft.AspNet.Localization/QueryStringRequestCultureProvider.cs b/src/Microsoft.AspNet.Localization/QueryStringRequestCultureProvider.cs index ea1398ed9f..9ab1fb2dbc 100644 --- a/src/Microsoft.AspNet.Localization/QueryStringRequestCultureProvider.cs +++ b/src/Microsoft.AspNet.Localization/QueryStringRequestCultureProvider.cs @@ -27,7 +27,7 @@ namespace Microsoft.AspNet.Localization public string UIQueryStringKey { get; set; } = "ui-culture"; /// - public override Task DetermineRequestCulture(HttpContext httpContext) + public override Task DetermineProviderCultureResult(HttpContext httpContext) { if (httpContext == null) { @@ -37,7 +37,7 @@ namespace Microsoft.AspNet.Localization var request = httpContext.Request; if (!request.QueryString.HasValue) { - return Task.FromResult((RequestCulture)null); + return Task.FromResult((ProviderCultureResult)null); } string queryCulture = null; @@ -56,7 +56,7 @@ namespace Microsoft.AspNet.Localization if (queryCulture == null && queryUICulture == null) { // No values specified for either so no match - return Task.FromResult((RequestCulture)null); + return Task.FromResult((ProviderCultureResult)null); } if (queryCulture != null && queryUICulture == null) @@ -71,19 +71,9 @@ namespace Microsoft.AspNet.Localization queryCulture = queryUICulture; } - var culture = CultureInfoCache.GetCultureInfo(queryCulture); - var uiCulture = CultureInfoCache.GetCultureInfo(queryUICulture); + var providerResultCulture = new ProviderCultureResult(queryCulture, queryUICulture); - if (culture == null || uiCulture == null) - { - return Task.FromResult((RequestCulture)null); - } - - var requestCulture = new RequestCulture(culture, uiCulture); - - requestCulture = ValidateRequestCulture(requestCulture); - - return Task.FromResult(requestCulture); + return Task.FromResult(providerResultCulture); } } } diff --git a/src/Microsoft.AspNet.Localization/RequestCulture.cs b/src/Microsoft.AspNet.Localization/RequestCulture.cs index 175a05ec0b..1460b5ba0c 100644 --- a/src/Microsoft.AspNet.Localization/RequestCulture.cs +++ b/src/Microsoft.AspNet.Localization/RequestCulture.cs @@ -21,6 +21,27 @@ namespace Microsoft.AspNet.Localization { } + /// + /// Creates a new object has its and + /// properties set to the same value. + /// + /// The culture for the request. + public RequestCulture(string culture) + : this(culture, culture) + { + } + + /// + /// Creates a new object has its and + /// properties set to the respective values provided. + /// + /// The culture for the request to be used for formatting. + /// The culture for the request to be used for text, i.e. language. + public RequestCulture(string culture, string uiCulture) + : this (new CultureInfo(culture), new CultureInfo(uiCulture)) + { + } + /// /// Creates a new object has its and /// properties set to the respective values provided. diff --git a/src/Microsoft.AspNet.Localization/RequestCultureProvider.cs b/src/Microsoft.AspNet.Localization/RequestCultureProvider.cs index 234d46dc22..8b0189dea4 100644 --- a/src/Microsoft.AspNet.Localization/RequestCultureProvider.cs +++ b/src/Microsoft.AspNet.Localization/RequestCultureProvider.cs @@ -17,45 +17,7 @@ namespace Microsoft.AspNet.Localization public RequestLocalizationOptions Options { get; set; } /// - public abstract Task DetermineRequestCulture(HttpContext httpContext); + public abstract Task DetermineProviderCultureResult(HttpContext httpContext); - /// - /// Determines if the given is valid according to the currently configured. - /// . - /// - /// The to validate. - /// - /// The original if it was valid, otherwise a new - /// with values for and that are - /// valid for the current configuration, or null if neither or - /// were valid. - /// - 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; - } } } diff --git a/src/Microsoft.AspNet.Localization/RequestLocalizationMiddleware.cs b/src/Microsoft.AspNet.Localization/RequestLocalizationMiddleware.cs index e92da44b6c..37bc88a6b4 100644 --- a/src/Microsoft.AspNet.Localization/RequestLocalizationMiddleware.cs +++ b/src/Microsoft.AspNet.Localization/RequestLocalizationMiddleware.cs @@ -2,12 +2,14 @@ // 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; using System.Threading.Tasks; using Microsoft.AspNet.Builder; using Microsoft.AspNet.Http; using Microsoft.AspNet.Http.Features; +using Microsoft.Extensions.Globalization; namespace Microsoft.AspNet.Localization { @@ -19,13 +21,20 @@ namespace Microsoft.AspNet.Localization { private readonly RequestDelegate _next; private readonly RequestLocalizationOptions _options; + private readonly RequestCulture _defaultRequestCulture; /// /// Creates a new . /// /// The representing the next middleware in the pipeline. - /// The representing the options for the . - public RequestLocalizationMiddleware(RequestDelegate next, RequestLocalizationOptions options) + /// The representing the options for the + /// . + /// The default to use if none of the + /// requested cultures match supported cultures. + public RequestLocalizationMiddleware( + RequestDelegate next, + RequestLocalizationOptions options, + RequestCulture defaultRequestCulture) { if (next == null) { @@ -37,8 +46,14 @@ namespace Microsoft.AspNet.Localization throw new ArgumentNullException(nameof(options)); } + if (defaultRequestCulture == null) + { + throw new ArgumentNullException(nameof(defaultRequestCulture)); + } + _next = next; _options = options; + _defaultRequestCulture = defaultRequestCulture; } /// @@ -53,8 +68,7 @@ namespace Microsoft.AspNet.Localization throw new ArgumentNullException(nameof(context)); } - var requestCulture = _options.DefaultRequestCulture ?? - new RequestCulture(CultureInfo.CurrentCulture, CultureInfo.CurrentUICulture); + var requestCulture = _defaultRequestCulture; IRequestCultureProvider winningProvider = null; @@ -62,12 +76,45 @@ namespace Microsoft.AspNet.Localization { foreach (var provider in _options.RequestCultureProviders) { - var result = await provider.DetermineRequestCulture(context); - if (result != null) + var providerResultCulture = await provider.DetermineProviderCultureResult(context); + if (providerResultCulture != null) { - requestCulture = result; - winningProvider = provider; - break; + var cultures = providerResultCulture.Cultures; + var uiCultures = providerResultCulture.UICultures; + + CultureInfo cultureInfo = null; + CultureInfo uiCultureInfo = null; + if (_options.SupportedCultures != null) + { + cultureInfo = GetCultureInfo(cultures, _options.SupportedCultures); + } + + if (_options.SupportedUICultures != null) + { + uiCultureInfo = GetCultureInfo(uiCultures, _options.SupportedUICultures); + } + + if (cultureInfo == null && uiCultureInfo == null) + { + continue; + } + if (cultureInfo == null && uiCultureInfo != null) + { + cultureInfo = _defaultRequestCulture.Culture; + } + if (cultureInfo != null && uiCultureInfo == null) + { + uiCultureInfo = _defaultRequestCulture.UICulture; + } + + var result = new RequestCulture(cultureInfo, uiCultureInfo); + + if (result != null) + { + requestCulture = result; + winningProvider = provider; + break; + } } } } @@ -89,5 +136,24 @@ namespace Microsoft.AspNet.Localization CultureInfo.CurrentUICulture = requestCulture.UICulture; #endif } + + private CultureInfo GetCultureInfo(IList cultures, IList supportedCultures) + { + foreach (var culture in cultures) + { + // Allow empty string values as they map to InvariantCulture, whereas null culture values will throw in + // the CultureInfo ctor + if (culture != null) + { + var cultureInfo = CultureInfoCache.GetCultureInfo(culture, supportedCultures); + if (cultureInfo != null) + { + return cultureInfo; + } + } + } + + return null; + } } } \ No newline at end of file diff --git a/src/Microsoft.AspNet.Localization/RequestLocalizationOptions.cs b/src/Microsoft.AspNet.Localization/RequestLocalizationOptions.cs index a6e4bcc932..3f4d61e0ed 100644 --- a/src/Microsoft.AspNet.Localization/RequestLocalizationOptions.cs +++ b/src/Microsoft.AspNet.Localization/RequestLocalizationOptions.cs @@ -17,8 +17,6 @@ namespace Microsoft.AspNet.Localization /// public RequestLocalizationOptions() { - DefaultRequestCulture = new RequestCulture(CultureInfo.CurrentCulture, CultureInfo.CurrentUICulture); - RequestCultureProviders = new List { new QueryStringRequestCultureProvider { Options = this }, @@ -27,14 +25,6 @@ namespace Microsoft.AspNet.Localization }; } - /// - /// The default to use. This value will be used if none of the configured - /// options result in a non-null result. - /// Defaults to set to - /// and set to . - /// - public RequestCulture DefaultRequestCulture { get; set; } - /// /// The cultures supported by the application. If this value is non-null, the /// will only set the current request culture to an entry in this diff --git a/src/Microsoft.Extensions.Globalization.CultureInfoCache/CultureInfoCache.cs b/src/Microsoft.Extensions.Globalization.CultureInfoCache/CultureInfoCache.cs index 280e38c834..6dd7edefc1 100644 --- a/src/Microsoft.Extensions.Globalization.CultureInfoCache/CultureInfoCache.cs +++ b/src/Microsoft.Extensions.Globalization.CultureInfoCache/CultureInfoCache.cs @@ -1,15 +1,18 @@ // 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.Concurrent; +using System.Collections.Generic; using System.Globalization; +using System.Linq; namespace Microsoft.Extensions.Globalization { /// /// Provides read-only cached instances of . /// - public static partial class CultureInfoCache + public static class CultureInfoCache { private static readonly ConcurrentDictionary _cache = new ConcurrentDictionary(); @@ -18,15 +21,17 @@ namespace Microsoft.Extensions.Globalization /// will be used. /// /// The culture name. + /// The cultures supported by the application. /// /// A read-only cached or null a match wasn't found in /// . /// - public static CultureInfo GetCultureInfo(string name) + public static CultureInfo GetCultureInfo(string name, IList 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.Any(supportedCulture => string.Equals(supportedCulture.Name, name, StringComparison.OrdinalIgnoreCase))) { return null; } diff --git a/src/Microsoft.Extensions.Globalization.CultureInfoCache/CultureInfoList.cs b/src/Microsoft.Extensions.Globalization.CultureInfoCache/CultureInfoList.cs deleted file mode 100644 index 29c02b0e5c..0000000000 --- a/src/Microsoft.Extensions.Globalization.CultureInfoCache/CultureInfoList.cs +++ /dev/null @@ -1,445 +0,0 @@ -// 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. - -// *************************** THIS FILE IS GENERATED BY A TOOL *************************** -// To make changes to this file look at the CultureInfoGenerator project in this solution. - -using System; -using System.Collections.Generic; - -namespace Microsoft.Extensions.Globalization -{ - /// - /// Contains a list of known culture names that can be used to create a . - /// - public static partial class CultureInfoCache - { - /// - /// This list of known cultures was generated by CultureInfoGenerator using .NET Framework 4.6 RC or later on - /// Microsoft Windows NT 6.2.9200.0. - /// As new versions of .NET Framework and Windows are released, this list should be regenerated to ensure it - /// contains the latest culture names. - /// - public static readonly HashSet KnownCultureNames = new HashSet(StringComparer.OrdinalIgnoreCase) - { - "", - "af", - "af-ZA", - "am", - "am-ET", - "ar", - "ar-AE", - "ar-BH", - "ar-DZ", - "ar-EG", - "ar-IQ", - "ar-JO", - "ar-KW", - "ar-LB", - "ar-LY", - "ar-MA", - "ar-OM", - "ar-QA", - "ar-SA", - "ar-SY", - "ar-TN", - "ar-YE", - "arn", - "arn-CL", - "as", - "as-IN", - "az", - "az-Cyrl", - "az-Cyrl-AZ", - "az-Latn", - "az-Latn-AZ", - "ba", - "ba-RU", - "be", - "be-BY", - "bg", - "bg-BG", - "bn", - "bn-BD", - "bn-IN", - "bo", - "bo-CN", - "br", - "br-FR", - "bs", - "bs-Cyrl", - "bs-Cyrl-BA", - "bs-Latn", - "bs-Latn-BA", - "ca", - "ca-ES", - "ca-ES-valencia", - "chr", - "chr-Cher", - "chr-Cher-US", - "co", - "co-FR", - "cs", - "cs-CZ", - "cy", - "cy-GB", - "da", - "da-DK", - "de", - "de-AT", - "de-CH", - "de-DE", - "de-LI", - "de-LU", - "dsb", - "dsb-DE", - "dv", - "dv-MV", - "el", - "el-GR", - "en", - "en-029", - "en-AU", - "en-BZ", - "en-CA", - "en-GB", - "en-HK", - "en-IE", - "en-IN", - "en-JM", - "en-MY", - "en-NZ", - "en-PH", - "en-SG", - "en-TT", - "en-US", - "en-ZA", - "en-ZW", - "es", - "es-419", - "es-AR", - "es-BO", - "es-CL", - "es-CO", - "es-CR", - "es-DO", - "es-EC", - "es-ES", - "es-GT", - "es-HN", - "es-MX", - "es-NI", - "es-PA", - "es-PE", - "es-PR", - "es-PY", - "es-SV", - "es-US", - "es-UY", - "es-VE", - "et", - "et-EE", - "eu", - "eu-ES", - "fa", - "fa-IR", - "ff", - "ff-Latn", - "ff-Latn-SN", - "fi", - "fi-FI", - "fil", - "fil-PH", - "fo", - "fo-FO", - "fr", - "fr-BE", - "fr-CA", - "fr-CD", - "fr-CH", - "fr-CI", - "fr-CM", - "fr-FR", - "fr-HT", - "fr-LU", - "fr-MA", - "fr-MC", - "fr-ML", - "fr-RE", - "fr-SN", - "fy", - "fy-NL", - "ga", - "ga-IE", - "gd", - "gd-GB", - "gl", - "gl-ES", - "gn", - "gn-PY", - "gsw", - "gsw-FR", - "gu", - "gu-IN", - "ha", - "ha-Latn", - "ha-Latn-NG", - "haw", - "haw-US", - "he", - "he-IL", - "hi", - "hi-IN", - "hr", - "hr-BA", - "hr-HR", - "hsb", - "hsb-DE", - "hu", - "hu-HU", - "hy", - "hy-AM", - "id", - "id-ID", - "ig", - "ig-NG", - "ii", - "ii-CN", - "is", - "is-IS", - "it", - "it-CH", - "it-IT", - "iu", - "iu-Cans", - "iu-Cans-CA", - "iu-Latn", - "iu-Latn-CA", - "ja", - "ja-JP", - "jv", - "jv-Latn", - "jv-Latn-ID", - "ka", - "ka-GE", - "kk", - "kk-KZ", - "kl", - "kl-GL", - "km", - "km-KH", - "kn", - "kn-IN", - "ko", - "ko-KR", - "kok", - "kok-IN", - "ku", - "ku-Arab", - "ku-Arab-IQ", - "ky", - "ky-KG", - "lb", - "lb-LU", - "lo", - "lo-LA", - "lt", - "lt-LT", - "lv", - "lv-LV", - "mg", - "mg-MG", - "mi", - "mi-NZ", - "mk", - "mk-MK", - "ml", - "ml-IN", - "mn", - "mn-Cyrl", - "mn-MN", - "mn-Mong", - "mn-Mong-CN", - "mn-Mong-MN", - "moh", - "moh-CA", - "mr", - "mr-IN", - "ms", - "ms-BN", - "ms-MY", - "mt", - "mt-MT", - "my", - "my-MM", - "nb", - "nb-NO", - "ne", - "ne-IN", - "ne-NP", - "nl", - "nl-BE", - "nl-NL", - "nn", - "nn-NO", - "no", - "nqo", - "nqo-GN", - "nso", - "nso-ZA", - "oc", - "oc-FR", - "om", - "om-ET", - "or", - "or-IN", - "pa", - "pa-Arab", - "pa-Arab-PK", - "pa-IN", - "pl", - "pl-PL", - "prs", - "prs-AF", - "ps", - "ps-AF", - "pt", - "pt-AO", - "pt-BR", - "pt-PT", - "qut", - "qut-GT", - "quz", - "quz-BO", - "quz-EC", - "quz-PE", - "rm", - "rm-CH", - "ro", - "ro-MD", - "ro-RO", - "ru", - "ru-RU", - "rw", - "rw-RW", - "sa", - "sa-IN", - "sah", - "sah-RU", - "sd", - "sd-Arab", - "sd-Arab-PK", - "se", - "se-FI", - "se-NO", - "se-SE", - "si", - "si-LK", - "sk", - "sk-SK", - "sl", - "sl-SI", - "sma", - "sma-NO", - "sma-SE", - "smj", - "smj-NO", - "smj-SE", - "smn", - "smn-FI", - "sms", - "sms-FI", - "sn", - "sn-Latn", - "sn-Latn-ZW", - "so", - "so-SO", - "sq", - "sq-AL", - "sr", - "sr-Cyrl", - "sr-Cyrl-BA", - "sr-Cyrl-CS", - "sr-Cyrl-ME", - "sr-Cyrl-RS", - "sr-Latn", - "sr-Latn-BA", - "sr-Latn-CS", - "sr-Latn-ME", - "sr-Latn-RS", - "st", - "st-ZA", - "sv", - "sv-FI", - "sv-SE", - "sw", - "sw-KE", - "syr", - "syr-SY", - "ta", - "ta-IN", - "ta-LK", - "te", - "te-IN", - "tg", - "tg-Cyrl", - "tg-Cyrl-TJ", - "th", - "th-TH", - "ti", - "ti-ER", - "ti-ET", - "tk", - "tk-TM", - "tn", - "tn-BW", - "tn-ZA", - "tr", - "tr-TR", - "ts", - "ts-ZA", - "tt", - "tt-RU", - "tzm", - "tzm-Latn", - "tzm-Latn-DZ", - "tzm-Tfng", - "tzm-Tfng-MA", - "ug", - "ug-CN", - "uk", - "uk-UA", - "ur", - "ur-IN", - "ur-PK", - "uz", - "uz-Cyrl", - "uz-Cyrl-UZ", - "uz-Latn", - "uz-Latn-UZ", - "vi", - "vi-VN", - "wo", - "wo-SN", - "xh", - "xh-ZA", - "yo", - "yo-NG", - "zgh", - "zgh-Tfng", - "zgh-Tfng-MA", - "zh", - "zh-CN", - "zh-Hans", - "zh-Hant", - "zh-HK", - "zh-MO", - "zh-SG", - "zh-TW", - "zu", - "zu-ZA", - "zh-CHS", - "zh-CHT" - }; - } -} diff --git a/test/LocalizationWebsite/StartupResourcesAtRootFolder.cs b/test/LocalizationWebsite/StartupResourcesAtRootFolder.cs index 20e91e44d6..6efa759bc8 100644 --- a/test/LocalizationWebsite/StartupResourcesAtRootFolder.cs +++ b/test/LocalizationWebsite/StartupResourcesAtRootFolder.cs @@ -1,9 +1,12 @@ // 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 LocalizationWebsite.Models; using Microsoft.AspNet.Builder; using Microsoft.AspNet.Http; +using Microsoft.AspNet.Localization; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Localization; using Microsoft.Extensions.Logging; @@ -26,7 +29,19 @@ namespace LocalizationWebsite { loggerFactory.AddConsole(minLevel: LogLevel.Warning); - app.UseRequestLocalization(); + var options = new RequestLocalizationOptions + { + SupportedCultures = new List() + { + new CultureInfo("fr-FR") + }, + SupportedUICultures = new List() + { + new CultureInfo("fr-FR") + } + }; + + app.UseRequestLocalization(options, defaultRequestCulture: new RequestCulture("en-US")); var stringLocalizer = stringLocalizerFactory.Create("Test", location: null); diff --git a/test/LocalizationWebsite/StartupResourcesInFolder.cs b/test/LocalizationWebsite/StartupResourcesInFolder.cs index 2df33ef997..d8027ab799 100644 --- a/test/LocalizationWebsite/StartupResourcesInFolder.cs +++ b/test/LocalizationWebsite/StartupResourcesInFolder.cs @@ -1,9 +1,12 @@ // 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 LocalizationWebsite.Models; using Microsoft.AspNet.Builder; using Microsoft.AspNet.Http; +using Microsoft.AspNet.Localization; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Localization; using Microsoft.Extensions.Logging; @@ -26,7 +29,19 @@ namespace LocalizationWebsite { loggerFactory.AddConsole(minLevel: LogLevel.Warning); - app.UseRequestLocalization(); + var options = new RequestLocalizationOptions + { + SupportedCultures = new List() + { + new CultureInfo("fr-FR") + }, + SupportedUICultures = new List() + { + new CultureInfo("fr-FR") + } + }; + + app.UseRequestLocalization(options, defaultRequestCulture: new RequestCulture("en-US")); var stringLocalizer = stringLocalizerFactory.Create("Test", location: null); diff --git a/test/Microsoft.AspNet.Localization.Tests/AccessLanguageHeaderRequestCultureProviderTest.cs b/test/Microsoft.AspNet.Localization.Tests/AcceptLanguageHeaderRequestCultureProviderTest.cs similarity index 92% rename from test/Microsoft.AspNet.Localization.Tests/AccessLanguageHeaderRequestCultureProviderTest.cs rename to test/Microsoft.AspNet.Localization.Tests/AcceptLanguageHeaderRequestCultureProviderTest.cs index 3a5f1ba701..288bc6f885 100644 --- a/test/Microsoft.AspNet.Localization.Tests/AccessLanguageHeaderRequestCultureProviderTest.cs +++ b/test/Microsoft.AspNet.Localization.Tests/AcceptLanguageHeaderRequestCultureProviderTest.cs @@ -12,7 +12,7 @@ using Xunit; namespace Microsoft.Extensions.Localization.Tests { - public class AccessLanguageHeaderRequestCultureProviderTest + public class AcceptLanguageHeaderRequestCultureProviderTest { [Fact] public async void GetFallbackLanguage_ReturnsFirstNonNullCultureFromSupportedCultureList() @@ -27,7 +27,7 @@ namespace Microsoft.Extensions.Localization.Tests new CultureInfo("en-US") } }; - app.UseRequestLocalization(options); + app.UseRequestLocalization(options, defaultRequestCulture: new RequestCulture("en-US")); app.Run(context => { var requestCultureFeature = context.Features.Get(); @@ -52,14 +52,13 @@ namespace Microsoft.Extensions.Localization.Tests { var options = new RequestLocalizationOptions { - DefaultRequestCulture = new RequestCulture(new CultureInfo("fr-FR")), SupportedCultures = new List { new CultureInfo("ar-SA"), new CultureInfo("en-US") } }; - app.UseRequestLocalization(options); + app.UseRequestLocalization(options, defaultRequestCulture: new RequestCulture("fr-FR")); app.Run(context => { var requestCultureFeature = context.Features.Get(); @@ -83,14 +82,13 @@ namespace Microsoft.Extensions.Localization.Tests { var options = new RequestLocalizationOptions { - DefaultRequestCulture = new RequestCulture(new CultureInfo("fr-FR")), SupportedCultures = new List { new CultureInfo("ar-SA"), new CultureInfo("af-ZA") } }; - app.UseRequestLocalization(options); + app.UseRequestLocalization(options, defaultRequestCulture: new RequestCulture("fr-FR")); app.Run(context => { var requestCultureFeature = context.Features.Get(); @@ -124,7 +122,7 @@ namespace Microsoft.Extensions.Localization.Tests new CultureInfo("ar-YE") } }; - app.UseRequestLocalization(options); + app.UseRequestLocalization(options, defaultRequestCulture: new RequestCulture("en-US")); app.Run(context => { var requestCultureFeature = context.Features.Get(); diff --git a/test/Microsoft.AspNet.Localization.Tests/CookieRequestCultureProviderTest.cs b/test/Microsoft.AspNet.Localization.Tests/CookieRequestCultureProviderTest.cs index 9bb01e3b8e..1327639595 100644 --- a/test/Microsoft.AspNet.Localization.Tests/CookieRequestCultureProviderTest.cs +++ b/test/Microsoft.AspNet.Localization.Tests/CookieRequestCultureProviderTest.cs @@ -1,6 +1,7 @@ // 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.Builder; @@ -19,11 +20,21 @@ namespace Microsoft.Extensions.Localization.Tests { using (var server = TestServer.Create(app => { - var options = new RequestLocalizationOptions(); + var options = new RequestLocalizationOptions() + { + SupportedCultures = new List + { + new CultureInfo("ar-SA") + }, + SupportedUICultures = new List + { + new CultureInfo("ar-SA") + } + }; var provider = new CookieRequestCultureProvider(); provider.CookieName = "Preferences"; options.RequestCultureProviders.Insert(0, provider); - app.UseRequestLocalization(options); + app.UseRequestLocalization(options, defaultRequestCulture: new RequestCulture("en-US")); app.Run(context => { var requestCultureFeature = context.Features.Get(); @@ -48,16 +59,26 @@ namespace Microsoft.Extensions.Localization.Tests { using (var server = TestServer.Create(app => { - var options = new RequestLocalizationOptions(); + var options = new RequestLocalizationOptions() + { + SupportedCultures = new List + { + new CultureInfo("ar-SA") + }, + SupportedUICultures = new List + { + new CultureInfo("ar-SA") + } + }; var provider = new CookieRequestCultureProvider(); provider.CookieName = "Preferences"; options.RequestCultureProviders.Insert(0, provider); - app.UseRequestLocalization(options); + app.UseRequestLocalization(options, defaultRequestCulture: new RequestCulture("en-US")); app.Run(context => { var requestCultureFeature = context.Features.Get(); var requestCulture = requestCultureFeature.RequestCulture; - Assert.Equal(options.DefaultRequestCulture.Culture.Name, requestCulture.Culture.Name); + Assert.Equal("en-US", requestCulture.Culture.Name); return Task.FromResult(0); }); })) @@ -73,16 +94,26 @@ namespace Microsoft.Extensions.Localization.Tests { using (var server = TestServer.Create(app => { - var options = new RequestLocalizationOptions(); + var options = new RequestLocalizationOptions() + { + SupportedCultures = new List + { + new CultureInfo("ar-SA") + }, + SupportedUICultures = new List + { + new CultureInfo("ar-SA") + } + }; var provider = new CookieRequestCultureProvider(); provider.CookieName = "Preferences"; options.RequestCultureProviders.Insert(0, provider); - app.UseRequestLocalization(options); + app.UseRequestLocalization(options, defaultRequestCulture: new RequestCulture("en-US")); app.Run(context => { var requestCultureFeature = context.Features.Get(); var requestCulture = requestCultureFeature.RequestCulture; - Assert.Equal(options.DefaultRequestCulture.Culture.Name, requestCulture.Culture.Name); + Assert.Equal("en-US", requestCulture.Culture.Name); return Task.FromResult(0); }); })) diff --git a/test/Microsoft.AspNet.Localization.Tests/CustomRequestCultureProviderTest.cs b/test/Microsoft.AspNet.Localization.Tests/CustomRequestCultureProviderTest.cs index 78a0ba5520..f668f9085b 100644 --- a/test/Microsoft.AspNet.Localization.Tests/CustomRequestCultureProviderTest.cs +++ b/test/Microsoft.AspNet.Localization.Tests/CustomRequestCultureProviderTest.cs @@ -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.Collections.Generic; using System.Globalization; using System.Threading.Tasks; using Microsoft.AspNet.Builder; @@ -21,14 +22,24 @@ namespace Microsoft.Extensions.Localization.Tests { using (var server = TestServer.Create(app => { - var options = new RequestLocalizationOptions(); + var options = new RequestLocalizationOptions() + { + SupportedCultures = new List + { + new CultureInfo("ar") + }, + SupportedUICultures = new List + { + new CultureInfo("ar") + } + }; options.RequestCultureProviders.Insert(0, new CustomRequestCultureProvider(context => { - var culture = GetCultureInfoFromUrl(context); - var requestCulture = new RequestCulture(culture); + var culture = GetCultureInfoFromUrl(context, options.SupportedCultures); + var requestCulture = new ProviderCultureResult(culture); return Task.FromResult(requestCulture); })); - app.UseRequestLocalization(options); + app.UseRequestLocalization(options, defaultRequestCulture: new RequestCulture("en-US")); app.Run(context => { var requestCultureFeature = context.Features.Get(); @@ -43,18 +54,16 @@ namespace Microsoft.Extensions.Localization.Tests } } - private CultureInfo GetCultureInfoFromUrl(HttpContext context) + private string GetCultureInfoFromUrl(HttpContext context, IList supportedCultures) { var currentCulture = "en"; var segments = context.Request.Path.Value.Split(new char[] { '/' }, StringSplitOptions.RemoveEmptyEntries); if (segments.Length > 1 && segments[0].Length == 2) { - if (CultureInfoCache.KnownCultureNames.Contains(segments[0])) - currentCulture = segments[0]; - else - throw new InvalidOperationException($"The '{segments[0]}' is invalid culture name."); + currentCulture = segments[0]; } - return CultureInfoCache.GetCultureInfo(currentCulture); + + return currentCulture; } } } \ No newline at end of file diff --git a/test/Microsoft.AspNet.Localization.Tests/QueryStringRequestCultureProviderTest.cs b/test/Microsoft.AspNet.Localization.Tests/QueryStringRequestCultureProviderTest.cs index f9229864c5..a71664e6ed 100644 --- a/test/Microsoft.AspNet.Localization.Tests/QueryStringRequestCultureProviderTest.cs +++ b/test/Microsoft.AspNet.Localization.Tests/QueryStringRequestCultureProviderTest.cs @@ -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.Builder; using Microsoft.AspNet.Http.Features; @@ -17,8 +19,19 @@ namespace Microsoft.Extensions.Localization.Tests { using (var server = TestServer.Create(app => { - var options = new RequestLocalizationOptions(); - app.UseRequestLocalization(options); + var options = new RequestLocalizationOptions() + { + SupportedCultures = new List + { + new CultureInfo("ar-SA") + }, + SupportedUICultures = new List + { + new CultureInfo("ar-YE") + } + }; + + app.UseRequestLocalization(options, defaultRequestCulture: new RequestCulture("en-US")); app.Run(context => { var requestCultureFeature = context.Features.Get(); @@ -40,13 +53,13 @@ namespace Microsoft.Extensions.Localization.Tests using (var server = TestServer.Create(app => { var options = new RequestLocalizationOptions(); - app.UseRequestLocalization(options); + app.UseRequestLocalization(options, defaultRequestCulture: new RequestCulture("en-US")); app.Run(context => { var requestCultureFeature = context.Features.Get(); var requestCulture = requestCultureFeature.RequestCulture; - Assert.Equal(options.DefaultRequestCulture.Culture.Name, requestCulture.Culture.Name); - Assert.Equal(options.DefaultRequestCulture.UICulture.Name, requestCulture.UICulture.Name); + Assert.Equal("en-US", requestCulture.Culture.Name); + Assert.Equal("en-US", requestCulture.UICulture.Name); return Task.FromResult(0); }); })) @@ -57,17 +70,27 @@ namespace Microsoft.Extensions.Localization.Tests } [Fact] - public async void GetDefaultCultureInfoIfCultureIsInvalid() + public async void GetDefaultCultureInfoIfCultureIsInSupportedCultureList() { using (var server = TestServer.Create(app => { - var options = new RequestLocalizationOptions(); - app.UseRequestLocalization(options); + var options = new RequestLocalizationOptions() + { + SupportedCultures = new List + { + new CultureInfo("ar-SA") + }, + SupportedUICultures = new List + { + new CultureInfo("ar-SA") + } + }; + app.UseRequestLocalization(options, defaultRequestCulture: new RequestCulture("en-US")); app.Run(context => { var requestCultureFeature = context.Features.Get(); var requestCulture = requestCultureFeature.RequestCulture; - Assert.Equal(options.DefaultRequestCulture.Culture.Name, requestCulture.Culture.Name); + Assert.Equal("en-US", requestCulture.Culture.Name); return Task.FromResult(0); }); })) @@ -78,17 +101,27 @@ namespace Microsoft.Extensions.Localization.Tests } [Fact] - public async void GetDefaultCultureInfoIfUICultureIsInvalid() + public async void GetDefaultCultureInfoIfUICultureIsNotInSupportedList() { using (var server = TestServer.Create(app => { - var options = new RequestLocalizationOptions(); - app.UseRequestLocalization(options); + var options = new RequestLocalizationOptions() + { + SupportedCultures = new List + { + new CultureInfo("ar-SA") + }, + SupportedUICultures = new List + { + new CultureInfo("ar-SA") + } + }; + app.UseRequestLocalization(options, defaultRequestCulture: new RequestCulture("en-US")); app.Run(context => { var requestCultureFeature = context.Features.Get(); var requestCulture = requestCultureFeature.RequestCulture; - Assert.Equal(options.DefaultRequestCulture.UICulture.Name, requestCulture.UICulture.Name); + Assert.Equal("en-US", requestCulture.UICulture.Name); return Task.FromResult(0); }); })) @@ -103,8 +136,18 @@ namespace Microsoft.Extensions.Localization.Tests { using (var server = TestServer.Create(app => { - var options = new RequestLocalizationOptions(); - app.UseRequestLocalization(options); + var options = new RequestLocalizationOptions() + { + SupportedCultures = new List + { + new CultureInfo("ar-SA") + }, + SupportedUICultures = new List + { + new CultureInfo("ar-SA") + } + }; + app.UseRequestLocalization(options, defaultRequestCulture: new RequestCulture("en-US")); app.Run(context => { var requestCultureFeature = context.Features.Get(); @@ -125,8 +168,18 @@ namespace Microsoft.Extensions.Localization.Tests { using (var server = TestServer.Create(app => { - var options = new RequestLocalizationOptions(); - app.UseRequestLocalization(options); + var options = new RequestLocalizationOptions() + { + SupportedCultures = new List + { + new CultureInfo("ar-SA") + }, + SupportedUICultures = new List + { + new CultureInfo("ar-SA") + } + }; + app.UseRequestLocalization(options, defaultRequestCulture: new RequestCulture("en-US")); app.Run(context => { var requestCultureFeature = context.Features.Get(); @@ -147,12 +200,22 @@ namespace Microsoft.Extensions.Localization.Tests { using (var server = TestServer.Create(app => { - var options = new RequestLocalizationOptions(); + var options = new RequestLocalizationOptions() + { + SupportedCultures = new List + { + new CultureInfo("ar-SA") + }, + SupportedUICultures = new List + { + new CultureInfo("ar-YE") + } + }; var provider = new QueryStringRequestCultureProvider(); provider.QueryStringKey = "c"; provider.UIQueryStringKey = "uic"; options.RequestCultureProviders.Insert(0, provider); - app.UseRequestLocalization(options); + app.UseRequestLocalization(options, defaultRequestCulture: new RequestCulture("en-US")); app.Run(context => { var requestCultureFeature = context.Features.Get(); @@ -173,12 +236,22 @@ namespace Microsoft.Extensions.Localization.Tests { using (var server = TestServer.Create(app => { - var options = new RequestLocalizationOptions(); + var options = new RequestLocalizationOptions() + { + SupportedCultures = new List + { + new CultureInfo("FR") + }, + SupportedUICultures = new List + { + new CultureInfo("FR") + } + }; var provider = new QueryStringRequestCultureProvider(); provider.QueryStringKey = "c"; provider.UIQueryStringKey = "uic"; options.RequestCultureProviders.Insert(0, provider); - app.UseRequestLocalization(options); + app.UseRequestLocalization(options, defaultRequestCulture: new RequestCulture("en-US")); app.Run(context => { var requestCultureFeature = context.Features.Get();