From 9eede89418428004f9b3e53d0ffb7f6f6e21af0a Mon Sep 17 00:00:00 2001 From: damianedwards Date: Tue, 5 May 2015 23:27:25 -0700 Subject: [PATCH 001/390] Initial commit --- .gitattributes | 50 ++++ .gitignore | 28 +++ .travis.yml | 4 + CONTRIBUTING.md | 4 + LICENSE.txt | 12 + Localization.sln | 41 ++++ NuGet.Config | 7 + README.md | 9 + appveyor.yml | 7 + build.cmd | 28 +++ build.sh | 39 +++ makefile.shade | 7 + .../IApplicationBuilderExtensions.cs | 24 ++ .../IRequestCultureFeature.cs | 16 ++ .../Microsoft.AspNet.Localization.xproj | 20 ++ .../RequestCulture.cs | 46 ++++ .../RequestCultureFeature.cs | 23 ++ .../RequestLocalizationMiddleware.cs | 80 +++++++ .../project.json | 21 ++ .../IStringLocalizer.cs | 51 ++++ .../IStringLocalizerFactory.cs | 29 +++ .../IStringLocalizerOfT.cs | 16 ++ .../LocalizedString.cs | 61 +++++ ....Framework.Localization.Abstractions.xproj | 20 ++ .../StringLocalizerOfT.cs | 48 ++++ .../project.json | 21 ++ .../IServiceCollectionExtensions.cs | 32 +++ .../Microsoft.Framework.Localization.xproj | 20 ++ .../ResourceManagerStringLocalizer.cs | 226 ++++++++++++++++++ .../ResourceManagerStringLocalizerFactory.cs | 54 +++++ ...sourceManagerWithCultureStringLocalizer.cs | 64 +++++ .../project.json | 26 ++ 32 files changed, 1134 insertions(+) create mode 100644 .gitattributes create mode 100644 .gitignore create mode 100644 .travis.yml create mode 100644 CONTRIBUTING.md create mode 100644 LICENSE.txt create mode 100644 Localization.sln create mode 100644 NuGet.Config create mode 100644 README.md create mode 100644 appveyor.yml create mode 100644 build.cmd create mode 100644 build.sh create mode 100644 makefile.shade create mode 100644 src/Microsoft.AspNet.Localization/IApplicationBuilderExtensions.cs create mode 100644 src/Microsoft.AspNet.Localization/IRequestCultureFeature.cs create mode 100644 src/Microsoft.AspNet.Localization/Microsoft.AspNet.Localization.xproj create mode 100644 src/Microsoft.AspNet.Localization/RequestCulture.cs create mode 100644 src/Microsoft.AspNet.Localization/RequestCultureFeature.cs create mode 100644 src/Microsoft.AspNet.Localization/RequestLocalizationMiddleware.cs create mode 100644 src/Microsoft.AspNet.Localization/project.json create mode 100644 src/Microsoft.Framework.Localization.Abstractions/IStringLocalizer.cs create mode 100644 src/Microsoft.Framework.Localization.Abstractions/IStringLocalizerFactory.cs create mode 100644 src/Microsoft.Framework.Localization.Abstractions/IStringLocalizerOfT.cs create mode 100644 src/Microsoft.Framework.Localization.Abstractions/LocalizedString.cs create mode 100644 src/Microsoft.Framework.Localization.Abstractions/Microsoft.Framework.Localization.Abstractions.xproj create mode 100644 src/Microsoft.Framework.Localization.Abstractions/StringLocalizerOfT.cs create mode 100644 src/Microsoft.Framework.Localization.Abstractions/project.json create mode 100644 src/Microsoft.Framework.Localization/IServiceCollectionExtensions.cs create mode 100644 src/Microsoft.Framework.Localization/Microsoft.Framework.Localization.xproj create mode 100644 src/Microsoft.Framework.Localization/ResourceManagerStringLocalizer.cs create mode 100644 src/Microsoft.Framework.Localization/ResourceManagerStringLocalizerFactory.cs create mode 100644 src/Microsoft.Framework.Localization/ResourceManagerWithCultureStringLocalizer.cs create mode 100644 src/Microsoft.Framework.Localization/project.json diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000000..bdaa5ba982 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,50 @@ +*.doc diff=astextplain +*.DOC diff=astextplain +*.docx diff=astextplain +*.DOCX diff=astextplain +*.dot diff=astextplain +*.DOT diff=astextplain +*.pdf diff=astextplain +*.PDF diff=astextplain +*.rtf diff=astextplain +*.RTF diff=astextplain + +*.jpg binary +*.png binary +*.gif binary + +*.cs text=auto diff=csharp +*.vb text=auto +*.resx text=auto +*.c text=auto +*.cpp text=auto +*.cxx text=auto +*.h text=auto +*.hxx text=auto +*.py text=auto +*.rb text=auto +*.java text=auto +*.html text=auto +*.htm text=auto +*.css text=auto +*.scss text=auto +*.sass text=auto +*.less text=auto +*.js text=auto +*.lisp text=auto +*.clj text=auto +*.sql text=auto +*.php text=auto +*.lua text=auto +*.m text=auto +*.asm text=auto +*.erl text=auto +*.fs text=auto +*.fsx text=auto +*.hs text=auto + +*.csproj text=auto +*.vbproj text=auto +*.fsproj text=auto +*.dbproj text=auto +*.sln text=auto eol=crlf diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000000..65df2761c4 --- /dev/null +++ b/.gitignore @@ -0,0 +1,28 @@ +[Oo]bj/ +[Bb]in/ +TestResults/ +.nuget/ +_ReSharper.*/ +packages/ +artifacts/ +PublishProfiles/ +*.user +*.suo +*.cache +*.docstates +_ReSharper.* +nuget.exe +*net45.csproj +*net451.csproj +*k10.csproj +*.psess +*.vsp +*.pidb +*.userprefs +*DS_Store +*.ncrunchsolution +*.*sdf +*.ipch +*.sln.ide +debugSettings.json +project.lock.json diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000000..947bf868ee --- /dev/null +++ b/.travis.yml @@ -0,0 +1,4 @@ +language: csharp +sudo: false +script: + - ./build.sh --quiet verify \ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000000..eac4268e4c --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,4 @@ +Contributing +====== + +Information on contributing to this repo is in the [Contributing Guide](https://github.com/aspnet/Home/blob/master/CONTRIBUTING.md) in the Home repo. diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000000..0bdc1962b6 --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,12 @@ +Copyright (c) .NET Foundation. All rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); you may not use +these files except in compliance with the License. You may obtain a copy of the +License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed +under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +CONDITIONS OF ANY KIND, either express or implied. See the License for the +specific language governing permissions and limitations under the License. diff --git a/Localization.sln b/Localization.sln new file mode 100644 index 0000000000..0df01140f0 --- /dev/null +++ b/Localization.sln @@ -0,0 +1,41 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 14 +VisualStudioVersion = 14.0.22823.1 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{FB313677-BAB3-4E49-8CDB-4FA4A9564767}" +EndProject +Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.Framework.Localization", "src\Microsoft.Framework.Localization\Microsoft.Framework.Localization.xproj", "{29743CFF-120E-40EB-9B89-5818425946FA}" +EndProject +Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.AspNet.Localization", "src\Microsoft.AspNet.Localization\Microsoft.AspNet.Localization.xproj", "{23E3BC23-3464-4D9B-BF78-02CB2182BEF0}" +EndProject +Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.Framework.Localization.Abstractions", "src\Microsoft.Framework.Localization.Abstractions\Microsoft.Framework.Localization.Abstractions.xproj", "{A1FCF259-70F6-4605-AA2D-E4B356BE771A}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {29743CFF-120E-40EB-9B89-5818425946FA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {29743CFF-120E-40EB-9B89-5818425946FA}.Debug|Any CPU.Build.0 = Debug|Any CPU + {29743CFF-120E-40EB-9B89-5818425946FA}.Release|Any CPU.ActiveCfg = Release|Any CPU + {29743CFF-120E-40EB-9B89-5818425946FA}.Release|Any CPU.Build.0 = Release|Any CPU + {23E3BC23-3464-4D9B-BF78-02CB2182BEF0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {23E3BC23-3464-4D9B-BF78-02CB2182BEF0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {23E3BC23-3464-4D9B-BF78-02CB2182BEF0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {23E3BC23-3464-4D9B-BF78-02CB2182BEF0}.Release|Any CPU.Build.0 = Release|Any CPU + {A1FCF259-70F6-4605-AA2D-E4B356BE771A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A1FCF259-70F6-4605-AA2D-E4B356BE771A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A1FCF259-70F6-4605-AA2D-E4B356BE771A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A1FCF259-70F6-4605-AA2D-E4B356BE771A}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {29743CFF-120E-40EB-9B89-5818425946FA} = {FB313677-BAB3-4E49-8CDB-4FA4A9564767} + {23E3BC23-3464-4D9B-BF78-02CB2182BEF0} = {FB313677-BAB3-4E49-8CDB-4FA4A9564767} + {A1FCF259-70F6-4605-AA2D-E4B356BE771A} = {FB313677-BAB3-4E49-8CDB-4FA4A9564767} + EndGlobalSection +EndGlobal diff --git a/NuGet.Config b/NuGet.Config new file mode 100644 index 0000000000..da57d47267 --- /dev/null +++ b/NuGet.Config @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000000..c0250d425f --- /dev/null +++ b/README.md @@ -0,0 +1,9 @@ +Localization +========== +AppVeyor: TODO + +Travis: TODO + +Localization abstractions and implementations for ASP.NET 5 applications. + +This project is part of ASP.NET 5. You can find samples, documentation and getting started instructions for ASP.NET 5 at the [Home](https://github.com/aspnet/home) repo. diff --git a/appveyor.yml b/appveyor.yml new file mode 100644 index 0000000000..636a7618d3 --- /dev/null +++ b/appveyor.yml @@ -0,0 +1,7 @@ +init: + - git config --global core.autocrlf true +build_script: + - build.cmd --quiet verify +clone_depth: 1 +test: off +deploy: off \ No newline at end of file diff --git a/build.cmd b/build.cmd new file mode 100644 index 0000000000..41025afb26 --- /dev/null +++ b/build.cmd @@ -0,0 +1,28 @@ +@echo off +cd %~dp0 + +SETLOCAL +SET CACHED_NUGET=%LocalAppData%\NuGet\NuGet.exe + +IF EXIST %CACHED_NUGET% goto copynuget +echo Downloading latest version of NuGet.exe... +IF NOT EXIST %LocalAppData%\NuGet md %LocalAppData%\NuGet +@powershell -NoProfile -ExecutionPolicy unrestricted -Command "$ProgressPreference = 'SilentlyContinue'; Invoke-WebRequest 'https://www.nuget.org/nuget.exe' -OutFile '%CACHED_NUGET%'" + +:copynuget +IF EXIST .nuget\nuget.exe goto restore +md .nuget +copy %CACHED_NUGET% .nuget\nuget.exe > nul + +:restore +IF EXIST packages\KoreBuild goto run +.nuget\NuGet.exe install KoreBuild -ExcludeVersion -o packages -nocache -pre +.nuget\NuGet.exe install Sake -version 0.2 -o packages -ExcludeVersion + +IF "%SKIP_DNX_INSTALL%"=="1" goto run +CALL packages\KoreBuild\build\dnvm upgrade -runtime CLR -arch x86 +CALL packages\KoreBuild\build\dnvm install default -runtime CoreCLR -arch x86 + +:run +CALL packages\KoreBuild\build\dnvm use default -runtime CLR -arch x86 +packages\Sake\tools\Sake.exe -I packages\KoreBuild\build -f makefile.shade %* diff --git a/build.sh b/build.sh new file mode 100644 index 0000000000..d81164353c --- /dev/null +++ b/build.sh @@ -0,0 +1,39 @@ +#!/bin/bash + +if test `uname` = Darwin; then + cachedir=~/Library/Caches/KBuild +else + if [ -z $XDG_DATA_HOME ]; then + cachedir=$HOME/.local/share + else + cachedir=$XDG_DATA_HOME; + fi +fi +mkdir -p $cachedir + +url=https://www.nuget.org/nuget.exe + +if test ! -f $cachedir/nuget.exe; then + wget -O $cachedir/nuget.exe $url 2>/dev/null || curl -o $cachedir/nuget.exe --location $url /dev/null +fi + +if test ! -e .nuget; then + mkdir .nuget + cp $cachedir/nuget.exe .nuget/nuget.exe +fi + +if test ! -d packages/KoreBuild; then + mono .nuget/nuget.exe install KoreBuild -ExcludeVersion -o packages -nocache -pre + mono .nuget/nuget.exe install Sake -version 0.2 -o packages -ExcludeVersion +fi + +if ! type dnvm > /dev/null 2>&1; then + source packages/KoreBuild/build/dnvm.sh +fi + +if ! type dnx > /dev/null 2>&1; then + dnvm upgrade +fi + +mono packages/Sake/tools/Sake.exe -I packages/KoreBuild/build -f makefile.shade "$@" + diff --git a/makefile.shade b/makefile.shade new file mode 100644 index 0000000000..562494d144 --- /dev/null +++ b/makefile.shade @@ -0,0 +1,7 @@ + +var VERSION='0.1' +var FULL_VERSION='0.1' +var AUTHORS='Microsoft Open Technologies, Inc.' + +use-standard-lifecycle +k-standard-goals diff --git a/src/Microsoft.AspNet.Localization/IApplicationBuilderExtensions.cs b/src/Microsoft.AspNet.Localization/IApplicationBuilderExtensions.cs new file mode 100644 index 0000000000..96330b7bd6 --- /dev/null +++ b/src/Microsoft.AspNet.Localization/IApplicationBuilderExtensions.cs @@ -0,0 +1,24 @@ +// 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.Localization; + +namespace Microsoft.AspNet.Builder +{ + /// + /// Extension methods for adding the to an application. + /// + public static class IApplicationBuilderExtensions + { + /// + /// Adds the to automatically set culture information for + /// requests based on information provided by the client. + /// + /// The . + /// The . + public static IApplicationBuilder UseRequestLocalization(this IApplicationBuilder builder) + { + return builder.UseMiddleware(); + } + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Localization/IRequestCultureFeature.cs b/src/Microsoft.AspNet.Localization/IRequestCultureFeature.cs new file mode 100644 index 0000000000..65417e228d --- /dev/null +++ b/src/Microsoft.AspNet.Localization/IRequestCultureFeature.cs @@ -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. + +namespace Microsoft.AspNet.Localization +{ + /// + /// Represents the feature that provides the current request's culture information. + /// + public interface IRequestCultureFeature + { + /// + /// The of the request. + /// + RequestCulture RequestCulture { get; } + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Localization/Microsoft.AspNet.Localization.xproj b/src/Microsoft.AspNet.Localization/Microsoft.AspNet.Localization.xproj new file mode 100644 index 0000000000..d1edd7f61f --- /dev/null +++ b/src/Microsoft.AspNet.Localization/Microsoft.AspNet.Localization.xproj @@ -0,0 +1,20 @@ + + + + 14.0 + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) + + + + + 23e3bc23-3464-4d9b-bf78-02cb2182bef0 + Microsoft.AspNet.Localization + ..\..\artifacts\obj\$(MSBuildProjectName) + ..\..\artifacts\bin\$(MSBuildProjectName)\ + + + + 2.0 + + + diff --git a/src/Microsoft.AspNet.Localization/RequestCulture.cs b/src/Microsoft.AspNet.Localization/RequestCulture.cs new file mode 100644 index 0000000000..c0dc4a5680 --- /dev/null +++ b/src/Microsoft.AspNet.Localization/RequestCulture.cs @@ -0,0 +1,46 @@ +// 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.Globalization; + +namespace Microsoft.AspNet.Localization +{ + /// + /// Details about the culture for an . + /// + public class RequestCulture + { + /// + /// Creates a new object has its and + /// properties set to the same value. + /// + /// The for the request. + public RequestCulture(CultureInfo culture) + : this (culture, culture) + { + + } + + /// + /// Creates a new object has its and + /// properties set to the respective values provided. + /// + /// The for the request to be used for formatting. + /// The for the request to be used for text, i.e. language. + public RequestCulture(CultureInfo culture, CultureInfo uiCulture) + { + Culture = culture; + UICulture = uiCulture; + } + + /// + /// Gets the for the request to be used for formatting. + /// + public CultureInfo Culture { get; } + + /// + /// Gets the for the request to be used for text, i.e. language; + /// + public CultureInfo UICulture { get; } + } +} diff --git a/src/Microsoft.AspNet.Localization/RequestCultureFeature.cs b/src/Microsoft.AspNet.Localization/RequestCultureFeature.cs new file mode 100644 index 0000000000..730145bd30 --- /dev/null +++ b/src/Microsoft.AspNet.Localization/RequestCultureFeature.cs @@ -0,0 +1,23 @@ +// 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.Localization +{ + /// + /// Provides the current request's culture information. + /// + public class RequestCultureFeature : IRequestCultureFeature + { + /// + /// Creates a new with the specified . + /// + /// The . + public RequestCultureFeature(RequestCulture requestCulture) + { + RequestCulture = requestCulture; + } + + /// + public RequestCulture RequestCulture { get; } + } +} diff --git a/src/Microsoft.AspNet.Localization/RequestLocalizationMiddleware.cs b/src/Microsoft.AspNet.Localization/RequestLocalizationMiddleware.cs new file mode 100644 index 0000000000..3be800652f --- /dev/null +++ b/src/Microsoft.AspNet.Localization/RequestLocalizationMiddleware.cs @@ -0,0 +1,80 @@ +// 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.Globalization; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.AspNet.Builder; +using Microsoft.AspNet.Http; + +namespace Microsoft.AspNet.Localization +{ + /// + /// Enables automatic setting of the culture for s based on information + /// sent by the client in headers and logic provided by the application. + /// + public class RequestLocalizationMiddleware + { + private readonly RequestDelegate _next; + + /// + /// Creates a new . + /// + /// The representing the next middleware in the pipeline. + public RequestLocalizationMiddleware(RequestDelegate next) + { + _next = next; + } + + /// + /// Invokes the logic of the middleware. + /// + /// The . + /// A that completes when the middleware has completed processing. + public async Task Invoke(HttpContext context) + { + // TODO: Make this read from Accept-Language header, cookie, app-provided delegate, etc. + if (context.Request.QueryString.HasValue) + { + var queryCulture = context.Request.Query["culture"]; + if (!string.IsNullOrEmpty(queryCulture)) + { + var requestCulture = new RequestCulture(new CultureInfo(queryCulture)); + + context.SetFeature(new RequestCultureFeature(requestCulture)); + + var originalCulture = CultureInfo.CurrentCulture; + var originalUICulture = CultureInfo.CurrentUICulture; + + SetCurrentCulture(requestCulture); + + await _next(context); + + return; + } + } + else + { + // NOTE: The below doesn't seem to be needed anymore now that DNX is correctly managing culture across + // async calls but we'll need to verify properly. + // Forcibly set thread to en-US as sometimes previous threads have wrong culture across async calls, + // see note above. + //var defaultRequestCulture = new RequestCulture(new CultureInfo("en-US")); + //SetCurrentCulture(defaultRequestCulture); + } + + await _next(context); + } + + private void SetCurrentCulture(RequestCulture requestCulture) + { +#if DNX451 + Thread.CurrentThread.CurrentCulture = requestCulture.Culture; + Thread.CurrentThread.CurrentUICulture = requestCulture.UICulture; +#else + CultureInfo.CurrentCulture = requestCulture.Culture; + CultureInfo.CurrentUICulture = requestCulture.UICulture; +#endif + } + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Localization/project.json b/src/Microsoft.AspNet.Localization/project.json new file mode 100644 index 0000000000..ca6767dc74 --- /dev/null +++ b/src/Microsoft.AspNet.Localization/project.json @@ -0,0 +1,21 @@ +{ + "version": "1.0.0-*", + "description": "", + + "dependencies": { + "Microsoft.Framework.Localization.Abstractions": "1.0.0-*", + "Microsoft.AspNet.Http.Extensions": "1.0.0-*" + }, + + "frameworks": { + "dnx451": { }, + "dnxcore50": { + "dependencies": { + "System.Collections": "4.0.10-*", + "System.Linq": "4.0.0-*", + "System.Threading": "4.0.10-*", + "Microsoft.CSharp": "4.0.0-*" + } + } + } +} diff --git a/src/Microsoft.Framework.Localization.Abstractions/IStringLocalizer.cs b/src/Microsoft.Framework.Localization.Abstractions/IStringLocalizer.cs new file mode 100644 index 0000000000..4dddf75f25 --- /dev/null +++ b/src/Microsoft.Framework.Localization.Abstractions/IStringLocalizer.cs @@ -0,0 +1,51 @@ +// 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; + +namespace Microsoft.Framework.Localization +{ + /// + /// Represents a service that provides localized strings. + /// + public interface IStringLocalizer : IEnumerable + { + /// + /// Gets the string resource with the given name. + /// + /// The name of the string resource. + /// The string resource as a . + LocalizedString this[string name] { get; } + + /// + /// Gets the string resource with the given name and formatted with the supplied arguments. + /// + /// The name of the string resource. + /// The values to format the string with. + /// The formatted string resource as a . + LocalizedString this[string name, params object[] arguments] { get; } + + /// + /// Gets the string resource with the given name. + /// + /// The name of the string resource. + /// The string resource as a . + LocalizedString GetString(string name); + + /// + /// Gets the string resource with the given name and formatted with the supplied arguments. + /// + /// The name of the string resource. + /// The values to format the string with. + /// The formatted string resource as a . + LocalizedString GetString(string name, params object[] arguments); + + /// + /// Creates a new for a specific . + /// + /// The to use. + /// A culture-specific . + IStringLocalizer WithCulture(CultureInfo culture); + } +} \ No newline at end of file diff --git a/src/Microsoft.Framework.Localization.Abstractions/IStringLocalizerFactory.cs b/src/Microsoft.Framework.Localization.Abstractions/IStringLocalizerFactory.cs new file mode 100644 index 0000000000..0902b26277 --- /dev/null +++ b/src/Microsoft.Framework.Localization.Abstractions/IStringLocalizerFactory.cs @@ -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 System; + +namespace Microsoft.Framework.Localization +{ + /// + /// Represents a factory that creates instances. + /// + public interface IStringLocalizerFactory + { + /// + /// Creates an using the and + /// of the specified . + /// + /// The . + /// The . + IStringLocalizer Create(Type resourceSource); + + /// + /// Creates an . + /// + /// The base name of the resource to load strings from. + /// The location to load resources from. + /// The . + IStringLocalizer Create(string baseName, string location); + } +} \ No newline at end of file diff --git a/src/Microsoft.Framework.Localization.Abstractions/IStringLocalizerOfT.cs b/src/Microsoft.Framework.Localization.Abstractions/IStringLocalizerOfT.cs new file mode 100644 index 0000000000..f81a3c0eaf --- /dev/null +++ b/src/Microsoft.Framework.Localization.Abstractions/IStringLocalizerOfT.cs @@ -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 System; + +namespace Microsoft.Framework.Localization +{ + /// + /// Represents an that provides strings for . + /// + /// The to provide strings for. + public interface IStringLocalizer : IStringLocalizer + { + + } +} \ No newline at end of file diff --git a/src/Microsoft.Framework.Localization.Abstractions/LocalizedString.cs b/src/Microsoft.Framework.Localization.Abstractions/LocalizedString.cs new file mode 100644 index 0000000000..4faf864309 --- /dev/null +++ b/src/Microsoft.Framework.Localization.Abstractions/LocalizedString.cs @@ -0,0 +1,61 @@ +// 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.Framework.Localization +{ + /// + /// A locale specific string. + /// + public struct LocalizedString + { + /// + /// Creates a new . + /// + /// The name of the string in the resource it was loaded from. + /// The actual string. + public LocalizedString(string name, string value) + : this(name, value, resourceNotFound: false) + { + + } + + /// + /// Creates a new . + /// + /// The name of the string in the resource it was loaded from. + /// The actual string. + /// Whether the string was found in a resource. Set this to false to indicate an alternate string value was used. + public LocalizedString(string name, string value, bool resourceNotFound) + { + Name = name; + Value = value; + ResourceNotFound = resourceNotFound; + } + + public static implicit operator string (LocalizedString localizedString) + { + return localizedString.Value; + } + + /// + /// The name of the string in the resource it was loaded from. + /// + public string Name { get; } + + /// + /// The actual string. + /// + public string Value { get; } + + /// + /// Whether the string was found in a resource. If false, an alternate string value was used. + /// + public bool ResourceNotFound { get; } + + /// + /// Returns the actual string. + /// + /// The actual string. + public override string ToString() => Value; + } +} \ No newline at end of file diff --git a/src/Microsoft.Framework.Localization.Abstractions/Microsoft.Framework.Localization.Abstractions.xproj b/src/Microsoft.Framework.Localization.Abstractions/Microsoft.Framework.Localization.Abstractions.xproj new file mode 100644 index 0000000000..7e088aa5ce --- /dev/null +++ b/src/Microsoft.Framework.Localization.Abstractions/Microsoft.Framework.Localization.Abstractions.xproj @@ -0,0 +1,20 @@ + + + + 14.0 + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) + + + + + a1fcf259-70f6-4605-aa2d-e4b356be771a + Microsoft.Framework.Localization.Abstractions + ..\..\artifacts\obj\$(MSBuildProjectName) + ..\..\artifacts\bin\$(MSBuildProjectName)\ + + + + 2.0 + + + diff --git a/src/Microsoft.Framework.Localization.Abstractions/StringLocalizerOfT.cs b/src/Microsoft.Framework.Localization.Abstractions/StringLocalizerOfT.cs new file mode 100644 index 0000000000..f222bf93b3 --- /dev/null +++ b/src/Microsoft.Framework.Localization.Abstractions/StringLocalizerOfT.cs @@ -0,0 +1,48 @@ +// 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; +using System.Collections.Generic; +using System.Globalization; + +namespace Microsoft.Framework.Localization +{ + /// + /// Provides strings for . + /// + /// The to provide strings for. + public class StringLocalizer : IStringLocalizer + { + private IStringLocalizer _localizer; + + /// + /// Creates a new . + /// + /// The to use. + public StringLocalizer(IStringLocalizerFactory factory) + { + _localizer = factory.Create(typeof(TResourceSource)); + } + + /// + public virtual IStringLocalizer WithCulture(CultureInfo culture) => _localizer.WithCulture(culture); + + /// + public virtual LocalizedString this[string key] => _localizer[key]; + + /// + public virtual LocalizedString this[string key, params object[] arguments] => _localizer[key, arguments]; + + /// + public virtual LocalizedString GetString(string key) => _localizer.GetString(key); + + /// + public virtual LocalizedString GetString(string key, params object[] arguments) => + _localizer.GetString(key, arguments); + + /// + public IEnumerator GetEnumerator() => _localizer.GetEnumerator(); + + IEnumerator IEnumerable.GetEnumerator() => _localizer.GetEnumerator(); + } +} \ No newline at end of file diff --git a/src/Microsoft.Framework.Localization.Abstractions/project.json b/src/Microsoft.Framework.Localization.Abstractions/project.json new file mode 100644 index 0000000000..87aa883bfd --- /dev/null +++ b/src/Microsoft.Framework.Localization.Abstractions/project.json @@ -0,0 +1,21 @@ +{ + "version": "1.0.0-*", + "description": "Abstractions of application localization services.", + + "dependencies": { + + }, + + "frameworks": { + "dnx451": { }, + "dnxcore50": { + "dependencies": { + "System.Collections": "4.0.10-beta-*", + "System.Globalization": "4.0.10-beta-*", + "System.Reflection": "4.0.10-beta-*", + "System.Runtime": "4.0.20-beta-22904", + "Microsoft.CSharp": "4.0.0-beta-*" + } + } + } +} diff --git a/src/Microsoft.Framework.Localization/IServiceCollectionExtensions.cs b/src/Microsoft.Framework.Localization/IServiceCollectionExtensions.cs new file mode 100644 index 0000000000..9b4a0079fc --- /dev/null +++ b/src/Microsoft.Framework.Localization/IServiceCollectionExtensions.cs @@ -0,0 +1,32 @@ +// 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.Framework.Localization; + +namespace Microsoft.Framework.DependencyInjection +{ + /// + /// Extension methods for adding localization servics to the DI container. + /// + public static class LocalizationServiceCollectionExtensions + { + /// + /// Adds services required for application localization. + /// + /// The to add the services to. + /// The . + public static IServiceCollection AddLocalization(this IServiceCollection services) + { + services.TryAdd(new ServiceDescriptor( + typeof(IStringLocalizerFactory), + typeof(ResourceManagerStringLocalizerFactory), + ServiceLifetime.Singleton)); + services.TryAdd(new ServiceDescriptor( + typeof(IStringLocalizer<>), + typeof(StringLocalizer<>), + ServiceLifetime.Transient)); + + return services; + } + } +} \ No newline at end of file diff --git a/src/Microsoft.Framework.Localization/Microsoft.Framework.Localization.xproj b/src/Microsoft.Framework.Localization/Microsoft.Framework.Localization.xproj new file mode 100644 index 0000000000..487327e0ee --- /dev/null +++ b/src/Microsoft.Framework.Localization/Microsoft.Framework.Localization.xproj @@ -0,0 +1,20 @@ + + + + 14.0 + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) + + + + + 29743cff-120e-40eb-9b89-5818425946fa + Microsoft.Framework.Localization + ..\..\artifacts\obj\$(MSBuildProjectName) + ..\..\artifacts\bin\$(MSBuildProjectName)\ + + + + 2.0 + + + diff --git a/src/Microsoft.Framework.Localization/ResourceManagerStringLocalizer.cs b/src/Microsoft.Framework.Localization/ResourceManagerStringLocalizer.cs new file mode 100644 index 0000000000..b6db0b6d13 --- /dev/null +++ b/src/Microsoft.Framework.Localization/ResourceManagerStringLocalizer.cs @@ -0,0 +1,226 @@ +// 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; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Globalization; +using System.Reflection; +using System.Resources; + +namespace Microsoft.Framework.Localization +{ + /// + /// An that uses the and + /// to provide localized strings. + /// + public class ResourceManagerStringLocalizer : IStringLocalizer + { + private readonly ConcurrentDictionary _missingManifestCache = + new ConcurrentDictionary(); + + /// + /// Creates a new . + /// + /// The to read strings from. + /// The that contains the strings as embedded resources. + /// The base name of the embedded resource in the that contains the strings. + public ResourceManagerStringLocalizer( + ResourceManager resourceManager, + Assembly resourceAssembly, + string baseName) + { + ResourceManager = resourceManager; + ResourceAssembly = resourceAssembly; + ResourceBaseName = baseName; + } + + /// + /// The to read strings from. + /// + protected ResourceManager ResourceManager { get; } + + /// + /// The that contains the strings as embedded resources. + /// + protected Assembly ResourceAssembly { get; } + + /// + /// The base name of the embedded resource in the that contains the strings. + /// + protected string ResourceBaseName { get; } + + /// + public virtual LocalizedString this[string name] => GetString(name); + + /// + public virtual LocalizedString this[string name, params object[] arguments] => GetString(name, arguments); + + /// + public virtual LocalizedString GetString(string name) + { + var value = GetStringSafely(name, null); + return new LocalizedString(name, value ?? name, resourceNotFound: value == null); + } + + /// + public virtual LocalizedString GetString(string name, params object[] arguments) + { + var format = GetStringSafely(name, null); + var value = string.Format(format ?? name, arguments); + return new LocalizedString(name, value, resourceNotFound: format == null); + } + + /// + /// Creates a new for a specific . + /// + /// The to use. + /// A culture-specific . + public IStringLocalizer WithCulture(CultureInfo culture) + { + return culture == null + ? new ResourceManagerStringLocalizer(ResourceManager, ResourceAssembly, ResourceBaseName) + : new ResourceManagerWithCultureStringLocalizer( + ResourceManager, + ResourceAssembly, + ResourceBaseName, + culture); + } + + /// + /// Gets a resource string from the and returns null instead of + /// throwing exceptions if a match isn't found. + /// + /// The name of the string resource. + /// The to get the string for. + /// The resource string, or null if none was found. + protected string GetStringSafely(string name, CultureInfo culture) + { + var cacheKey = new MissingManifestCacheKey(name, culture); + if (_missingManifestCache.ContainsKey(cacheKey)) + { + return null; + } + + try + { + return culture == null ? ResourceManager.GetString(name) : ResourceManager.GetString(name, culture); + } + catch (MissingManifestResourceException) + { + _missingManifestCache.TryAdd(cacheKey, null); + return null; + } + } + + /// + /// Returns an for all strings in the current culture. + /// + /// The . + public virtual IEnumerator GetEnumerator() + { + return GetEnumerator(CultureInfo.CurrentUICulture); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + /// + /// Returns an for all strings in the specified culture. + /// + /// The to get strings for. + /// The . + protected IEnumerator GetEnumerator(CultureInfo culture) + { + // TODO: I'm sure something here should be cached, probably the whole result + var resourceNames = GetResourceNamesFromCultureHierarchy(culture); + + foreach (var name in resourceNames) + { + var value = GetStringSafely(name, culture); + yield return new LocalizedString(name, value ?? name, resourceNotFound: value == null); + } + } + + private IEnumerable GetResourceNamesFromCultureHierarchy(CultureInfo startingCulture) + { + var currentCulture = startingCulture; + var resourceNames = new HashSet(); + + while (true) + { + try + { + var resourceStreamName = ResourceBaseName; + if (!string.IsNullOrEmpty(currentCulture.Name)) + { + resourceStreamName += "." + currentCulture.Name; + } + resourceStreamName += ".resources"; + using (var cultureResourceStream = ResourceAssembly.GetManifestResourceStream(resourceStreamName)) + using (var resources = new ResourceReader(cultureResourceStream)) + { + foreach (DictionaryEntry entry in resources) + { + var resourceName = (string)entry.Key; + resourceNames.Add(resourceName); + } + } + } + catch (MissingManifestResourceException) { } + + if (currentCulture == currentCulture.Parent) + { + // currentCulture begat currentCulture, probably time to leave + break; + } + + currentCulture = currentCulture.Parent; + } + + return resourceNames; + } + + private class MissingManifestCacheKey : IEquatable + { + private readonly int _hashCode; + + public MissingManifestCacheKey(string name, CultureInfo culture) + { + Name = name; + CultureInfo = culture; + _hashCode = new { Name, CultureInfo }.GetHashCode(); + } + + public string Name { get; } + + public CultureInfo CultureInfo { get; } + + public bool Equals(MissingManifestCacheKey other) + { + return string.Equals(Name, other.Name, StringComparison.Ordinal) + && CultureInfo == other.CultureInfo; + } + + public override bool Equals(object obj) + { + var other = obj as MissingManifestCacheKey; + + if (other != null) + { + return Equals(other); + } + + return base.Equals(obj); + } + + public override int GetHashCode() + { + return _hashCode; + } + } + } +} \ No newline at end of file diff --git a/src/Microsoft.Framework.Localization/ResourceManagerStringLocalizerFactory.cs b/src/Microsoft.Framework.Localization/ResourceManagerStringLocalizerFactory.cs new file mode 100644 index 0000000000..801f6acef5 --- /dev/null +++ b/src/Microsoft.Framework.Localization/ResourceManagerStringLocalizerFactory.cs @@ -0,0 +1,54 @@ +// 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.Reflection; +using System.Resources; +using Microsoft.Framework.Runtime; + +namespace Microsoft.Framework.Localization +{ + /// + /// An that creates instances of . + /// + public class ResourceManagerStringLocalizerFactory : IStringLocalizerFactory + { + private readonly IApplicationEnvironment _applicationEnvironment; + + /// + /// Creates a new . + /// + /// + public ResourceManagerStringLocalizerFactory(IApplicationEnvironment applicationEnvironment) + { + _applicationEnvironment = applicationEnvironment; + } + + /// + /// Creates a using the and + /// of the specified . + /// + /// The . + /// The . + public IStringLocalizer Create(Type resourceSource) + { + var typeInfo = resourceSource.GetTypeInfo(); + var assembly = typeInfo.Assembly; + var baseName = typeInfo.FullName; + return new ResourceManagerStringLocalizer(new ResourceManager(resourceSource), assembly, baseName); + } + + /// + /// Creates a . + /// + /// The base name of the resource to load strings from. + /// The location to load resources from. + /// The . + public IStringLocalizer Create(string baseName, string location) + { + var assembly = Assembly.Load(new AssemblyName(location ?? _applicationEnvironment.ApplicationName)); + + return new ResourceManagerStringLocalizer(new ResourceManager(baseName, assembly), assembly, baseName); + } + } +} \ No newline at end of file diff --git a/src/Microsoft.Framework.Localization/ResourceManagerWithCultureStringLocalizer.cs b/src/Microsoft.Framework.Localization/ResourceManagerWithCultureStringLocalizer.cs new file mode 100644 index 0000000000..7b988b04ba --- /dev/null +++ b/src/Microsoft.Framework.Localization/ResourceManagerWithCultureStringLocalizer.cs @@ -0,0 +1,64 @@ +// 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.Globalization; +using System.Reflection; +using System.Resources; + +namespace Microsoft.Framework.Localization +{ + /// + /// An that uses the and + /// to provide localized strings for a specific . + /// + public class ResourceManagerWithCultureStringLocalizer : ResourceManagerStringLocalizer + { + private readonly CultureInfo _culture; + + /// + /// Creates a new . + /// + /// The to read strings from. + /// The that contains the strings as embedded resources. + /// The base name of the embedded resource in the that contains the strings. + /// The specific to use. + public ResourceManagerWithCultureStringLocalizer( + ResourceManager resourceManager, + Assembly assembly, + string baseName, + CultureInfo culture) + : base(resourceManager, assembly, baseName) + { + _culture = culture; + } + + /// + public override LocalizedString this[string name] => GetString(name); + + /// + public override LocalizedString this[string name, params object[] arguments] => GetString(name, arguments); + + /// + public override LocalizedString GetString(string name) + { + var value = GetStringSafely(name, _culture); + return new LocalizedString(name, value ?? name); + } + + /// + public override LocalizedString GetString(string name, params object[] arguments) + { + var format = GetStringSafely(name, _culture); + var value = string.Format(_culture, format ?? name, arguments); + return new LocalizedString(name, value ?? name, resourceNotFound: format == null); + } + + /// + public override IEnumerator GetEnumerator() + { + return GetEnumerator(_culture); + } + } +} \ No newline at end of file diff --git a/src/Microsoft.Framework.Localization/project.json b/src/Microsoft.Framework.Localization/project.json new file mode 100644 index 0000000000..32c9bd7b33 --- /dev/null +++ b/src/Microsoft.Framework.Localization/project.json @@ -0,0 +1,26 @@ +{ + "version": "1.0.0-*", + "description": "", + + "dependencies": { + "Microsoft.Framework.DependencyInjection.Abstractions": "1.0.0-*", + "Microsoft.Framework.Localization.Abstractions": "1.0.0-*", + "Microsoft.Framework.Runtime.Abstractions": "1.0.0-*", + "System.Resources.ReaderWriter": "4.0.0-*" + }, + + "frameworks": { + "dnx451": { }, + "dnxcore50": { + "dependencies": { + "System.Collections": "4.0.10-*", + "System.Collections.Concurrent": "4.0.10-*", + "System.Globalization": "4.0.10-*", + "System.Linq": "4.0.0-*", + "System.Resources.ResourceManager": "4.0.0-*", + "System.Threading": "4.0.10-*", + "Microsoft.CSharp": "4.0.0-*" + } + } + } +} From 52f7aa954040a4e7aded33f73d29aa68dd7874cb Mon Sep 17 00:00:00 2001 From: damianedwards Date: Tue, 5 May 2015 23:32:44 -0700 Subject: [PATCH 002/390] Rename file --- ...onExtensions.cs => LocalizationServiceCollectionExtensions.cs} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/Microsoft.Framework.Localization/{IServiceCollectionExtensions.cs => LocalizationServiceCollectionExtensions.cs} (100%) diff --git a/src/Microsoft.Framework.Localization/IServiceCollectionExtensions.cs b/src/Microsoft.Framework.Localization/LocalizationServiceCollectionExtensions.cs similarity index 100% rename from src/Microsoft.Framework.Localization/IServiceCollectionExtensions.cs rename to src/Microsoft.Framework.Localization/LocalizationServiceCollectionExtensions.cs From acd643660aad4841216af62f65a790cf97031c59 Mon Sep 17 00:00:00 2001 From: damianedwards Date: Wed, 6 May 2015 10:53:25 -0700 Subject: [PATCH 003/390] Added missing descriptions --- src/Microsoft.AspNet.Localization/project.json | 2 +- src/Microsoft.Framework.Localization/project.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Microsoft.AspNet.Localization/project.json b/src/Microsoft.AspNet.Localization/project.json index ca6767dc74..7561196f6f 100644 --- a/src/Microsoft.AspNet.Localization/project.json +++ b/src/Microsoft.AspNet.Localization/project.json @@ -1,6 +1,6 @@ { "version": "1.0.0-*", - "description": "", + "description": "Middleware for automatically applying culture information to HTTP requests.", "dependencies": { "Microsoft.Framework.Localization.Abstractions": "1.0.0-*", diff --git a/src/Microsoft.Framework.Localization/project.json b/src/Microsoft.Framework.Localization/project.json index 32c9bd7b33..39a74a1e5a 100644 --- a/src/Microsoft.Framework.Localization/project.json +++ b/src/Microsoft.Framework.Localization/project.json @@ -1,6 +1,6 @@ { "version": "1.0.0-*", - "description": "", + "description": "Application localization services.", "dependencies": { "Microsoft.Framework.DependencyInjection.Abstractions": "1.0.0-*", From 5795d331bc37e2f97b14a46aa120d06cf518298d Mon Sep 17 00:00:00 2001 From: Damian Edwards Date: Wed, 6 May 2015 11:12:34 -0700 Subject: [PATCH 004/390] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index c0250d425f..d9df9e9182 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,8 @@ Localization ========== -AppVeyor: TODO +AppVeyor: [![AppVeyor](https://ci.appveyor.com/api/projects/status/omn0l2l3mfhfjjpp?svg=true)](https://ci.appveyor.com/project/aspnetci/Localization/branch/dev) -Travis: TODO +Travis: [![Travis](https://travis-ci.org/aspnet/Localization.svg?branch=dev)](https://travis-ci.org/aspnet/Localization) Localization abstractions and implementations for ASP.NET 5 applications. From b11f7d51c3959181622b95750dcf010ca415948b Mon Sep 17 00:00:00 2001 From: damianedwards Date: Wed, 6 May 2015 16:50:43 -0700 Subject: [PATCH 005/390] Bunch of changes: - Implemented pluggable strategies for determining request culture - Added NotNull on public APIs - Added support for a default request culture - Added options class for configuring the middleware - Improved the query string logic to support separate formatting & language cultures - Implemented the logic for accept-language header - Added more doc comments --- ...eptLanguageHeaderRequestCultureStrategy.cs | 67 ++++++++++++++++ .../CookieRequestCultureStrategy.cs | 18 +++++ .../CustomRequestCultureStrategy.cs | 24 ++++++ .../IApplicationBuilderExtensions.cs | 23 +++++- .../IRequestCultureFeature.cs | 7 ++ .../IRequestCultureStrategy.cs | 12 +++ .../Internal/CultureUtilities.cs | 29 +++++++ .../QueryStringRequestCultureStrategy.cs | 67 ++++++++++++++++ .../RequestCulture.cs | 5 +- .../RequestCultureFeature.cs | 8 +- .../RequestLocalizationMiddleware.cs | 55 +++++++------- .../RequestLocalizationMiddlewareOptions.cs | 76 +++++++++++++++++++ .../project.json | 2 + .../LocalizedString.cs | 6 +- .../StringLocalizerOfT.cs | 12 +-- .../project.json | 2 +- ...LocalizationServiceCollectionExtensions.cs | 3 +- .../ResourceManagerStringLocalizer.cs | 19 ++--- .../ResourceManagerStringLocalizerFactory.cs | 7 +- ...sourceManagerWithCultureStringLocalizer.cs | 17 +++-- .../project.json | 1 + 21 files changed, 396 insertions(+), 64 deletions(-) create mode 100644 src/Microsoft.AspNet.Localization/AcceptLanguageHeaderRequestCultureStrategy.cs create mode 100644 src/Microsoft.AspNet.Localization/CookieRequestCultureStrategy.cs create mode 100644 src/Microsoft.AspNet.Localization/CustomRequestCultureStrategy.cs create mode 100644 src/Microsoft.AspNet.Localization/IRequestCultureStrategy.cs create mode 100644 src/Microsoft.AspNet.Localization/Internal/CultureUtilities.cs create mode 100644 src/Microsoft.AspNet.Localization/QueryStringRequestCultureStrategy.cs create mode 100644 src/Microsoft.AspNet.Localization/RequestLocalizationMiddlewareOptions.cs diff --git a/src/Microsoft.AspNet.Localization/AcceptLanguageHeaderRequestCultureStrategy.cs b/src/Microsoft.AspNet.Localization/AcceptLanguageHeaderRequestCultureStrategy.cs new file mode 100644 index 0000000000..1a90e5332a --- /dev/null +++ b/src/Microsoft.AspNet.Localization/AcceptLanguageHeaderRequestCultureStrategy.cs @@ -0,0 +1,67 @@ +// 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.Globalization; +using System.Linq; +using Microsoft.AspNet.Http; +using Microsoft.AspNet.Localization.Internal; +using Microsoft.Framework.Internal; +using Microsoft.Net.Http.Headers; + +namespace Microsoft.AspNet.Localization +{ + /// + /// Determines the culture information for a request via the value of the Accept-Language header. + /// + /// + /// + /// + public class AcceptLanguageHeaderRequestCultureStrategy : IRequestCultureStrategy + { + /// + /// The maximum number of values in the Accept-Language header to attempt to create a + /// from for the current request. + /// Defaults to 3. + /// + public int MaximumAcceptLanguageHeaderValuesToTry { get; set; } = 3; + + /// + public RequestCulture DetermineRequestCulture([NotNull] HttpContext httpContext) + { + var acceptLanguageHeader = httpContext.Request.GetTypedHeaders().AcceptLanguage; + + if (acceptLanguageHeader == null || acceptLanguageHeader.Count == 0) + { + return null; + } + + var languages = acceptLanguageHeader.AsEnumerable(); + + if (MaximumAcceptLanguageHeaderValuesToTry > 0) + { + // We take only the first configured number of languages from the header and then order those that we + // attempt to parse as a CultureInfo to mitigate potentially spinning CPU on lots of parse attempts. + languages = languages.Take(MaximumAcceptLanguageHeaderValuesToTry); + } + + var orderedLanguages = languages.OrderByDescending(h => h, StringWithQualityHeaderValueComparer.QualityComparer) + .ToList(); + + foreach (var language in orderedLanguages) + { + // Allow empty string values as they map to InvariantCulture, whereas null culture values will throw in + // the CultureInfo ctor + if (language.Value != null) + { + try + { + return new RequestCulture(CultureUtilities.GetCultureFromName(language.Value)); + } + catch (CultureNotFoundException) { } + } + } + + return null; + } + } +} diff --git a/src/Microsoft.AspNet.Localization/CookieRequestCultureStrategy.cs b/src/Microsoft.AspNet.Localization/CookieRequestCultureStrategy.cs new file mode 100644 index 0000000000..902d5819dd --- /dev/null +++ b/src/Microsoft.AspNet.Localization/CookieRequestCultureStrategy.cs @@ -0,0 +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 Microsoft.AspNet.Http; +using Microsoft.Framework.Internal; + +namespace Microsoft.AspNet.Localization +{ + public class CookieRequestCultureStrategy : IRequestCultureStrategy + { + public RequestCulture DetermineRequestCulture([NotNull] HttpContext httpContext) + { + // TODO + return null; + } + } +} diff --git a/src/Microsoft.AspNet.Localization/CustomRequestCultureStrategy.cs b/src/Microsoft.AspNet.Localization/CustomRequestCultureStrategy.cs new file mode 100644 index 0000000000..629862c1e5 --- /dev/null +++ b/src/Microsoft.AspNet.Localization/CustomRequestCultureStrategy.cs @@ -0,0 +1,24 @@ +// 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 Microsoft.AspNet.Http; +using Microsoft.Framework.Internal; + +namespace Microsoft.AspNet.Localization +{ + public class CustomRequestCultureStrategy : IRequestCultureStrategy + { + private readonly Func _strategy; + + public CustomRequestCultureStrategy([NotNull] Func strategy) + { + _strategy = strategy; + } + + public RequestCulture DetermineRequestCulture([NotNull] HttpContext httpContext) + { + return _strategy(httpContext); + } + } +} diff --git a/src/Microsoft.AspNet.Localization/IApplicationBuilderExtensions.cs b/src/Microsoft.AspNet.Localization/IApplicationBuilderExtensions.cs index 96330b7bd6..d3d7a65077 100644 --- a/src/Microsoft.AspNet.Localization/IApplicationBuilderExtensions.cs +++ b/src/Microsoft.AspNet.Localization/IApplicationBuilderExtensions.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using Microsoft.AspNet.Localization; +using Microsoft.Framework.Internal; namespace Microsoft.AspNet.Builder { @@ -12,13 +13,29 @@ namespace Microsoft.AspNet.Builder { /// /// Adds the to automatically set culture information for - /// requests based on information provided by the client. + /// requests based on information provided by the client using the default options. /// /// The . /// The . - public static IApplicationBuilder UseRequestLocalization(this IApplicationBuilder builder) + public static IApplicationBuilder UseRequestLocalization([NotNull] this IApplicationBuilder builder) { - return builder.UseMiddleware(); + var options = new RequestLocalizationMiddlewareOptions(); + + return UseRequestLocalization(builder, options); + } + + /// + /// Adds the to automatically set culture information for + /// requests based on information provided by the client. + /// + /// The . + /// The options to configure the middleware with. + /// The . + public static IApplicationBuilder UseRequestLocalization( + [NotNull] this IApplicationBuilder builder, + [NotNull] RequestLocalizationMiddlewareOptions options) + { + return builder.UseMiddleware(options); } } } \ No newline at end of file diff --git a/src/Microsoft.AspNet.Localization/IRequestCultureFeature.cs b/src/Microsoft.AspNet.Localization/IRequestCultureFeature.cs index 65417e228d..73cd046f50 100644 --- a/src/Microsoft.AspNet.Localization/IRequestCultureFeature.cs +++ b/src/Microsoft.AspNet.Localization/IRequestCultureFeature.cs @@ -12,5 +12,12 @@ namespace Microsoft.AspNet.Localization /// The of the request. /// RequestCulture RequestCulture { get; } + + /// + /// The that determined the request's culture information. + /// If the value is null then no strategy was used and the request's culture was set to the value of + /// . + /// + IRequestCultureStrategy Strategy { get; } } } \ No newline at end of file diff --git a/src/Microsoft.AspNet.Localization/IRequestCultureStrategy.cs b/src/Microsoft.AspNet.Localization/IRequestCultureStrategy.cs new file mode 100644 index 0000000000..93f943488b --- /dev/null +++ b/src/Microsoft.AspNet.Localization/IRequestCultureStrategy.cs @@ -0,0 +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 Microsoft.AspNet.Http; + +namespace Microsoft.AspNet.Localization +{ + public interface IRequestCultureStrategy + { + RequestCulture DetermineRequestCulture(HttpContext httpContext); + } +} diff --git a/src/Microsoft.AspNet.Localization/Internal/CultureUtilities.cs b/src/Microsoft.AspNet.Localization/Internal/CultureUtilities.cs new file mode 100644 index 0000000000..bea6d2145d --- /dev/null +++ b/src/Microsoft.AspNet.Localization/Internal/CultureUtilities.cs @@ -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 System.Globalization; + +namespace Microsoft.AspNet.Localization.Internal +{ + public static class CultureUtilities + { + public static CultureInfo GetCultureFromName(string cultureName) + { + // Allow empty string values as they map to InvariantCulture, whereas null culture values will throw in + // the CultureInfo ctor + if (cultureName == null) + { + return null; + } + + try + { + return new CultureInfo(cultureName); + } + catch (CultureNotFoundException) + { + return null; + } + } + } +} diff --git a/src/Microsoft.AspNet.Localization/QueryStringRequestCultureStrategy.cs b/src/Microsoft.AspNet.Localization/QueryStringRequestCultureStrategy.cs new file mode 100644 index 0000000000..c7cfdb77e7 --- /dev/null +++ b/src/Microsoft.AspNet.Localization/QueryStringRequestCultureStrategy.cs @@ -0,0 +1,67 @@ +// 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.Http; +using Microsoft.AspNet.Localization.Internal; +using Microsoft.Framework.Internal; + +namespace Microsoft.AspNet.Localization +{ + /// + /// Determines the culture information for a request via values in the query string. + /// + public class QueryStringRequestCultureStrategy : IRequestCultureStrategy + { + /// + /// The key that contains the culture name. + /// Defaults to "culture". + /// + public string QueryStringKey { get; set; } = "culture"; + + /// + /// The key that contains the UI culture name. If not specified or no value is found, + /// will be used. + /// Defaults to "ui-culture". + /// + public string UIQueryStringKey { get; set; } = "ui-culture"; + + /// + public RequestCulture DetermineRequestCulture([NotNull] HttpContext httpContext) + { + var request = httpContext.Request; + if (!request.QueryString.HasValue) + { + return null; + } + + string queryCulture = null; + string queryUICulture = null; + + if (!string.IsNullOrWhiteSpace(QueryStringKey)) + { + queryCulture = request.Query[QueryStringKey]; + } + + if (!string.IsNullOrWhiteSpace(UIQueryStringKey)) + { + queryUICulture = request.Query[UIQueryStringKey]; + } + + if (queryCulture == null && queryUICulture == null) + { + // No values specified for either so no match + return null; + } + + if (queryCulture != null && queryUICulture == null) + { + // Value for culture but not for UI culture so default to culture value for both + queryUICulture = queryCulture; + } + + return new RequestCulture( + CultureUtilities.GetCultureFromName(queryCulture), + CultureUtilities.GetCultureFromName(queryUICulture)); + } + } +} diff --git a/src/Microsoft.AspNet.Localization/RequestCulture.cs b/src/Microsoft.AspNet.Localization/RequestCulture.cs index c0dc4a5680..ec109e6d8e 100644 --- a/src/Microsoft.AspNet.Localization/RequestCulture.cs +++ b/src/Microsoft.AspNet.Localization/RequestCulture.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.Globalization; +using Microsoft.Framework.Internal; namespace Microsoft.AspNet.Localization { @@ -15,7 +16,7 @@ namespace Microsoft.AspNet.Localization /// properties set to the same value. /// /// The for the request. - public RequestCulture(CultureInfo culture) + public RequestCulture([NotNull] CultureInfo culture) : this (culture, culture) { @@ -27,7 +28,7 @@ namespace Microsoft.AspNet.Localization /// /// The for the request to be used for formatting. /// The for the request to be used for text, i.e. language. - public RequestCulture(CultureInfo culture, CultureInfo uiCulture) + public RequestCulture([NotNull] CultureInfo culture, [NotNull] CultureInfo uiCulture) { Culture = culture; UICulture = uiCulture; diff --git a/src/Microsoft.AspNet.Localization/RequestCultureFeature.cs b/src/Microsoft.AspNet.Localization/RequestCultureFeature.cs index 730145bd30..76505b4759 100644 --- a/src/Microsoft.AspNet.Localization/RequestCultureFeature.cs +++ b/src/Microsoft.AspNet.Localization/RequestCultureFeature.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 Microsoft.Framework.Internal; + namespace Microsoft.AspNet.Localization { /// @@ -12,12 +14,16 @@ namespace Microsoft.AspNet.Localization /// Creates a new with the specified . /// /// The . - public RequestCultureFeature(RequestCulture requestCulture) + public RequestCultureFeature([NotNull] RequestCulture requestCulture, IRequestCultureStrategy strategy) { RequestCulture = requestCulture; + Strategy = strategy; } /// public RequestCulture RequestCulture { get; } + + /// + public IRequestCultureStrategy Strategy { get; } } } diff --git a/src/Microsoft.AspNet.Localization/RequestLocalizationMiddleware.cs b/src/Microsoft.AspNet.Localization/RequestLocalizationMiddleware.cs index 3be800652f..2064690662 100644 --- a/src/Microsoft.AspNet.Localization/RequestLocalizationMiddleware.cs +++ b/src/Microsoft.AspNet.Localization/RequestLocalizationMiddleware.cs @@ -6,6 +6,7 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.AspNet.Builder; using Microsoft.AspNet.Http; +using Microsoft.Framework.Internal; namespace Microsoft.AspNet.Localization { @@ -16,14 +17,17 @@ namespace Microsoft.AspNet.Localization public class RequestLocalizationMiddleware { private readonly RequestDelegate _next; - + private readonly RequestLocalizationMiddlewareOptions _options; + /// /// Creates a new . /// /// The representing the next middleware in the pipeline. - public RequestLocalizationMiddleware(RequestDelegate next) + /// + public RequestLocalizationMiddleware([NotNull] RequestDelegate next, [NotNull] RequestLocalizationMiddlewareOptions options) { _next = next; + _options = options; } /// @@ -31,42 +35,35 @@ namespace Microsoft.AspNet.Localization /// /// The . /// A that completes when the middleware has completed processing. - public async Task Invoke(HttpContext context) + public async Task Invoke([NotNull] HttpContext context) { - // TODO: Make this read from Accept-Language header, cookie, app-provided delegate, etc. - if (context.Request.QueryString.HasValue) + var requestCulture = _options.DefaultRequestCulture ?? + new RequestCulture(CultureInfo.DefaultThreadCurrentCulture, CultureInfo.DefaultThreadCurrentUICulture); + + IRequestCultureStrategy winningStrategy = null; + + if (_options.RequestCultureStrategies != null) { - var queryCulture = context.Request.Query["culture"]; - if (!string.IsNullOrEmpty(queryCulture)) + foreach (var strategy in _options.RequestCultureStrategies) { - var requestCulture = new RequestCulture(new CultureInfo(queryCulture)); - - context.SetFeature(new RequestCultureFeature(requestCulture)); - - var originalCulture = CultureInfo.CurrentCulture; - var originalUICulture = CultureInfo.CurrentUICulture; - - SetCurrentCulture(requestCulture); - - await _next(context); - - return; + var result = strategy.DetermineRequestCulture(context); + if (result != null) + { + requestCulture = result; + winningStrategy = strategy; + break; + } } } - else - { - // NOTE: The below doesn't seem to be needed anymore now that DNX is correctly managing culture across - // async calls but we'll need to verify properly. - // Forcibly set thread to en-US as sometimes previous threads have wrong culture across async calls, - // see note above. - //var defaultRequestCulture = new RequestCulture(new CultureInfo("en-US")); - //SetCurrentCulture(defaultRequestCulture); - } + + context.SetFeature(new RequestCultureFeature(requestCulture, winningStrategy)); + + SetCurrentThreadCulture(requestCulture); await _next(context); } - private void SetCurrentCulture(RequestCulture requestCulture) + private static void SetCurrentThreadCulture(RequestCulture requestCulture) { #if DNX451 Thread.CurrentThread.CurrentCulture = requestCulture.Culture; diff --git a/src/Microsoft.AspNet.Localization/RequestLocalizationMiddlewareOptions.cs b/src/Microsoft.AspNet.Localization/RequestLocalizationMiddlewareOptions.cs new file mode 100644 index 0000000000..cfa8e6e196 --- /dev/null +++ b/src/Microsoft.AspNet.Localization/RequestLocalizationMiddlewareOptions.cs @@ -0,0 +1,76 @@ +// 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.Diagnostics.CodeAnalysis; +using System.Globalization; + +namespace Microsoft.AspNet.Localization +{ + /// + /// Specifies options for the . + /// + public class RequestLocalizationMiddlewareOptions + { + /// + /// Creates a new with default values. + /// + public RequestLocalizationMiddlewareOptions() + { + DefaultRequestCulture = new RequestCulture(CultureInfo.DefaultThreadCurrentCulture, CultureInfo.DefaultThreadCurrentUICulture); + + RequestCultureStrategies = new List + { + new QueryStringRequestCultureStrategy(), + new CookieRequestCultureStrategy(), + new AcceptLanguageHeaderRequestCultureStrategy { MaximumAcceptLanguageHeaderValuesToTry = MaximumAcceptLanguageHeaderValuesToTry } + }; + } + + /// + /// The maximum number of values in the Accept-Language header to attempt to create a + /// from for the current request. + /// Defaults to 3. + /// + public int MaximumAcceptLanguageHeaderValuesToTry { get; set; } = 3; + + /// + /// 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 + /// list. A value of null means all cultures are supported. + /// Defaults to null. + /// + [SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly", Justification = "Improves usability")] + public IList SupportedCultures { get; set; } + + /// + /// The UI cultures supported by the application. If this value is non-null, the + /// will only set the current request culture to an entry in this + /// list. A value of null means all cultures are supported. + /// Defaults to null. + /// + [SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly", Justification = "Improves usability")] + public IList SupportedUICultures { get; set; } + + /// + /// An ordered list of strategies used to determine a request's culture information. The first strategy that + /// returns a non-null result for a given request will be used. + /// Defaults to the following: + /// + /// + /// + /// + /// + /// + [SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly", Justification = "Improves usability")] + public IList RequestCultureStrategies { get; set; } + } +} diff --git a/src/Microsoft.AspNet.Localization/project.json b/src/Microsoft.AspNet.Localization/project.json index 7561196f6f..a6d3910968 100644 --- a/src/Microsoft.AspNet.Localization/project.json +++ b/src/Microsoft.AspNet.Localization/project.json @@ -4,6 +4,7 @@ "dependencies": { "Microsoft.Framework.Localization.Abstractions": "1.0.0-*", + "Microsoft.Framework.NotNullAttribute.Internal": { "type": "build", "version": "1.0.0-*" }, "Microsoft.AspNet.Http.Extensions": "1.0.0-*" }, @@ -13,6 +14,7 @@ "dependencies": { "System.Collections": "4.0.10-*", "System.Linq": "4.0.0-*", + "System.Globalization": "4.0.10-*", "System.Threading": "4.0.10-*", "Microsoft.CSharp": "4.0.0-*" } diff --git a/src/Microsoft.Framework.Localization.Abstractions/LocalizedString.cs b/src/Microsoft.Framework.Localization.Abstractions/LocalizedString.cs index 4faf864309..4549457ff1 100644 --- a/src/Microsoft.Framework.Localization.Abstractions/LocalizedString.cs +++ b/src/Microsoft.Framework.Localization.Abstractions/LocalizedString.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 Microsoft.Framework.Internal; + namespace Microsoft.Framework.Localization { /// @@ -13,7 +15,7 @@ namespace Microsoft.Framework.Localization /// /// The name of the string in the resource it was loaded from. /// The actual string. - public LocalizedString(string name, string value) + public LocalizedString([NotNull] string name, [NotNull] string value) : this(name, value, resourceNotFound: false) { @@ -25,7 +27,7 @@ namespace Microsoft.Framework.Localization /// The name of the string in the resource it was loaded from. /// The actual string. /// Whether the string was found in a resource. Set this to false to indicate an alternate string value was used. - public LocalizedString(string name, string value, bool resourceNotFound) + public LocalizedString([NotNull] string name, [NotNull] string value, bool resourceNotFound) { Name = name; Value = value; diff --git a/src/Microsoft.Framework.Localization.Abstractions/StringLocalizerOfT.cs b/src/Microsoft.Framework.Localization.Abstractions/StringLocalizerOfT.cs index f222bf93b3..8801cdf234 100644 --- a/src/Microsoft.Framework.Localization.Abstractions/StringLocalizerOfT.cs +++ b/src/Microsoft.Framework.Localization.Abstractions/StringLocalizerOfT.cs @@ -4,6 +4,7 @@ using System.Collections; using System.Collections.Generic; using System.Globalization; +using Microsoft.Framework.Internal; namespace Microsoft.Framework.Localization { @@ -19,7 +20,7 @@ namespace Microsoft.Framework.Localization /// Creates a new . /// /// The to use. - public StringLocalizer(IStringLocalizerFactory factory) + public StringLocalizer([NotNull] IStringLocalizerFactory factory) { _localizer = factory.Create(typeof(TResourceSource)); } @@ -28,16 +29,17 @@ namespace Microsoft.Framework.Localization public virtual IStringLocalizer WithCulture(CultureInfo culture) => _localizer.WithCulture(culture); /// - public virtual LocalizedString this[string key] => _localizer[key]; + public virtual LocalizedString this[[NotNull] string key] => _localizer[key]; /// - public virtual LocalizedString this[string key, params object[] arguments] => _localizer[key, arguments]; + public virtual LocalizedString this[[NotNull] string key, params object[] arguments] => + _localizer[key, arguments]; /// - public virtual LocalizedString GetString(string key) => _localizer.GetString(key); + public virtual LocalizedString GetString([NotNull] string key) => _localizer.GetString(key); /// - public virtual LocalizedString GetString(string key, params object[] arguments) => + public virtual LocalizedString GetString([NotNull] string key, params object[] arguments) => _localizer.GetString(key, arguments); /// diff --git a/src/Microsoft.Framework.Localization.Abstractions/project.json b/src/Microsoft.Framework.Localization.Abstractions/project.json index 87aa883bfd..63eb5f2524 100644 --- a/src/Microsoft.Framework.Localization.Abstractions/project.json +++ b/src/Microsoft.Framework.Localization.Abstractions/project.json @@ -3,7 +3,7 @@ "description": "Abstractions of application localization services.", "dependencies": { - + "Microsoft.Framework.NotNullAttribute.Internal": { "type": "build", "version": "1.0.0-*" } }, "frameworks": { diff --git a/src/Microsoft.Framework.Localization/LocalizationServiceCollectionExtensions.cs b/src/Microsoft.Framework.Localization/LocalizationServiceCollectionExtensions.cs index 9b4a0079fc..fe6f4694f2 100644 --- a/src/Microsoft.Framework.Localization/LocalizationServiceCollectionExtensions.cs +++ b/src/Microsoft.Framework.Localization/LocalizationServiceCollectionExtensions.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 Microsoft.Framework.Internal; using Microsoft.Framework.Localization; namespace Microsoft.Framework.DependencyInjection @@ -15,7 +16,7 @@ namespace Microsoft.Framework.DependencyInjection /// /// The to add the services to. /// The . - public static IServiceCollection AddLocalization(this IServiceCollection services) + public static IServiceCollection AddLocalization([NotNull] this IServiceCollection services) { services.TryAdd(new ServiceDescriptor( typeof(IStringLocalizerFactory), diff --git a/src/Microsoft.Framework.Localization/ResourceManagerStringLocalizer.cs b/src/Microsoft.Framework.Localization/ResourceManagerStringLocalizer.cs index b6db0b6d13..526b16ab99 100644 --- a/src/Microsoft.Framework.Localization/ResourceManagerStringLocalizer.cs +++ b/src/Microsoft.Framework.Localization/ResourceManagerStringLocalizer.cs @@ -8,6 +8,7 @@ using System.Collections.Generic; using System.Globalization; using System.Reflection; using System.Resources; +using Microsoft.Framework.Internal; namespace Microsoft.Framework.Localization { @@ -27,9 +28,9 @@ namespace Microsoft.Framework.Localization /// The that contains the strings as embedded resources. /// The base name of the embedded resource in the that contains the strings. public ResourceManagerStringLocalizer( - ResourceManager resourceManager, - Assembly resourceAssembly, - string baseName) + [NotNull] ResourceManager resourceManager, + [NotNull] Assembly resourceAssembly, + [NotNull] string baseName) { ResourceManager = resourceManager; ResourceAssembly = resourceAssembly; @@ -52,20 +53,20 @@ namespace Microsoft.Framework.Localization protected string ResourceBaseName { get; } /// - public virtual LocalizedString this[string name] => GetString(name); + public virtual LocalizedString this[[NotNull] string name] => GetString(name); /// - public virtual LocalizedString this[string name, params object[] arguments] => GetString(name, arguments); + public virtual LocalizedString this[[NotNull] string name, params object[] arguments] => GetString(name, arguments); /// - public virtual LocalizedString GetString(string name) + public virtual LocalizedString GetString([NotNull] string name) { var value = GetStringSafely(name, null); return new LocalizedString(name, value ?? name, resourceNotFound: value == null); } /// - public virtual LocalizedString GetString(string name, params object[] arguments) + public virtual LocalizedString GetString([NotNull] string name, params object[] arguments) { var format = GetStringSafely(name, null); var value = string.Format(format ?? name, arguments); @@ -95,7 +96,7 @@ namespace Microsoft.Framework.Localization /// The name of the string resource. /// The to get the string for. /// The resource string, or null if none was found. - protected string GetStringSafely(string name, CultureInfo culture) + protected string GetStringSafely([NotNull] string name, [NotNull] CultureInfo culture) { var cacheKey = new MissingManifestCacheKey(name, culture); if (_missingManifestCache.ContainsKey(cacheKey)) @@ -133,7 +134,7 @@ namespace Microsoft.Framework.Localization /// /// The to get strings for. /// The . - protected IEnumerator GetEnumerator(CultureInfo culture) + protected IEnumerator GetEnumerator([NotNull] CultureInfo culture) { // TODO: I'm sure something here should be cached, probably the whole result var resourceNames = GetResourceNamesFromCultureHierarchy(culture); diff --git a/src/Microsoft.Framework.Localization/ResourceManagerStringLocalizerFactory.cs b/src/Microsoft.Framework.Localization/ResourceManagerStringLocalizerFactory.cs index 801f6acef5..3039a9d377 100644 --- a/src/Microsoft.Framework.Localization/ResourceManagerStringLocalizerFactory.cs +++ b/src/Microsoft.Framework.Localization/ResourceManagerStringLocalizerFactory.cs @@ -4,6 +4,7 @@ using System; using System.Reflection; using System.Resources; +using Microsoft.Framework.Internal; using Microsoft.Framework.Runtime; namespace Microsoft.Framework.Localization @@ -19,7 +20,7 @@ namespace Microsoft.Framework.Localization /// Creates a new . /// /// - public ResourceManagerStringLocalizerFactory(IApplicationEnvironment applicationEnvironment) + public ResourceManagerStringLocalizerFactory([NotNull] IApplicationEnvironment applicationEnvironment) { _applicationEnvironment = applicationEnvironment; } @@ -30,7 +31,7 @@ namespace Microsoft.Framework.Localization /// /// The . /// The . - public IStringLocalizer Create(Type resourceSource) + public IStringLocalizer Create([NotNull] Type resourceSource) { var typeInfo = resourceSource.GetTypeInfo(); var assembly = typeInfo.Assembly; @@ -44,7 +45,7 @@ namespace Microsoft.Framework.Localization /// The base name of the resource to load strings from. /// The location to load resources from. /// The . - public IStringLocalizer Create(string baseName, string location) + public IStringLocalizer Create([NotNull] string baseName, [NotNull] string location) { var assembly = Assembly.Load(new AssemblyName(location ?? _applicationEnvironment.ApplicationName)); diff --git a/src/Microsoft.Framework.Localization/ResourceManagerWithCultureStringLocalizer.cs b/src/Microsoft.Framework.Localization/ResourceManagerWithCultureStringLocalizer.cs index 7b988b04ba..8e9027af76 100644 --- a/src/Microsoft.Framework.Localization/ResourceManagerWithCultureStringLocalizer.cs +++ b/src/Microsoft.Framework.Localization/ResourceManagerWithCultureStringLocalizer.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using System.Globalization; using System.Reflection; using System.Resources; +using Microsoft.Framework.Internal; namespace Microsoft.Framework.Localization { @@ -25,30 +26,30 @@ namespace Microsoft.Framework.Localization /// The base name of the embedded resource in the that contains the strings. /// The specific to use. public ResourceManagerWithCultureStringLocalizer( - ResourceManager resourceManager, - Assembly assembly, - string baseName, - CultureInfo culture) + [NotNull] ResourceManager resourceManager, + [NotNull] Assembly assembly, + [NotNull] string baseName, + [NotNull] CultureInfo culture) : base(resourceManager, assembly, baseName) { _culture = culture; } /// - public override LocalizedString this[string name] => GetString(name); + public override LocalizedString this[[NotNull] string name] => GetString(name); /// - public override LocalizedString this[string name, params object[] arguments] => GetString(name, arguments); + public override LocalizedString this[[NotNull] string name, params object[] arguments] => GetString(name, arguments); /// - public override LocalizedString GetString(string name) + public override LocalizedString GetString([NotNull] string name) { var value = GetStringSafely(name, _culture); return new LocalizedString(name, value ?? name); } /// - public override LocalizedString GetString(string name, params object[] arguments) + public override LocalizedString GetString([NotNull] string name, params object[] arguments) { var format = GetStringSafely(name, _culture); var value = string.Format(_culture, format ?? name, arguments); diff --git a/src/Microsoft.Framework.Localization/project.json b/src/Microsoft.Framework.Localization/project.json index 39a74a1e5a..c0ad0046fd 100644 --- a/src/Microsoft.Framework.Localization/project.json +++ b/src/Microsoft.Framework.Localization/project.json @@ -5,6 +5,7 @@ "dependencies": { "Microsoft.Framework.DependencyInjection.Abstractions": "1.0.0-*", "Microsoft.Framework.Localization.Abstractions": "1.0.0-*", + "Microsoft.Framework.NotNullAttribute.Internal": { "type": "build", "version": "1.0.0-*" }, "Microsoft.Framework.Runtime.Abstractions": "1.0.0-*", "System.Resources.ReaderWriter": "4.0.0-*" }, From 3dc0d40f7d0ae4da6f7a0488bd5e56da91b522e6 Mon Sep 17 00:00:00 2001 From: damianedwards Date: Wed, 6 May 2015 18:09:59 -0700 Subject: [PATCH 006/390] Added a sample & fixed some issues it found --- Localization.sln | 14 +++ global.json | 3 + .../LocalizationSample.xproj | 19 ++++ samples/LocalizationSample/Startup.cs | 95 +++++++++++++++++++ samples/LocalizationSample/project.json | 38 ++++++++ .../RequestLocalizationMiddleware.cs | 16 +++- .../RequestLocalizationMiddlewareOptions.cs | 2 +- .../project.json | 10 +- 8 files changed, 190 insertions(+), 7 deletions(-) create mode 100644 global.json create mode 100644 samples/LocalizationSample/LocalizationSample.xproj create mode 100644 samples/LocalizationSample/Startup.cs create mode 100644 samples/LocalizationSample/project.json diff --git a/Localization.sln b/Localization.sln index 0df01140f0..ddfd06056c 100644 --- a/Localization.sln +++ b/Localization.sln @@ -11,6 +11,15 @@ Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.AspNet.Localizati EndProject Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.Framework.Localization.Abstractions", "src\Microsoft.Framework.Localization.Abstractions\Microsoft.Framework.Localization.Abstractions.xproj", "{A1FCF259-70F6-4605-AA2D-E4B356BE771A}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "samples", "samples", "{79878809-8D1C-4BD4-BA99-F1F13FF96FD8}" +EndProject +Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "LocalizationSample", "samples\LocalizationSample\LocalizationSample.xproj", "{55D9501F-15B9-4339-A0AB-6082850E5FCE}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{5E7E7712-E3E3-4AD5-9B89-7FC140359FF1}" + ProjectSection(SolutionItems) = preProject + global.json = global.json + EndProjectSection +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -29,6 +38,10 @@ Global {A1FCF259-70F6-4605-AA2D-E4B356BE771A}.Debug|Any CPU.Build.0 = Debug|Any CPU {A1FCF259-70F6-4605-AA2D-E4B356BE771A}.Release|Any CPU.ActiveCfg = Release|Any CPU {A1FCF259-70F6-4605-AA2D-E4B356BE771A}.Release|Any CPU.Build.0 = Release|Any CPU + {55D9501F-15B9-4339-A0AB-6082850E5FCE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {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 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -37,5 +50,6 @@ Global {29743CFF-120E-40EB-9B89-5818425946FA} = {FB313677-BAB3-4E49-8CDB-4FA4A9564767} {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} EndGlobalSection EndGlobal diff --git a/global.json b/global.json new file mode 100644 index 0000000000..0ddf69c7e5 --- /dev/null +++ b/global.json @@ -0,0 +1,3 @@ +{ + "projects": [ "src" ] +} \ No newline at end of file diff --git a/samples/LocalizationSample/LocalizationSample.xproj b/samples/LocalizationSample/LocalizationSample.xproj new file mode 100644 index 0000000000..e81f2086c0 --- /dev/null +++ b/samples/LocalizationSample/LocalizationSample.xproj @@ -0,0 +1,19 @@ + + + + 14.0 + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) + + + + 55d9501f-15b9-4339-a0ab-6082850e5fce + LocalizationSample + ..\..\artifacts\obj\$(MSBuildProjectName) + ..\..\artifacts\bin\$(MSBuildProjectName)\ + + + 2.0 + 31454 + + + \ No newline at end of file diff --git a/samples/LocalizationSample/Startup.cs b/samples/LocalizationSample/Startup.cs new file mode 100644 index 0000000000..75d1cd2004 --- /dev/null +++ b/samples/LocalizationSample/Startup.cs @@ -0,0 +1,95 @@ +// 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.Globalization; +using Microsoft.AspNet.Builder; +using Microsoft.AspNet.Http; +using Microsoft.AspNet.Localization; +using Microsoft.Framework.DependencyInjection; +using Microsoft.Framework.Localization; + +namespace LocalizationSample +{ + public class Startup + { + public void ConfigureServices(IServiceCollection services) + { + services.AddLocalization(); + } + + public void Configure(IApplicationBuilder app, IStringLocalizer SR) + { + var options = new RequestLocalizationMiddlewareOptions(); + app.UseRequestLocalization(options); + + app.Run(async (context) => + { + context.Response.StatusCode = 200; + context.Response.ContentType = "text/html; charset=utf-8"; + + var requestCulture = context.GetFeature().RequestCulture; + + await context.Response.WriteAsync( +$@" + + + Request Localization + + +"); + await context.Response.WriteAsync($"

{SR["Request Localization Sample"]}

"); + await context.Response.WriteAsync("
"); + await context.Response.WriteAsync($""); + await context.Response.WriteAsync("
"); + await context.Response.WriteAsync($""); + await context.Response.WriteAsync("
"); + await context.Response.WriteAsync(" "); + await context.Response.WriteAsync($"{SR["reset"]}"); + await context.Response.WriteAsync("
"); + await context.Response.WriteAsync("
"); + await context.Response.WriteAsync(""); + await context.Response.WriteAsync($""); + await context.Response.WriteAsync($""); + await context.Response.WriteAsync($""); + await context.Response.WriteAsync($""); + await context.Response.WriteAsync($""); + await context.Response.WriteAsync($""); + await context.Response.WriteAsync($""); + await context.Response.WriteAsync($""); + await context.Response.WriteAsync($""); + await context.Response.WriteAsync($""); + await context.Response.WriteAsync($""); + await context.Response.WriteAsync($""); + await context.Response.WriteAsync("
{SR["Current request culture:"]}{requestCulture.Culture.DisplayName} ({requestCulture.Culture})
{SR["Current request UI culture:"]}{requestCulture.UICulture.DisplayName} ({requestCulture.UICulture})
{SR["Current thread culture:"]}{CultureInfo.CurrentCulture.DisplayName} ({CultureInfo.CurrentCulture})
{SR["Current thread UI culture:"]}{CultureInfo.CurrentUICulture.DisplayName} ({CultureInfo.CurrentUICulture})
{SR["Current date (invariant full):"]}{DateTime.Now.ToString("F", CultureInfo.InvariantCulture)}
{SR["Current date (invariant):"]}{DateTime.Now.ToString(CultureInfo.InvariantCulture)}
{SR["Current date (request full):"]}{DateTime.Now.ToString("F")}
{SR["Current date (request):"]}{DateTime.Now.ToString()}
{SR["Current time (invariant):"]}{DateTime.Now.ToString("T", CultureInfo.InvariantCulture)}
{SR["Current time (request):"]}{DateTime.Now.ToString("T")}
{SR["Big number (invariant):"]}{(Math.Pow(2, 42) + 0.42).ToString("N", CultureInfo.InvariantCulture)}
{SR["Big number (request):"]}{(Math.Pow(2, 42) + 0.42).ToString("N")}
"); + await context.Response.WriteAsync( +@" +"); + }); + } + + private static async System.Threading.Tasks.Task WriteCultureSelectOptions(HttpContext context) + { + await context.Response.WriteAsync($" "); + await context.Response.WriteAsync($" "); + await context.Response.WriteAsync($" "); + await context.Response.WriteAsync($" "); + await context.Response.WriteAsync($" "); + await context.Response.WriteAsync($" "); + await context.Response.WriteAsync($" "); + await context.Response.WriteAsync($" "); + await context.Response.WriteAsync($" "); +#if DNX451 + await context.Response.WriteAsync($" "); +#endif + } + } +} diff --git a/samples/LocalizationSample/project.json b/samples/LocalizationSample/project.json new file mode 100644 index 0000000000..9ab24a6df7 --- /dev/null +++ b/samples/LocalizationSample/project.json @@ -0,0 +1,38 @@ +{ + "webroot": "wwwroot", + "version": "1.0.0-*", + + "dependencies": { + "Microsoft.AspNet.Server.IIS": "1.0.0-*", + "Microsoft.AspNet.Server.WebListener": "1.0.0-*", + "Microsoft.AspNet.Localization": "1.0.0-*", + "Microsoft.Framework.Localization": "1.0.0-*" + }, + + "commands": { + "web": "Microsoft.AspNet.Hosting --server Microsoft.AspNet.Server.WebListener --server.urls http://localhost:5000" + }, + + "frameworks": { + "dnx451": { }, + "dnxcore50": { + "dependencies": { + "System.Globalization": "4.0.10-*", + "System.Globalization.Extensions": "4.0.0-*" + } + } + }, + + "publishExclude": [ + "node_modules", + "bower_components", + "**.xproj", + "**.user", + "**.vspscc" + ], + "exclude": [ + "wwwroot", + "node_modules", + "bower_components" + ] +} diff --git a/src/Microsoft.AspNet.Localization/RequestLocalizationMiddleware.cs b/src/Microsoft.AspNet.Localization/RequestLocalizationMiddleware.cs index 2064690662..43ac3496dc 100644 --- a/src/Microsoft.AspNet.Localization/RequestLocalizationMiddleware.cs +++ b/src/Microsoft.AspNet.Localization/RequestLocalizationMiddleware.cs @@ -38,7 +38,7 @@ namespace Microsoft.AspNet.Localization public async Task Invoke([NotNull] HttpContext context) { var requestCulture = _options.DefaultRequestCulture ?? - new RequestCulture(CultureInfo.DefaultThreadCurrentCulture, CultureInfo.DefaultThreadCurrentUICulture); + new RequestCulture(CultureInfo.CurrentCulture, CultureInfo.CurrentUICulture); IRequestCultureStrategy winningStrategy = null; @@ -56,6 +56,20 @@ namespace Microsoft.AspNet.Localization } } + if (_options.SupportedCultures != null) + { + // Ensure that selected cultures are in the supported list and if not, set them to the default + if (!_options.SupportedCultures.Contains(requestCulture.Culture)) + { + requestCulture = new RequestCulture(_options.DefaultRequestCulture.Culture, requestCulture.UICulture); + } + + if (!_options.SupportedUICultures.Contains(requestCulture.UICulture)) + { + requestCulture = new RequestCulture(requestCulture.Culture, _options.DefaultRequestCulture.UICulture); + } + } + context.SetFeature(new RequestCultureFeature(requestCulture, winningStrategy)); SetCurrentThreadCulture(requestCulture); diff --git a/src/Microsoft.AspNet.Localization/RequestLocalizationMiddlewareOptions.cs b/src/Microsoft.AspNet.Localization/RequestLocalizationMiddlewareOptions.cs index cfa8e6e196..32827ebb1a 100644 --- a/src/Microsoft.AspNet.Localization/RequestLocalizationMiddlewareOptions.cs +++ b/src/Microsoft.AspNet.Localization/RequestLocalizationMiddlewareOptions.cs @@ -17,7 +17,7 @@ namespace Microsoft.AspNet.Localization /// public RequestLocalizationMiddlewareOptions() { - DefaultRequestCulture = new RequestCulture(CultureInfo.DefaultThreadCurrentCulture, CultureInfo.DefaultThreadCurrentUICulture); + DefaultRequestCulture = new RequestCulture(CultureInfo.CurrentCulture, CultureInfo.CurrentUICulture); RequestCultureStrategies = new List { diff --git a/src/Microsoft.Framework.Localization.Abstractions/project.json b/src/Microsoft.Framework.Localization.Abstractions/project.json index 63eb5f2524..b725e19028 100644 --- a/src/Microsoft.Framework.Localization.Abstractions/project.json +++ b/src/Microsoft.Framework.Localization.Abstractions/project.json @@ -10,11 +10,11 @@ "dnx451": { }, "dnxcore50": { "dependencies": { - "System.Collections": "4.0.10-beta-*", - "System.Globalization": "4.0.10-beta-*", - "System.Reflection": "4.0.10-beta-*", - "System.Runtime": "4.0.20-beta-22904", - "Microsoft.CSharp": "4.0.0-beta-*" + "System.Collections": "4.0.10-*", + "System.Globalization": "4.0.10-*", + "System.Reflection": "4.0.10-*", + "System.Runtime": "4.0.20-*", + "Microsoft.CSharp": "4.0.0-*" } } } From 41fe2150272a38446e26cfe5f74a96236903ae7b Mon Sep 17 00:00:00 2001 From: damianedwards Date: Wed, 6 May 2015 18:21:34 -0700 Subject: [PATCH 007/390] Sample improvements --- samples/LocalizationSample/Startup.cs | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/samples/LocalizationSample/Startup.cs b/samples/LocalizationSample/Startup.cs index 75d1cd2004..dc7fb81799 100644 --- a/samples/LocalizationSample/Startup.cs +++ b/samples/LocalizationSample/Startup.cs @@ -20,7 +20,11 @@ namespace LocalizationSample public void Configure(IApplicationBuilder app, IStringLocalizer SR) { - var options = new RequestLocalizationMiddlewareOptions(); + var options = new RequestLocalizationMiddlewareOptions + { + // Set options here to change middleware behavior + + }; app.UseRequestLocalization(options); app.Run(async (context) => @@ -28,7 +32,8 @@ namespace LocalizationSample context.Response.StatusCode = 200; context.Response.ContentType = "text/html; charset=utf-8"; - var requestCulture = context.GetFeature().RequestCulture; + var requestCultureFeature = context.GetFeature(); + var requestCulture = requestCultureFeature.RequestCulture; await context.Response.WriteAsync( $@" @@ -57,6 +62,7 @@ $@" await context.Response.WriteAsync(""); await context.Response.WriteAsync("
"); await context.Response.WriteAsync(""); + await context.Response.WriteAsync($""); await context.Response.WriteAsync($""); await context.Response.WriteAsync($""); await context.Response.WriteAsync($""); @@ -69,6 +75,12 @@ $@" await context.Response.WriteAsync($""); await context.Response.WriteAsync($""); await context.Response.WriteAsync($""); + await context.Response.WriteAsync($""); + await context.Response.WriteAsync($""); + await context.Response.WriteAsync($""); + await context.Response.WriteAsync($""); + await context.Response.WriteAsync($""); + await context.Response.WriteAsync($""); await context.Response.WriteAsync("
Winning strategy:{requestCultureFeature.Strategy.GetType().Name}
{SR["Current request culture:"]}{requestCulture.Culture.DisplayName} ({requestCulture.Culture})
{SR["Current request UI culture:"]}{requestCulture.UICulture.DisplayName} ({requestCulture.UICulture})
{SR["Current thread culture:"]}{CultureInfo.CurrentCulture.DisplayName} ({CultureInfo.CurrentCulture})
{SR["Current time (request):"]}{DateTime.Now.ToString("T")}
{SR["Big number (invariant):"]}{(Math.Pow(2, 42) + 0.42).ToString("N", CultureInfo.InvariantCulture)}
{SR["Big number (request):"]}{(Math.Pow(2, 42) + 0.42).ToString("N")}
{SR["Big number negative (invariant):"]}{(-Math.Pow(2, 42) + 0.42).ToString("N", CultureInfo.InvariantCulture)}
{SR["Big number negative (request):"]}{(-Math.Pow(2, 42) + 0.42).ToString("N")}
{SR["Money (invariant):"]}{2199.50.ToString("C", CultureInfo.InvariantCulture)}
{SR["Money (request):"]}{2199.50.ToString("C")}
{SR["Money negative (invariant):"]}{(-2199.50).ToString("C", CultureInfo.InvariantCulture)}
{SR["Money negative (request):"]}{(-2199.50).ToString("C")}
"); await context.Response.WriteAsync( @" From ec8ede5d8a6d2abdb1fdf480b6f5d0c9af678ddd Mon Sep 17 00:00:00 2001 From: damianedwards Date: Thu, 7 May 2015 10:32:59 -0700 Subject: [PATCH 008/390] Fixed missing resource caching issue in ResourceManagerStringLocalizer --- .../ResourceManagerStringLocalizer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Microsoft.Framework.Localization/ResourceManagerStringLocalizer.cs b/src/Microsoft.Framework.Localization/ResourceManagerStringLocalizer.cs index 526b16ab99..f17517a168 100644 --- a/src/Microsoft.Framework.Localization/ResourceManagerStringLocalizer.cs +++ b/src/Microsoft.Framework.Localization/ResourceManagerStringLocalizer.cs @@ -98,7 +98,7 @@ namespace Microsoft.Framework.Localization /// The resource string, or null if none was found. protected string GetStringSafely([NotNull] string name, [NotNull] CultureInfo culture) { - var cacheKey = new MissingManifestCacheKey(name, culture); + var cacheKey = new MissingManifestCacheKey(name, culture ?? CultureInfo.CurrentUICulture); if (_missingManifestCache.ContainsKey(cacheKey)) { return null; From 91b3ea45471086e17eb48ba805ce5f7fd39f9ac4 Mon Sep 17 00:00:00 2001 From: Hisham Abdullah Bin Ateya Date: Fri, 8 May 2015 01:52:03 +0300 Subject: [PATCH 009/390] Using expression-bodies methods --- .../CustomRequestCultureStrategy.cs | 5 +---- .../IApplicationBuilderExtensions.cs | 5 +---- .../ResourceManagerStringLocalizer.cs | 22 +++++-------------- ...sourceManagerWithCultureStringLocalizer.cs | 5 +---- 4 files changed, 8 insertions(+), 29 deletions(-) diff --git a/src/Microsoft.AspNet.Localization/CustomRequestCultureStrategy.cs b/src/Microsoft.AspNet.Localization/CustomRequestCultureStrategy.cs index 629862c1e5..f67804ab44 100644 --- a/src/Microsoft.AspNet.Localization/CustomRequestCultureStrategy.cs +++ b/src/Microsoft.AspNet.Localization/CustomRequestCultureStrategy.cs @@ -16,9 +16,6 @@ namespace Microsoft.AspNet.Localization _strategy = strategy; } - public RequestCulture DetermineRequestCulture([NotNull] HttpContext httpContext) - { - return _strategy(httpContext); - } + public RequestCulture DetermineRequestCulture([NotNull] HttpContext httpContext) => _strategy(httpContext); } } diff --git a/src/Microsoft.AspNet.Localization/IApplicationBuilderExtensions.cs b/src/Microsoft.AspNet.Localization/IApplicationBuilderExtensions.cs index d3d7a65077..b08c756bc9 100644 --- a/src/Microsoft.AspNet.Localization/IApplicationBuilderExtensions.cs +++ b/src/Microsoft.AspNet.Localization/IApplicationBuilderExtensions.cs @@ -33,9 +33,6 @@ namespace Microsoft.AspNet.Builder /// The . public static IApplicationBuilder UseRequestLocalization( [NotNull] this IApplicationBuilder builder, - [NotNull] RequestLocalizationMiddlewareOptions options) - { - return builder.UseMiddleware(options); - } + [NotNull] RequestLocalizationMiddlewareOptions options) => builder.UseMiddleware(options); } } \ No newline at end of file diff --git a/src/Microsoft.Framework.Localization/ResourceManagerStringLocalizer.cs b/src/Microsoft.Framework.Localization/ResourceManagerStringLocalizer.cs index 526b16ab99..1db2031649 100644 --- a/src/Microsoft.Framework.Localization/ResourceManagerStringLocalizer.cs +++ b/src/Microsoft.Framework.Localization/ResourceManagerStringLocalizer.cs @@ -119,15 +119,9 @@ namespace Microsoft.Framework.Localization /// Returns an for all strings in the current culture. /// /// The . - public virtual IEnumerator GetEnumerator() - { - return GetEnumerator(CultureInfo.CurrentUICulture); - } + public virtual IEnumerator GetEnumerator() => GetEnumerator(CultureInfo.CurrentUICulture); - IEnumerator IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); /// /// Returns an for all strings in the specified culture. @@ -200,11 +194,8 @@ namespace Microsoft.Framework.Localization public CultureInfo CultureInfo { get; } - public bool Equals(MissingManifestCacheKey other) - { - return string.Equals(Name, other.Name, StringComparison.Ordinal) - && CultureInfo == other.CultureInfo; - } + public bool Equals(MissingManifestCacheKey other) => + string.Equals(Name, other.Name, StringComparison.Ordinal) && CultureInfo == other.CultureInfo; public override bool Equals(object obj) { @@ -218,10 +209,7 @@ namespace Microsoft.Framework.Localization return base.Equals(obj); } - public override int GetHashCode() - { - return _hashCode; - } + public override int GetHashCode() => _hashCode; } } } \ No newline at end of file diff --git a/src/Microsoft.Framework.Localization/ResourceManagerWithCultureStringLocalizer.cs b/src/Microsoft.Framework.Localization/ResourceManagerWithCultureStringLocalizer.cs index 8e9027af76..2c582c0854 100644 --- a/src/Microsoft.Framework.Localization/ResourceManagerWithCultureStringLocalizer.cs +++ b/src/Microsoft.Framework.Localization/ResourceManagerWithCultureStringLocalizer.cs @@ -57,9 +57,6 @@ namespace Microsoft.Framework.Localization } /// - public override IEnumerator GetEnumerator() - { - return GetEnumerator(_culture); - } + public override IEnumerator GetEnumerator() => GetEnumerator(_culture); } } \ No newline at end of file From 944c84bc5d4d5551d328673ebafbc5374bab76af Mon Sep 17 00:00:00 2001 From: damianedwards Date: Thu, 7 May 2015 18:11:10 -0700 Subject: [PATCH 010/390] Implemented CookieRequestCultureStrategy & other changes: - Updated sample to enable setting/clearing cultures via cookie - Cache CultureInfo construction as it's not built into .NET Core - Cache RequestCulture construction as they're immutable anyway and created lots per app if the middleware is running - Fix issue where by invalid culture names were not handled (it crashed) - Handle the pesky favicon.ico request from browsers - Ignore .vs folder --- .gitignore | 3 +- samples/LocalizationSample/Startup.cs | 30 ++++++- ...eptLanguageHeaderRequestCultureStrategy.cs | 9 +- .../CookieRequestCultureStrategy.cs | 86 ++++++++++++++++++- .../Internal/CultureInfoCache.cs | 60 +++++++++++++ .../Internal/CultureUtilities.cs | 29 ------- .../QueryStringRequestCultureStrategy.cs | 12 ++- .../RequestCulture.cs | 79 ++++++++++++++--- .../RequestLocalizationMiddleware.cs | 6 +- .../RequestLocalizationMiddlewareOptions.cs | 2 +- .../project.json | 1 + 11 files changed, 255 insertions(+), 62 deletions(-) create mode 100644 src/Microsoft.AspNet.Localization/Internal/CultureInfoCache.cs delete mode 100644 src/Microsoft.AspNet.Localization/Internal/CultureUtilities.cs diff --git a/.gitignore b/.gitignore index 65df2761c4..aba84927ed 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,7 @@ [Bb]in/ TestResults/ .nuget/ +.vs/ _ReSharper.*/ packages/ artifacts/ @@ -25,4 +26,4 @@ nuget.exe *.ipch *.sln.ide debugSettings.json -project.lock.json +project.lock.json \ No newline at end of file diff --git a/samples/LocalizationSample/Startup.cs b/samples/LocalizationSample/Startup.cs index dc7fb81799..d819c93999 100644 --- a/samples/LocalizationSample/Startup.cs +++ b/samples/LocalizationSample/Startup.cs @@ -27,8 +27,15 @@ namespace LocalizationSample }; app.UseRequestLocalization(options); - app.Run(async (context) => + app.Use(async (context, next) => { + if (context.Request.Path.Value.EndsWith("favicon.ico")) + { + // Pesky browsers + context.Response.StatusCode = 404; + return; + } + context.Response.StatusCode = 200; context.Response.ContentType = "text/html; charset=utf-8"; @@ -39,12 +46,25 @@ namespace LocalizationSample $@" - Request Localization + {SR["Request Localization"]} + "); await context.Response.WriteAsync($"

{SR["Request Localization Sample"]}

"); @@ -57,8 +77,9 @@ $@" await context.Response.WriteAsync("
"); - await context.Response.WriteAsync(" "); - await context.Response.WriteAsync($"{SR["reset"]}"); + await context.Response.WriteAsync(" "); + await context.Response.WriteAsync($" "); + await context.Response.WriteAsync($"{SR["reset"]}"); await context.Response.WriteAsync(""); await context.Response.WriteAsync("
"); await context.Response.WriteAsync(""); @@ -102,6 +123,7 @@ $@" #if DNX451 await context.Response.WriteAsync($" "); #endif + await context.Response.WriteAsync($" "); } } } diff --git a/src/Microsoft.AspNet.Localization/AcceptLanguageHeaderRequestCultureStrategy.cs b/src/Microsoft.AspNet.Localization/AcceptLanguageHeaderRequestCultureStrategy.cs index 1a90e5332a..030e294361 100644 --- a/src/Microsoft.AspNet.Localization/AcceptLanguageHeaderRequestCultureStrategy.cs +++ b/src/Microsoft.AspNet.Localization/AcceptLanguageHeaderRequestCultureStrategy.cs @@ -13,9 +13,6 @@ namespace Microsoft.AspNet.Localization /// /// Determines the culture information for a request via the value of the Accept-Language header. /// - /// - /// - /// public class AcceptLanguageHeaderRequestCultureStrategy : IRequestCultureStrategy { /// @@ -53,11 +50,11 @@ namespace Microsoft.AspNet.Localization // the CultureInfo ctor if (language.Value != null) { - try + var culture = CultureInfoCache.GetCultureInfo(language.Value); + if (culture != null) { - return new RequestCulture(CultureUtilities.GetCultureFromName(language.Value)); + return RequestCulture.GetRequestCulture(culture); } - catch (CultureNotFoundException) { } } } diff --git a/src/Microsoft.AspNet.Localization/CookieRequestCultureStrategy.cs b/src/Microsoft.AspNet.Localization/CookieRequestCultureStrategy.cs index 902d5819dd..ac9a1b3089 100644 --- a/src/Microsoft.AspNet.Localization/CookieRequestCultureStrategy.cs +++ b/src/Microsoft.AspNet.Localization/CookieRequestCultureStrategy.cs @@ -3,16 +3,98 @@ using System; using Microsoft.AspNet.Http; +using Microsoft.AspNet.Localization.Internal; using Microsoft.Framework.Internal; namespace Microsoft.AspNet.Localization { + /// + /// Determines the culture information for a request via the value of a cookie. + /// public class CookieRequestCultureStrategy : IRequestCultureStrategy { + private static readonly char[] _cookieSeparator = new[] { '|' }; + private static readonly string _culturePrefix = "c="; + private static readonly string _uiCulturePrefix = "uic="; + + /// + /// The name of the cookie that contains the user's preferred culture information. + /// Defaults to . + /// + public string CookieName { get; set; } = DefaultCookieName; + + /// public RequestCulture DetermineRequestCulture([NotNull] HttpContext httpContext) { - // TODO - return null; + var cookie = httpContext.Request.Cookies[CookieName]; + + if (cookie == null) + { + return null; + } + + return ParseCookieValue(cookie); + } + + /// + /// The default name of the cookie used to track the user's preferred culture information. + /// + public static string DefaultCookieName { get; } = "ASPNET_CULTURE"; + + /// + /// Creates a string representation of a for placement in a cookie. + /// + /// The . + /// The cookie value. + public static string MakeCookieValue([NotNull] RequestCulture requestCulture) + { + var seperator = _cookieSeparator[0].ToString(); + + return string.Join(seperator, + $"{_culturePrefix}{requestCulture.Culture.Name}", + $"{_uiCulturePrefix}{requestCulture.UICulture.Name}"); + } + + /// + /// Parses a from the specified cookie value. + /// Returns null if parsing fails. + /// + /// The cookie value to parse. + /// The or null if parsing fails. + public static RequestCulture ParseCookieValue([NotNull] string value) + { + if (string.IsNullOrWhiteSpace(value)) + { + return null; + } + + var parts = value.Split(_cookieSeparator, StringSplitOptions.RemoveEmptyEntries); + + if (parts.Length != 2) + { + return null; + } + + var potentialCultureName = parts[0]; + var potentialUICultureName = parts[1]; + + if (!potentialCultureName.StartsWith(_culturePrefix) || !potentialUICultureName.StartsWith(_uiCulturePrefix)) + { + return null; + } + + 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) + { + return null; + } + + return RequestCulture.GetRequestCulture(culture, uiCulture); } } } diff --git a/src/Microsoft.AspNet.Localization/Internal/CultureInfoCache.cs b/src/Microsoft.AspNet.Localization/Internal/CultureInfoCache.cs new file mode 100644 index 0000000000..87662bf840 --- /dev/null +++ b/src/Microsoft.AspNet.Localization/Internal/CultureInfoCache.cs @@ -0,0 +1,60 @@ +// 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.Globalization; + +namespace Microsoft.AspNet.Localization.Internal +{ + public static class CultureInfoCache + { + private static readonly ConcurrentDictionary _cache = new ConcurrentDictionary(); + + public static CultureInfo GetCultureInfo(string name, bool throwIfNotFound = false) + { + // Allow empty string values as they map to InvariantCulture, whereas null culture values will throw in + // the CultureInfo ctor + if (name == null) + { + return null; + } + + var entry = _cache.GetOrAdd(name, n => + { + try + { + return new CacheEntry(CultureInfo.ReadOnly(new CultureInfo(n))); + } + catch (CultureNotFoundException ex) + { + return new CacheEntry(ex); + } + }); + + if (entry.Exception != null && throwIfNotFound) + { + throw entry.Exception; + } + + return entry.CultureInfo; + } + + private class CacheEntry + { + public CacheEntry(CultureInfo cultureInfo) + { + CultureInfo = cultureInfo; + } + + public CacheEntry(Exception exception) + { + Exception = exception; + } + + public CultureInfo CultureInfo { get; } + + public Exception Exception { get; } + } + } +} diff --git a/src/Microsoft.AspNet.Localization/Internal/CultureUtilities.cs b/src/Microsoft.AspNet.Localization/Internal/CultureUtilities.cs deleted file mode 100644 index bea6d2145d..0000000000 --- a/src/Microsoft.AspNet.Localization/Internal/CultureUtilities.cs +++ /dev/null @@ -1,29 +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. - -using System.Globalization; - -namespace Microsoft.AspNet.Localization.Internal -{ - public static class CultureUtilities - { - public static CultureInfo GetCultureFromName(string cultureName) - { - // Allow empty string values as they map to InvariantCulture, whereas null culture values will throw in - // the CultureInfo ctor - if (cultureName == null) - { - return null; - } - - try - { - return new CultureInfo(cultureName); - } - catch (CultureNotFoundException) - { - return null; - } - } - } -} diff --git a/src/Microsoft.AspNet.Localization/QueryStringRequestCultureStrategy.cs b/src/Microsoft.AspNet.Localization/QueryStringRequestCultureStrategy.cs index c7cfdb77e7..80a7ba7f1f 100644 --- a/src/Microsoft.AspNet.Localization/QueryStringRequestCultureStrategy.cs +++ b/src/Microsoft.AspNet.Localization/QueryStringRequestCultureStrategy.cs @@ -59,9 +59,15 @@ namespace Microsoft.AspNet.Localization queryUICulture = queryCulture; } - return new RequestCulture( - CultureUtilities.GetCultureFromName(queryCulture), - CultureUtilities.GetCultureFromName(queryUICulture)); + var culture = CultureInfoCache.GetCultureInfo(queryCulture); + var uiCulture = CultureInfoCache.GetCultureInfo(queryUICulture); + + if (culture == null || uiCulture == null) + { + return null; + } + + return RequestCulture.GetRequestCulture(culture, uiCulture); } } } diff --git a/src/Microsoft.AspNet.Localization/RequestCulture.cs b/src/Microsoft.AspNet.Localization/RequestCulture.cs index ec109e6d8e..9f0e2754db 100644 --- a/src/Microsoft.AspNet.Localization/RequestCulture.cs +++ b/src/Microsoft.AspNet.Localization/RequestCulture.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.Concurrent; using System.Globalization; using Microsoft.Framework.Internal; @@ -11,24 +12,15 @@ namespace Microsoft.AspNet.Localization /// public class RequestCulture { - /// - /// Creates a new object has its and - /// properties set to the same value. - /// - /// The for the request. - public RequestCulture([NotNull] CultureInfo culture) + private static readonly ConcurrentDictionary _cache = new ConcurrentDictionary(); + + private RequestCulture([NotNull] CultureInfo culture) : this (culture, culture) { } - /// - /// Creates a new object has its and - /// properties set to the respective values provided. - /// - /// The for the request to be used for formatting. - /// The for the request to be used for text, i.e. language. - public RequestCulture([NotNull] CultureInfo culture, [NotNull] CultureInfo uiCulture) + private RequestCulture([NotNull] CultureInfo culture, [NotNull] CultureInfo uiCulture) { Culture = culture; UICulture = uiCulture; @@ -43,5 +35,66 @@ namespace Microsoft.AspNet.Localization /// Gets the for the request to be used for text, i.e. language; /// public CultureInfo UICulture { get; } + + /// + /// Gets a cached instance that has its and + /// properties set to the same value. + /// + /// The for the request. + public static RequestCulture GetRequestCulture([NotNull] CultureInfo culture) + { + return GetRequestCulture(culture, culture); + } + + /// + /// Gets a cached instance that has its and + /// properties set to the respective values provided. + /// + /// The for the request to be used for formatting. + /// The for the request to be used for text, i.e. language. + /// + public static RequestCulture GetRequestCulture([NotNull] CultureInfo culture, [NotNull] CultureInfo uiCulture) + { + var key = new CacheKey(culture, uiCulture); + return _cache.GetOrAdd(key, k => new RequestCulture(culture, uiCulture)); + } + + private class CacheKey + { + private readonly int _hashCode; + + public CacheKey(CultureInfo culture, CultureInfo uiCulture) + { + Culture = culture; + UICulture = uiCulture; + _hashCode = new { Culture, UICulture }.GetHashCode(); + } + + public CultureInfo Culture { get; } + + public CultureInfo UICulture { get; } + + public bool Equals(CacheKey other) + { + return Culture == other.Culture && UICulture == other.UICulture; + } + + public override bool Equals(object obj) + { + var other = obj as CacheKey; + + if (other != null) + { + return Equals(other); + } + + return base.Equals(obj); + } + + public override int GetHashCode() + { + return _hashCode; + } + } } } diff --git a/src/Microsoft.AspNet.Localization/RequestLocalizationMiddleware.cs b/src/Microsoft.AspNet.Localization/RequestLocalizationMiddleware.cs index 43ac3496dc..1cd4ea2be1 100644 --- a/src/Microsoft.AspNet.Localization/RequestLocalizationMiddleware.cs +++ b/src/Microsoft.AspNet.Localization/RequestLocalizationMiddleware.cs @@ -38,7 +38,7 @@ namespace Microsoft.AspNet.Localization public async Task Invoke([NotNull] HttpContext context) { var requestCulture = _options.DefaultRequestCulture ?? - new RequestCulture(CultureInfo.CurrentCulture, CultureInfo.CurrentUICulture); + RequestCulture.GetRequestCulture(CultureInfo.CurrentCulture, CultureInfo.CurrentUICulture); IRequestCultureStrategy winningStrategy = null; @@ -61,12 +61,12 @@ namespace Microsoft.AspNet.Localization // Ensure that selected cultures are in the supported list and if not, set them to the default if (!_options.SupportedCultures.Contains(requestCulture.Culture)) { - requestCulture = new RequestCulture(_options.DefaultRequestCulture.Culture, requestCulture.UICulture); + requestCulture = RequestCulture.GetRequestCulture(_options.DefaultRequestCulture.Culture, requestCulture.UICulture); } if (!_options.SupportedUICultures.Contains(requestCulture.UICulture)) { - requestCulture = new RequestCulture(requestCulture.Culture, _options.DefaultRequestCulture.UICulture); + requestCulture = RequestCulture.GetRequestCulture(requestCulture.Culture, _options.DefaultRequestCulture.UICulture); } } diff --git a/src/Microsoft.AspNet.Localization/RequestLocalizationMiddlewareOptions.cs b/src/Microsoft.AspNet.Localization/RequestLocalizationMiddlewareOptions.cs index 32827ebb1a..0f24d18cf7 100644 --- a/src/Microsoft.AspNet.Localization/RequestLocalizationMiddlewareOptions.cs +++ b/src/Microsoft.AspNet.Localization/RequestLocalizationMiddlewareOptions.cs @@ -17,7 +17,7 @@ namespace Microsoft.AspNet.Localization /// public RequestLocalizationMiddlewareOptions() { - DefaultRequestCulture = new RequestCulture(CultureInfo.CurrentCulture, CultureInfo.CurrentUICulture); + DefaultRequestCulture = RequestCulture.GetRequestCulture(CultureInfo.CurrentCulture, CultureInfo.CurrentUICulture); RequestCultureStrategies = new List { diff --git a/src/Microsoft.AspNet.Localization/project.json b/src/Microsoft.AspNet.Localization/project.json index a6d3910968..069295bd00 100644 --- a/src/Microsoft.AspNet.Localization/project.json +++ b/src/Microsoft.AspNet.Localization/project.json @@ -13,6 +13,7 @@ "dnxcore50": { "dependencies": { "System.Collections": "4.0.10-*", + "System.Collections.Concurrent": "4.0.10-*", "System.Linq": "4.0.0-*", "System.Globalization": "4.0.10-*", "System.Threading": "4.0.10-*", From ca4b85e19fa960c087d956ece6a47e062170befd Mon Sep 17 00:00:00 2001 From: damianedwards Date: Thu, 7 May 2015 18:19:54 -0700 Subject: [PATCH 011/390] Added missing doc comments --- .../CustomRequestCultureStrategy.cs | 8 ++++++++ .../IRequestCultureStrategy.cs | 3 +++ 2 files changed, 11 insertions(+) diff --git a/src/Microsoft.AspNet.Localization/CustomRequestCultureStrategy.cs b/src/Microsoft.AspNet.Localization/CustomRequestCultureStrategy.cs index 629862c1e5..7ddee3f627 100644 --- a/src/Microsoft.AspNet.Localization/CustomRequestCultureStrategy.cs +++ b/src/Microsoft.AspNet.Localization/CustomRequestCultureStrategy.cs @@ -7,15 +7,23 @@ using Microsoft.Framework.Internal; namespace Microsoft.AspNet.Localization { + /// + /// Determines the culture information for a request via the configured delegate. + /// public class CustomRequestCultureStrategy : IRequestCultureStrategy { private readonly Func _strategy; + /// + /// Creates a new using the specified delegate. + /// + /// The strategy delegate. public CustomRequestCultureStrategy([NotNull] Func strategy) { _strategy = strategy; } + /// public RequestCulture DetermineRequestCulture([NotNull] HttpContext httpContext) { return _strategy(httpContext); diff --git a/src/Microsoft.AspNet.Localization/IRequestCultureStrategy.cs b/src/Microsoft.AspNet.Localization/IRequestCultureStrategy.cs index 93f943488b..eb96670263 100644 --- a/src/Microsoft.AspNet.Localization/IRequestCultureStrategy.cs +++ b/src/Microsoft.AspNet.Localization/IRequestCultureStrategy.cs @@ -5,6 +5,9 @@ using Microsoft.AspNet.Http; namespace Microsoft.AspNet.Localization { + /// + /// Represents a strategy for determining the culture information of an . + /// public interface IRequestCultureStrategy { RequestCulture DetermineRequestCulture(HttpContext httpContext); From 9834a277280f53afc88f1c17110ff2fd7ea8e802 Mon Sep 17 00:00:00 2001 From: damianedwards Date: Mon, 11 May 2015 12:58:18 -0700 Subject: [PATCH 012/390] Culture names are now limited to a known list: - Added tool to generate a set of known culture names from the OS/Fx - CultureInfoCache is now limited to only caching/returning cultures from the known list - #6 --- Localization.sln | 7 + samples/LocalizationSample/Startup.cs | 3 +- .../CultureInfoGenerator.xproj | 20 + src/CultureInfoGenerator/Program.cs | 116 +++++ src/CultureInfoGenerator/project.json | 16 + .../Internal/CultureInfoCache.cs | 27 +- .../Internal/CultureInfoList.cs | 436 ++++++++++++++++++ 7 files changed, 606 insertions(+), 19 deletions(-) create mode 100644 src/CultureInfoGenerator/CultureInfoGenerator.xproj create mode 100644 src/CultureInfoGenerator/Program.cs create mode 100644 src/CultureInfoGenerator/project.json create mode 100644 src/Microsoft.AspNet.Localization/Internal/CultureInfoList.cs diff --git a/Localization.sln b/Localization.sln index ddfd06056c..cf7a3d95c6 100644 --- a/Localization.sln +++ b/Localization.sln @@ -20,6 +20,8 @@ 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 Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -42,6 +44,10 @@ 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 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -51,5 +57,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} EndGlobalSection EndGlobal diff --git a/samples/LocalizationSample/Startup.cs b/samples/LocalizationSample/Startup.cs index d819c93999..2183eaf3af 100644 --- a/samples/LocalizationSample/Startup.cs +++ b/samples/LocalizationSample/Startup.cs @@ -123,7 +123,8 @@ $@" #if DNX451 await context.Response.WriteAsync($" "); #endif - await context.Response.WriteAsync($" "); + await context.Response.WriteAsync($" "); + await context.Response.WriteAsync($" "); } } } diff --git a/src/CultureInfoGenerator/CultureInfoGenerator.xproj b/src/CultureInfoGenerator/CultureInfoGenerator.xproj new file mode 100644 index 0000000000..6e980d08c1 --- /dev/null +++ b/src/CultureInfoGenerator/CultureInfoGenerator.xproj @@ -0,0 +1,20 @@ + + + + 14.0 + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) + + + + + bd22ae1c-6631-4da6-874d-0dc0f803ceab + CultureInfoGenerator + ..\..\artifacts\obj\$(MSBuildProjectName) + ..\..\artifacts\bin\$(MSBuildProjectName)\ + + + + 2.0 + + + diff --git a/src/CultureInfoGenerator/Program.cs b/src/CultureInfoGenerator/Program.cs new file mode 100644 index 0000000000..7843c928d7 --- /dev/null +++ b/src/CultureInfoGenerator/Program.cs @@ -0,0 +1,116 @@ +// 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.Globalization; +using System.IO; +using Microsoft.Framework.Runtime; +using Microsoft.Win32; + +namespace CultureInfoGenerator +{ + public class Program + { + private readonly string _appName; + private readonly string _appPath; + + public Program(IApplicationEnvironment appEnvironment) + { + _appName = appEnvironment.ApplicationName; + _appPath = appEnvironment.ApplicationBasePath; + } + + public void Main(string[] args) + { + var outputFilePath = args.Length > 0 ? args[0] : Path.Combine(_appPath, "../Microsoft.AspNet.Localization/Internal/CultureInfoList.cs"); + var netFxVersion = Get45or451FromRegistry(); + var windowsVersion = Environment.OSVersion; + + using (var writer = new StreamWriter(outputFilePath, false)) + { + writer.WriteLine($@"// 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.Internal +{{ + public static class CultureInfoList + {{ + // This list of known cultures was generated by {_appName} using .NET Framework {netFxVersion} on + // {windowsVersion}. + // 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 + {{" + ); + + var cultures = CultureInfo.GetCultures( + CultureTypes.NeutralCultures + | CultureTypes.InstalledWin32Cultures + | CultureTypes.SpecificCultures); + + var format = " \"{0}\""; + + for (int i = 0; i < cultures.Length; i++) + { + var culture = cultures[i]; + + writer.Write(format, culture.Name); + + if (i < cultures.Length - 1) + { + writer.WriteLine(","); + } + else + { + // Last entry + writer.WriteLine(); + } + } + + writer.WriteLine( +@" }; + } +}"); + } + } + + // .NET Framework detection code copied from https://msdn.microsoft.com/en-us/library/hh925568%28v=vs.110%29.aspx#net_d + private static string Get45or451FromRegistry() + { + using (var ndpKey = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry32) + .OpenSubKey("SOFTWARE\\Microsoft\\NET Framework Setup\\NDP\\v4\\Full\\")) + { + var releaseKey = Convert.ToInt32(ndpKey.GetValue("Release")); + return CheckFor45DotVersion(releaseKey); + } + } + + // Checking the version using >= will enable forward compatibility, + // however you should always compile your code on newer versions of + // the framework to ensure your app works the same. + private static string CheckFor45DotVersion(int releaseKey) + { + if (releaseKey >= 393273) + { + return "4.6 RC or later"; + } + if ((releaseKey >= 379893)) + { + return "4.5.2 or later"; + } + if ((releaseKey >= 378675)) + { + return "4.5.1 or later"; + } + if ((releaseKey >= 378389)) + { + return "4.5 or later"; + } + // This line should never execute. A non-null release key should mean + // that 4.5 or later is installed. + return "No 4.5 or later version detected"; + } + } +} diff --git a/src/CultureInfoGenerator/project.json b/src/CultureInfoGenerator/project.json new file mode 100644 index 0000000000..f88d824517 --- /dev/null +++ b/src/CultureInfoGenerator/project.json @@ -0,0 +1,16 @@ +{ + "version": "1.0.0-*", + "description": "Generates a list of known culture names from the OS using CultureInfo.GetCultures. This tool is intended to be run on Windows using full .NET Framework.", + + "dependencies": { + "Microsoft.Framework.Runtime.Abstractions": "1.0.0-beta5-11739" + }, + + "commands": { + "CultureInfoGenerator": "CultureInfoGenerator" + }, + + "frameworks": { + "dnx451": { } + } +} diff --git a/src/Microsoft.AspNet.Localization/Internal/CultureInfoCache.cs b/src/Microsoft.AspNet.Localization/Internal/CultureInfoCache.cs index 87662bf840..9e8ee0a993 100644 --- a/src/Microsoft.AspNet.Localization/Internal/CultureInfoCache.cs +++ b/src/Microsoft.AspNet.Localization/Internal/CultureInfoCache.cs @@ -11,11 +11,11 @@ namespace Microsoft.AspNet.Localization.Internal { private static readonly ConcurrentDictionary _cache = new ConcurrentDictionary(); - public static CultureInfo GetCultureInfo(string name, bool throwIfNotFound = false) + public static CultureInfo GetCultureInfo(string name) { - // Allow empty string values as they map to InvariantCulture, whereas null culture values will throw in - // the CultureInfo ctor - if (name == null) + // 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 || !CultureInfoList.KnownCultureNames.Contains(name)) { return null; } @@ -26,17 +26,15 @@ namespace Microsoft.AspNet.Localization.Internal { return new CacheEntry(CultureInfo.ReadOnly(new CultureInfo(n))); } - catch (CultureNotFoundException ex) + catch (CultureNotFoundException) { - return new CacheEntry(ex); + // This can still throw as the list of culture names we have is generated from latest .NET Framework + // on latest Windows and thus contains names that won't be supported on lower framework or OS versions. + // We can just cache the null result in these cases as it's ultimately bound by the list anyway. + return new CacheEntry(cultureInfo: null); } }); - if (entry.Exception != null && throwIfNotFound) - { - throw entry.Exception; - } - return entry.CultureInfo; } @@ -47,14 +45,7 @@ namespace Microsoft.AspNet.Localization.Internal CultureInfo = cultureInfo; } - public CacheEntry(Exception exception) - { - Exception = exception; - } - public CultureInfo CultureInfo { get; } - - public Exception Exception { get; } } } } diff --git a/src/Microsoft.AspNet.Localization/Internal/CultureInfoList.cs b/src/Microsoft.AspNet.Localization/Internal/CultureInfoList.cs new file mode 100644 index 0000000000..1d178738eb --- /dev/null +++ b/src/Microsoft.AspNet.Localization/Internal/CultureInfoList.cs @@ -0,0 +1,436 @@ +// 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.Internal +{ + public static class CultureInfoList + { + // 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 + { + "", + "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" + }; + } +} From 306d71ef43a7767b9ab6e298300bf3eed927099f Mon Sep 17 00:00:00 2001 From: damianedwards Date: Mon, 11 May 2015 15:28:50 -0700 Subject: [PATCH 013/390] Moved some things around: - Options are now given to strategies - Options only contains properties relevant to the middleware itself. Strategies can have their own properties, e.g. AcceptLanguageHeaderRequestCultureStrategy limits the number of values in the header to try - Strategies now derive from common base class and validate against the options, e.g. app specified supported cultures - Renamed RequestLocalizationMiddlewareOptions to RequestLocalizationOptions - Fixed missing doc comments --- samples/LocalizationSample/Startup.cs | 14 ++++- ...eptLanguageHeaderRequestCultureStrategy.cs | 14 +++-- .../CookieRequestCultureStrategy.cs | 10 ++- .../CustomRequestCultureStrategy.cs | 4 +- .../IApplicationBuilderExtensions.cs | 4 +- .../IRequestCultureFeature.cs | 2 +- .../IRequestCultureStrategy.cs | 8 +++ .../QueryStringRequestCultureStrategy.cs | 10 ++- .../RequestCultureStrategy.cs | 61 +++++++++++++++++++ .../RequestLocalizationMiddleware.cs | 18 +----- ...tions.cs => RequestLocalizationOptions.cs} | 19 ++---- 11 files changed, 118 insertions(+), 46 deletions(-) create mode 100644 src/Microsoft.AspNet.Localization/RequestCultureStrategy.cs rename src/Microsoft.AspNet.Localization/{RequestLocalizationMiddlewareOptions.cs => RequestLocalizationOptions.cs} (82%) diff --git a/samples/LocalizationSample/Startup.cs b/samples/LocalizationSample/Startup.cs index 2183eaf3af..2e2b93bd8b 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; @@ -20,10 +21,19 @@ namespace LocalizationSample public void Configure(IApplicationBuilder app, IStringLocalizer SR) { - var options = new RequestLocalizationMiddlewareOptions + 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") + //} }; app.UseRequestLocalization(options); diff --git a/src/Microsoft.AspNet.Localization/AcceptLanguageHeaderRequestCultureStrategy.cs b/src/Microsoft.AspNet.Localization/AcceptLanguageHeaderRequestCultureStrategy.cs index 030e294361..12d62ead83 100644 --- a/src/Microsoft.AspNet.Localization/AcceptLanguageHeaderRequestCultureStrategy.cs +++ b/src/Microsoft.AspNet.Localization/AcceptLanguageHeaderRequestCultureStrategy.cs @@ -1,7 +1,6 @@ // 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.Globalization; using System.Linq; using Microsoft.AspNet.Http; using Microsoft.AspNet.Localization.Internal; @@ -13,7 +12,7 @@ namespace Microsoft.AspNet.Localization /// /// Determines the culture information for a request via the value of the Accept-Language header. /// - public class AcceptLanguageHeaderRequestCultureStrategy : IRequestCultureStrategy + public class AcceptLanguageHeaderRequestCultureStrategy : RequestCultureStrategy { /// /// The maximum number of values in the Accept-Language header to attempt to create a @@ -23,7 +22,7 @@ namespace Microsoft.AspNet.Localization public int MaximumAcceptLanguageHeaderValuesToTry { get; set; } = 3; /// - public RequestCulture DetermineRequestCulture([NotNull] HttpContext httpContext) + public override RequestCulture DetermineRequestCulture([NotNull] HttpContext httpContext) { var acceptLanguageHeader = httpContext.Request.GetTypedHeaders().AcceptLanguage; @@ -53,7 +52,14 @@ namespace Microsoft.AspNet.Localization var culture = CultureInfoCache.GetCultureInfo(language.Value); if (culture != null) { - return RequestCulture.GetRequestCulture(culture); + var requestCulture = RequestCulture.GetRequestCulture(culture); + + requestCulture = ValidateRequestCulture(requestCulture); + + if (requestCulture != null) + { + return requestCulture; + } } } } diff --git a/src/Microsoft.AspNet.Localization/CookieRequestCultureStrategy.cs b/src/Microsoft.AspNet.Localization/CookieRequestCultureStrategy.cs index ac9a1b3089..ead04c8320 100644 --- a/src/Microsoft.AspNet.Localization/CookieRequestCultureStrategy.cs +++ b/src/Microsoft.AspNet.Localization/CookieRequestCultureStrategy.cs @@ -11,7 +11,7 @@ namespace Microsoft.AspNet.Localization /// /// Determines the culture information for a request via the value of a cookie. /// - public class CookieRequestCultureStrategy : IRequestCultureStrategy + public class CookieRequestCultureStrategy : RequestCultureStrategy { private static readonly char[] _cookieSeparator = new[] { '|' }; private static readonly string _culturePrefix = "c="; @@ -24,7 +24,7 @@ namespace Microsoft.AspNet.Localization public string CookieName { get; set; } = DefaultCookieName; /// - public RequestCulture DetermineRequestCulture([NotNull] HttpContext httpContext) + public override RequestCulture DetermineRequestCulture([NotNull] HttpContext httpContext) { var cookie = httpContext.Request.Cookies[CookieName]; @@ -33,7 +33,11 @@ namespace Microsoft.AspNet.Localization return null; } - return ParseCookieValue(cookie); + var requestCulture = ParseCookieValue(cookie); + + requestCulture = ValidateRequestCulture(requestCulture); + + return requestCulture; } /// diff --git a/src/Microsoft.AspNet.Localization/CustomRequestCultureStrategy.cs b/src/Microsoft.AspNet.Localization/CustomRequestCultureStrategy.cs index 7ddee3f627..5a5c87a825 100644 --- a/src/Microsoft.AspNet.Localization/CustomRequestCultureStrategy.cs +++ b/src/Microsoft.AspNet.Localization/CustomRequestCultureStrategy.cs @@ -10,7 +10,7 @@ namespace Microsoft.AspNet.Localization /// /// Determines the culture information for a request via the configured delegate. /// - public class CustomRequestCultureStrategy : IRequestCultureStrategy + public class CustomRequestCultureStrategy : RequestCultureStrategy { private readonly Func _strategy; @@ -24,7 +24,7 @@ namespace Microsoft.AspNet.Localization } /// - public RequestCulture DetermineRequestCulture([NotNull] HttpContext httpContext) + public override RequestCulture DetermineRequestCulture([NotNull] HttpContext httpContext) { return _strategy(httpContext); } diff --git a/src/Microsoft.AspNet.Localization/IApplicationBuilderExtensions.cs b/src/Microsoft.AspNet.Localization/IApplicationBuilderExtensions.cs index d3d7a65077..c383cf7117 100644 --- a/src/Microsoft.AspNet.Localization/IApplicationBuilderExtensions.cs +++ b/src/Microsoft.AspNet.Localization/IApplicationBuilderExtensions.cs @@ -19,7 +19,7 @@ namespace Microsoft.AspNet.Builder /// The . public static IApplicationBuilder UseRequestLocalization([NotNull] this IApplicationBuilder builder) { - var options = new RequestLocalizationMiddlewareOptions(); + var options = new RequestLocalizationOptions(); return UseRequestLocalization(builder, options); } @@ -33,7 +33,7 @@ namespace Microsoft.AspNet.Builder /// The . public static IApplicationBuilder UseRequestLocalization( [NotNull] this IApplicationBuilder builder, - [NotNull] RequestLocalizationMiddlewareOptions options) + [NotNull] RequestLocalizationOptions options) { return builder.UseMiddleware(options); } diff --git a/src/Microsoft.AspNet.Localization/IRequestCultureFeature.cs b/src/Microsoft.AspNet.Localization/IRequestCultureFeature.cs index 73cd046f50..44cc487a63 100644 --- a/src/Microsoft.AspNet.Localization/IRequestCultureFeature.cs +++ b/src/Microsoft.AspNet.Localization/IRequestCultureFeature.cs @@ -16,7 +16,7 @@ namespace Microsoft.AspNet.Localization /// /// The that determined the request's culture information. /// If the value is null then no strategy was used and the request's culture was set to the value of - /// . + /// . /// IRequestCultureStrategy Strategy { get; } } diff --git a/src/Microsoft.AspNet.Localization/IRequestCultureStrategy.cs b/src/Microsoft.AspNet.Localization/IRequestCultureStrategy.cs index eb96670263..9e71c133f4 100644 --- a/src/Microsoft.AspNet.Localization/IRequestCultureStrategy.cs +++ b/src/Microsoft.AspNet.Localization/IRequestCultureStrategy.cs @@ -10,6 +10,14 @@ namespace Microsoft.AspNet.Localization /// public interface IRequestCultureStrategy { + /// + /// Implements the strategy to determine the culture of the given request. + /// + /// The for the request. + /// + /// The determined . + /// Returns null if the strategy couldn't determine a . + /// RequestCulture DetermineRequestCulture(HttpContext httpContext); } } diff --git a/src/Microsoft.AspNet.Localization/QueryStringRequestCultureStrategy.cs b/src/Microsoft.AspNet.Localization/QueryStringRequestCultureStrategy.cs index 80a7ba7f1f..19c3c077b6 100644 --- a/src/Microsoft.AspNet.Localization/QueryStringRequestCultureStrategy.cs +++ b/src/Microsoft.AspNet.Localization/QueryStringRequestCultureStrategy.cs @@ -10,7 +10,7 @@ namespace Microsoft.AspNet.Localization /// /// Determines the culture information for a request via values in the query string. /// - public class QueryStringRequestCultureStrategy : IRequestCultureStrategy + public class QueryStringRequestCultureStrategy : RequestCultureStrategy { /// /// The key that contains the culture name. @@ -26,7 +26,7 @@ namespace Microsoft.AspNet.Localization public string UIQueryStringKey { get; set; } = "ui-culture"; /// - public RequestCulture DetermineRequestCulture([NotNull] HttpContext httpContext) + public override RequestCulture DetermineRequestCulture([NotNull] HttpContext httpContext) { var request = httpContext.Request; if (!request.QueryString.HasValue) @@ -67,7 +67,11 @@ namespace Microsoft.AspNet.Localization return null; } - return RequestCulture.GetRequestCulture(culture, uiCulture); + var requestCulture = RequestCulture.GetRequestCulture(culture, uiCulture); + + requestCulture = ValidateRequestCulture(requestCulture); + + return requestCulture; } } } diff --git a/src/Microsoft.AspNet.Localization/RequestCultureStrategy.cs b/src/Microsoft.AspNet.Localization/RequestCultureStrategy.cs new file mode 100644 index 0000000000..422f5d6f9e --- /dev/null +++ b/src/Microsoft.AspNet.Localization/RequestCultureStrategy.cs @@ -0,0 +1,61 @@ +// 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.Http; +using Microsoft.Framework.Internal; + +namespace Microsoft.AspNet.Localization +{ + /// + /// An abstract base class strategy for determining the culture information of an . + /// + public abstract class RequestCultureStrategy : IRequestCultureStrategy + { + /// + /// The current options for the . + /// + public RequestLocalizationOptions Options { get; set; } + + /// + public abstract RequestCulture DetermineRequestCulture(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 = RequestCulture.GetRequestCulture(Options.DefaultRequestCulture.Culture, result.UICulture); + } + + if (Options.SupportedUICultures != null && !Options.SupportedUICultures.Contains(result.UICulture)) + { + result = RequestCulture.GetRequestCulture(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 1cd4ea2be1..2ccfe10418 100644 --- a/src/Microsoft.AspNet.Localization/RequestLocalizationMiddleware.cs +++ b/src/Microsoft.AspNet.Localization/RequestLocalizationMiddleware.cs @@ -17,14 +17,14 @@ namespace Microsoft.AspNet.Localization public class RequestLocalizationMiddleware { private readonly RequestDelegate _next; - private readonly RequestLocalizationMiddlewareOptions _options; + private readonly RequestLocalizationOptions _options; /// /// Creates a new . /// /// The representing the next middleware in the pipeline. /// - public RequestLocalizationMiddleware([NotNull] RequestDelegate next, [NotNull] RequestLocalizationMiddlewareOptions options) + public RequestLocalizationMiddleware([NotNull] RequestDelegate next, [NotNull] RequestLocalizationOptions options) { _next = next; _options = options; @@ -56,20 +56,6 @@ namespace Microsoft.AspNet.Localization } } - if (_options.SupportedCultures != null) - { - // Ensure that selected cultures are in the supported list and if not, set them to the default - if (!_options.SupportedCultures.Contains(requestCulture.Culture)) - { - requestCulture = RequestCulture.GetRequestCulture(_options.DefaultRequestCulture.Culture, requestCulture.UICulture); - } - - if (!_options.SupportedUICultures.Contains(requestCulture.UICulture)) - { - requestCulture = RequestCulture.GetRequestCulture(requestCulture.Culture, _options.DefaultRequestCulture.UICulture); - } - } - context.SetFeature(new RequestCultureFeature(requestCulture, winningStrategy)); SetCurrentThreadCulture(requestCulture); diff --git a/src/Microsoft.AspNet.Localization/RequestLocalizationMiddlewareOptions.cs b/src/Microsoft.AspNet.Localization/RequestLocalizationOptions.cs similarity index 82% rename from src/Microsoft.AspNet.Localization/RequestLocalizationMiddlewareOptions.cs rename to src/Microsoft.AspNet.Localization/RequestLocalizationOptions.cs index 0f24d18cf7..5279c98d8e 100644 --- a/src/Microsoft.AspNet.Localization/RequestLocalizationMiddlewareOptions.cs +++ b/src/Microsoft.AspNet.Localization/RequestLocalizationOptions.cs @@ -10,30 +10,23 @@ namespace Microsoft.AspNet.Localization /// /// Specifies options for the . /// - public class RequestLocalizationMiddlewareOptions + public class RequestLocalizationOptions { /// - /// Creates a new with default values. + /// Creates a new with default values. /// - public RequestLocalizationMiddlewareOptions() + public RequestLocalizationOptions() { DefaultRequestCulture = RequestCulture.GetRequestCulture(CultureInfo.CurrentCulture, CultureInfo.CurrentUICulture); RequestCultureStrategies = new List { - new QueryStringRequestCultureStrategy(), - new CookieRequestCultureStrategy(), - new AcceptLanguageHeaderRequestCultureStrategy { MaximumAcceptLanguageHeaderValuesToTry = MaximumAcceptLanguageHeaderValuesToTry } + new QueryStringRequestCultureStrategy { Options = this }, + new CookieRequestCultureStrategy { Options = this }, + new AcceptLanguageHeaderRequestCultureStrategy { Options = this } }; } - /// - /// The maximum number of values in the Accept-Language header to attempt to create a - /// from for the current request. - /// Defaults to 3. - /// - public int MaximumAcceptLanguageHeaderValuesToTry { get; set; } = 3; - /// /// The default to use. This value will be used if none of the configured /// options result in a non-null result. From bcabbbc9b83b3032e4cb8ace7f22ac0d6da78f12 Mon Sep 17 00:00:00 2001 From: damianedwards Date: Mon, 11 May 2015 17:14:57 -0700 Subject: [PATCH 014/390] Fix Travis by pinning to earlier Mono --- .travis.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.travis.yml b/.travis.yml index 947bf868ee..65b9f21ec2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,6 @@ language: csharp sudo: false +mono: + - 3.12.0 script: - ./build.sh --quiet verify \ No newline at end of file From 5f12c3b1e37037b970c082e0d219d40b5f987dda Mon Sep 17 00:00:00 2001 From: David Fowler Date: Mon, 11 May 2015 17:55:59 -0700 Subject: [PATCH 015/390] Made build.sh executable --- build.sh | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 build.sh diff --git a/build.sh b/build.sh old mode 100644 new mode 100755 From d22adcbef022291fd87eff4d28179e952ea480c4 Mon Sep 17 00:00:00 2001 From: damianedwards Date: Mon, 11 May 2015 18:05:13 -0700 Subject: [PATCH 016/390] Remove RequestCulture caching as perf testing shows it has no benefit --- ...eptLanguageHeaderRequestCultureStrategy.cs | 4 +- .../CookieRequestCultureStrategy.cs | 2 +- .../QueryStringRequestCultureStrategy.cs | 2 +- .../RequestCulture.cs | 79 +++---------------- .../RequestCultureStrategy.cs | 4 +- .../RequestLocalizationMiddleware.cs | 2 +- .../RequestLocalizationOptions.cs | 2 +- 7 files changed, 21 insertions(+), 74 deletions(-) diff --git a/src/Microsoft.AspNet.Localization/AcceptLanguageHeaderRequestCultureStrategy.cs b/src/Microsoft.AspNet.Localization/AcceptLanguageHeaderRequestCultureStrategy.cs index 12d62ead83..dbc9883cd1 100644 --- a/src/Microsoft.AspNet.Localization/AcceptLanguageHeaderRequestCultureStrategy.cs +++ b/src/Microsoft.AspNet.Localization/AcceptLanguageHeaderRequestCultureStrategy.cs @@ -15,7 +15,7 @@ namespace Microsoft.AspNet.Localization public class AcceptLanguageHeaderRequestCultureStrategy : RequestCultureStrategy { /// - /// The maximum number of values in the Accept-Language header to attempt to create a + /// The maximum number of values in the Accept-Language header to attempt to create a /// from for the current request. /// Defaults to 3. /// @@ -52,7 +52,7 @@ namespace Microsoft.AspNet.Localization var culture = CultureInfoCache.GetCultureInfo(language.Value); if (culture != null) { - var requestCulture = RequestCulture.GetRequestCulture(culture); + var requestCulture = new RequestCulture(culture); requestCulture = ValidateRequestCulture(requestCulture); diff --git a/src/Microsoft.AspNet.Localization/CookieRequestCultureStrategy.cs b/src/Microsoft.AspNet.Localization/CookieRequestCultureStrategy.cs index ead04c8320..97c70d82ed 100644 --- a/src/Microsoft.AspNet.Localization/CookieRequestCultureStrategy.cs +++ b/src/Microsoft.AspNet.Localization/CookieRequestCultureStrategy.cs @@ -98,7 +98,7 @@ namespace Microsoft.AspNet.Localization return null; } - return RequestCulture.GetRequestCulture(culture, uiCulture); + return new RequestCulture(culture, uiCulture); } } } diff --git a/src/Microsoft.AspNet.Localization/QueryStringRequestCultureStrategy.cs b/src/Microsoft.AspNet.Localization/QueryStringRequestCultureStrategy.cs index 19c3c077b6..64a5a3b520 100644 --- a/src/Microsoft.AspNet.Localization/QueryStringRequestCultureStrategy.cs +++ b/src/Microsoft.AspNet.Localization/QueryStringRequestCultureStrategy.cs @@ -67,7 +67,7 @@ namespace Microsoft.AspNet.Localization return null; } - var requestCulture = RequestCulture.GetRequestCulture(culture, uiCulture); + var requestCulture = new RequestCulture(culture, uiCulture); requestCulture = ValidateRequestCulture(requestCulture); diff --git a/src/Microsoft.AspNet.Localization/RequestCulture.cs b/src/Microsoft.AspNet.Localization/RequestCulture.cs index 9f0e2754db..ec109e6d8e 100644 --- a/src/Microsoft.AspNet.Localization/RequestCulture.cs +++ b/src/Microsoft.AspNet.Localization/RequestCulture.cs @@ -1,7 +1,6 @@ // 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.Concurrent; using System.Globalization; using Microsoft.Framework.Internal; @@ -12,15 +11,24 @@ namespace Microsoft.AspNet.Localization /// public class RequestCulture { - private static readonly ConcurrentDictionary _cache = new ConcurrentDictionary(); - - private RequestCulture([NotNull] CultureInfo culture) + /// + /// Creates a new object has its and + /// properties set to the same value. + /// + /// The for the request. + public RequestCulture([NotNull] CultureInfo culture) : this (culture, culture) { } - private RequestCulture([NotNull] CultureInfo culture, [NotNull] CultureInfo uiCulture) + /// + /// Creates a new object has its and + /// properties set to the respective values provided. + /// + /// The for the request to be used for formatting. + /// The for the request to be used for text, i.e. language. + public RequestCulture([NotNull] CultureInfo culture, [NotNull] CultureInfo uiCulture) { Culture = culture; UICulture = uiCulture; @@ -35,66 +43,5 @@ namespace Microsoft.AspNet.Localization /// Gets the for the request to be used for text, i.e. language; /// public CultureInfo UICulture { get; } - - /// - /// Gets a cached instance that has its and - /// properties set to the same value. - /// - /// The for the request. - public static RequestCulture GetRequestCulture([NotNull] CultureInfo culture) - { - return GetRequestCulture(culture, culture); - } - - /// - /// Gets a cached instance that has its and - /// properties set to the respective values provided. - /// - /// The for the request to be used for formatting. - /// The for the request to be used for text, i.e. language. - /// - public static RequestCulture GetRequestCulture([NotNull] CultureInfo culture, [NotNull] CultureInfo uiCulture) - { - var key = new CacheKey(culture, uiCulture); - return _cache.GetOrAdd(key, k => new RequestCulture(culture, uiCulture)); - } - - private class CacheKey - { - private readonly int _hashCode; - - public CacheKey(CultureInfo culture, CultureInfo uiCulture) - { - Culture = culture; - UICulture = uiCulture; - _hashCode = new { Culture, UICulture }.GetHashCode(); - } - - public CultureInfo Culture { get; } - - public CultureInfo UICulture { get; } - - public bool Equals(CacheKey other) - { - return Culture == other.Culture && UICulture == other.UICulture; - } - - public override bool Equals(object obj) - { - var other = obj as CacheKey; - - if (other != null) - { - return Equals(other); - } - - return base.Equals(obj); - } - - public override int GetHashCode() - { - return _hashCode; - } - } } } diff --git a/src/Microsoft.AspNet.Localization/RequestCultureStrategy.cs b/src/Microsoft.AspNet.Localization/RequestCultureStrategy.cs index 422f5d6f9e..17afbcd397 100644 --- a/src/Microsoft.AspNet.Localization/RequestCultureStrategy.cs +++ b/src/Microsoft.AspNet.Localization/RequestCultureStrategy.cs @@ -41,12 +41,12 @@ namespace Microsoft.AspNet.Localization if (Options.SupportedCultures != null && !Options.SupportedCultures.Contains(result.Culture)) { - result = RequestCulture.GetRequestCulture(Options.DefaultRequestCulture.Culture, result.UICulture); + result = new RequestCulture(Options.DefaultRequestCulture.Culture, result.UICulture); } if (Options.SupportedUICultures != null && !Options.SupportedUICultures.Contains(result.UICulture)) { - result = RequestCulture.GetRequestCulture(result.Culture, Options.DefaultRequestCulture.UICulture); + result = new RequestCulture(result.Culture, Options.DefaultRequestCulture.UICulture); } if (requestCulture.Culture != result.Culture && requestCulture.UICulture != result.UICulture) diff --git a/src/Microsoft.AspNet.Localization/RequestLocalizationMiddleware.cs b/src/Microsoft.AspNet.Localization/RequestLocalizationMiddleware.cs index 2ccfe10418..c640fcbb8f 100644 --- a/src/Microsoft.AspNet.Localization/RequestLocalizationMiddleware.cs +++ b/src/Microsoft.AspNet.Localization/RequestLocalizationMiddleware.cs @@ -38,7 +38,7 @@ namespace Microsoft.AspNet.Localization public async Task Invoke([NotNull] HttpContext context) { var requestCulture = _options.DefaultRequestCulture ?? - RequestCulture.GetRequestCulture(CultureInfo.CurrentCulture, CultureInfo.CurrentUICulture); + new RequestCulture(CultureInfo.CurrentCulture, CultureInfo.CurrentUICulture); IRequestCultureStrategy winningStrategy = null; diff --git a/src/Microsoft.AspNet.Localization/RequestLocalizationOptions.cs b/src/Microsoft.AspNet.Localization/RequestLocalizationOptions.cs index 5279c98d8e..74eb335921 100644 --- a/src/Microsoft.AspNet.Localization/RequestLocalizationOptions.cs +++ b/src/Microsoft.AspNet.Localization/RequestLocalizationOptions.cs @@ -17,7 +17,7 @@ namespace Microsoft.AspNet.Localization /// public RequestLocalizationOptions() { - DefaultRequestCulture = RequestCulture.GetRequestCulture(CultureInfo.CurrentCulture, CultureInfo.CurrentUICulture); + DefaultRequestCulture = new RequestCulture(CultureInfo.CurrentCulture, CultureInfo.CurrentUICulture); RequestCultureStrategies = new List { From 21dc2909a14751f98c2c6c689433a1d411ea8312 Mon Sep 17 00:00:00 2001 From: damianedwards Date: Mon, 11 May 2015 18:29:03 -0700 Subject: [PATCH 017/390] Made IRequestCultureStrategy async to support things like user profile lookup --- samples/LocalizationSample/Startup.cs | 8 ++++++++ .../AcceptLanguageHeaderRequestCultureStrategy.cs | 9 +++++---- .../CookieRequestCultureStrategy.cs | 7 ++++--- .../CustomRequestCultureStrategy.cs | 7 ++++--- .../IRequestCultureStrategy.cs | 3 ++- .../QueryStringRequestCultureStrategy.cs | 11 ++++++----- .../RequestCultureStrategy.cs | 3 ++- .../RequestLocalizationMiddleware.cs | 2 +- 8 files changed, 32 insertions(+), 18 deletions(-) diff --git a/samples/LocalizationSample/Startup.cs b/samples/LocalizationSample/Startup.cs index 2e2b93bd8b..ea9fb7c6c3 100644 --- a/samples/LocalizationSample/Startup.cs +++ b/samples/LocalizationSample/Startup.cs @@ -35,6 +35,14 @@ namespace LocalizationSample // new CultureInfo("en-AU") //} }; + + // Optionally create an app-specific strategy with just a delegate, e.g. look up user preference from DB. + // Inserting it as position 0 ensures it has priority over any of the default strategies. + //options.RequestCultureStrategies.Insert(0, new CustomRequestCultureStrategy(async context => + //{ + + //})); + app.UseRequestLocalization(options); app.Use(async (context, next) => diff --git a/src/Microsoft.AspNet.Localization/AcceptLanguageHeaderRequestCultureStrategy.cs b/src/Microsoft.AspNet.Localization/AcceptLanguageHeaderRequestCultureStrategy.cs index dbc9883cd1..45ac85e2d3 100644 --- a/src/Microsoft.AspNet.Localization/AcceptLanguageHeaderRequestCultureStrategy.cs +++ b/src/Microsoft.AspNet.Localization/AcceptLanguageHeaderRequestCultureStrategy.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.Linq; +using System.Threading.Tasks; using Microsoft.AspNet.Http; using Microsoft.AspNet.Localization.Internal; using Microsoft.Framework.Internal; @@ -22,13 +23,13 @@ namespace Microsoft.AspNet.Localization public int MaximumAcceptLanguageHeaderValuesToTry { get; set; } = 3; /// - public override RequestCulture DetermineRequestCulture([NotNull] HttpContext httpContext) + public override Task DetermineRequestCulture([NotNull] HttpContext httpContext) { var acceptLanguageHeader = httpContext.Request.GetTypedHeaders().AcceptLanguage; if (acceptLanguageHeader == null || acceptLanguageHeader.Count == 0) { - return null; + return Task.FromResult((RequestCulture)null); } var languages = acceptLanguageHeader.AsEnumerable(); @@ -58,13 +59,13 @@ namespace Microsoft.AspNet.Localization if (requestCulture != null) { - return requestCulture; + return Task.FromResult(requestCulture); } } } } - return null; + return Task.FromResult((RequestCulture)null); } } } diff --git a/src/Microsoft.AspNet.Localization/CookieRequestCultureStrategy.cs b/src/Microsoft.AspNet.Localization/CookieRequestCultureStrategy.cs index 97c70d82ed..68e7c96d06 100644 --- a/src/Microsoft.AspNet.Localization/CookieRequestCultureStrategy.cs +++ b/src/Microsoft.AspNet.Localization/CookieRequestCultureStrategy.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.Threading.Tasks; using Microsoft.AspNet.Http; using Microsoft.AspNet.Localization.Internal; using Microsoft.Framework.Internal; @@ -24,20 +25,20 @@ namespace Microsoft.AspNet.Localization public string CookieName { get; set; } = DefaultCookieName; /// - public override RequestCulture DetermineRequestCulture([NotNull] HttpContext httpContext) + public override Task DetermineRequestCulture([NotNull] HttpContext httpContext) { var cookie = httpContext.Request.Cookies[CookieName]; if (cookie == null) { - return null; + return Task.FromResult((RequestCulture)null); } var requestCulture = ParseCookieValue(cookie); requestCulture = ValidateRequestCulture(requestCulture); - return requestCulture; + return Task.FromResult(requestCulture); } /// diff --git a/src/Microsoft.AspNet.Localization/CustomRequestCultureStrategy.cs b/src/Microsoft.AspNet.Localization/CustomRequestCultureStrategy.cs index 5a5c87a825..65546bed44 100644 --- a/src/Microsoft.AspNet.Localization/CustomRequestCultureStrategy.cs +++ b/src/Microsoft.AspNet.Localization/CustomRequestCultureStrategy.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.Threading.Tasks; using Microsoft.AspNet.Http; using Microsoft.Framework.Internal; @@ -12,19 +13,19 @@ namespace Microsoft.AspNet.Localization /// public class CustomRequestCultureStrategy : RequestCultureStrategy { - private readonly Func _strategy; + private readonly Func> _strategy; /// /// Creates a new using the specified delegate. /// /// The strategy delegate. - public CustomRequestCultureStrategy([NotNull] Func strategy) + public CustomRequestCultureStrategy([NotNull] Func> strategy) { _strategy = strategy; } /// - public override RequestCulture DetermineRequestCulture([NotNull] HttpContext httpContext) + public override Task DetermineRequestCulture([NotNull] HttpContext httpContext) { return _strategy(httpContext); } diff --git a/src/Microsoft.AspNet.Localization/IRequestCultureStrategy.cs b/src/Microsoft.AspNet.Localization/IRequestCultureStrategy.cs index 9e71c133f4..91001e57cc 100644 --- a/src/Microsoft.AspNet.Localization/IRequestCultureStrategy.cs +++ b/src/Microsoft.AspNet.Localization/IRequestCultureStrategy.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.Threading.Tasks; using Microsoft.AspNet.Http; namespace Microsoft.AspNet.Localization @@ -18,6 +19,6 @@ namespace Microsoft.AspNet.Localization /// The determined . /// Returns null if the strategy couldn't determine a . /// - RequestCulture DetermineRequestCulture(HttpContext httpContext); + Task DetermineRequestCulture(HttpContext httpContext); } } diff --git a/src/Microsoft.AspNet.Localization/QueryStringRequestCultureStrategy.cs b/src/Microsoft.AspNet.Localization/QueryStringRequestCultureStrategy.cs index 64a5a3b520..4a9a0b9140 100644 --- a/src/Microsoft.AspNet.Localization/QueryStringRequestCultureStrategy.cs +++ b/src/Microsoft.AspNet.Localization/QueryStringRequestCultureStrategy.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.Threading.Tasks; using Microsoft.AspNet.Http; using Microsoft.AspNet.Localization.Internal; using Microsoft.Framework.Internal; @@ -26,12 +27,12 @@ namespace Microsoft.AspNet.Localization public string UIQueryStringKey { get; set; } = "ui-culture"; /// - public override RequestCulture DetermineRequestCulture([NotNull] HttpContext httpContext) + public override Task DetermineRequestCulture([NotNull] HttpContext httpContext) { var request = httpContext.Request; if (!request.QueryString.HasValue) { - return null; + return Task.FromResult((RequestCulture)null); } string queryCulture = null; @@ -50,7 +51,7 @@ namespace Microsoft.AspNet.Localization if (queryCulture == null && queryUICulture == null) { // No values specified for either so no match - return null; + return Task.FromResult((RequestCulture)null); } if (queryCulture != null && queryUICulture == null) @@ -64,14 +65,14 @@ namespace Microsoft.AspNet.Localization if (culture == null || uiCulture == null) { - return null; + return Task.FromResult((RequestCulture)null); } var requestCulture = new RequestCulture(culture, uiCulture); requestCulture = ValidateRequestCulture(requestCulture); - return requestCulture; + return Task.FromResult(requestCulture); } } } diff --git a/src/Microsoft.AspNet.Localization/RequestCultureStrategy.cs b/src/Microsoft.AspNet.Localization/RequestCultureStrategy.cs index 17afbcd397..6d30805119 100644 --- a/src/Microsoft.AspNet.Localization/RequestCultureStrategy.cs +++ b/src/Microsoft.AspNet.Localization/RequestCultureStrategy.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.Threading.Tasks; using Microsoft.AspNet.Http; using Microsoft.Framework.Internal; @@ -17,7 +18,7 @@ namespace Microsoft.AspNet.Localization public RequestLocalizationOptions Options { get; set; } /// - public abstract RequestCulture DetermineRequestCulture(HttpContext httpContext); + public abstract Task DetermineRequestCulture(HttpContext httpContext); /// /// Determines if the given is valid according to the currently configured. diff --git a/src/Microsoft.AspNet.Localization/RequestLocalizationMiddleware.cs b/src/Microsoft.AspNet.Localization/RequestLocalizationMiddleware.cs index c640fcbb8f..54600e17a4 100644 --- a/src/Microsoft.AspNet.Localization/RequestLocalizationMiddleware.cs +++ b/src/Microsoft.AspNet.Localization/RequestLocalizationMiddleware.cs @@ -46,7 +46,7 @@ namespace Microsoft.AspNet.Localization { foreach (var strategy in _options.RequestCultureStrategies) { - var result = strategy.DetermineRequestCulture(context); + var result = await strategy.DetermineRequestCulture(context); if (result != null) { requestCulture = result; From f4d18f5a5d2b14027a6c8bb8de52746f86bbc8a4 Mon Sep 17 00:00:00 2001 From: Chris R Date: Tue, 12 May 2015 06:39:15 -0700 Subject: [PATCH 018/390] Use wildcard dependency version. --- src/CultureInfoGenerator/project.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/CultureInfoGenerator/project.json b/src/CultureInfoGenerator/project.json index f88d824517..ea3b4ec35e 100644 --- a/src/CultureInfoGenerator/project.json +++ b/src/CultureInfoGenerator/project.json @@ -3,7 +3,7 @@ "description": "Generates a list of known culture names from the OS using CultureInfo.GetCultures. This tool is intended to be run on Windows using full .NET Framework.", "dependencies": { - "Microsoft.Framework.Runtime.Abstractions": "1.0.0-beta5-11739" + "Microsoft.Framework.Runtime.Abstractions": "1.0.0-*" }, "commands": { From 805dc3f25b5dab4a0e91ee3ae60f806633cd7f46 Mon Sep 17 00:00:00 2001 From: Eilon Lipton Date: Tue, 12 May 2015 11:53:31 -0700 Subject: [PATCH 019/390] Update Home master -> Home dev --- CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index eac4268e4c..64ff041d5c 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,4 +1,4 @@ Contributing ====== -Information on contributing to this repo is in the [Contributing Guide](https://github.com/aspnet/Home/blob/master/CONTRIBUTING.md) in the Home repo. +Information on contributing to this repo is in the [Contributing Guide](https://github.com/aspnet/Home/blob/dev/CONTRIBUTING.md) in the Home repo. From 2431e1a542bf4e79eb309e26eb0b00197221b1b2 Mon Sep 17 00:00:00 2001 From: damianedwards Date: Wed, 13 May 2015 15:50:00 -0700 Subject: [PATCH 020/390] Move CultureInfoCache into its own package. --- Localization.sln | 7 ++++++ src/CultureInfoGenerator/Program.cs | 22 +++++++++++++------ ...eptLanguageHeaderRequestCultureStrategy.cs | 2 +- .../CookieRequestCultureStrategy.cs | 2 +- .../QueryStringRequestCultureStrategy.cs | 2 +- .../project.json | 6 ++--- .../CultureInfoCache.cs | 19 ++++++++++++---- .../CultureInfoList.cs | 20 ++++++++++++----- ...ework.Globalization.CultureInfoCache.xproj | 20 +++++++++++++++++ .../project.json | 19 ++++++++++++++++ 10 files changed, 96 insertions(+), 23 deletions(-) rename src/{Microsoft.AspNet.Localization/Internal => Microsoft.Framework.Globalization.CultureInfoCache}/CultureInfoCache.cs (70%) rename src/{Microsoft.AspNet.Localization/Internal => Microsoft.Framework.Globalization.CultureInfoCache}/CultureInfoList.cs (91%) create mode 100644 src/Microsoft.Framework.Globalization.CultureInfoCache/Microsoft.Framework.Globalization.CultureInfoCache.xproj create mode 100644 src/Microsoft.Framework.Globalization.CultureInfoCache/project.json diff --git a/Localization.sln b/Localization.sln index cf7a3d95c6..841e5c453f 100644 --- a/Localization.sln +++ b/Localization.sln @@ -22,6 +22,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution 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.Framework.Globalization.CultureInfoCache", "src\Microsoft.Framework.Globalization.CultureInfoCache\Microsoft.Framework.Globalization.CultureInfoCache.xproj", "{F3988D3A-A4C8-4FD7-BAFE-13E0D0A1659A}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -48,6 +50,10 @@ Global {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 + {F3988D3A-A4C8-4FD7-BAFE-13E0D0A1659A}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -58,5 +64,6 @@ Global {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} EndGlobalSection EndGlobal diff --git a/src/CultureInfoGenerator/Program.cs b/src/CultureInfoGenerator/Program.cs index 7843c928d7..f45c1c7f1c 100644 --- a/src/CultureInfoGenerator/Program.cs +++ b/src/CultureInfoGenerator/Program.cs @@ -22,7 +22,7 @@ namespace CultureInfoGenerator public void Main(string[] args) { - var outputFilePath = args.Length > 0 ? args[0] : Path.Combine(_appPath, "../Microsoft.AspNet.Localization/Internal/CultureInfoList.cs"); + var outputFilePath = args.Length > 0 ? args[0] : Path.Combine(_appPath, "../Microsoft.Framework.Globalization.CultureCache/CultureInfoList.cs"); var netFxVersion = Get45or451FromRegistry(); var windowsVersion = Environment.OSVersion; @@ -31,16 +31,24 @@ namespace CultureInfoGenerator writer.WriteLine($@"// 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.Collections.Generic; -namespace Microsoft.AspNet.Localization.Internal +namespace Microsoft.Framework.Globalization {{ - public static class CultureInfoList + /// + /// 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 {_appName} using .NET Framework {netFxVersion} on - // {windowsVersion}. - // As new versions of .NET Framework and Windows are released, this list should be regenerated to ensure it - // contains the latest culture names. + /// + /// This list of known cultures was generated by {_appName} using .NET Framework {netFxVersion} on + /// {windowsVersion}. + /// 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 {{" ); diff --git a/src/Microsoft.AspNet.Localization/AcceptLanguageHeaderRequestCultureStrategy.cs b/src/Microsoft.AspNet.Localization/AcceptLanguageHeaderRequestCultureStrategy.cs index 45ac85e2d3..eef5cf798b 100644 --- a/src/Microsoft.AspNet.Localization/AcceptLanguageHeaderRequestCultureStrategy.cs +++ b/src/Microsoft.AspNet.Localization/AcceptLanguageHeaderRequestCultureStrategy.cs @@ -4,7 +4,7 @@ using System.Linq; using System.Threading.Tasks; using Microsoft.AspNet.Http; -using Microsoft.AspNet.Localization.Internal; +using Microsoft.Framework.Globalization; using Microsoft.Framework.Internal; using Microsoft.Net.Http.Headers; diff --git a/src/Microsoft.AspNet.Localization/CookieRequestCultureStrategy.cs b/src/Microsoft.AspNet.Localization/CookieRequestCultureStrategy.cs index 68e7c96d06..e1422656d7 100644 --- a/src/Microsoft.AspNet.Localization/CookieRequestCultureStrategy.cs +++ b/src/Microsoft.AspNet.Localization/CookieRequestCultureStrategy.cs @@ -4,7 +4,7 @@ using System; using System.Threading.Tasks; using Microsoft.AspNet.Http; -using Microsoft.AspNet.Localization.Internal; +using Microsoft.Framework.Globalization; using Microsoft.Framework.Internal; namespace Microsoft.AspNet.Localization diff --git a/src/Microsoft.AspNet.Localization/QueryStringRequestCultureStrategy.cs b/src/Microsoft.AspNet.Localization/QueryStringRequestCultureStrategy.cs index 4a9a0b9140..5c6ebab146 100644 --- a/src/Microsoft.AspNet.Localization/QueryStringRequestCultureStrategy.cs +++ b/src/Microsoft.AspNet.Localization/QueryStringRequestCultureStrategy.cs @@ -3,7 +3,7 @@ using System.Threading.Tasks; using Microsoft.AspNet.Http; -using Microsoft.AspNet.Localization.Internal; +using Microsoft.Framework.Globalization; using Microsoft.Framework.Internal; namespace Microsoft.AspNet.Localization diff --git a/src/Microsoft.AspNet.Localization/project.json b/src/Microsoft.AspNet.Localization/project.json index 069295bd00..e127f985f7 100644 --- a/src/Microsoft.AspNet.Localization/project.json +++ b/src/Microsoft.AspNet.Localization/project.json @@ -3,9 +3,10 @@ "description": "Middleware for automatically applying culture information to HTTP requests.", "dependencies": { + "Microsoft.AspNet.Http.Extensions": "1.0.0-*", + "Microsoft.Framework.Globalization.CultureInfoCache": "1.0.0-*", "Microsoft.Framework.Localization.Abstractions": "1.0.0-*", - "Microsoft.Framework.NotNullAttribute.Internal": { "type": "build", "version": "1.0.0-*" }, - "Microsoft.AspNet.Http.Extensions": "1.0.0-*" + "Microsoft.Framework.NotNullAttribute.Internal": { "type": "build", "version": "1.0.0-*" } }, "frameworks": { @@ -13,7 +14,6 @@ "dnxcore50": { "dependencies": { "System.Collections": "4.0.10-*", - "System.Collections.Concurrent": "4.0.10-*", "System.Linq": "4.0.0-*", "System.Globalization": "4.0.10-*", "System.Threading": "4.0.10-*", diff --git a/src/Microsoft.AspNet.Localization/Internal/CultureInfoCache.cs b/src/Microsoft.Framework.Globalization.CultureInfoCache/CultureInfoCache.cs similarity index 70% rename from src/Microsoft.AspNet.Localization/Internal/CultureInfoCache.cs rename to src/Microsoft.Framework.Globalization.CultureInfoCache/CultureInfoCache.cs index 9e8ee0a993..906fe777d2 100644 --- a/src/Microsoft.AspNet.Localization/Internal/CultureInfoCache.cs +++ b/src/Microsoft.Framework.Globalization.CultureInfoCache/CultureInfoCache.cs @@ -1,21 +1,32 @@ // 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.Globalization; -namespace Microsoft.AspNet.Localization.Internal +namespace Microsoft.Framework.Globalization { - public static class CultureInfoCache + /// + /// Provides read-only cached instances of . + /// + public static partial class CultureInfoCache { private static readonly ConcurrentDictionary _cache = new ConcurrentDictionary(); + /// + /// Gets a read-only cached for the specified name. Only names that exist in + /// will be used. + /// + /// The culture name. + /// + /// A read-only cached or null a match wasn't found in + /// . + /// public static CultureInfo GetCultureInfo(string name) { // 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 || !CultureInfoList.KnownCultureNames.Contains(name)) + if (name == null || !KnownCultureNames.Contains(name)) { return null; } diff --git a/src/Microsoft.AspNet.Localization/Internal/CultureInfoList.cs b/src/Microsoft.Framework.Globalization.CultureInfoCache/CultureInfoList.cs similarity index 91% rename from src/Microsoft.AspNet.Localization/Internal/CultureInfoList.cs rename to src/Microsoft.Framework.Globalization.CultureInfoCache/CultureInfoList.cs index 1d178738eb..c29f8dcb28 100644 --- a/src/Microsoft.AspNet.Localization/Internal/CultureInfoList.cs +++ b/src/Microsoft.Framework.Globalization.CultureInfoCache/CultureInfoList.cs @@ -1,16 +1,24 @@ // 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.Collections.Generic; -namespace Microsoft.AspNet.Localization.Internal +namespace Microsoft.Framework.Globalization { - public static class CultureInfoList + /// + /// 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. + /// + /// 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 { "", diff --git a/src/Microsoft.Framework.Globalization.CultureInfoCache/Microsoft.Framework.Globalization.CultureInfoCache.xproj b/src/Microsoft.Framework.Globalization.CultureInfoCache/Microsoft.Framework.Globalization.CultureInfoCache.xproj new file mode 100644 index 0000000000..9b2995ad7a --- /dev/null +++ b/src/Microsoft.Framework.Globalization.CultureInfoCache/Microsoft.Framework.Globalization.CultureInfoCache.xproj @@ -0,0 +1,20 @@ + + + + 14.0 + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) + + + + + f3988d3a-a4c8-4fd7-bafe-13e0d0a1659a + Microsoft.Framework.Globalization.CultureInfoList + ..\..\artifacts\obj\$(MSBuildProjectName) + ..\..\artifacts\bin\$(MSBuildProjectName)\ + + + + 2.0 + + + diff --git a/src/Microsoft.Framework.Globalization.CultureInfoCache/project.json b/src/Microsoft.Framework.Globalization.CultureInfoCache/project.json new file mode 100644 index 0000000000..25b5bca79f --- /dev/null +++ b/src/Microsoft.Framework.Globalization.CultureInfoCache/project.json @@ -0,0 +1,19 @@ +{ + "version": "1.0.0-*", + "description": "Provides cached instances of CultureInfo using a generated list of known culture names for use in scenarios where unbounded CultureInfo creation is undesirable.", + + "dependencies": { + }, + + "frameworks": { + "dnx451": { }, + "dnxcore50": { + "dependencies": { + "System.Collections": "4.0.10-*", + "System.Collections.Concurrent": "4.0.10-*", + "System.Globalization": "4.0.10-*", + "Microsoft.CSharp": "4.0.0-*" + } + } + } +} From ecfb7f342a79de16cda039980ca2b2123c014669 Mon Sep 17 00:00:00 2001 From: damianedwards Date: Fri, 15 May 2015 12:14:14 -0700 Subject: [PATCH 021/390] Sort project.json dependencies --- samples/LocalizationSample/project.json | 2 +- src/Microsoft.AspNet.Localization/project.json | 6 +++--- .../project.json | 4 ++-- .../project.json | 4 ++-- src/Microsoft.Framework.Localization/project.json | 4 ++-- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/samples/LocalizationSample/project.json b/samples/LocalizationSample/project.json index 9ab24a6df7..fc4e2a0b98 100644 --- a/samples/LocalizationSample/project.json +++ b/samples/LocalizationSample/project.json @@ -3,9 +3,9 @@ "version": "1.0.0-*", "dependencies": { + "Microsoft.AspNet.Localization": "1.0.0-*", "Microsoft.AspNet.Server.IIS": "1.0.0-*", "Microsoft.AspNet.Server.WebListener": "1.0.0-*", - "Microsoft.AspNet.Localization": "1.0.0-*", "Microsoft.Framework.Localization": "1.0.0-*" }, diff --git a/src/Microsoft.AspNet.Localization/project.json b/src/Microsoft.AspNet.Localization/project.json index e127f985f7..4eaed6e9a4 100644 --- a/src/Microsoft.AspNet.Localization/project.json +++ b/src/Microsoft.AspNet.Localization/project.json @@ -13,11 +13,11 @@ "dnx451": { }, "dnxcore50": { "dependencies": { + "Microsoft.CSharp": "4.0.0-*", "System.Collections": "4.0.10-*", - "System.Linq": "4.0.0-*", "System.Globalization": "4.0.10-*", - "System.Threading": "4.0.10-*", - "Microsoft.CSharp": "4.0.0-*" + "System.Linq": "4.0.0-*", + "System.Threading": "4.0.10-*" } } } diff --git a/src/Microsoft.Framework.Globalization.CultureInfoCache/project.json b/src/Microsoft.Framework.Globalization.CultureInfoCache/project.json index 25b5bca79f..2a10252ec1 100644 --- a/src/Microsoft.Framework.Globalization.CultureInfoCache/project.json +++ b/src/Microsoft.Framework.Globalization.CultureInfoCache/project.json @@ -9,10 +9,10 @@ "dnx451": { }, "dnxcore50": { "dependencies": { + "Microsoft.CSharp": "4.0.0-*", "System.Collections": "4.0.10-*", "System.Collections.Concurrent": "4.0.10-*", - "System.Globalization": "4.0.10-*", - "Microsoft.CSharp": "4.0.0-*" + "System.Globalization": "4.0.10-*" } } } diff --git a/src/Microsoft.Framework.Localization.Abstractions/project.json b/src/Microsoft.Framework.Localization.Abstractions/project.json index b725e19028..8245e1c5e9 100644 --- a/src/Microsoft.Framework.Localization.Abstractions/project.json +++ b/src/Microsoft.Framework.Localization.Abstractions/project.json @@ -10,11 +10,11 @@ "dnx451": { }, "dnxcore50": { "dependencies": { + "Microsoft.CSharp": "4.0.0-*", "System.Collections": "4.0.10-*", "System.Globalization": "4.0.10-*", "System.Reflection": "4.0.10-*", - "System.Runtime": "4.0.20-*", - "Microsoft.CSharp": "4.0.0-*" + "System.Runtime": "4.0.20-*" } } } diff --git a/src/Microsoft.Framework.Localization/project.json b/src/Microsoft.Framework.Localization/project.json index c0ad0046fd..e7145276d5 100644 --- a/src/Microsoft.Framework.Localization/project.json +++ b/src/Microsoft.Framework.Localization/project.json @@ -14,13 +14,13 @@ "dnx451": { }, "dnxcore50": { "dependencies": { + "Microsoft.CSharp": "4.0.0-*", "System.Collections": "4.0.10-*", "System.Collections.Concurrent": "4.0.10-*", "System.Globalization": "4.0.10-*", "System.Linq": "4.0.0-*", "System.Resources.ResourceManager": "4.0.0-*", - "System.Threading": "4.0.10-*", - "Microsoft.CSharp": "4.0.0-*" + "System.Threading": "4.0.10-*" } } } From 2ba7049648d6fcc53581e9aed8341ce574385e7f Mon Sep 17 00:00:00 2001 From: damianedwards Date: Fri, 15 May 2015 12:37:02 -0700 Subject: [PATCH 022/390] Moved methods off of IStringLocalizer and into extension methods --- .../IStringLocalizer.cs | 17 +---------- .../IStringLocalizerExtensions.cs | 29 +++++++++++++++++++ .../ResourceManagerStringLocalizer.cs | 26 ++++++++--------- ...sourceManagerWithCultureStringLocalizer.cs | 26 ++++++++--------- 4 files changed, 56 insertions(+), 42 deletions(-) create mode 100644 src/Microsoft.Framework.Localization.Abstractions/IStringLocalizerExtensions.cs diff --git a/src/Microsoft.Framework.Localization.Abstractions/IStringLocalizer.cs b/src/Microsoft.Framework.Localization.Abstractions/IStringLocalizer.cs index 4dddf75f25..aad6b9bb3e 100644 --- a/src/Microsoft.Framework.Localization.Abstractions/IStringLocalizer.cs +++ b/src/Microsoft.Framework.Localization.Abstractions/IStringLocalizer.cs @@ -25,22 +25,7 @@ namespace Microsoft.Framework.Localization /// The values to format the string with. /// The formatted string resource as a . LocalizedString this[string name, params object[] arguments] { get; } - - /// - /// Gets the string resource with the given name. - /// - /// The name of the string resource. - /// The string resource as a . - LocalizedString GetString(string name); - - /// - /// Gets the string resource with the given name and formatted with the supplied arguments. - /// - /// The name of the string resource. - /// The values to format the string with. - /// The formatted string resource as a . - LocalizedString GetString(string name, params object[] arguments); - + /// /// Creates a new for a specific . /// diff --git a/src/Microsoft.Framework.Localization.Abstractions/IStringLocalizerExtensions.cs b/src/Microsoft.Framework.Localization.Abstractions/IStringLocalizerExtensions.cs new file mode 100644 index 0000000000..f38388a371 --- /dev/null +++ b/src/Microsoft.Framework.Localization.Abstractions/IStringLocalizerExtensions.cs @@ -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. + +namespace Microsoft.Framework.Localization +{ + public static class IStringLocalizerExtensions + { + /// + /// Gets the string resource with the given name. + /// + /// The name of the string resource. + /// The string resource as a . + public static LocalizedString GetString(this IStringLocalizer stringLocalizer, string name) + { + return stringLocalizer[name]; + } + + /// + /// Gets the string resource with the given name and formatted with the supplied arguments. + /// + /// The name of the string resource. + /// The values to format the string with. + /// The formatted string resource as a . + public static LocalizedString GetString(this IStringLocalizer stringLocalizer, string name, params object[] arguments) + { + return stringLocalizer[name, arguments]; + } + } +} diff --git a/src/Microsoft.Framework.Localization/ResourceManagerStringLocalizer.cs b/src/Microsoft.Framework.Localization/ResourceManagerStringLocalizer.cs index f17517a168..ae0772ac64 100644 --- a/src/Microsoft.Framework.Localization/ResourceManagerStringLocalizer.cs +++ b/src/Microsoft.Framework.Localization/ResourceManagerStringLocalizer.cs @@ -51,26 +51,26 @@ namespace Microsoft.Framework.Localization /// The base name of the embedded resource in the that contains the strings. /// protected string ResourceBaseName { get; } - - /// - public virtual LocalizedString this[[NotNull] string name] => GetString(name); /// - public virtual LocalizedString this[[NotNull] string name, params object[] arguments] => GetString(name, arguments); - - /// - public virtual LocalizedString GetString([NotNull] string name) + public virtual LocalizedString this[[NotNull] string name] { - var value = GetStringSafely(name, null); - return new LocalizedString(name, value ?? name, resourceNotFound: value == null); + get + { + var value = GetStringSafely(name, null); + return new LocalizedString(name, value ?? name, resourceNotFound: value == null); + } } /// - public virtual LocalizedString GetString([NotNull] string name, params object[] arguments) + public virtual LocalizedString this[[NotNull] string name, params object[] arguments] { - var format = GetStringSafely(name, null); - var value = string.Format(format ?? name, arguments); - return new LocalizedString(name, value, resourceNotFound: format == null); + get + { + var format = GetStringSafely(name, null); + var value = string.Format(format ?? name, arguments); + return new LocalizedString(name, value, resourceNotFound: format == null); + } } /// diff --git a/src/Microsoft.Framework.Localization/ResourceManagerWithCultureStringLocalizer.cs b/src/Microsoft.Framework.Localization/ResourceManagerWithCultureStringLocalizer.cs index 8e9027af76..c8d2f36dbb 100644 --- a/src/Microsoft.Framework.Localization/ResourceManagerWithCultureStringLocalizer.cs +++ b/src/Microsoft.Framework.Localization/ResourceManagerWithCultureStringLocalizer.cs @@ -36,24 +36,24 @@ namespace Microsoft.Framework.Localization } /// - public override LocalizedString this[[NotNull] string name] => GetString(name); - - /// - public override LocalizedString this[[NotNull] string name, params object[] arguments] => GetString(name, arguments); - - /// - public override LocalizedString GetString([NotNull] string name) + public override LocalizedString this[[NotNull] string name] { - var value = GetStringSafely(name, _culture); - return new LocalizedString(name, value ?? name); + get + { + var value = GetStringSafely(name, _culture); + return new LocalizedString(name, value ?? name); + } } /// - public override LocalizedString GetString([NotNull] string name, params object[] arguments) + public override LocalizedString this[[NotNull] string name, params object[] arguments] { - var format = GetStringSafely(name, _culture); - var value = string.Format(_culture, format ?? name, arguments); - return new LocalizedString(name, value ?? name, resourceNotFound: format == null); + get + { + var format = GetStringSafely(name, _culture); + var value = string.Format(_culture, format ?? name, arguments); + return new LocalizedString(name, value ?? name, resourceNotFound: format == null); + } } /// From cda137ca9fa6dcf869837c777c618e35ba496fb1 Mon Sep 17 00:00:00 2001 From: damianedwards Date: Mon, 18 May 2015 10:11:01 -0700 Subject: [PATCH 023/390] Drop leading "I" from IStringLocalizerExtensions --- ...tringLocalizerExtensions.cs => StringLocalizerExtensions.cs} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename src/Microsoft.Framework.Localization.Abstractions/{IStringLocalizerExtensions.cs => StringLocalizerExtensions.cs} (96%) diff --git a/src/Microsoft.Framework.Localization.Abstractions/IStringLocalizerExtensions.cs b/src/Microsoft.Framework.Localization.Abstractions/StringLocalizerExtensions.cs similarity index 96% rename from src/Microsoft.Framework.Localization.Abstractions/IStringLocalizerExtensions.cs rename to src/Microsoft.Framework.Localization.Abstractions/StringLocalizerExtensions.cs index f38388a371..78ed2625a0 100644 --- a/src/Microsoft.Framework.Localization.Abstractions/IStringLocalizerExtensions.cs +++ b/src/Microsoft.Framework.Localization.Abstractions/StringLocalizerExtensions.cs @@ -3,7 +3,7 @@ namespace Microsoft.Framework.Localization { - public static class IStringLocalizerExtensions + public static class StringLocalizerExtensions { /// /// Gets the string resource with the given name. From 6abeff9f0987170c24c34f9ad1933f37acdd2faf Mon Sep 17 00:00:00 2001 From: damianedwards Date: Mon, 18 May 2015 12:12:47 -0700 Subject: [PATCH 024/390] Add missing [NotNull] attributes on StringLocalizerExtensions --- .../StringLocalizerExtensions.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Microsoft.Framework.Localization.Abstractions/StringLocalizerExtensions.cs b/src/Microsoft.Framework.Localization.Abstractions/StringLocalizerExtensions.cs index 78ed2625a0..c3cd604ad6 100644 --- a/src/Microsoft.Framework.Localization.Abstractions/StringLocalizerExtensions.cs +++ b/src/Microsoft.Framework.Localization.Abstractions/StringLocalizerExtensions.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 Microsoft.Framework.Internal; + namespace Microsoft.Framework.Localization { public static class StringLocalizerExtensions @@ -10,7 +12,7 @@ namespace Microsoft.Framework.Localization /// /// The name of the string resource. /// The string resource as a . - public static LocalizedString GetString(this IStringLocalizer stringLocalizer, string name) + public static LocalizedString GetString([NotNull] this IStringLocalizer stringLocalizer, [NotNull] string name) { return stringLocalizer[name]; } @@ -21,7 +23,7 @@ namespace Microsoft.Framework.Localization /// The name of the string resource. /// The values to format the string with. /// The formatted string resource as a . - public static LocalizedString GetString(this IStringLocalizer stringLocalizer, string name, params object[] arguments) + public static LocalizedString GetString([NotNull] this IStringLocalizer stringLocalizer, [NotNull] string name, params object[] arguments) { return stringLocalizer[name, arguments]; } From b86af22c4d9b25739379036d936929c02e8ade64 Mon Sep 17 00:00:00 2001 From: damianedwards Date: Mon, 18 May 2015 16:48:59 -0700 Subject: [PATCH 025/390] Fix inconsistent terminology name/key --- .../StringLocalizerOfT.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Microsoft.Framework.Localization.Abstractions/StringLocalizerOfT.cs b/src/Microsoft.Framework.Localization.Abstractions/StringLocalizerOfT.cs index 8801cdf234..2a5334cce0 100644 --- a/src/Microsoft.Framework.Localization.Abstractions/StringLocalizerOfT.cs +++ b/src/Microsoft.Framework.Localization.Abstractions/StringLocalizerOfT.cs @@ -29,18 +29,18 @@ namespace Microsoft.Framework.Localization public virtual IStringLocalizer WithCulture(CultureInfo culture) => _localizer.WithCulture(culture); /// - public virtual LocalizedString this[[NotNull] string key] => _localizer[key]; + public virtual LocalizedString this[[NotNull] string name] => _localizer[name]; /// - public virtual LocalizedString this[[NotNull] string key, params object[] arguments] => - _localizer[key, arguments]; + public virtual LocalizedString this[[NotNull] string name, params object[] arguments] => + _localizer[name, arguments]; /// - public virtual LocalizedString GetString([NotNull] string key) => _localizer.GetString(key); + public virtual LocalizedString GetString([NotNull] string name) => _localizer.GetString(name); /// - public virtual LocalizedString GetString([NotNull] string key, params object[] arguments) => - _localizer.GetString(key, arguments); + public virtual LocalizedString GetString([NotNull] string name, params object[] arguments) => + _localizer.GetString(name, arguments); /// public IEnumerator GetEnumerator() => _localizer.GetEnumerator(); From f6119d48568bda63ba2540323a57f78a6eb16e52 Mon Sep 17 00:00:00 2001 From: Troy Dai Date: Wed, 20 May 2015 12:41:17 -0700 Subject: [PATCH 026/390] Update references Microsoft.Framework.NotNullAttribute.Internal => Microsoft.Framework.NotNullAttribute.Sources --- src/Microsoft.AspNet.Localization/project.json | 2 +- src/Microsoft.Framework.Localization.Abstractions/project.json | 2 +- src/Microsoft.Framework.Localization/project.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Microsoft.AspNet.Localization/project.json b/src/Microsoft.AspNet.Localization/project.json index 4eaed6e9a4..0a8b395c28 100644 --- a/src/Microsoft.AspNet.Localization/project.json +++ b/src/Microsoft.AspNet.Localization/project.json @@ -6,7 +6,7 @@ "Microsoft.AspNet.Http.Extensions": "1.0.0-*", "Microsoft.Framework.Globalization.CultureInfoCache": "1.0.0-*", "Microsoft.Framework.Localization.Abstractions": "1.0.0-*", - "Microsoft.Framework.NotNullAttribute.Internal": { "type": "build", "version": "1.0.0-*" } + "Microsoft.Framework.NotNullAttribute.Sources": { "type": "build", "version": "1.0.0-*" } }, "frameworks": { diff --git a/src/Microsoft.Framework.Localization.Abstractions/project.json b/src/Microsoft.Framework.Localization.Abstractions/project.json index 8245e1c5e9..bb9914834f 100644 --- a/src/Microsoft.Framework.Localization.Abstractions/project.json +++ b/src/Microsoft.Framework.Localization.Abstractions/project.json @@ -3,7 +3,7 @@ "description": "Abstractions of application localization services.", "dependencies": { - "Microsoft.Framework.NotNullAttribute.Internal": { "type": "build", "version": "1.0.0-*" } + "Microsoft.Framework.NotNullAttribute.Sources": { "type": "build", "version": "1.0.0-*" } }, "frameworks": { diff --git a/src/Microsoft.Framework.Localization/project.json b/src/Microsoft.Framework.Localization/project.json index e7145276d5..5e53c266ea 100644 --- a/src/Microsoft.Framework.Localization/project.json +++ b/src/Microsoft.Framework.Localization/project.json @@ -5,7 +5,7 @@ "dependencies": { "Microsoft.Framework.DependencyInjection.Abstractions": "1.0.0-*", "Microsoft.Framework.Localization.Abstractions": "1.0.0-*", - "Microsoft.Framework.NotNullAttribute.Internal": { "type": "build", "version": "1.0.0-*" }, + "Microsoft.Framework.NotNullAttribute.Sources": { "type": "build", "version": "1.0.0-*" }, "Microsoft.Framework.Runtime.Abstractions": "1.0.0-*", "System.Resources.ReaderWriter": "4.0.0-*" }, From 9384848cc7c0a4faf92c3d2157fcd7621ac3fe57 Mon Sep 17 00:00:00 2001 From: damianedwards Date: Wed, 20 May 2015 14:41:36 -0700 Subject: [PATCH 027/390] Cache walk of the culture tree when building resource name list: - #15 --- Localization.sln | 9 ++ NuGet.Config | 1 + .../Properties/AssemblyInfo.cs | 6 + .../ResourceManagerStringLocalizer.cs | 80 +++++----- ...icrosoft.Framework.Localization.Test.xproj | 21 +++ .../ResourceManagerStringLocalizerTest.cs | 139 ++++++++++++++++++ .../project.json | 16 ++ 7 files changed, 227 insertions(+), 45 deletions(-) create mode 100644 src/Microsoft.Framework.Localization/Properties/AssemblyInfo.cs create mode 100644 test/Microsoft.Framework.Localization.Test/Microsoft.Framework.Localization.Test.xproj create mode 100644 test/Microsoft.Framework.Localization.Test/ResourceManagerStringLocalizerTest.cs create mode 100644 test/Microsoft.Framework.Localization.Test/project.json diff --git a/Localization.sln b/Localization.sln index 841e5c453f..f5fe65e09e 100644 --- a/Localization.sln +++ b/Localization.sln @@ -24,6 +24,10 @@ Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "CultureInfoGenerator", "src EndProject Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.Framework.Globalization.CultureInfoCache", "src\Microsoft.Framework.Globalization.CultureInfoCache\Microsoft.Framework.Globalization.CultureInfoCache.xproj", "{F3988D3A-A4C8-4FD7-BAFE-13E0D0A1659A}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{B723DB83-A670-4BCB-95FB-195361331AD2}" +EndProject +Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.Framework.Localization.Test", "test\Microsoft.Framework.Localization.Test\Microsoft.Framework.Localization.Test.xproj", "{287AD58D-DF34-4F16-8616-FD78FA1CADF9}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -54,6 +58,10 @@ Global {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 {F3988D3A-A4C8-4FD7-BAFE-13E0D0A1659A}.Release|Any CPU.Build.0 = Release|Any CPU + {287AD58D-DF34-4F16-8616-FD78FA1CADF9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {287AD58D-DF34-4F16-8616-FD78FA1CADF9}.Debug|Any CPU.Build.0 = Debug|Any CPU + {287AD58D-DF34-4F16-8616-FD78FA1CADF9}.Release|Any CPU.ActiveCfg = Release|Any CPU + {287AD58D-DF34-4F16-8616-FD78FA1CADF9}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -65,5 +73,6 @@ Global {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} EndGlobalSection EndGlobal diff --git a/NuGet.Config b/NuGet.Config index da57d47267..da5e353ffb 100644 --- a/NuGet.Config +++ b/NuGet.Config @@ -2,6 +2,7 @@ + \ No newline at end of file diff --git a/src/Microsoft.Framework.Localization/Properties/AssemblyInfo.cs b/src/Microsoft.Framework.Localization/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..e3c2061307 --- /dev/null +++ b/src/Microsoft.Framework.Localization/Properties/AssemblyInfo.cs @@ -0,0 +1,6 @@ +// 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.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("Microsoft.Framework.Localization.Test")] diff --git a/src/Microsoft.Framework.Localization/ResourceManagerStringLocalizer.cs b/src/Microsoft.Framework.Localization/ResourceManagerStringLocalizer.cs index ae0772ac64..1ff97dfb8d 100644 --- a/src/Microsoft.Framework.Localization/ResourceManagerStringLocalizer.cs +++ b/src/Microsoft.Framework.Localization/ResourceManagerStringLocalizer.cs @@ -18,9 +18,12 @@ namespace Microsoft.Framework.Localization /// public class ResourceManagerStringLocalizer : IStringLocalizer { - private readonly ConcurrentDictionary _missingManifestCache = - new ConcurrentDictionary(); + private readonly ConcurrentDictionary _missingManifestCache = + new ConcurrentDictionary(); + private static readonly ConcurrentDictionary> _resourceNamesCache = + new ConcurrentDictionary>(); + /// /// Creates a new . /// @@ -96,9 +99,10 @@ namespace Microsoft.Framework.Localization /// The name of the string resource. /// The to get the string for. /// The resource string, or null if none was found. - protected string GetStringSafely([NotNull] string name, [NotNull] CultureInfo culture) + protected string GetStringSafely([NotNull] string name, CultureInfo culture) { - var cacheKey = new MissingManifestCacheKey(name, culture ?? CultureInfo.CurrentUICulture); + var cacheKey = $"name={name}&culture={(culture ?? CultureInfo.CurrentUICulture).Name}"; + if (_missingManifestCache.ContainsKey(cacheKey)) { return null; @@ -136,7 +140,6 @@ namespace Microsoft.Framework.Localization /// The . protected IEnumerator GetEnumerator([NotNull] CultureInfo culture) { - // TODO: I'm sure something here should be cached, probably the whole result var resourceNames = GetResourceNamesFromCultureHierarchy(culture); foreach (var name in resourceNames) @@ -146,6 +149,12 @@ namespace Microsoft.Framework.Localization } } + // Internal to allow testing + internal static void ClearResourceNamesCache() + { + _resourceNamesCache.Clear(); + } + private IEnumerable GetResourceNamesFromCultureHierarchy(CultureInfo startingCulture) { var currentCulture = startingCulture; @@ -155,20 +164,10 @@ namespace Microsoft.Framework.Localization { try { - var resourceStreamName = ResourceBaseName; - if (!string.IsNullOrEmpty(currentCulture.Name)) + var cultureResourceNames = GetResourceNamesForCulture(currentCulture); + foreach (var resourceName in cultureResourceNames) { - resourceStreamName += "." + currentCulture.Name; - } - resourceStreamName += ".resources"; - using (var cultureResourceStream = ResourceAssembly.GetManifestResourceStream(resourceStreamName)) - using (var resources = new ResourceReader(cultureResourceStream)) - { - foreach (DictionaryEntry entry in resources) - { - var resourceName = (string)entry.Key; - resourceNames.Add(resourceName); - } + resourceNames.Add(resourceName); } } catch (MissingManifestResourceException) { } @@ -185,43 +184,34 @@ namespace Microsoft.Framework.Localization return resourceNames; } - private class MissingManifestCacheKey : IEquatable + private IList GetResourceNamesForCulture(CultureInfo culture) { - private readonly int _hashCode; - - public MissingManifestCacheKey(string name, CultureInfo culture) + var resourceStreamName = ResourceBaseName; + if (!string.IsNullOrEmpty(culture.Name)) { - Name = name; - CultureInfo = culture; - _hashCode = new { Name, CultureInfo }.GetHashCode(); + resourceStreamName += "." + culture.Name; } + resourceStreamName += ".resources"; - public string Name { get; } + var cacheKey = $"assembly={ResourceAssembly.FullName};resourceStreamName={resourceStreamName}"; - public CultureInfo CultureInfo { get; } - - public bool Equals(MissingManifestCacheKey other) + var cultureResourceNames = _resourceNamesCache.GetOrAdd(cacheKey, key => { - return string.Equals(Name, other.Name, StringComparison.Ordinal) - && CultureInfo == other.CultureInfo; - } - - public override bool Equals(object obj) - { - var other = obj as MissingManifestCacheKey; - - if (other != null) + var names = new List(); + using (var cultureResourceStream = ResourceAssembly.GetManifestResourceStream(key)) + using (var resources = new ResourceReader(cultureResourceStream)) { - return Equals(other); + foreach (DictionaryEntry entry in resources) + { + var resourceName = (string)entry.Key; + names.Add(resourceName); + } } - return base.Equals(obj); - } + return names; + }); - public override int GetHashCode() - { - return _hashCode; - } + return cultureResourceNames; } } } \ No newline at end of file diff --git a/test/Microsoft.Framework.Localization.Test/Microsoft.Framework.Localization.Test.xproj b/test/Microsoft.Framework.Localization.Test/Microsoft.Framework.Localization.Test.xproj new file mode 100644 index 0000000000..f307c04f02 --- /dev/null +++ b/test/Microsoft.Framework.Localization.Test/Microsoft.Framework.Localization.Test.xproj @@ -0,0 +1,21 @@ + + + + 14.0 + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) + + + + 287ad58d-df34-4f16-8616-fd78fa1cadf9 + Microsoft.Framework.Localization.Test + ..\..\artifacts\obj\$(MSBuildProjectName) + ..\..\artifacts\bin\$(MSBuildProjectName)\ + + + 2.0 + + + + + + \ No newline at end of file diff --git a/test/Microsoft.Framework.Localization.Test/ResourceManagerStringLocalizerTest.cs b/test/Microsoft.Framework.Localization.Test/ResourceManagerStringLocalizerTest.cs new file mode 100644 index 0000000000..ce88695724 --- /dev/null +++ b/test/Microsoft.Framework.Localization.Test/ResourceManagerStringLocalizerTest.cs @@ -0,0 +1,139 @@ +// 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.Globalization; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Resources; +using Moq; +using Xunit; + +namespace Microsoft.Framework.Localization.Test +{ + public class ResourceManagerStringLocalizerTest + { + [Fact] + public void EnumeratorCachesCultureWalkForSameAssembly() + { + // Arrange + ResourceManagerStringLocalizer.ClearResourceNamesCache(); + var resourceManager = new Mock(); + var resourceAssembly = new Mock(); + resourceAssembly.Setup(rm => rm.GetManifestResourceStream(It.IsAny())) + .Returns(() => MakeResourceStream()); + var baseName = "test"; + var localizer1 = new ResourceManagerStringLocalizer( + resourceManager.Object, + resourceAssembly.Object, + baseName); + var localizer2 = new ResourceManagerStringLocalizer( + resourceManager.Object, + resourceAssembly.Object, + baseName); + + // Act + for (int i = 0; i < 5; i++) + { + localizer1.ToList(); + localizer2.ToList(); + } + + // Assert + var expectedCallCount = GetCultureInfoDepth(CultureInfo.CurrentUICulture); + resourceAssembly.Verify( + rm => rm.GetManifestResourceStream(It.IsAny()), + Times.Exactly(expectedCallCount)); + } + + [Fact] + public void EnumeratorCacheIsScopedByAssembly() + { + // Arrange + ResourceManagerStringLocalizer.ClearResourceNamesCache(); + var resourceManager = new Mock(); + var resourceAssembly1 = new Mock(); + resourceAssembly1.CallBase = true; + var resourceAssembly2 = new Mock(); + resourceAssembly2.CallBase = true; + resourceAssembly1.Setup(rm => rm.GetManifestResourceStream(It.IsAny())) + .Returns(() => MakeResourceStream()); + resourceAssembly2.Setup(rm => rm.GetManifestResourceStream(It.IsAny())) + .Returns(() => MakeResourceStream()); + var baseName = "test"; + var localizer1 = new ResourceManagerStringLocalizer( + resourceManager.Object, + resourceAssembly1.Object, + baseName); + var localizer2 = new ResourceManagerStringLocalizer( + resourceManager.Object, + resourceAssembly2.Object, + baseName); + + // Act + localizer1.ToList(); + localizer2.ToList(); + + // Assert + var expectedCallCount = GetCultureInfoDepth(CultureInfo.CurrentUICulture); + resourceAssembly1.Verify( + rm => rm.GetManifestResourceStream(It.IsAny()), + Times.Exactly(expectedCallCount)); + resourceAssembly2.Verify( + rm => rm.GetManifestResourceStream(It.IsAny()), + Times.Exactly(expectedCallCount)); + } + + private static Stream MakeResourceStream() + { + var stream = new MemoryStream(); + var resourceWriter = new ResourceWriter(stream); + resourceWriter.AddResource("TestName", "value"); + resourceWriter.Generate(); + stream.Position = 0; + return stream; + } + + private static int GetCultureInfoDepth(CultureInfo culture) + { + var result = 0; + var currentCulture = culture; + + while (true) + { + result++; + + if (currentCulture == currentCulture.Parent) + { + break; + } + + currentCulture = currentCulture.Parent; + } + + return result; + } + + public class TestAssembly1 : Assembly + { + public override string FullName + { + get + { + return nameof(TestAssembly1); + } + } + } + + public class TestAssembly2 : Assembly + { + public override string FullName + { + get + { + return nameof(TestAssembly2); + } + } + } + } +} diff --git a/test/Microsoft.Framework.Localization.Test/project.json b/test/Microsoft.Framework.Localization.Test/project.json new file mode 100644 index 0000000000..45f8dd778c --- /dev/null +++ b/test/Microsoft.Framework.Localization.Test/project.json @@ -0,0 +1,16 @@ +{ + "dependencies": { + "Moq": "4.2.1502.911", + "xunit": "2.1.0-*", + "xunit.runner.dnx": "2.1.0-*", + "Microsoft.Framework.Localization": "1.0.0-*" + }, + + "commands": { + "test": "xunit.runner.dnx" + }, + + "frameworks": { + "dnx451": { } + } +} From 4ba159afe791acda823b9564e54e628d388012b2 Mon Sep 17 00:00:00 2001 From: damianedwards Date: Wed, 20 May 2015 11:56:47 -0700 Subject: [PATCH 028/390] Removed Moq from tests & enabled .NET Core - Introduced AssemblyWrapper to enable testing - Cleaned up properties on ResourceManagerStringLocalizer - #15 Change "Strategy" to "Provider" - #19 --- samples/LocalizationSample/Startup.cs | 12 +-- ...ptLanguageHeaderRequestCultureProvider.cs} | 2 +- ...egy.cs => CookieRequestCultureProvider.cs} | 2 +- ...egy.cs => CustomRequestCultureProvider.cs} | 14 +-- .../IRequestCultureFeature.cs | 6 +- ...Strategy.cs => IRequestCultureProvider.cs} | 8 +- ...s => QueryStringRequestCultureProvider.cs} | 2 +- .../RequestCultureFeature.cs | 6 +- ...eStrategy.cs => RequestCultureProvider.cs} | 5 +- .../RequestLocalizationMiddleware.cs | 12 +-- .../RequestLocalizationOptions.cs | 20 ++--- .../Internal/AssemblyWrapper.cs | 23 +++++ .../ResourceManagerStringLocalizer.cs | 62 +++++++------ .../ResourceManagerStringLocalizerFactory.cs | 8 +- ...sourceManagerWithCultureStringLocalizer.cs | 15 +++- .../ResourceManagerStringLocalizerTest.cs | 87 ++++++++----------- .../project.json | 25 +++--- 17 files changed, 166 insertions(+), 143 deletions(-) rename src/Microsoft.AspNet.Localization/{AcceptLanguageHeaderRequestCultureStrategy.cs => AcceptLanguageHeaderRequestCultureProvider.cs} (97%) rename src/Microsoft.AspNet.Localization/{CookieRequestCultureStrategy.cs => CookieRequestCultureProvider.cs} (98%) rename src/Microsoft.AspNet.Localization/{CustomRequestCultureStrategy.cs => CustomRequestCultureProvider.cs} (63%) rename src/Microsoft.AspNet.Localization/{IRequestCultureStrategy.cs => IRequestCultureProvider.cs} (75%) rename src/Microsoft.AspNet.Localization/{QueryStringRequestCultureStrategy.cs => QueryStringRequestCultureProvider.cs} (97%) rename src/Microsoft.AspNet.Localization/{RequestCultureStrategy.cs => RequestCultureProvider.cs} (93%) create mode 100644 src/Microsoft.Framework.Localization/Internal/AssemblyWrapper.cs diff --git a/samples/LocalizationSample/Startup.cs b/samples/LocalizationSample/Startup.cs index ea9fb7c6c3..b67fa550c0 100644 --- a/samples/LocalizationSample/Startup.cs +++ b/samples/LocalizationSample/Startup.cs @@ -36,9 +36,9 @@ namespace LocalizationSample //} }; - // Optionally create an app-specific strategy with just a delegate, e.g. look up user preference from DB. - // Inserting it as position 0 ensures it has priority over any of the default strategies. - //options.RequestCultureStrategies.Insert(0, new CustomRequestCultureStrategy(async context => + // Optionally create an app-specific provider with just a delegate, e.g. look up user preference from DB. + // Inserting it as position 0 ensures it has priority over any of the default providers. + //options.RequestCultureProviders.Insert(0, new CustomRequestCultureProvider(async context => //{ //})); @@ -74,13 +74,13 @@ $@" function useCookie() {{ var culture = document.getElementById('culture'); var uiCulture = document.getElementById('uiCulture'); - var cookieValue = '{CookieRequestCultureStrategy.DefaultCookieName}=c='+culture.options[culture.selectedIndex].value+'|uic='+uiCulture.options[uiCulture.selectedIndex].value; + var cookieValue = '{CookieRequestCultureProvider.DefaultCookieName}=c='+culture.options[culture.selectedIndex].value+'|uic='+uiCulture.options[uiCulture.selectedIndex].value; document.cookie = cookieValue; window.location = window.location.href.split('?')[0]; }} function clearCookie() {{ - document.cookie='{CookieRequestCultureStrategy.DefaultCookieName}=""""'; + document.cookie='{CookieRequestCultureProvider.DefaultCookieName}=""""'; }} @@ -101,7 +101,7 @@ $@" await context.Response.WriteAsync(""); await context.Response.WriteAsync("
"); await context.Response.WriteAsync("
"); - await context.Response.WriteAsync($""); + await context.Response.WriteAsync($""); await context.Response.WriteAsync($""); await context.Response.WriteAsync($""); await context.Response.WriteAsync($""); diff --git a/src/Microsoft.AspNet.Localization/AcceptLanguageHeaderRequestCultureStrategy.cs b/src/Microsoft.AspNet.Localization/AcceptLanguageHeaderRequestCultureProvider.cs similarity index 97% rename from src/Microsoft.AspNet.Localization/AcceptLanguageHeaderRequestCultureStrategy.cs rename to src/Microsoft.AspNet.Localization/AcceptLanguageHeaderRequestCultureProvider.cs index eef5cf798b..46fbed8cc2 100644 --- a/src/Microsoft.AspNet.Localization/AcceptLanguageHeaderRequestCultureStrategy.cs +++ b/src/Microsoft.AspNet.Localization/AcceptLanguageHeaderRequestCultureProvider.cs @@ -13,7 +13,7 @@ namespace Microsoft.AspNet.Localization /// /// Determines the culture information for a request via the value of the Accept-Language header. /// - public class AcceptLanguageHeaderRequestCultureStrategy : RequestCultureStrategy + public class AcceptLanguageHeaderRequestCultureProvider : RequestCultureProvider { /// /// The maximum number of values in the Accept-Language header to attempt to create a diff --git a/src/Microsoft.AspNet.Localization/CookieRequestCultureStrategy.cs b/src/Microsoft.AspNet.Localization/CookieRequestCultureProvider.cs similarity index 98% rename from src/Microsoft.AspNet.Localization/CookieRequestCultureStrategy.cs rename to src/Microsoft.AspNet.Localization/CookieRequestCultureProvider.cs index e1422656d7..a9a20b03a7 100644 --- a/src/Microsoft.AspNet.Localization/CookieRequestCultureStrategy.cs +++ b/src/Microsoft.AspNet.Localization/CookieRequestCultureProvider.cs @@ -12,7 +12,7 @@ namespace Microsoft.AspNet.Localization /// /// Determines the culture information for a request via the value of a cookie. /// - public class CookieRequestCultureStrategy : RequestCultureStrategy + public class CookieRequestCultureProvider : RequestCultureProvider { private static readonly char[] _cookieSeparator = new[] { '|' }; private static readonly string _culturePrefix = "c="; diff --git a/src/Microsoft.AspNet.Localization/CustomRequestCultureStrategy.cs b/src/Microsoft.AspNet.Localization/CustomRequestCultureProvider.cs similarity index 63% rename from src/Microsoft.AspNet.Localization/CustomRequestCultureStrategy.cs rename to src/Microsoft.AspNet.Localization/CustomRequestCultureProvider.cs index 65546bed44..0bbb94f42b 100644 --- a/src/Microsoft.AspNet.Localization/CustomRequestCultureStrategy.cs +++ b/src/Microsoft.AspNet.Localization/CustomRequestCultureProvider.cs @@ -11,23 +11,23 @@ namespace Microsoft.AspNet.Localization /// /// Determines the culture information for a request via the configured delegate. /// - public class CustomRequestCultureStrategy : RequestCultureStrategy + public class CustomRequestCultureProvider : RequestCultureProvider { - private readonly Func> _strategy; + private readonly Func> _provider; /// - /// Creates a new using the specified delegate. + /// Creates a new using the specified delegate. /// - /// The strategy delegate. - public CustomRequestCultureStrategy([NotNull] Func> strategy) + /// The provider delegate. + public CustomRequestCultureProvider([NotNull] Func> provider) { - _strategy = strategy; + _provider = provider; } /// public override Task DetermineRequestCulture([NotNull] HttpContext httpContext) { - return _strategy(httpContext); + return _provider(httpContext); } } } diff --git a/src/Microsoft.AspNet.Localization/IRequestCultureFeature.cs b/src/Microsoft.AspNet.Localization/IRequestCultureFeature.cs index 44cc487a63..4ea6aed42c 100644 --- a/src/Microsoft.AspNet.Localization/IRequestCultureFeature.cs +++ b/src/Microsoft.AspNet.Localization/IRequestCultureFeature.cs @@ -14,10 +14,10 @@ namespace Microsoft.AspNet.Localization RequestCulture RequestCulture { get; } /// - /// The that determined the request's culture information. - /// If the value is null then no strategy was used and the request's culture was set to the value of + /// The that determined the request's culture information. + /// If the value is null then no provider was used and the request's culture was set to the value of /// . /// - IRequestCultureStrategy Strategy { get; } + IRequestCultureProvider Provider { get; } } } \ No newline at end of file diff --git a/src/Microsoft.AspNet.Localization/IRequestCultureStrategy.cs b/src/Microsoft.AspNet.Localization/IRequestCultureProvider.cs similarity index 75% rename from src/Microsoft.AspNet.Localization/IRequestCultureStrategy.cs rename to src/Microsoft.AspNet.Localization/IRequestCultureProvider.cs index 91001e57cc..4af4908b2e 100644 --- a/src/Microsoft.AspNet.Localization/IRequestCultureStrategy.cs +++ b/src/Microsoft.AspNet.Localization/IRequestCultureProvider.cs @@ -7,17 +7,17 @@ using Microsoft.AspNet.Http; namespace Microsoft.AspNet.Localization { /// - /// Represents a strategy for determining the culture information of an . + /// Represents a provider for determining the culture information of an . /// - public interface IRequestCultureStrategy + public interface IRequestCultureProvider { /// - /// Implements the strategy to determine the culture of the given request. + /// Implements the provider to determine the culture of the given request. /// /// The for the request. /// /// The determined . - /// Returns null if the strategy couldn't determine a . + /// Returns null if the provider couldn't determine a . /// Task DetermineRequestCulture(HttpContext httpContext); } diff --git a/src/Microsoft.AspNet.Localization/QueryStringRequestCultureStrategy.cs b/src/Microsoft.AspNet.Localization/QueryStringRequestCultureProvider.cs similarity index 97% rename from src/Microsoft.AspNet.Localization/QueryStringRequestCultureStrategy.cs rename to src/Microsoft.AspNet.Localization/QueryStringRequestCultureProvider.cs index 5c6ebab146..0b5f0aef16 100644 --- a/src/Microsoft.AspNet.Localization/QueryStringRequestCultureStrategy.cs +++ b/src/Microsoft.AspNet.Localization/QueryStringRequestCultureProvider.cs @@ -11,7 +11,7 @@ namespace Microsoft.AspNet.Localization /// /// Determines the culture information for a request via values in the query string. /// - public class QueryStringRequestCultureStrategy : RequestCultureStrategy + public class QueryStringRequestCultureProvider : RequestCultureProvider { /// /// The key that contains the culture name. diff --git a/src/Microsoft.AspNet.Localization/RequestCultureFeature.cs b/src/Microsoft.AspNet.Localization/RequestCultureFeature.cs index 76505b4759..f512cba2aa 100644 --- a/src/Microsoft.AspNet.Localization/RequestCultureFeature.cs +++ b/src/Microsoft.AspNet.Localization/RequestCultureFeature.cs @@ -14,16 +14,16 @@ namespace Microsoft.AspNet.Localization /// Creates a new with the specified . /// /// The . - public RequestCultureFeature([NotNull] RequestCulture requestCulture, IRequestCultureStrategy strategy) + public RequestCultureFeature([NotNull] RequestCulture requestCulture, IRequestCultureProvider provider) { RequestCulture = requestCulture; - Strategy = strategy; + Provider = provider; } /// public RequestCulture RequestCulture { get; } /// - public IRequestCultureStrategy Strategy { get; } + public IRequestCultureProvider Provider { get; } } } diff --git a/src/Microsoft.AspNet.Localization/RequestCultureStrategy.cs b/src/Microsoft.AspNet.Localization/RequestCultureProvider.cs similarity index 93% rename from src/Microsoft.AspNet.Localization/RequestCultureStrategy.cs rename to src/Microsoft.AspNet.Localization/RequestCultureProvider.cs index 6d30805119..234d46dc22 100644 --- a/src/Microsoft.AspNet.Localization/RequestCultureStrategy.cs +++ b/src/Microsoft.AspNet.Localization/RequestCultureProvider.cs @@ -3,14 +3,13 @@ using System.Threading.Tasks; using Microsoft.AspNet.Http; -using Microsoft.Framework.Internal; namespace Microsoft.AspNet.Localization { /// - /// An abstract base class strategy for determining the culture information of an . + /// An abstract base class provider for determining the culture information of an . /// - public abstract class RequestCultureStrategy : IRequestCultureStrategy + public abstract class RequestCultureProvider : IRequestCultureProvider { /// /// The current options for the . diff --git a/src/Microsoft.AspNet.Localization/RequestLocalizationMiddleware.cs b/src/Microsoft.AspNet.Localization/RequestLocalizationMiddleware.cs index 54600e17a4..4dc60d7a86 100644 --- a/src/Microsoft.AspNet.Localization/RequestLocalizationMiddleware.cs +++ b/src/Microsoft.AspNet.Localization/RequestLocalizationMiddleware.cs @@ -40,23 +40,23 @@ namespace Microsoft.AspNet.Localization var requestCulture = _options.DefaultRequestCulture ?? new RequestCulture(CultureInfo.CurrentCulture, CultureInfo.CurrentUICulture); - IRequestCultureStrategy winningStrategy = null; + IRequestCultureProvider winningProvider = null; - if (_options.RequestCultureStrategies != null) + if (_options.RequestCultureProviders != null) { - foreach (var strategy in _options.RequestCultureStrategies) + foreach (var provider in _options.RequestCultureProviders) { - var result = await strategy.DetermineRequestCulture(context); + var result = await provider.DetermineRequestCulture(context); if (result != null) { requestCulture = result; - winningStrategy = strategy; + winningProvider = provider; break; } } } - context.SetFeature(new RequestCultureFeature(requestCulture, winningStrategy)); + context.SetFeature(new RequestCultureFeature(requestCulture, winningProvider)); SetCurrentThreadCulture(requestCulture); diff --git a/src/Microsoft.AspNet.Localization/RequestLocalizationOptions.cs b/src/Microsoft.AspNet.Localization/RequestLocalizationOptions.cs index 74eb335921..a6e4bcc932 100644 --- a/src/Microsoft.AspNet.Localization/RequestLocalizationOptions.cs +++ b/src/Microsoft.AspNet.Localization/RequestLocalizationOptions.cs @@ -19,17 +19,17 @@ namespace Microsoft.AspNet.Localization { DefaultRequestCulture = new RequestCulture(CultureInfo.CurrentCulture, CultureInfo.CurrentUICulture); - RequestCultureStrategies = new List + RequestCultureProviders = new List { - new QueryStringRequestCultureStrategy { Options = this }, - new CookieRequestCultureStrategy { Options = this }, - new AcceptLanguageHeaderRequestCultureStrategy { Options = this } + new QueryStringRequestCultureProvider { Options = this }, + new CookieRequestCultureProvider { Options = this }, + new AcceptLanguageHeaderRequestCultureProvider { Options = this } }; } /// /// The default to use. This value will be used if none of the configured - /// options result in a non-null result. + /// options result in a non-null result. /// Defaults to set to /// and set to . /// @@ -54,16 +54,16 @@ namespace Microsoft.AspNet.Localization public IList SupportedUICultures { get; set; } /// - /// An ordered list of strategies used to determine a request's culture information. The first strategy that + /// An ordered list of providers used to determine a request's culture information. The first provider that /// returns a non-null result for a given request will be used. /// Defaults to the following: /// - /// - /// - /// + /// + /// + /// /// /// [SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly", Justification = "Improves usability")] - public IList RequestCultureStrategies { get; set; } + public IList RequestCultureProviders { get; set; } } } diff --git a/src/Microsoft.Framework.Localization/Internal/AssemblyWrapper.cs b/src/Microsoft.Framework.Localization/Internal/AssemblyWrapper.cs new file mode 100644 index 0000000000..ca95f85540 --- /dev/null +++ b/src/Microsoft.Framework.Localization/Internal/AssemblyWrapper.cs @@ -0,0 +1,23 @@ +// 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.IO; +using System.Reflection; +using Microsoft.Framework.Internal; + +namespace Microsoft.Framework.Localization.Internal +{ + public class AssemblyWrapper + { + public AssemblyWrapper([NotNull] Assembly assembly) + { + Assembly = assembly; + } + + public Assembly Assembly { get; } + + public virtual string FullName => Assembly.FullName; + + public virtual Stream GetManifestResourceStream(string name) => Assembly.GetManifestResourceStream(name); + } +} diff --git a/src/Microsoft.Framework.Localization/ResourceManagerStringLocalizer.cs b/src/Microsoft.Framework.Localization/ResourceManagerStringLocalizer.cs index 1ff97dfb8d..bbcf7498e9 100644 --- a/src/Microsoft.Framework.Localization/ResourceManagerStringLocalizer.cs +++ b/src/Microsoft.Framework.Localization/ResourceManagerStringLocalizer.cs @@ -9,6 +9,7 @@ using System.Globalization; using System.Reflection; using System.Resources; using Microsoft.Framework.Internal; +using Microsoft.Framework.Localization.Internal; namespace Microsoft.Framework.Localization { @@ -18,12 +19,16 @@ namespace Microsoft.Framework.Localization /// public class ResourceManagerStringLocalizer : IStringLocalizer { + private static readonly ConcurrentDictionary> _resourceNamesCache = + new ConcurrentDictionary>(); + private readonly ConcurrentDictionary _missingManifestCache = new ConcurrentDictionary(); - private static readonly ConcurrentDictionary> _resourceNamesCache = - new ConcurrentDictionary>(); - + private readonly ResourceManager _resourceManager; + private readonly AssemblyWrapper _resourceAssemblyWrapper; + private readonly string _resourceBaseName; + /// /// Creates a new . /// @@ -34,26 +39,23 @@ namespace Microsoft.Framework.Localization [NotNull] ResourceManager resourceManager, [NotNull] Assembly resourceAssembly, [NotNull] string baseName) + : this(resourceManager, new AssemblyWrapper(resourceAssembly), baseName) { - ResourceManager = resourceManager; - ResourceAssembly = resourceAssembly; - ResourceBaseName = baseName; + } /// - /// The to read strings from. + /// Intended for testing purposes only. /// - protected ResourceManager ResourceManager { get; } - - /// - /// The that contains the strings as embedded resources. - /// - protected Assembly ResourceAssembly { get; } - - /// - /// The base name of the embedded resource in the that contains the strings. - /// - protected string ResourceBaseName { get; } + public ResourceManagerStringLocalizer( + [NotNull] ResourceManager resourceManager, + [NotNull] AssemblyWrapper resourceAssemblyWrapper, + [NotNull] string baseName) + { + _resourceAssemblyWrapper = resourceAssemblyWrapper; + _resourceManager = resourceManager; + _resourceBaseName = baseName; + } /// public virtual LocalizedString this[[NotNull] string name] @@ -84,16 +86,15 @@ namespace Microsoft.Framework.Localization public IStringLocalizer WithCulture(CultureInfo culture) { return culture == null - ? new ResourceManagerStringLocalizer(ResourceManager, ResourceAssembly, ResourceBaseName) - : new ResourceManagerWithCultureStringLocalizer( - ResourceManager, - ResourceAssembly, - ResourceBaseName, + ? new ResourceManagerStringLocalizer(_resourceManager, _resourceAssemblyWrapper, _resourceBaseName) + : new ResourceManagerWithCultureStringLocalizer(_resourceManager, + _resourceAssemblyWrapper, + _resourceBaseName, culture); } /// - /// Gets a resource string from the and returns null instead of + /// Gets a resource string from the and returns null instead of /// throwing exceptions if a match isn't found. /// /// The name of the string resource. @@ -110,7 +111,7 @@ namespace Microsoft.Framework.Localization try { - return culture == null ? ResourceManager.GetString(name) : ResourceManager.GetString(name, culture); + return culture == null ? _resourceManager.GetString(name) : _resourceManager.GetString(name, culture); } catch (MissingManifestResourceException) { @@ -150,10 +151,7 @@ namespace Microsoft.Framework.Localization } // Internal to allow testing - internal static void ClearResourceNamesCache() - { - _resourceNamesCache.Clear(); - } + internal static void ClearResourceNamesCache() => _resourceNamesCache.Clear(); private IEnumerable GetResourceNamesFromCultureHierarchy(CultureInfo startingCulture) { @@ -186,19 +184,19 @@ namespace Microsoft.Framework.Localization private IList GetResourceNamesForCulture(CultureInfo culture) { - var resourceStreamName = ResourceBaseName; + var resourceStreamName = _resourceBaseName; if (!string.IsNullOrEmpty(culture.Name)) { resourceStreamName += "." + culture.Name; } resourceStreamName += ".resources"; - var cacheKey = $"assembly={ResourceAssembly.FullName};resourceStreamName={resourceStreamName}"; + var cacheKey = $"assembly={_resourceAssemblyWrapper.FullName};resourceStreamName={resourceStreamName}"; var cultureResourceNames = _resourceNamesCache.GetOrAdd(cacheKey, key => { var names = new List(); - using (var cultureResourceStream = ResourceAssembly.GetManifestResourceStream(key)) + using (var cultureResourceStream = _resourceAssemblyWrapper.GetManifestResourceStream(key)) using (var resources = new ResourceReader(cultureResourceStream)) { foreach (DictionaryEntry entry in resources) diff --git a/src/Microsoft.Framework.Localization/ResourceManagerStringLocalizerFactory.cs b/src/Microsoft.Framework.Localization/ResourceManagerStringLocalizerFactory.cs index 3039a9d377..92899e15a4 100644 --- a/src/Microsoft.Framework.Localization/ResourceManagerStringLocalizerFactory.cs +++ b/src/Microsoft.Framework.Localization/ResourceManagerStringLocalizerFactory.cs @@ -5,6 +5,7 @@ using System; using System.Reflection; using System.Resources; using Microsoft.Framework.Internal; +using Microsoft.Framework.Localization.Internal; using Microsoft.Framework.Runtime; namespace Microsoft.Framework.Localization @@ -34,7 +35,7 @@ namespace Microsoft.Framework.Localization public IStringLocalizer Create([NotNull] Type resourceSource) { var typeInfo = resourceSource.GetTypeInfo(); - var assembly = typeInfo.Assembly; + var assembly = new AssemblyWrapper(typeInfo.Assembly); var baseName = typeInfo.FullName; return new ResourceManagerStringLocalizer(new ResourceManager(resourceSource), assembly, baseName); } @@ -49,7 +50,10 @@ namespace Microsoft.Framework.Localization { var assembly = Assembly.Load(new AssemblyName(location ?? _applicationEnvironment.ApplicationName)); - return new ResourceManagerStringLocalizer(new ResourceManager(baseName, assembly), assembly, baseName); + return new ResourceManagerStringLocalizer( + new ResourceManager(baseName, assembly), + new AssemblyWrapper(assembly), + baseName); } } } \ No newline at end of file diff --git a/src/Microsoft.Framework.Localization/ResourceManagerWithCultureStringLocalizer.cs b/src/Microsoft.Framework.Localization/ResourceManagerWithCultureStringLocalizer.cs index c8d2f36dbb..1260e26579 100644 --- a/src/Microsoft.Framework.Localization/ResourceManagerWithCultureStringLocalizer.cs +++ b/src/Microsoft.Framework.Localization/ResourceManagerWithCultureStringLocalizer.cs @@ -1,12 +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; using System.Collections.Generic; using System.Globalization; using System.Reflection; using System.Resources; using Microsoft.Framework.Internal; +using Microsoft.Framework.Localization.Internal; namespace Microsoft.Framework.Localization { @@ -35,6 +35,19 @@ namespace Microsoft.Framework.Localization _culture = culture; } + /// + /// Intended for testing purposes only. + /// + public ResourceManagerWithCultureStringLocalizer( + [NotNull] ResourceManager resourceManager, + [NotNull] AssemblyWrapper assemblyWrapper, + [NotNull] string baseName, + [NotNull] CultureInfo culture) + : base(resourceManager, assemblyWrapper, baseName) + { + _culture = culture; + } + /// public override LocalizedString this[[NotNull] string name] { diff --git a/test/Microsoft.Framework.Localization.Test/ResourceManagerStringLocalizerTest.cs b/test/Microsoft.Framework.Localization.Test/ResourceManagerStringLocalizerTest.cs index ce88695724..410fc2c50f 100644 --- a/test/Microsoft.Framework.Localization.Test/ResourceManagerStringLocalizerTest.cs +++ b/test/Microsoft.Framework.Localization.Test/ResourceManagerStringLocalizerTest.cs @@ -6,7 +6,7 @@ using System.IO; using System.Linq; using System.Reflection; using System.Resources; -using Moq; +using Microsoft.Framework.Localization.Internal; using Xunit; namespace Microsoft.Framework.Localization.Test @@ -18,19 +18,11 @@ namespace Microsoft.Framework.Localization.Test { // Arrange ResourceManagerStringLocalizer.ClearResourceNamesCache(); - var resourceManager = new Mock(); - var resourceAssembly = new Mock(); - resourceAssembly.Setup(rm => rm.GetManifestResourceStream(It.IsAny())) - .Returns(() => MakeResourceStream()); var baseName = "test"; - var localizer1 = new ResourceManagerStringLocalizer( - resourceManager.Object, - resourceAssembly.Object, - baseName); - var localizer2 = new ResourceManagerStringLocalizer( - resourceManager.Object, - resourceAssembly.Object, - baseName); + var resourceAssembly = new TestAssemblyWrapper(); + var resourceManager = new TestResourceManager(baseName, resourceAssembly.Assembly); + var localizer1 = new ResourceManagerStringLocalizer(resourceManager, resourceAssembly, baseName); + var localizer2 = new ResourceManagerStringLocalizer(resourceManager, resourceAssembly, baseName); // Act for (int i = 0; i < 5; i++) @@ -41,9 +33,7 @@ namespace Microsoft.Framework.Localization.Test // Assert var expectedCallCount = GetCultureInfoDepth(CultureInfo.CurrentUICulture); - resourceAssembly.Verify( - rm => rm.GetManifestResourceStream(It.IsAny()), - Times.Exactly(expectedCallCount)); + Assert.Equal(expectedCallCount, resourceAssembly.GetManifestResourceStreamCallCount); } [Fact] @@ -51,24 +41,13 @@ namespace Microsoft.Framework.Localization.Test { // Arrange ResourceManagerStringLocalizer.ClearResourceNamesCache(); - var resourceManager = new Mock(); - var resourceAssembly1 = new Mock(); - resourceAssembly1.CallBase = true; - var resourceAssembly2 = new Mock(); - resourceAssembly2.CallBase = true; - resourceAssembly1.Setup(rm => rm.GetManifestResourceStream(It.IsAny())) - .Returns(() => MakeResourceStream()); - resourceAssembly2.Setup(rm => rm.GetManifestResourceStream(It.IsAny())) - .Returns(() => MakeResourceStream()); var baseName = "test"; - var localizer1 = new ResourceManagerStringLocalizer( - resourceManager.Object, - resourceAssembly1.Object, - baseName); - var localizer2 = new ResourceManagerStringLocalizer( - resourceManager.Object, - resourceAssembly2.Object, - baseName); + var resourceAssembly1 = new TestAssemblyWrapper("Assembly1"); + var resourceAssembly2 = new TestAssemblyWrapper("Assembly2"); + var resourceManager1 = new TestResourceManager(baseName, resourceAssembly1.Assembly); + var resourceManager2 = new TestResourceManager(baseName, resourceAssembly2.Assembly); + var localizer1 = new ResourceManagerStringLocalizer(resourceManager1, resourceAssembly1, baseName); + var localizer2 = new ResourceManagerStringLocalizer(resourceManager2, resourceAssembly2, baseName); // Act localizer1.ToList(); @@ -76,12 +55,8 @@ namespace Microsoft.Framework.Localization.Test // Assert var expectedCallCount = GetCultureInfoDepth(CultureInfo.CurrentUICulture); - resourceAssembly1.Verify( - rm => rm.GetManifestResourceStream(It.IsAny()), - Times.Exactly(expectedCallCount)); - resourceAssembly2.Verify( - rm => rm.GetManifestResourceStream(It.IsAny()), - Times.Exactly(expectedCallCount)); + Assert.Equal(expectedCallCount, resourceAssembly1.GetManifestResourceStreamCallCount); + Assert.Equal(expectedCallCount, resourceAssembly2.GetManifestResourceStreamCallCount); } private static Stream MakeResourceStream() @@ -114,25 +89,35 @@ namespace Microsoft.Framework.Localization.Test return result; } - public class TestAssembly1 : Assembly + public class TestResourceManager : ResourceManager { - public override string FullName + public TestResourceManager(string baseName, Assembly assembly) + : base(baseName, assembly) { - get - { - return nameof(TestAssembly1); - } + } + + public override string GetString(string name, CultureInfo culture) => null; } - public class TestAssembly2 : Assembly + public class TestAssemblyWrapper : AssemblyWrapper { - public override string FullName + private readonly string _name; + + public TestAssemblyWrapper(string name = nameof(TestAssemblyWrapper)) + : base(typeof(TestAssemblyWrapper).GetTypeInfo().Assembly) { - get - { - return nameof(TestAssembly2); - } + _name = name; + } + + public int GetManifestResourceStreamCallCount { get; private set; } + + public override string FullName => _name; + + public override Stream GetManifestResourceStream(string name) + { + GetManifestResourceStreamCallCount++; + return MakeResourceStream(); } } } diff --git a/test/Microsoft.Framework.Localization.Test/project.json b/test/Microsoft.Framework.Localization.Test/project.json index 45f8dd778c..420577a021 100644 --- a/test/Microsoft.Framework.Localization.Test/project.json +++ b/test/Microsoft.Framework.Localization.Test/project.json @@ -1,16 +1,17 @@ { - "dependencies": { - "Moq": "4.2.1502.911", - "xunit": "2.1.0-*", - "xunit.runner.dnx": "2.1.0-*", - "Microsoft.Framework.Localization": "1.0.0-*" - }, + "dependencies": { + "xunit": "2.1.0-*", + "xunit.runner.dnx": "2.1.0-*", + "Microsoft.Framework.Localization": "1.0.0-*", + "System.Reflection": "4.0.10-*" + }, - "commands": { - "test": "xunit.runner.dnx" - }, + "commands": { + "test": "xunit.runner.dnx" + }, - "frameworks": { - "dnx451": { } - } + "frameworks": { + "dnx451": { }, + "dnxcore50": { } + } } From 5c7d811aa68a72cd8336deb2b254198b9dfd6cce Mon Sep 17 00:00:00 2001 From: damianedwards Date: Wed, 20 May 2015 16:22:54 -0700 Subject: [PATCH 029/390] Fix the merge --- .../CustomRequestCultureProvider.cs | 4 +--- .../IApplicationBuilderExtensions.cs | 3 ++- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/Microsoft.AspNet.Localization/CustomRequestCultureProvider.cs b/src/Microsoft.AspNet.Localization/CustomRequestCultureProvider.cs index 0bbb94f42b..571a9a0e90 100644 --- a/src/Microsoft.AspNet.Localization/CustomRequestCultureProvider.cs +++ b/src/Microsoft.AspNet.Localization/CustomRequestCultureProvider.cs @@ -26,8 +26,6 @@ namespace Microsoft.AspNet.Localization /// public override Task DetermineRequestCulture([NotNull] HttpContext httpContext) - { - return _provider(httpContext); - } + => _provider(httpContext); } } diff --git a/src/Microsoft.AspNet.Localization/IApplicationBuilderExtensions.cs b/src/Microsoft.AspNet.Localization/IApplicationBuilderExtensions.cs index afa218e30f..fbc6b08cba 100644 --- a/src/Microsoft.AspNet.Localization/IApplicationBuilderExtensions.cs +++ b/src/Microsoft.AspNet.Localization/IApplicationBuilderExtensions.cs @@ -33,6 +33,7 @@ namespace Microsoft.AspNet.Builder /// The . public static IApplicationBuilder UseRequestLocalization( [NotNull] this IApplicationBuilder builder, - [NotNull] RequestLocalizationOptions options => builder.UseMiddleware(options); + [NotNull] RequestLocalizationOptions options) + => builder.UseMiddleware(options); } } \ No newline at end of file From 8382170178a40149f0af104a7f0e1172a907e51f Mon Sep 17 00:00:00 2001 From: Hisham Abdullah Bin Ateya Date: Thu, 21 May 2015 03:31:26 +0300 Subject: [PATCH 030/390] Unnecessary using directives --- samples/LocalizationSample/Startup.cs | 1 - .../IStringLocalizerOfT.cs | 2 -- .../ResourceManagerStringLocalizer.cs | 1 - 3 files changed, 4 deletions(-) diff --git a/samples/LocalizationSample/Startup.cs b/samples/LocalizationSample/Startup.cs index b67fa550c0..973663d740 100644 --- a/samples/LocalizationSample/Startup.cs +++ b/samples/LocalizationSample/Startup.cs @@ -2,7 +2,6 @@ // 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; diff --git a/src/Microsoft.Framework.Localization.Abstractions/IStringLocalizerOfT.cs b/src/Microsoft.Framework.Localization.Abstractions/IStringLocalizerOfT.cs index f81a3c0eaf..d7e6f6f196 100644 --- a/src/Microsoft.Framework.Localization.Abstractions/IStringLocalizerOfT.cs +++ b/src/Microsoft.Framework.Localization.Abstractions/IStringLocalizerOfT.cs @@ -1,8 +1,6 @@ // 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; - namespace Microsoft.Framework.Localization { /// diff --git a/src/Microsoft.Framework.Localization/ResourceManagerStringLocalizer.cs b/src/Microsoft.Framework.Localization/ResourceManagerStringLocalizer.cs index 76f86d6e8a..e8b684599c 100644 --- a/src/Microsoft.Framework.Localization/ResourceManagerStringLocalizer.cs +++ b/src/Microsoft.Framework.Localization/ResourceManagerStringLocalizer.cs @@ -1,7 +1,6 @@ // 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; using System.Collections.Concurrent; using System.Collections.Generic; From 12bf8f28e0717f11ee2c87af611ecd5986bb4c90 Mon Sep 17 00:00:00 2001 From: Pranav K Date: Wed, 27 May 2015 16:35:43 -0700 Subject: [PATCH 031/390] Updating release NuGet.Config --- NuGet.Config | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/NuGet.Config b/NuGet.Config index da5e353ffb..0e74a4912d 100644 --- a/NuGet.Config +++ b/NuGet.Config @@ -1,8 +1,8 @@  - - + + - \ No newline at end of file + From 34223d892cc05b94c97cd721ad2d5c5bc2ac55a4 Mon Sep 17 00:00:00 2001 From: Pranav K Date: Wed, 27 May 2015 18:01:44 -0700 Subject: [PATCH 032/390] Adding xunit feed to release --- NuGet.Config | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/NuGet.Config b/NuGet.Config index 0e74a4912d..c4baf69176 100644 --- a/NuGet.Config +++ b/NuGet.Config @@ -2,7 +2,8 @@ - + + From 59033fed8055c9959691cb7a200c1233f7744834 Mon Sep 17 00:00:00 2001 From: Pranav K Date: Wed, 27 May 2015 18:08:52 -0700 Subject: [PATCH 033/390] Updating release NuGet.config --- NuGet.Config | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NuGet.Config b/NuGet.Config index c4baf69176..5c4e60808b 100644 --- a/NuGet.Config +++ b/NuGet.Config @@ -2,7 +2,7 @@ - + From 90ca64c3dde4135d6a78fa191722e928660fb0c1 Mon Sep 17 00:00:00 2001 From: damianedwards Date: Thu, 28 May 2015 12:14:03 -0700 Subject: [PATCH 034/390] Statics be gone! - Cleaned up ctors of ResourceManagerStringLocalizer and ResourceManagerWithCultureStringLocalizer too --- .../IResourceNamesCache.cs | 22 ++++++++++++++ .../ResourceManagerStringLocalizer.cs | 29 +++++++++++-------- .../ResourceManagerStringLocalizerFactory.cs | 16 ++++++---- ...sourceManagerWithCultureStringLocalizer.cs | 20 +++---------- .../ResourceNamesCache.cs | 23 +++++++++++++++ .../ResourceManagerStringLocalizerTest.cs | 12 ++++---- 6 files changed, 83 insertions(+), 39 deletions(-) create mode 100644 src/Microsoft.Framework.Localization/IResourceNamesCache.cs create mode 100644 src/Microsoft.Framework.Localization/ResourceNamesCache.cs diff --git a/src/Microsoft.Framework.Localization/IResourceNamesCache.cs b/src/Microsoft.Framework.Localization/IResourceNamesCache.cs new file mode 100644 index 0000000000..60b7c825cc --- /dev/null +++ b/src/Microsoft.Framework.Localization/IResourceNamesCache.cs @@ -0,0 +1,22 @@ +// 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; + +namespace Microsoft.Framework.Localization +{ + /// + /// Represents a cache of string names in resources. + /// + public interface IResourceNamesCache + { + /// + /// Adds a set of resource names to the cache by using the specified function, if the name does not already exist. + /// + /// The resource name to add string names for. + /// The function used to generate the string names for the resource. + /// The string names for the resource. + IList GetOrAdd(string name, Func> valueFactory); + } +} diff --git a/src/Microsoft.Framework.Localization/ResourceManagerStringLocalizer.cs b/src/Microsoft.Framework.Localization/ResourceManagerStringLocalizer.cs index e8b684599c..ee62f3db03 100644 --- a/src/Microsoft.Framework.Localization/ResourceManagerStringLocalizer.cs +++ b/src/Microsoft.Framework.Localization/ResourceManagerStringLocalizer.cs @@ -18,12 +18,10 @@ namespace Microsoft.Framework.Localization /// public class ResourceManagerStringLocalizer : IStringLocalizer { - private static readonly ConcurrentDictionary> _resourceNamesCache = - new ConcurrentDictionary>(); - private readonly ConcurrentDictionary _missingManifestCache = new ConcurrentDictionary(); + private readonly IResourceNamesCache _resourceNamesCache; private readonly ResourceManager _resourceManager; private readonly AssemblyWrapper _resourceAssemblyWrapper; private readonly string _resourceBaseName; @@ -34,11 +32,13 @@ namespace Microsoft.Framework.Localization /// The to read strings from. /// The that contains the strings as embedded resources. /// The base name of the embedded resource in the that contains the strings. + /// Cache of the list of strings for a given resource assembly name. public ResourceManagerStringLocalizer( [NotNull] ResourceManager resourceManager, [NotNull] Assembly resourceAssembly, - [NotNull] string baseName) - : this(resourceManager, new AssemblyWrapper(resourceAssembly), baseName) + [NotNull] string baseName, + [NotNull] IResourceNamesCache resourceNamesCache) + : this(resourceManager, new AssemblyWrapper(resourceAssembly), baseName, resourceNamesCache) { } @@ -49,11 +49,13 @@ namespace Microsoft.Framework.Localization public ResourceManagerStringLocalizer( [NotNull] ResourceManager resourceManager, [NotNull] AssemblyWrapper resourceAssemblyWrapper, - [NotNull] string baseName) + [NotNull] string baseName, + [NotNull] IResourceNamesCache resourceNamesCache) { _resourceAssemblyWrapper = resourceAssemblyWrapper; _resourceManager = resourceManager; _resourceBaseName = baseName; + _resourceNamesCache = resourceNamesCache; } /// @@ -85,10 +87,16 @@ namespace Microsoft.Framework.Localization public IStringLocalizer WithCulture(CultureInfo culture) { return culture == null - ? new ResourceManagerStringLocalizer(_resourceManager, _resourceAssemblyWrapper, _resourceBaseName) - : new ResourceManagerWithCultureStringLocalizer(_resourceManager, - _resourceAssemblyWrapper, + ? new ResourceManagerStringLocalizer( + _resourceManager, + _resourceAssemblyWrapper.Assembly, _resourceBaseName, + _resourceNamesCache) + : new ResourceManagerWithCultureStringLocalizer( + _resourceManager, + _resourceAssemblyWrapper.Assembly, + _resourceBaseName, + _resourceNamesCache, culture); } @@ -143,9 +151,6 @@ namespace Microsoft.Framework.Localization } } - // Internal to allow testing - internal static void ClearResourceNamesCache() => _resourceNamesCache.Clear(); - private IEnumerable GetResourceNamesFromCultureHierarchy(CultureInfo startingCulture) { var currentCulture = startingCulture; diff --git a/src/Microsoft.Framework.Localization/ResourceManagerStringLocalizerFactory.cs b/src/Microsoft.Framework.Localization/ResourceManagerStringLocalizerFactory.cs index 92899e15a4..d0ebe781a1 100644 --- a/src/Microsoft.Framework.Localization/ResourceManagerStringLocalizerFactory.cs +++ b/src/Microsoft.Framework.Localization/ResourceManagerStringLocalizerFactory.cs @@ -5,7 +5,6 @@ using System; using System.Reflection; using System.Resources; using Microsoft.Framework.Internal; -using Microsoft.Framework.Localization.Internal; using Microsoft.Framework.Runtime; namespace Microsoft.Framework.Localization @@ -15,6 +14,8 @@ namespace Microsoft.Framework.Localization /// public class ResourceManagerStringLocalizerFactory : IStringLocalizerFactory { + private readonly IResourceNamesCache _resourceNamesCache = new ResourceNamesCache(); + private readonly IApplicationEnvironment _applicationEnvironment; /// @@ -35,9 +36,13 @@ namespace Microsoft.Framework.Localization public IStringLocalizer Create([NotNull] Type resourceSource) { var typeInfo = resourceSource.GetTypeInfo(); - var assembly = new AssemblyWrapper(typeInfo.Assembly); + var assembly = typeInfo.Assembly; var baseName = typeInfo.FullName; - return new ResourceManagerStringLocalizer(new ResourceManager(resourceSource), assembly, baseName); + return new ResourceManagerStringLocalizer( + new ResourceManager(resourceSource), + assembly, + baseName, + _resourceNamesCache); } /// @@ -52,8 +57,9 @@ namespace Microsoft.Framework.Localization return new ResourceManagerStringLocalizer( new ResourceManager(baseName, assembly), - new AssemblyWrapper(assembly), - baseName); + assembly, + baseName, + _resourceNamesCache); } } } \ No newline at end of file diff --git a/src/Microsoft.Framework.Localization/ResourceManagerWithCultureStringLocalizer.cs b/src/Microsoft.Framework.Localization/ResourceManagerWithCultureStringLocalizer.cs index a451b10dbc..bc79cf838b 100644 --- a/src/Microsoft.Framework.Localization/ResourceManagerWithCultureStringLocalizer.cs +++ b/src/Microsoft.Framework.Localization/ResourceManagerWithCultureStringLocalizer.cs @@ -6,7 +6,6 @@ using System.Globalization; using System.Reflection; using System.Resources; using Microsoft.Framework.Internal; -using Microsoft.Framework.Localization.Internal; namespace Microsoft.Framework.Localization { @@ -24,26 +23,15 @@ namespace Microsoft.Framework.Localization /// The to read strings from. /// The that contains the strings as embedded resources. /// The base name of the embedded resource in the that contains the strings. + /// Cache of the list of strings for a given resource assembly name. /// The specific to use. public ResourceManagerWithCultureStringLocalizer( [NotNull] ResourceManager resourceManager, - [NotNull] Assembly assembly, + [NotNull] Assembly resourceAssembly, [NotNull] string baseName, + [NotNull] IResourceNamesCache resourceNamesCache, [NotNull] CultureInfo culture) - : base(resourceManager, assembly, baseName) - { - _culture = culture; - } - - /// - /// Intended for testing purposes only. - /// - public ResourceManagerWithCultureStringLocalizer( - [NotNull] ResourceManager resourceManager, - [NotNull] AssemblyWrapper assemblyWrapper, - [NotNull] string baseName, - [NotNull] CultureInfo culture) - : base(resourceManager, assemblyWrapper, baseName) + : base(resourceManager, resourceAssembly, baseName, resourceNamesCache) { _culture = culture; } diff --git a/src/Microsoft.Framework.Localization/ResourceNamesCache.cs b/src/Microsoft.Framework.Localization/ResourceNamesCache.cs new file mode 100644 index 0000000000..27ba70cf95 --- /dev/null +++ b/src/Microsoft.Framework.Localization/ResourceNamesCache.cs @@ -0,0 +1,23 @@ +// 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; + +namespace Microsoft.Framework.Localization +{ + /// + /// An implementation of backed by a . + /// + public class ResourceNamesCache : IResourceNamesCache + { + private readonly ConcurrentDictionary> _cache = new ConcurrentDictionary>(); + + /// + public IList GetOrAdd(string name, Func> valueFactory) + { + return _cache.GetOrAdd(name, valueFactory); + } + } +} diff --git a/test/Microsoft.Framework.Localization.Test/ResourceManagerStringLocalizerTest.cs b/test/Microsoft.Framework.Localization.Test/ResourceManagerStringLocalizerTest.cs index 410fc2c50f..92191c0bf8 100644 --- a/test/Microsoft.Framework.Localization.Test/ResourceManagerStringLocalizerTest.cs +++ b/test/Microsoft.Framework.Localization.Test/ResourceManagerStringLocalizerTest.cs @@ -17,12 +17,12 @@ namespace Microsoft.Framework.Localization.Test public void EnumeratorCachesCultureWalkForSameAssembly() { // Arrange - ResourceManagerStringLocalizer.ClearResourceNamesCache(); + var resourceNamesCache = new ResourceNamesCache(); var baseName = "test"; var resourceAssembly = new TestAssemblyWrapper(); var resourceManager = new TestResourceManager(baseName, resourceAssembly.Assembly); - var localizer1 = new ResourceManagerStringLocalizer(resourceManager, resourceAssembly, baseName); - var localizer2 = new ResourceManagerStringLocalizer(resourceManager, resourceAssembly, baseName); + var localizer1 = new ResourceManagerStringLocalizer(resourceManager, resourceAssembly, baseName, resourceNamesCache); + var localizer2 = new ResourceManagerStringLocalizer(resourceManager, resourceAssembly, baseName, resourceNamesCache); // Act for (int i = 0; i < 5; i++) @@ -40,14 +40,14 @@ namespace Microsoft.Framework.Localization.Test public void EnumeratorCacheIsScopedByAssembly() { // Arrange - ResourceManagerStringLocalizer.ClearResourceNamesCache(); + var resourceNamesCache = new ResourceNamesCache(); var baseName = "test"; var resourceAssembly1 = new TestAssemblyWrapper("Assembly1"); var resourceAssembly2 = new TestAssemblyWrapper("Assembly2"); var resourceManager1 = new TestResourceManager(baseName, resourceAssembly1.Assembly); var resourceManager2 = new TestResourceManager(baseName, resourceAssembly2.Assembly); - var localizer1 = new ResourceManagerStringLocalizer(resourceManager1, resourceAssembly1, baseName); - var localizer2 = new ResourceManagerStringLocalizer(resourceManager2, resourceAssembly2, baseName); + var localizer1 = new ResourceManagerStringLocalizer(resourceManager1, resourceAssembly1, baseName, resourceNamesCache); + var localizer2 = new ResourceManagerStringLocalizer(resourceManager2, resourceAssembly2, baseName, resourceNamesCache); // Act localizer1.ToList(); From 1a47d19bb3360ece9655310d23cdd9fed15bddbe Mon Sep 17 00:00:00 2001 From: damianedwards Date: Thu, 28 May 2015 12:25:03 -0700 Subject: [PATCH 035/390] Introduces IStringLocalizer.GetAllStrings to replace direct IEnumerable impl: - #18 Support not loading ancestor culture strings on IStringLocalizer.GetAllStrings: - #23 --- .../IStringLocalizer.cs | 14 ++++- .../StringLocalizerExtensions.cs | 26 +++++++--- .../StringLocalizerOfT.cs | 13 +---- .../ResourceManagerStringLocalizer.cs | 51 +++++++++---------- ...sourceManagerWithCultureStringLocalizer.cs | 3 +- .../ResourceManagerStringLocalizerTest.cs | 8 +-- 6 files changed, 63 insertions(+), 52 deletions(-) diff --git a/src/Microsoft.Framework.Localization.Abstractions/IStringLocalizer.cs b/src/Microsoft.Framework.Localization.Abstractions/IStringLocalizer.cs index aad6b9bb3e..4a1613504e 100644 --- a/src/Microsoft.Framework.Localization.Abstractions/IStringLocalizer.cs +++ b/src/Microsoft.Framework.Localization.Abstractions/IStringLocalizer.cs @@ -9,7 +9,7 @@ namespace Microsoft.Framework.Localization /// /// Represents a service that provides localized strings. /// - public interface IStringLocalizer : IEnumerable + public interface IStringLocalizer { /// /// Gets the string resource with the given name. @@ -25,7 +25,17 @@ namespace Microsoft.Framework.Localization /// The values to format the string with. /// The formatted string resource as a . LocalizedString this[string name, params object[] arguments] { get; } - + + /// + /// Gets all string resources. + /// + /// + /// A indicating whether to include + /// strings from ancestor cultures. + /// + /// The strings. + IEnumerable GetAllStrings(bool includeAncestorCultures); + /// /// Creates a new for a specific . /// diff --git a/src/Microsoft.Framework.Localization.Abstractions/StringLocalizerExtensions.cs b/src/Microsoft.Framework.Localization.Abstractions/StringLocalizerExtensions.cs index c3cd604ad6..0bd30c124e 100644 --- a/src/Microsoft.Framework.Localization.Abstractions/StringLocalizerExtensions.cs +++ b/src/Microsoft.Framework.Localization.Abstractions/StringLocalizerExtensions.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 Microsoft.Framework.Internal; namespace Microsoft.Framework.Localization @@ -10,22 +11,31 @@ namespace Microsoft.Framework.Localization /// /// Gets the string resource with the given name. /// + /// The . /// The name of the string resource. /// The string resource as a . - public static LocalizedString GetString([NotNull] this IStringLocalizer stringLocalizer, [NotNull] string name) - { - return stringLocalizer[name]; - } + public static LocalizedString GetString( + [NotNull] this IStringLocalizer stringLocalizer, + [NotNull] string name) => stringLocalizer[name]; /// /// Gets the string resource with the given name and formatted with the supplied arguments. /// + /// The . /// The name of the string resource. /// The values to format the string with. /// The formatted string resource as a . - public static LocalizedString GetString([NotNull] this IStringLocalizer stringLocalizer, [NotNull] string name, params object[] arguments) - { - return stringLocalizer[name, arguments]; - } + public static LocalizedString GetString( + [NotNull] this IStringLocalizer stringLocalizer, + [NotNull] string name, + params object[] arguments) => stringLocalizer[name, arguments]; + + /// + /// Gets all string resources including those for ancestor cultures. + /// + /// The . + /// The string resources. + public static IEnumerable GetAllStrings([NotNull] this IStringLocalizer stringLocalizer) => + stringLocalizer.GetAllStrings(includeAncestorCultures: true); } } diff --git a/src/Microsoft.Framework.Localization.Abstractions/StringLocalizerOfT.cs b/src/Microsoft.Framework.Localization.Abstractions/StringLocalizerOfT.cs index 2a5334cce0..d8621cf516 100644 --- a/src/Microsoft.Framework.Localization.Abstractions/StringLocalizerOfT.cs +++ b/src/Microsoft.Framework.Localization.Abstractions/StringLocalizerOfT.cs @@ -1,7 +1,6 @@ // 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; using System.Collections.Generic; using System.Globalization; using Microsoft.Framework.Internal; @@ -36,15 +35,7 @@ namespace Microsoft.Framework.Localization _localizer[name, arguments]; /// - public virtual LocalizedString GetString([NotNull] string name) => _localizer.GetString(name); - - /// - public virtual LocalizedString GetString([NotNull] string name, params object[] arguments) => - _localizer.GetString(name, arguments); - - /// - public IEnumerator GetEnumerator() => _localizer.GetEnumerator(); - - IEnumerator IEnumerable.GetEnumerator() => _localizer.GetEnumerator(); + public IEnumerable GetAllStrings(bool includeAncestorCultures) => + _localizer.GetAllStrings(includeAncestorCultures); } } \ No newline at end of file diff --git a/src/Microsoft.Framework.Localization/ResourceManagerStringLocalizer.cs b/src/Microsoft.Framework.Localization/ResourceManagerStringLocalizer.cs index ee62f3db03..b970d5a4d4 100644 --- a/src/Microsoft.Framework.Localization/ResourceManagerStringLocalizer.cs +++ b/src/Microsoft.Framework.Localization/ResourceManagerStringLocalizer.cs @@ -100,6 +100,29 @@ namespace Microsoft.Framework.Localization culture); } + /// + public virtual IEnumerable GetAllStrings(bool includeAncestorCultures) => + GetAllStrings(includeAncestorCultures, CultureInfo.CurrentUICulture); + + /// + /// Returns all strings in the specified culture. + /// + /// + /// The to get strings for. + /// The strings. + protected IEnumerable GetAllStrings(bool includeAncestorCultures, [NotNull] CultureInfo culture) + { + var resourceNames = includeAncestorCultures + ? GetResourceNamesFromCultureHierarchy(culture) + : GetResourceNamesForCulture(culture); + + foreach (var name in resourceNames) + { + var value = GetStringSafely(name, culture); + yield return new LocalizedString(name, value ?? name, resourceNotFound: value == null); + } + } + /// /// Gets a resource string from the and returns null instead of /// throwing exceptions if a match isn't found. @@ -127,30 +150,6 @@ namespace Microsoft.Framework.Localization } } - /// - /// Returns an for all strings in the current culture. - /// - /// The . - public virtual IEnumerator GetEnumerator() => GetEnumerator(CultureInfo.CurrentUICulture); - - IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); - - /// - /// Returns an for all strings in the specified culture. - /// - /// The to get strings for. - /// The . - protected IEnumerator GetEnumerator([NotNull] CultureInfo culture) - { - var resourceNames = GetResourceNamesFromCultureHierarchy(culture); - - foreach (var name in resourceNames) - { - var value = GetStringSafely(name, culture); - yield return new LocalizedString(name, value ?? name, resourceNotFound: value == null); - } - } - private IEnumerable GetResourceNamesFromCultureHierarchy(CultureInfo startingCulture) { var currentCulture = startingCulture; @@ -191,10 +190,10 @@ namespace Microsoft.Framework.Localization var cacheKey = $"assembly={_resourceAssemblyWrapper.FullName};resourceStreamName={resourceStreamName}"; - var cultureResourceNames = _resourceNamesCache.GetOrAdd(cacheKey, key => + var cultureResourceNames = _resourceNamesCache.GetOrAdd(cacheKey, _ => { var names = new List(); - using (var cultureResourceStream = _resourceAssemblyWrapper.GetManifestResourceStream(key)) + using (var cultureResourceStream = _resourceAssemblyWrapper.GetManifestResourceStream(resourceStreamName)) using (var resources = new ResourceReader(cultureResourceStream)) { foreach (DictionaryEntry entry in resources) diff --git a/src/Microsoft.Framework.Localization/ResourceManagerWithCultureStringLocalizer.cs b/src/Microsoft.Framework.Localization/ResourceManagerWithCultureStringLocalizer.cs index bc79cf838b..03f0135b54 100644 --- a/src/Microsoft.Framework.Localization/ResourceManagerWithCultureStringLocalizer.cs +++ b/src/Microsoft.Framework.Localization/ResourceManagerWithCultureStringLocalizer.cs @@ -58,6 +58,7 @@ namespace Microsoft.Framework.Localization } /// - public override IEnumerator GetEnumerator() => GetEnumerator(_culture); + public override IEnumerable GetAllStrings(bool includeAncestorCultures) => + GetAllStrings(includeAncestorCultures, _culture); } } \ No newline at end of file diff --git a/test/Microsoft.Framework.Localization.Test/ResourceManagerStringLocalizerTest.cs b/test/Microsoft.Framework.Localization.Test/ResourceManagerStringLocalizerTest.cs index 92191c0bf8..0bb208bdf8 100644 --- a/test/Microsoft.Framework.Localization.Test/ResourceManagerStringLocalizerTest.cs +++ b/test/Microsoft.Framework.Localization.Test/ResourceManagerStringLocalizerTest.cs @@ -27,8 +27,8 @@ namespace Microsoft.Framework.Localization.Test // Act for (int i = 0; i < 5; i++) { - localizer1.ToList(); - localizer2.ToList(); + localizer1.GetAllStrings().ToList(); + localizer2.GetAllStrings().ToList(); } // Assert @@ -50,8 +50,8 @@ namespace Microsoft.Framework.Localization.Test var localizer2 = new ResourceManagerStringLocalizer(resourceManager2, resourceAssembly2, baseName, resourceNamesCache); // Act - localizer1.ToList(); - localizer2.ToList(); + localizer1.GetAllStrings().ToList(); + localizer2.GetAllStrings().ToList(); // Assert var expectedCallCount = GetCultureInfoDepth(CultureInfo.CurrentUICulture); From 5eda65f3365291b7b5805f9e6815596e60df8364 Mon Sep 17 00:00:00 2001 From: damianedwards Date: Thu, 28 May 2015 12:45:52 -0700 Subject: [PATCH 036/390] Fix default path in CultureInfoGenerator: - #26 --- src/CultureInfoGenerator/Program.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/CultureInfoGenerator/Program.cs b/src/CultureInfoGenerator/Program.cs index f45c1c7f1c..350399c19f 100644 --- a/src/CultureInfoGenerator/Program.cs +++ b/src/CultureInfoGenerator/Program.cs @@ -22,7 +22,7 @@ namespace CultureInfoGenerator public void Main(string[] args) { - var outputFilePath = args.Length > 0 ? args[0] : Path.Combine(_appPath, "../Microsoft.Framework.Globalization.CultureCache/CultureInfoList.cs"); + var outputFilePath = Path.GetFullPath(args.Length > 0 ? args[0] : Path.Combine(_appPath, "../Microsoft.Framework.Globalization.CultureInfoCache/CultureInfoList.cs")); var netFxVersion = Get45or451FromRegistry(); var windowsVersion = Environment.OSVersion; @@ -76,11 +76,12 @@ namespace Microsoft.Framework.Globalization writer.WriteLine(); } } - writer.WriteLine( @" }; } }"); + + Console.WriteLine($"{cultures.Length} culture names written to {outputFilePath}"); } } From 39f40d2c5fb377fc8c9512f32bb30e035f0848a7 Mon Sep 17 00:00:00 2001 From: damianedwards Date: Fri, 29 May 2015 11:46:24 -0700 Subject: [PATCH 037/390] Fix references to include beta-* --- samples/LocalizationSample/project.json | 4 ++-- src/Microsoft.AspNet.Localization/project.json | 10 +++++----- .../project.json | 8 ++++---- .../project.json | 10 +++++----- src/Microsoft.Framework.Localization/project.json | 14 +++++++------- .../project.json | 2 +- 6 files changed, 24 insertions(+), 24 deletions(-) diff --git a/samples/LocalizationSample/project.json b/samples/LocalizationSample/project.json index fc4e2a0b98..f0b995b1db 100644 --- a/samples/LocalizationSample/project.json +++ b/samples/LocalizationSample/project.json @@ -17,8 +17,8 @@ "dnx451": { }, "dnxcore50": { "dependencies": { - "System.Globalization": "4.0.10-*", - "System.Globalization.Extensions": "4.0.0-*" + "System.Globalization": "4.0.10-beta-*", + "System.Globalization.Extensions": "4.0.0-beta-*" } } }, diff --git a/src/Microsoft.AspNet.Localization/project.json b/src/Microsoft.AspNet.Localization/project.json index 0a8b395c28..a32ebadea8 100644 --- a/src/Microsoft.AspNet.Localization/project.json +++ b/src/Microsoft.AspNet.Localization/project.json @@ -13,11 +13,11 @@ "dnx451": { }, "dnxcore50": { "dependencies": { - "Microsoft.CSharp": "4.0.0-*", - "System.Collections": "4.0.10-*", - "System.Globalization": "4.0.10-*", - "System.Linq": "4.0.0-*", - "System.Threading": "4.0.10-*" + "Microsoft.CSharp": "4.0.0-beta-*", + "System.Collections": "4.0.10-beta-*", + "System.Globalization": "4.0.10-beta-*", + "System.Linq": "4.0.0-beta-*", + "System.Threading": "4.0.10-beta-*" } } } diff --git a/src/Microsoft.Framework.Globalization.CultureInfoCache/project.json b/src/Microsoft.Framework.Globalization.CultureInfoCache/project.json index 2a10252ec1..0da4293651 100644 --- a/src/Microsoft.Framework.Globalization.CultureInfoCache/project.json +++ b/src/Microsoft.Framework.Globalization.CultureInfoCache/project.json @@ -9,10 +9,10 @@ "dnx451": { }, "dnxcore50": { "dependencies": { - "Microsoft.CSharp": "4.0.0-*", - "System.Collections": "4.0.10-*", - "System.Collections.Concurrent": "4.0.10-*", - "System.Globalization": "4.0.10-*" + "Microsoft.CSharp": "4.0.0-beta-*", + "System.Collections": "4.0.10-beta-*", + "System.Collections.Concurrent": "4.0.10-beta-*", + "System.Globalization": "4.0.10-beta-*" } } } diff --git a/src/Microsoft.Framework.Localization.Abstractions/project.json b/src/Microsoft.Framework.Localization.Abstractions/project.json index bb9914834f..71c915d446 100644 --- a/src/Microsoft.Framework.Localization.Abstractions/project.json +++ b/src/Microsoft.Framework.Localization.Abstractions/project.json @@ -10,11 +10,11 @@ "dnx451": { }, "dnxcore50": { "dependencies": { - "Microsoft.CSharp": "4.0.0-*", - "System.Collections": "4.0.10-*", - "System.Globalization": "4.0.10-*", - "System.Reflection": "4.0.10-*", - "System.Runtime": "4.0.20-*" + "Microsoft.CSharp": "4.0.0-beta-*", + "System.Collections": "4.0.10-beta-*", + "System.Globalization": "4.0.10-beta-*", + "System.Reflection": "4.0.10-beta-*", + "System.Runtime": "4.0.20-beta-*" } } } diff --git a/src/Microsoft.Framework.Localization/project.json b/src/Microsoft.Framework.Localization/project.json index 5e53c266ea..cbe316fa97 100644 --- a/src/Microsoft.Framework.Localization/project.json +++ b/src/Microsoft.Framework.Localization/project.json @@ -14,13 +14,13 @@ "dnx451": { }, "dnxcore50": { "dependencies": { - "Microsoft.CSharp": "4.0.0-*", - "System.Collections": "4.0.10-*", - "System.Collections.Concurrent": "4.0.10-*", - "System.Globalization": "4.0.10-*", - "System.Linq": "4.0.0-*", - "System.Resources.ResourceManager": "4.0.0-*", - "System.Threading": "4.0.10-*" + "Microsoft.CSharp": "4.0.0-beta-*", + "System.Collections": "4.0.10-beta-*", + "System.Collections.Concurrent": "4.0.10-beta-*", + "System.Globalization": "4.0.10-beta-*", + "System.Linq": "4.0.0-beta-*", + "System.Resources.ResourceManager": "4.0.0-beta-*", + "System.Threading": "4.0.10-beta-*" } } } diff --git a/test/Microsoft.Framework.Localization.Test/project.json b/test/Microsoft.Framework.Localization.Test/project.json index 420577a021..363986e497 100644 --- a/test/Microsoft.Framework.Localization.Test/project.json +++ b/test/Microsoft.Framework.Localization.Test/project.json @@ -3,7 +3,7 @@ "xunit": "2.1.0-*", "xunit.runner.dnx": "2.1.0-*", "Microsoft.Framework.Localization": "1.0.0-*", - "System.Reflection": "4.0.10-*" + "System.Reflection": "4.0.10-beta-*" }, "commands": { From 6644b5beba1a7d433caa09c3f2032037981e7a78 Mon Sep 17 00:00:00 2001 From: Pranav K Date: Wed, 3 Jun 2015 22:41:05 -0700 Subject: [PATCH 038/390] Move System.Resources.ReaderWriter to dnxcore50 node --- src/Microsoft.Framework.Localization/project.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Microsoft.Framework.Localization/project.json b/src/Microsoft.Framework.Localization/project.json index cbe316fa97..448282be8c 100644 --- a/src/Microsoft.Framework.Localization/project.json +++ b/src/Microsoft.Framework.Localization/project.json @@ -7,7 +7,6 @@ "Microsoft.Framework.Localization.Abstractions": "1.0.0-*", "Microsoft.Framework.NotNullAttribute.Sources": { "type": "build", "version": "1.0.0-*" }, "Microsoft.Framework.Runtime.Abstractions": "1.0.0-*", - "System.Resources.ReaderWriter": "4.0.0-*" }, "frameworks": { @@ -19,6 +18,7 @@ "System.Collections.Concurrent": "4.0.10-beta-*", "System.Globalization": "4.0.10-beta-*", "System.Linq": "4.0.0-beta-*", + "System.Resources.ReaderWriter": "4.0.0-beta-*", "System.Resources.ResourceManager": "4.0.0-beta-*", "System.Threading": "4.0.10-beta-*" } From f5818634450e07949d33d30267dbf193d5d245f7 Mon Sep 17 00:00:00 2001 From: anurse Date: Fri, 12 Jun 2015 23:43:46 -0700 Subject: [PATCH 039/390] move System.Reflection dependency to dnxcore50 in tests --- test/Microsoft.Framework.Localization.Test/project.json | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/test/Microsoft.Framework.Localization.Test/project.json b/test/Microsoft.Framework.Localization.Test/project.json index 363986e497..358f1452fa 100644 --- a/test/Microsoft.Framework.Localization.Test/project.json +++ b/test/Microsoft.Framework.Localization.Test/project.json @@ -2,8 +2,7 @@ "dependencies": { "xunit": "2.1.0-*", "xunit.runner.dnx": "2.1.0-*", - "Microsoft.Framework.Localization": "1.0.0-*", - "System.Reflection": "4.0.10-beta-*" + "Microsoft.Framework.Localization": "1.0.0-*" }, "commands": { @@ -12,6 +11,10 @@ "frameworks": { "dnx451": { }, - "dnxcore50": { } + "dnxcore50": { + "dependencies": { + "System.Reflection": "4.0.10-beta-*" + } + } } } From d436955e02a83831df96bb61d3ee587c497e93b7 Mon Sep 17 00:00:00 2001 From: anurse Date: Sat, 13 Jun 2015 00:12:33 -0700 Subject: [PATCH 040/390] Switch to xunit.runner.aspnet --- test/Microsoft.Framework.Localization.Test/project.json | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/test/Microsoft.Framework.Localization.Test/project.json b/test/Microsoft.Framework.Localization.Test/project.json index 358f1452fa..a16410ca8e 100644 --- a/test/Microsoft.Framework.Localization.Test/project.json +++ b/test/Microsoft.Framework.Localization.Test/project.json @@ -1,17 +1,16 @@ { "dependencies": { - "xunit": "2.1.0-*", - "xunit.runner.dnx": "2.1.0-*", + "xunit.runner.aspnet": "2.0.0-aspnet-*", "Microsoft.Framework.Localization": "1.0.0-*" }, "commands": { - "test": "xunit.runner.dnx" + "test": "xunit.runner.aspnet" }, "frameworks": { "dnx451": { }, - "dnxcore50": { + "dnxcore50": { "dependencies": { "System.Reflection": "4.0.10-beta-*" } From 1e3b46221036a48f01e07fd0cbf07efd5d71ea02 Mon Sep 17 00:00:00 2001 From: Hisham Abdullah Bin Ateya Date: Sun, 31 May 2015 03:09:31 +0300 Subject: [PATCH 041/390] Remove unnecessary parantheses --- src/CultureInfoGenerator/Program.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/CultureInfoGenerator/Program.cs b/src/CultureInfoGenerator/Program.cs index 350399c19f..06fac3f98c 100644 --- a/src/CultureInfoGenerator/Program.cs +++ b/src/CultureInfoGenerator/Program.cs @@ -105,15 +105,15 @@ namespace Microsoft.Framework.Globalization { return "4.6 RC or later"; } - if ((releaseKey >= 379893)) + if (releaseKey >= 379893) { return "4.5.2 or later"; } - if ((releaseKey >= 378675)) + if (releaseKey >= 378675) { return "4.5.1 or later"; } - if ((releaseKey >= 378389)) + if (releaseKey >= 378389) { return "4.5 or later"; } From 113fa6d54549d5c2b9673aa3eb52d93cfd592ac2 Mon Sep 17 00:00:00 2001 From: Hisham Abdullah Bin Ateya Date: Wed, 17 Jun 2015 04:17:36 +0300 Subject: [PATCH 042/390] Remove "I" from "ApplicationBuilderExtensions" --- ...tionBuilderExtensions.cs => ApplicationBuilderExtensions.cs} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename src/Microsoft.AspNet.Localization/{IApplicationBuilderExtensions.cs => ApplicationBuilderExtensions.cs} (97%) diff --git a/src/Microsoft.AspNet.Localization/IApplicationBuilderExtensions.cs b/src/Microsoft.AspNet.Localization/ApplicationBuilderExtensions.cs similarity index 97% rename from src/Microsoft.AspNet.Localization/IApplicationBuilderExtensions.cs rename to src/Microsoft.AspNet.Localization/ApplicationBuilderExtensions.cs index fbc6b08cba..5c3389616c 100644 --- a/src/Microsoft.AspNet.Localization/IApplicationBuilderExtensions.cs +++ b/src/Microsoft.AspNet.Localization/ApplicationBuilderExtensions.cs @@ -9,7 +9,7 @@ namespace Microsoft.AspNet.Builder /// /// Extension methods for adding the to an application. /// - public static class IApplicationBuilderExtensions + public static class ApplicationBuilderExtensions { /// /// Adds the to automatically set culture information for From 47ff0f26acf2267596a51c7370e1d8d427119adf Mon Sep 17 00:00:00 2001 From: anurse Date: Tue, 16 Jun 2015 09:48:47 -0700 Subject: [PATCH 043/390] return to the official xunit build --- test/Microsoft.Framework.Localization.Test/project.json | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/test/Microsoft.Framework.Localization.Test/project.json b/test/Microsoft.Framework.Localization.Test/project.json index a16410ca8e..1b327e5aea 100644 --- a/test/Microsoft.Framework.Localization.Test/project.json +++ b/test/Microsoft.Framework.Localization.Test/project.json @@ -1,11 +1,12 @@ { "dependencies": { - "xunit.runner.aspnet": "2.0.0-aspnet-*", + "xunit": "2.1.0-*", + "xunit.runner.dnx": "2.1.0-*", "Microsoft.Framework.Localization": "1.0.0-*" }, "commands": { - "test": "xunit.runner.aspnet" + "test": "xunit.runner.dnx" }, "frameworks": { From a710c55ba9e5e624331a8d505c8aa8629c7dc5e8 Mon Sep 17 00:00:00 2001 From: Doug Bunting Date: Tue, 23 Jun 2015 12:11:32 -0700 Subject: [PATCH 044/390] Change hardcoded `bash` shebang to `env` - aspnet/Home#695 - support various `bash` installation locations - in particular, enable building on FreeBSD --- build.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.sh b/build.sh index d81164353c..3ef874f9bd 100755 --- a/build.sh +++ b/build.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash if test `uname` = Darwin; then cachedir=~/Library/Caches/KBuild From 8f9a47358e84499ee137b8a3d8ca6c65b59a7d19 Mon Sep 17 00:00:00 2001 From: Hisham Abdullah Bin Ateya Date: Fri, 26 Jun 2015 04:49:43 +0300 Subject: [PATCH 045/390] Fix 'RequestLocalizationMiddleware' missing docs --- .../RequestLocalizationMiddleware.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Microsoft.AspNet.Localization/RequestLocalizationMiddleware.cs b/src/Microsoft.AspNet.Localization/RequestLocalizationMiddleware.cs index 4dc60d7a86..c09abc1915 100644 --- a/src/Microsoft.AspNet.Localization/RequestLocalizationMiddleware.cs +++ b/src/Microsoft.AspNet.Localization/RequestLocalizationMiddleware.cs @@ -23,7 +23,7 @@ namespace Microsoft.AspNet.Localization /// Creates a new . /// /// The representing the next middleware in the pipeline. - /// + /// The representing the options for the . public RequestLocalizationMiddleware([NotNull] RequestDelegate next, [NotNull] RequestLocalizationOptions options) { _next = next; From 576d4eaf8d37bf1d13544a1a55e6047000122c79 Mon Sep 17 00:00:00 2001 From: Hisham Abdullah Bin Ateya Date: Fri, 26 Jun 2015 23:17:04 +0300 Subject: [PATCH 046/390] Fix missing docs in 'RequestCultureFeature' --- src/Microsoft.AspNet.Localization/RequestCultureFeature.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Microsoft.AspNet.Localization/RequestCultureFeature.cs b/src/Microsoft.AspNet.Localization/RequestCultureFeature.cs index f512cba2aa..601ff29bd6 100644 --- a/src/Microsoft.AspNet.Localization/RequestCultureFeature.cs +++ b/src/Microsoft.AspNet.Localization/RequestCultureFeature.cs @@ -14,6 +14,7 @@ namespace Microsoft.AspNet.Localization /// Creates a new with the specified . /// /// The . + /// The . public RequestCultureFeature([NotNull] RequestCulture requestCulture, IRequestCultureProvider provider) { RequestCulture = requestCulture; From bce1e8f5b4ed2372f6913e96c18a5f2af5ebc3db Mon Sep 17 00:00:00 2001 From: Hisham Abdullah Bin Ateya Date: Sun, 5 Jul 2015 05:36:50 +0300 Subject: [PATCH 047/390] Remove extra comma from project.json --- src/Microsoft.Framework.Localization/project.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Microsoft.Framework.Localization/project.json b/src/Microsoft.Framework.Localization/project.json index 448282be8c..db3e57eca4 100644 --- a/src/Microsoft.Framework.Localization/project.json +++ b/src/Microsoft.Framework.Localization/project.json @@ -6,7 +6,7 @@ "Microsoft.Framework.DependencyInjection.Abstractions": "1.0.0-*", "Microsoft.Framework.Localization.Abstractions": "1.0.0-*", "Microsoft.Framework.NotNullAttribute.Sources": { "type": "build", "version": "1.0.0-*" }, - "Microsoft.Framework.Runtime.Abstractions": "1.0.0-*", + "Microsoft.Framework.Runtime.Abstractions": "1.0.0-*" }, "frameworks": { From 064e2fe553e4169a87bfdd835020fcc6bed4e6cc Mon Sep 17 00:00:00 2001 From: Kiran Challa Date: Wed, 22 Jul 2015 15:00:14 -0700 Subject: [PATCH 048/390] Added repository information to project.json files --- src/CultureInfoGenerator/project.json | 27 ++++++---- .../project.json | 46 +++++++++------- .../project.json | 33 +++++++----- .../project.json | 40 ++++++++------ .../project.json | 52 +++++++++++-------- 5 files changed, 116 insertions(+), 82 deletions(-) diff --git a/src/CultureInfoGenerator/project.json b/src/CultureInfoGenerator/project.json index ea3b4ec35e..6eddb7c95e 100644 --- a/src/CultureInfoGenerator/project.json +++ b/src/CultureInfoGenerator/project.json @@ -1,16 +1,21 @@ { - "version": "1.0.0-*", - "description": "Generates a list of known culture names from the OS using CultureInfo.GetCultures. This tool is intended to be run on Windows using full .NET Framework.", + "version": "1.0.0-*", + "description": "Generates a list of known culture names from the OS using CultureInfo.GetCultures. This tool is intended to be run on Windows using full .NET Framework.", - "dependencies": { - "Microsoft.Framework.Runtime.Abstractions": "1.0.0-*" - }, + "dependencies": { + "Microsoft.Framework.Runtime.Abstractions": "1.0.0-*" + }, - "commands": { - "CultureInfoGenerator": "CultureInfoGenerator" - }, + "commands": { + "CultureInfoGenerator": "CultureInfoGenerator" + }, - "frameworks": { - "dnx451": { } - } + "frameworks": { + "dnx451": { } + }, + + "repository": { + "type": "git", + "url": "https://github.com/aspnet/localization" + } } diff --git a/src/Microsoft.AspNet.Localization/project.json b/src/Microsoft.AspNet.Localization/project.json index a32ebadea8..06cc6c35e1 100644 --- a/src/Microsoft.AspNet.Localization/project.json +++ b/src/Microsoft.AspNet.Localization/project.json @@ -1,24 +1,32 @@ { - "version": "1.0.0-*", - "description": "Middleware for automatically applying culture information to HTTP requests.", + "version": "1.0.0-*", + "description": "Middleware for automatically applying culture information to HTTP requests.", - "dependencies": { - "Microsoft.AspNet.Http.Extensions": "1.0.0-*", - "Microsoft.Framework.Globalization.CultureInfoCache": "1.0.0-*", - "Microsoft.Framework.Localization.Abstractions": "1.0.0-*", - "Microsoft.Framework.NotNullAttribute.Sources": { "type": "build", "version": "1.0.0-*" } - }, + "dependencies": { + "Microsoft.AspNet.Http.Extensions": "1.0.0-*", + "Microsoft.Framework.Globalization.CultureInfoCache": "1.0.0-*", + "Microsoft.Framework.Localization.Abstractions": "1.0.0-*", + "Microsoft.Framework.NotNullAttribute.Sources": { + "type": "build", + "version": "1.0.0-*" + } + }, - "frameworks": { - "dnx451": { }, - "dnxcore50": { - "dependencies": { - "Microsoft.CSharp": "4.0.0-beta-*", - "System.Collections": "4.0.10-beta-*", - "System.Globalization": "4.0.10-beta-*", - "System.Linq": "4.0.0-beta-*", - "System.Threading": "4.0.10-beta-*" - } + "frameworks": { + "dnx451": { }, + "dnxcore50": { + "dependencies": { + "Microsoft.CSharp": "4.0.0-beta-*", + "System.Collections": "4.0.10-beta-*", + "System.Globalization": "4.0.10-beta-*", + "System.Linq": "4.0.0-beta-*", + "System.Threading": "4.0.10-beta-*" + } + } + }, + + "repository": { + "type": "git", + "url": "https://github.com/aspnet/localization" } - } } diff --git a/src/Microsoft.Framework.Globalization.CultureInfoCache/project.json b/src/Microsoft.Framework.Globalization.CultureInfoCache/project.json index 0da4293651..68ae296ee7 100644 --- a/src/Microsoft.Framework.Globalization.CultureInfoCache/project.json +++ b/src/Microsoft.Framework.Globalization.CultureInfoCache/project.json @@ -1,19 +1,24 @@ { - "version": "1.0.0-*", - "description": "Provides cached instances of CultureInfo using a generated list of known culture names for use in scenarios where unbounded CultureInfo creation is undesirable.", + "version": "1.0.0-*", + "description": "Provides cached instances of CultureInfo using a generated list of known culture names for use in scenarios where unbounded CultureInfo creation is undesirable.", - "dependencies": { - }, + "dependencies": { + }, - "frameworks": { - "dnx451": { }, - "dnxcore50": { - "dependencies": { - "Microsoft.CSharp": "4.0.0-beta-*", - "System.Collections": "4.0.10-beta-*", - "System.Collections.Concurrent": "4.0.10-beta-*", - "System.Globalization": "4.0.10-beta-*" - } + "frameworks": { + "dnx451": { }, + "dnxcore50": { + "dependencies": { + "Microsoft.CSharp": "4.0.0-beta-*", + "System.Collections": "4.0.10-beta-*", + "System.Collections.Concurrent": "4.0.10-beta-*", + "System.Globalization": "4.0.10-beta-*" + } + } + }, + + "repository": { + "type": "git", + "url": "https://github.com/aspnet/localization" } - } } diff --git a/src/Microsoft.Framework.Localization.Abstractions/project.json b/src/Microsoft.Framework.Localization.Abstractions/project.json index 71c915d446..a6ff9e3105 100644 --- a/src/Microsoft.Framework.Localization.Abstractions/project.json +++ b/src/Microsoft.Framework.Localization.Abstractions/project.json @@ -1,21 +1,29 @@ { - "version": "1.0.0-*", - "description": "Abstractions of application localization services.", + "version": "1.0.0-*", + "description": "Abstractions of application localization services.", - "dependencies": { - "Microsoft.Framework.NotNullAttribute.Sources": { "type": "build", "version": "1.0.0-*" } - }, + "dependencies": { + "Microsoft.Framework.NotNullAttribute.Sources": { + "type": "build", + "version": "1.0.0-*" + } + }, - "frameworks": { - "dnx451": { }, - "dnxcore50": { - "dependencies": { - "Microsoft.CSharp": "4.0.0-beta-*", - "System.Collections": "4.0.10-beta-*", - "System.Globalization": "4.0.10-beta-*", - "System.Reflection": "4.0.10-beta-*", - "System.Runtime": "4.0.20-beta-*" - } + "frameworks": { + "dnx451": { }, + "dnxcore50": { + "dependencies": { + "Microsoft.CSharp": "4.0.0-beta-*", + "System.Collections": "4.0.10-beta-*", + "System.Globalization": "4.0.10-beta-*", + "System.Reflection": "4.0.10-beta-*", + "System.Runtime": "4.0.20-beta-*" + } + } + }, + + "repository": { + "type": "git", + "url": "https://github.com/aspnet/localization" } - } } diff --git a/src/Microsoft.Framework.Localization/project.json b/src/Microsoft.Framework.Localization/project.json index db3e57eca4..27b21989e3 100644 --- a/src/Microsoft.Framework.Localization/project.json +++ b/src/Microsoft.Framework.Localization/project.json @@ -1,27 +1,35 @@ { - "version": "1.0.0-*", - "description": "Application localization services.", + "version": "1.0.0-*", + "description": "Application localization services.", - "dependencies": { - "Microsoft.Framework.DependencyInjection.Abstractions": "1.0.0-*", - "Microsoft.Framework.Localization.Abstractions": "1.0.0-*", - "Microsoft.Framework.NotNullAttribute.Sources": { "type": "build", "version": "1.0.0-*" }, - "Microsoft.Framework.Runtime.Abstractions": "1.0.0-*" - }, + "dependencies": { + "Microsoft.Framework.DependencyInjection.Abstractions": "1.0.0-*", + "Microsoft.Framework.Localization.Abstractions": "1.0.0-*", + "Microsoft.Framework.NotNullAttribute.Sources": { + "type": "build", + "version": "1.0.0-*" + }, + "Microsoft.Framework.Runtime.Abstractions": "1.0.0-*" + }, - "frameworks": { - "dnx451": { }, - "dnxcore50": { - "dependencies": { - "Microsoft.CSharp": "4.0.0-beta-*", - "System.Collections": "4.0.10-beta-*", - "System.Collections.Concurrent": "4.0.10-beta-*", - "System.Globalization": "4.0.10-beta-*", - "System.Linq": "4.0.0-beta-*", - "System.Resources.ReaderWriter": "4.0.0-beta-*", - "System.Resources.ResourceManager": "4.0.0-beta-*", - "System.Threading": "4.0.10-beta-*" - } + "frameworks": { + "dnx451": { }, + "dnxcore50": { + "dependencies": { + "Microsoft.CSharp": "4.0.0-beta-*", + "System.Collections": "4.0.10-beta-*", + "System.Collections.Concurrent": "4.0.10-beta-*", + "System.Globalization": "4.0.10-beta-*", + "System.Linq": "4.0.0-beta-*", + "System.Resources.ReaderWriter": "4.0.0-beta-*", + "System.Resources.ResourceManager": "4.0.0-beta-*", + "System.Threading": "4.0.10-beta-*" + } + } + }, + + "repository": { + "type": "git", + "url": "https://github.com/aspnet/localization" } - } } From 16a05755bfbeb16590d32d17f3b5a87e3023ba0b Mon Sep 17 00:00:00 2001 From: Victor Hurdugaci Date: Wed, 29 Jul 2015 11:01:20 -0700 Subject: [PATCH 049/390] React to DNX renames --- src/CultureInfoGenerator/Program.cs | 2 +- src/CultureInfoGenerator/project.json | 2 +- .../ResourceManagerStringLocalizerFactory.cs | 2 +- src/Microsoft.Framework.Localization/project.json | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/CultureInfoGenerator/Program.cs b/src/CultureInfoGenerator/Program.cs index 06fac3f98c..8f0f72dc45 100644 --- a/src/CultureInfoGenerator/Program.cs +++ b/src/CultureInfoGenerator/Program.cs @@ -4,7 +4,7 @@ using System; using System.Globalization; using System.IO; -using Microsoft.Framework.Runtime; +using Microsoft.Dnx.Runtime; using Microsoft.Win32; namespace CultureInfoGenerator diff --git a/src/CultureInfoGenerator/project.json b/src/CultureInfoGenerator/project.json index 6eddb7c95e..d2ee9714b8 100644 --- a/src/CultureInfoGenerator/project.json +++ b/src/CultureInfoGenerator/project.json @@ -3,7 +3,7 @@ "description": "Generates a list of known culture names from the OS using CultureInfo.GetCultures. This tool is intended to be run on Windows using full .NET Framework.", "dependencies": { - "Microsoft.Framework.Runtime.Abstractions": "1.0.0-*" + "Microsoft.Dnx.Runtime.Abstractions": "1.0.0-*" }, "commands": { diff --git a/src/Microsoft.Framework.Localization/ResourceManagerStringLocalizerFactory.cs b/src/Microsoft.Framework.Localization/ResourceManagerStringLocalizerFactory.cs index d0ebe781a1..b76fbdde53 100644 --- a/src/Microsoft.Framework.Localization/ResourceManagerStringLocalizerFactory.cs +++ b/src/Microsoft.Framework.Localization/ResourceManagerStringLocalizerFactory.cs @@ -5,7 +5,7 @@ using System; using System.Reflection; using System.Resources; using Microsoft.Framework.Internal; -using Microsoft.Framework.Runtime; +using Microsoft.Dnx.Runtime; namespace Microsoft.Framework.Localization { diff --git a/src/Microsoft.Framework.Localization/project.json b/src/Microsoft.Framework.Localization/project.json index 27b21989e3..66fccaea35 100644 --- a/src/Microsoft.Framework.Localization/project.json +++ b/src/Microsoft.Framework.Localization/project.json @@ -9,7 +9,7 @@ "type": "build", "version": "1.0.0-*" }, - "Microsoft.Framework.Runtime.Abstractions": "1.0.0-*" + "Microsoft.Dnx.Runtime.Abstractions": "1.0.0-*" }, "frameworks": { From 5f83c0df434d117abbca89dc7b5e2e2286d738c3 Mon Sep 17 00:00:00 2001 From: Hisham Abdullah Bin Ateya Date: Wed, 29 Jul 2015 10:09:57 +0300 Subject: [PATCH 050/390] Rename Microsoft.Framework.Localization.Test to Microsoft.Framework.Localization.Tests Microsoft.Framework.Localization.Test -> Microsoft.Framework.Localization.Tests Rename the .xproj Rename the solution file --- Localization.sln | 2 +- .../Microsoft.Framework.Localization.Tests.xproj} | 0 .../ResourceManagerStringLocalizerTest.cs | 0 .../project.json | 0 4 files changed, 1 insertion(+), 1 deletion(-) rename test/{Microsoft.Framework.Localization.Test/Microsoft.Framework.Localization.Test.xproj => Microsoft.Framework.Localization.Tests/Microsoft.Framework.Localization.Tests.xproj} (100%) rename test/{Microsoft.Framework.Localization.Test => Microsoft.Framework.Localization.Tests}/ResourceManagerStringLocalizerTest.cs (100%) rename test/{Microsoft.Framework.Localization.Test => Microsoft.Framework.Localization.Tests}/project.json (100%) diff --git a/Localization.sln b/Localization.sln index f5fe65e09e..db586522a9 100644 --- a/Localization.sln +++ b/Localization.sln @@ -26,7 +26,7 @@ Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.Framework.Globali EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{B723DB83-A670-4BCB-95FB-195361331AD2}" EndProject -Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.Framework.Localization.Test", "test\Microsoft.Framework.Localization.Test\Microsoft.Framework.Localization.Test.xproj", "{287AD58D-DF34-4F16-8616-FD78FA1CADF9}" +Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.Framework.Localization.Tests", "test\Microsoft.Framework.Localization.Tests\Microsoft.Framework.Localization.Tests.xproj", "{287AD58D-DF34-4F16-8616-FD78FA1CADF9}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution diff --git a/test/Microsoft.Framework.Localization.Test/Microsoft.Framework.Localization.Test.xproj b/test/Microsoft.Framework.Localization.Tests/Microsoft.Framework.Localization.Tests.xproj similarity index 100% rename from test/Microsoft.Framework.Localization.Test/Microsoft.Framework.Localization.Test.xproj rename to test/Microsoft.Framework.Localization.Tests/Microsoft.Framework.Localization.Tests.xproj diff --git a/test/Microsoft.Framework.Localization.Test/ResourceManagerStringLocalizerTest.cs b/test/Microsoft.Framework.Localization.Tests/ResourceManagerStringLocalizerTest.cs similarity index 100% rename from test/Microsoft.Framework.Localization.Test/ResourceManagerStringLocalizerTest.cs rename to test/Microsoft.Framework.Localization.Tests/ResourceManagerStringLocalizerTest.cs diff --git a/test/Microsoft.Framework.Localization.Test/project.json b/test/Microsoft.Framework.Localization.Tests/project.json similarity index 100% rename from test/Microsoft.Framework.Localization.Test/project.json rename to test/Microsoft.Framework.Localization.Tests/project.json From 59d957df3193902c9ee9beea0fe488b75fecee46 Mon Sep 17 00:00:00 2001 From: Hisham Abdullah Bin Ateya Date: Sun, 31 May 2015 04:25:27 +0300 Subject: [PATCH 051/390] Simplify generating culture names Replace '\n' with Environment.NewLine --- src/CultureInfoGenerator/Program.cs | 23 +++-------------------- 1 file changed, 3 insertions(+), 20 deletions(-) diff --git a/src/CultureInfoGenerator/Program.cs b/src/CultureInfoGenerator/Program.cs index 8f0f72dc45..ffae2c7fa3 100644 --- a/src/CultureInfoGenerator/Program.cs +++ b/src/CultureInfoGenerator/Program.cs @@ -4,8 +4,8 @@ using System; using System.Globalization; using System.IO; -using Microsoft.Dnx.Runtime; -using Microsoft.Win32; +using System.Linq; +using Microsoft.Dnx.Runtime;using Microsoft.Win32; namespace CultureInfoGenerator { @@ -58,24 +58,7 @@ namespace Microsoft.Framework.Globalization | CultureTypes.InstalledWin32Cultures | CultureTypes.SpecificCultures); - var format = " \"{0}\""; - - for (int i = 0; i < cultures.Length; i++) - { - var culture = cultures[i]; - - writer.Write(format, culture.Name); - - if (i < cultures.Length - 1) - { - writer.WriteLine(","); - } - else - { - // Last entry - writer.WriteLine(); - } - } + writer.WriteLine(string.Join($",{Environment.NewLine}", cultures.Select(c => $" \"{c.Name}\""))); writer.WriteLine( @" }; } From 01d0b5db22d6ba2d71b025bd08be099529efc20a Mon Sep 17 00:00:00 2001 From: Hisham Abdullah Bin Ateya Date: Thu, 30 Jul 2015 07:42:44 +0300 Subject: [PATCH 052/390] Reformat project.json Reformat project.json Fix typo --- src/CultureInfoGenerator/project.json | 24 ++++++++----------- .../project.json | 13 ++++------ .../project.json | 13 ++++------ .../project.json | 13 ++++------ .../project.json | 13 ++++------ .../project.json | 10 ++++---- 6 files changed, 34 insertions(+), 52 deletions(-) diff --git a/src/CultureInfoGenerator/project.json b/src/CultureInfoGenerator/project.json index d2ee9714b8..43523da025 100644 --- a/src/CultureInfoGenerator/project.json +++ b/src/CultureInfoGenerator/project.json @@ -1,21 +1,17 @@ { "version": "1.0.0-*", "description": "Generates a list of known culture names from the OS using CultureInfo.GetCultures. This tool is intended to be run on Windows using full .NET Framework.", - - "dependencies": { - "Microsoft.Dnx.Runtime.Abstractions": "1.0.0-*" - }, - - "commands": { - "CultureInfoGenerator": "CultureInfoGenerator" - }, - - "frameworks": { - "dnx451": { } - }, - "repository": { "type": "git", "url": "https://github.com/aspnet/localization" + }, + "dependencies": { + "Microsoft.Dnx.Runtime.Abstractions": "1.0.0-*" + }, + "commands": { + "CultureInfoGenerator": "CultureInfoGenerator" + }, + "frameworks": { + "dnx451": { } } -} +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Localization/project.json b/src/Microsoft.AspNet.Localization/project.json index 06cc6c35e1..6b2e9db20f 100644 --- a/src/Microsoft.AspNet.Localization/project.json +++ b/src/Microsoft.AspNet.Localization/project.json @@ -1,7 +1,10 @@ { "version": "1.0.0-*", "description": "Middleware for automatically applying culture information to HTTP requests.", - + "repository": { + "type": "git", + "url": "https://github.com/aspnet/localization" + }, "dependencies": { "Microsoft.AspNet.Http.Extensions": "1.0.0-*", "Microsoft.Framework.Globalization.CultureInfoCache": "1.0.0-*", @@ -11,7 +14,6 @@ "version": "1.0.0-*" } }, - "frameworks": { "dnx451": { }, "dnxcore50": { @@ -23,10 +25,5 @@ "System.Threading": "4.0.10-beta-*" } } - }, - - "repository": { - "type": "git", - "url": "https://github.com/aspnet/localization" } -} +} \ No newline at end of file diff --git a/src/Microsoft.Framework.Globalization.CultureInfoCache/project.json b/src/Microsoft.Framework.Globalization.CultureInfoCache/project.json index 68ae296ee7..76158774a3 100644 --- a/src/Microsoft.Framework.Globalization.CultureInfoCache/project.json +++ b/src/Microsoft.Framework.Globalization.CultureInfoCache/project.json @@ -1,10 +1,12 @@ { "version": "1.0.0-*", "description": "Provides cached instances of CultureInfo using a generated list of known culture names for use in scenarios where unbounded CultureInfo creation is undesirable.", - + "repository": { + "type": "git", + "url": "https://github.com/aspnet/localization" + }, "dependencies": { }, - "frameworks": { "dnx451": { }, "dnxcore50": { @@ -15,10 +17,5 @@ "System.Globalization": "4.0.10-beta-*" } } - }, - - "repository": { - "type": "git", - "url": "https://github.com/aspnet/localization" } -} +} \ No newline at end of file diff --git a/src/Microsoft.Framework.Localization.Abstractions/project.json b/src/Microsoft.Framework.Localization.Abstractions/project.json index a6ff9e3105..8f343277a6 100644 --- a/src/Microsoft.Framework.Localization.Abstractions/project.json +++ b/src/Microsoft.Framework.Localization.Abstractions/project.json @@ -1,14 +1,16 @@ { "version": "1.0.0-*", "description": "Abstractions of application localization services.", - + "repository": { + "type": "git", + "url": "https://github.com/aspnet/localization" + }, "dependencies": { "Microsoft.Framework.NotNullAttribute.Sources": { "type": "build", "version": "1.0.0-*" } }, - "frameworks": { "dnx451": { }, "dnxcore50": { @@ -20,10 +22,5 @@ "System.Runtime": "4.0.20-beta-*" } } - }, - - "repository": { - "type": "git", - "url": "https://github.com/aspnet/localization" } -} +} \ No newline at end of file diff --git a/src/Microsoft.Framework.Localization/project.json b/src/Microsoft.Framework.Localization/project.json index 66fccaea35..d0837431ec 100644 --- a/src/Microsoft.Framework.Localization/project.json +++ b/src/Microsoft.Framework.Localization/project.json @@ -1,7 +1,10 @@ { "version": "1.0.0-*", "description": "Application localization services.", - + "repository": { + "type": "git", + "url": "https://github.com/aspnet/localization" + }, "dependencies": { "Microsoft.Framework.DependencyInjection.Abstractions": "1.0.0-*", "Microsoft.Framework.Localization.Abstractions": "1.0.0-*", @@ -11,7 +14,6 @@ }, "Microsoft.Dnx.Runtime.Abstractions": "1.0.0-*" }, - "frameworks": { "dnx451": { }, "dnxcore50": { @@ -26,10 +28,5 @@ "System.Threading": "4.0.10-beta-*" } } - }, - - "repository": { - "type": "git", - "url": "https://github.com/aspnet/localization" } -} +} \ No newline at end of file diff --git a/test/Microsoft.Framework.Localization.Tests/project.json b/test/Microsoft.Framework.Localization.Tests/project.json index 1b327e5aea..ec7ac09df6 100644 --- a/test/Microsoft.Framework.Localization.Tests/project.json +++ b/test/Microsoft.Framework.Localization.Tests/project.json @@ -4,11 +4,6 @@ "xunit.runner.dnx": "2.1.0-*", "Microsoft.Framework.Localization": "1.0.0-*" }, - - "commands": { - "test": "xunit.runner.dnx" - }, - "frameworks": { "dnx451": { }, "dnxcore50": { @@ -16,5 +11,8 @@ "System.Reflection": "4.0.10-beta-*" } } + }, + "commands": { + "test": "xunit.runner.dnx" } -} +} \ No newline at end of file From c228b65d478561869daa9161fc97a89ff02cad2e Mon Sep 17 00:00:00 2001 From: Hisham Abdullah Bin Ateya Date: Tue, 7 Jul 2015 07:35:37 +0300 Subject: [PATCH 053/390] Make 'DefaultCookieName' static readonly field instead of a property DefaultCookieName as Constant Add empty line --- .../CookieRequestCultureProvider.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Microsoft.AspNet.Localization/CookieRequestCultureProvider.cs b/src/Microsoft.AspNet.Localization/CookieRequestCultureProvider.cs index a9a20b03a7..7d477cbbb7 100644 --- a/src/Microsoft.AspNet.Localization/CookieRequestCultureProvider.cs +++ b/src/Microsoft.AspNet.Localization/CookieRequestCultureProvider.cs @@ -17,6 +17,11 @@ namespace Microsoft.AspNet.Localization private static readonly char[] _cookieSeparator = new[] { '|' }; private static readonly string _culturePrefix = "c="; private static readonly string _uiCulturePrefix = "uic="; + + /// + /// Represent the default cookie name used to track the user's preferred culture information, which is "ASPNET_CULTURE". + /// + public static readonly string DefaultCookieName = "ASPNET_CULTURE"; /// /// The name of the cookie that contains the user's preferred culture information. @@ -41,11 +46,6 @@ namespace Microsoft.AspNet.Localization return Task.FromResult(requestCulture); } - /// - /// The default name of the cookie used to track the user's preferred culture information. - /// - public static string DefaultCookieName { get; } = "ASPNET_CULTURE"; - /// /// Creates a string representation of a for placement in a cookie. /// From cc0c4e1f55b6985327e1a95b569aeb10a2970091 Mon Sep 17 00:00:00 2001 From: Troy Dai Date: Tue, 4 Aug 2015 10:15:29 -0700 Subject: [PATCH 054/390] Update CoreCLR versions --- samples/LocalizationSample/project.json | 4 ++-- src/Microsoft.AspNet.Localization/project.json | 12 ++++++------ .../project.json | 12 ++++++------ .../project.json | 12 ++++++------ .../project.json | 18 +++++++++--------- .../project.json | 4 ++-- 6 files changed, 31 insertions(+), 31 deletions(-) diff --git a/samples/LocalizationSample/project.json b/samples/LocalizationSample/project.json index f0b995b1db..0371ca4fb9 100644 --- a/samples/LocalizationSample/project.json +++ b/samples/LocalizationSample/project.json @@ -17,8 +17,8 @@ "dnx451": { }, "dnxcore50": { "dependencies": { - "System.Globalization": "4.0.10-beta-*", - "System.Globalization.Extensions": "4.0.0-beta-*" + "System.Globalization": "4.0.11-beta-*", + "System.Globalization.Extensions": "4.0.1-beta-*" } } }, diff --git a/src/Microsoft.AspNet.Localization/project.json b/src/Microsoft.AspNet.Localization/project.json index 6b2e9db20f..476b94b634 100644 --- a/src/Microsoft.AspNet.Localization/project.json +++ b/src/Microsoft.AspNet.Localization/project.json @@ -18,12 +18,12 @@ "dnx451": { }, "dnxcore50": { "dependencies": { - "Microsoft.CSharp": "4.0.0-beta-*", - "System.Collections": "4.0.10-beta-*", - "System.Globalization": "4.0.10-beta-*", - "System.Linq": "4.0.0-beta-*", - "System.Threading": "4.0.10-beta-*" + "Microsoft.CSharp": "4.0.1-beta-*", + "System.Collections": "4.0.11-beta-*", + "System.Globalization": "4.0.11-beta-*", + "System.Linq": "4.0.1-beta-*", + "System.Threading": "4.0.11-beta-*" } } } -} \ No newline at end of file +} diff --git a/src/Microsoft.Framework.Globalization.CultureInfoCache/project.json b/src/Microsoft.Framework.Globalization.CultureInfoCache/project.json index 76158774a3..8bb390e713 100644 --- a/src/Microsoft.Framework.Globalization.CultureInfoCache/project.json +++ b/src/Microsoft.Framework.Globalization.CultureInfoCache/project.json @@ -1,4 +1,4 @@ -{ +{ "version": "1.0.0-*", "description": "Provides cached instances of CultureInfo using a generated list of known culture names for use in scenarios where unbounded CultureInfo creation is undesirable.", "repository": { @@ -11,11 +11,11 @@ "dnx451": { }, "dnxcore50": { "dependencies": { - "Microsoft.CSharp": "4.0.0-beta-*", - "System.Collections": "4.0.10-beta-*", - "System.Collections.Concurrent": "4.0.10-beta-*", - "System.Globalization": "4.0.10-beta-*" + "Microsoft.CSharp": "4.0.1-beta-*", + "System.Collections": "4.0.11-beta-*", + "System.Collections.Concurrent": "4.0.11-beta-*", + "System.Globalization": "4.0.11-beta-*" } } } -} \ No newline at end of file +} diff --git a/src/Microsoft.Framework.Localization.Abstractions/project.json b/src/Microsoft.Framework.Localization.Abstractions/project.json index 8f343277a6..fcfa40fffe 100644 --- a/src/Microsoft.Framework.Localization.Abstractions/project.json +++ b/src/Microsoft.Framework.Localization.Abstractions/project.json @@ -15,12 +15,12 @@ "dnx451": { }, "dnxcore50": { "dependencies": { - "Microsoft.CSharp": "4.0.0-beta-*", - "System.Collections": "4.0.10-beta-*", - "System.Globalization": "4.0.10-beta-*", - "System.Reflection": "4.0.10-beta-*", - "System.Runtime": "4.0.20-beta-*" + "Microsoft.CSharp": "4.0.1-beta-*", + "System.Collections": "4.0.11-beta-*", + "System.Globalization": "4.0.11-beta-*", + "System.Reflection": "4.0.11-beta-*", + "System.Runtime": "4.0.21-beta-*" } } } -} \ No newline at end of file +} diff --git a/src/Microsoft.Framework.Localization/project.json b/src/Microsoft.Framework.Localization/project.json index d0837431ec..44e1cb40b3 100644 --- a/src/Microsoft.Framework.Localization/project.json +++ b/src/Microsoft.Framework.Localization/project.json @@ -1,4 +1,4 @@ -{ +{ "version": "1.0.0-*", "description": "Application localization services.", "repository": { @@ -18,15 +18,15 @@ "dnx451": { }, "dnxcore50": { "dependencies": { - "Microsoft.CSharp": "4.0.0-beta-*", - "System.Collections": "4.0.10-beta-*", - "System.Collections.Concurrent": "4.0.10-beta-*", - "System.Globalization": "4.0.10-beta-*", - "System.Linq": "4.0.0-beta-*", + "Microsoft.CSharp": "4.0.1-beta-*", + "System.Collections": "4.0.11-beta-*", + "System.Collections.Concurrent": "4.0.11-beta-*", + "System.Globalization": "4.0.11-beta-*", + "System.Linq": "4.0.1-beta-*", "System.Resources.ReaderWriter": "4.0.0-beta-*", - "System.Resources.ResourceManager": "4.0.0-beta-*", - "System.Threading": "4.0.10-beta-*" + "System.Resources.ResourceManager": "4.0.1-beta-*", + "System.Threading": "4.0.11-beta-*" } } } -} \ No newline at end of file +} diff --git a/test/Microsoft.Framework.Localization.Tests/project.json b/test/Microsoft.Framework.Localization.Tests/project.json index ec7ac09df6..bc4a953cec 100644 --- a/test/Microsoft.Framework.Localization.Tests/project.json +++ b/test/Microsoft.Framework.Localization.Tests/project.json @@ -8,11 +8,11 @@ "dnx451": { }, "dnxcore50": { "dependencies": { - "System.Reflection": "4.0.10-beta-*" + "System.Reflection": "4.0.11-beta-*" } } }, "commands": { "test": "xunit.runner.dnx" } -} \ No newline at end of file +} From 1cfc62dbfd048cb43faf0ff1fcc5c6ceea904a59 Mon Sep 17 00:00:00 2001 From: Hisham Abdullah Bin Ateya Date: Wed, 5 Aug 2015 20:23:19 +0300 Subject: [PATCH 055/390] Fix 'QueryStringRequestCultureProvider' Issue --- .../QueryStringRequestCultureProvider.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/Microsoft.AspNet.Localization/QueryStringRequestCultureProvider.cs b/src/Microsoft.AspNet.Localization/QueryStringRequestCultureProvider.cs index 0b5f0aef16..6c858e32a6 100644 --- a/src/Microsoft.AspNet.Localization/QueryStringRequestCultureProvider.cs +++ b/src/Microsoft.AspNet.Localization/QueryStringRequestCultureProvider.cs @@ -60,6 +60,12 @@ namespace Microsoft.AspNet.Localization queryUICulture = queryCulture; } + if (queryCulture == null && queryUICulture != null) + { + // Value for UI culture but not for culture so default to UI culture value for both + queryCulture = queryUICulture; + } + var culture = CultureInfoCache.GetCultureInfo(queryCulture); var uiCulture = CultureInfoCache.GetCultureInfo(queryUICulture); From 1034e8a343e1c9c9784ca710ec1fb8a40943b891 Mon Sep 17 00:00:00 2001 From: Pranav K Date: Tue, 11 Aug 2015 13:58:06 -0700 Subject: [PATCH 056/390] Reacting to DI changes --- .../LocalizationServiceCollectionExtensions.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Microsoft.Framework.Localization/LocalizationServiceCollectionExtensions.cs b/src/Microsoft.Framework.Localization/LocalizationServiceCollectionExtensions.cs index fe6f4694f2..f81c13afb4 100644 --- a/src/Microsoft.Framework.Localization/LocalizationServiceCollectionExtensions.cs +++ b/src/Microsoft.Framework.Localization/LocalizationServiceCollectionExtensions.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 Microsoft.Framework.DependencyInjection.Extensions; using Microsoft.Framework.Internal; using Microsoft.Framework.Localization; From 043670874ebe51d82a917135f87c6daa48c2d693 Mon Sep 17 00:00:00 2001 From: Kiran Challa Date: Tue, 11 Aug 2015 17:09:51 -0700 Subject: [PATCH 057/390] Enable pinning build script --- build.cmd | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/build.cmd b/build.cmd index 41025afb26..ccf195aee8 100644 --- a/build.cmd +++ b/build.cmd @@ -3,6 +3,8 @@ cd %~dp0 SETLOCAL SET CACHED_NUGET=%LocalAppData%\NuGet\NuGet.exe +SET BUILDCMD_KOREBUILD_VERSION="" +SET BUILDCMD_DNX_VERSION="" IF EXIST %CACHED_NUGET% goto copynuget echo Downloading latest version of NuGet.exe... @@ -16,13 +18,21 @@ copy %CACHED_NUGET% .nuget\nuget.exe > nul :restore IF EXIST packages\KoreBuild goto run -.nuget\NuGet.exe install KoreBuild -ExcludeVersion -o packages -nocache -pre +IF %BUILDCMD_KOREBUILD_VERSION%=="" ( + .nuget\NuGet.exe install KoreBuild -ExcludeVersion -o packages -nocache -pre +) ELSE ( + .nuget\NuGet.exe install KoreBuild -version %BUILDCMD_KOREBUILD_VERSION% -ExcludeVersion -o packages -nocache -pre +) .nuget\NuGet.exe install Sake -version 0.2 -o packages -ExcludeVersion IF "%SKIP_DNX_INSTALL%"=="1" goto run -CALL packages\KoreBuild\build\dnvm upgrade -runtime CLR -arch x86 +IF %BUILDCMD_DNX_VERSION%=="" ( + CALL packages\KoreBuild\build\dnvm upgrade -runtime CLR -arch x86 +) ELSE ( + CALL packages\KoreBuild\build\dnvm install %BUILDCMD_DNX_VERSION% -runtime CLR -arch x86 -a default +) CALL packages\KoreBuild\build\dnvm install default -runtime CoreCLR -arch x86 :run CALL packages\KoreBuild\build\dnvm use default -runtime CLR -arch x86 -packages\Sake\tools\Sake.exe -I packages\KoreBuild\build -f makefile.shade %* +packages\Sake\tools\Sake.exe -I packages\KoreBuild\build -f makefile.shade %* \ No newline at end of file From 8389d740b9461212af7912c438e0e47c31963d28 Mon Sep 17 00:00:00 2001 From: Pranav K Date: Mon, 17 Aug 2015 14:49:25 -0700 Subject: [PATCH 058/390] Updating to release NuGet.config. --- NuGet.Config | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/NuGet.Config b/NuGet.Config index da5e353ffb..a749821542 100644 --- a/NuGet.Config +++ b/NuGet.Config @@ -1,7 +1,7 @@ - + - + From 0b0d7d33f9eac6a8193889606dd570c365ec70ae Mon Sep 17 00:00:00 2001 From: Pranav K Date: Tue, 18 Aug 2015 14:00:27 -0700 Subject: [PATCH 059/390] Updating to aspnetliterelease. --- NuGet.Config | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NuGet.Config b/NuGet.Config index a749821542..7bdf59a0ae 100644 --- a/NuGet.Config +++ b/NuGet.Config @@ -1,7 +1,7 @@ - + From 694c15c1b1219615c6c8e6771add46e165c77be6 Mon Sep 17 00:00:00 2001 From: Pranav K Date: Tue, 18 Aug 2015 14:00:27 -0700 Subject: [PATCH 060/390] Updating to aspnetlitedev. --- NuGet.Config | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/NuGet.Config b/NuGet.Config index da5e353ffb..9f2efa6b96 100644 --- a/NuGet.Config +++ b/NuGet.Config @@ -1,7 +1,7 @@ - + - + From ce6404ea98dda9d0cbd85c713d1ab0e0f6a867fe Mon Sep 17 00:00:00 2001 From: "N. Taylor Mullen" Date: Wed, 19 Aug 2015 14:55:12 -0700 Subject: [PATCH 061/390] Update NuGet feed from v2 => v3. --- NuGet.Config | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NuGet.Config b/NuGet.Config index 9f2efa6b96..8c288a1431 100644 --- a/NuGet.Config +++ b/NuGet.Config @@ -3,6 +3,6 @@ - + \ No newline at end of file From a42eaefc588287552645efe7c3a4c4ab1c9f3c50 Mon Sep 17 00:00:00 2001 From: "N. Taylor Mullen" Date: Thu, 20 Aug 2015 15:38:35 -0700 Subject: [PATCH 062/390] Update 'build.cmd' to pull Sake from v2 NuGet feed. --- build.cmd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.cmd b/build.cmd index ccf195aee8..b54d91cf74 100644 --- a/build.cmd +++ b/build.cmd @@ -23,7 +23,7 @@ IF %BUILDCMD_KOREBUILD_VERSION%=="" ( ) ELSE ( .nuget\NuGet.exe install KoreBuild -version %BUILDCMD_KOREBUILD_VERSION% -ExcludeVersion -o packages -nocache -pre ) -.nuget\NuGet.exe install Sake -version 0.2 -o packages -ExcludeVersion +.nuget\NuGet.exe install Sake -ExcludeVersion -Source https://www.nuget.org/api/v2/ -Out packages IF "%SKIP_DNX_INSTALL%"=="1" goto run IF %BUILDCMD_DNX_VERSION%=="" ( From c885b92c99e521d25c54bcee0b74e6e66b844d81 Mon Sep 17 00:00:00 2001 From: "N. Taylor Mullen" Date: Thu, 20 Aug 2015 20:47:40 -0700 Subject: [PATCH 063/390] Update 'build.sh' to pull Sake from v2 NuGet feed. --- build.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.sh b/build.sh index 3ef874f9bd..68c3e8cb52 100755 --- a/build.sh +++ b/build.sh @@ -24,7 +24,7 @@ fi if test ! -d packages/KoreBuild; then mono .nuget/nuget.exe install KoreBuild -ExcludeVersion -o packages -nocache -pre - mono .nuget/nuget.exe install Sake -version 0.2 -o packages -ExcludeVersion + mono .nuget/nuget.exe install Sake -ExcludeVersion -Source https://www.nuget.org/api/v2/ -Out packages fi if ! type dnvm > /dev/null 2>&1; then From 2a1cf633fcd1a20fa1d39710f778cf04badfc332 Mon Sep 17 00:00:00 2001 From: Chris R Date: Mon, 31 Aug 2015 09:55:57 -0700 Subject: [PATCH 064/390] Use new HttpContext.Features API. --- samples/LocalizationSample/Startup.cs | 2 +- .../RequestLocalizationMiddleware.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/samples/LocalizationSample/Startup.cs b/samples/LocalizationSample/Startup.cs index 973663d740..e8c910b055 100644 --- a/samples/LocalizationSample/Startup.cs +++ b/samples/LocalizationSample/Startup.cs @@ -56,7 +56,7 @@ namespace LocalizationSample context.Response.StatusCode = 200; context.Response.ContentType = "text/html; charset=utf-8"; - var requestCultureFeature = context.GetFeature(); + var requestCultureFeature = context.Features.Get(); var requestCulture = requestCultureFeature.RequestCulture; await context.Response.WriteAsync( diff --git a/src/Microsoft.AspNet.Localization/RequestLocalizationMiddleware.cs b/src/Microsoft.AspNet.Localization/RequestLocalizationMiddleware.cs index c09abc1915..a437f2956d 100644 --- a/src/Microsoft.AspNet.Localization/RequestLocalizationMiddleware.cs +++ b/src/Microsoft.AspNet.Localization/RequestLocalizationMiddleware.cs @@ -56,7 +56,7 @@ namespace Microsoft.AspNet.Localization } } - context.SetFeature(new RequestCultureFeature(requestCulture, winningProvider)); + context.Features.Set(new RequestCultureFeature(requestCulture, winningProvider)); SetCurrentThreadCulture(requestCulture); From d9273140476eaa20377c770091a89f5a7991daac Mon Sep 17 00:00:00 2001 From: Chris R Date: Mon, 31 Aug 2015 10:23:01 -0700 Subject: [PATCH 065/390] Add missing namespaces. --- samples/LocalizationSample/Startup.cs | 1 + .../RequestLocalizationMiddleware.cs | 1 + 2 files changed, 2 insertions(+) diff --git a/samples/LocalizationSample/Startup.cs b/samples/LocalizationSample/Startup.cs index e8c910b055..155ed6bbca 100644 --- a/samples/LocalizationSample/Startup.cs +++ b/samples/LocalizationSample/Startup.cs @@ -5,6 +5,7 @@ using System; using System.Globalization; using Microsoft.AspNet.Builder; using Microsoft.AspNet.Http; +using Microsoft.AspNet.Http.Features; using Microsoft.AspNet.Localization; using Microsoft.Framework.DependencyInjection; using Microsoft.Framework.Localization; diff --git a/src/Microsoft.AspNet.Localization/RequestLocalizationMiddleware.cs b/src/Microsoft.AspNet.Localization/RequestLocalizationMiddleware.cs index a437f2956d..a4808a6dab 100644 --- a/src/Microsoft.AspNet.Localization/RequestLocalizationMiddleware.cs +++ b/src/Microsoft.AspNet.Localization/RequestLocalizationMiddleware.cs @@ -6,6 +6,7 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.AspNet.Builder; using Microsoft.AspNet.Http; +using Microsoft.AspNet.Http.Features; using Microsoft.Framework.Internal; namespace Microsoft.AspNet.Localization From e484834c39947d7d7abfdb390951093d8ccfdc4c Mon Sep 17 00:00:00 2001 From: David Fowler Date: Wed, 2 Sep 2015 21:00:55 -0700 Subject: [PATCH 066/390] Update .travis.yml Remove hardcoded mono version --- .travis.yml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 65b9f21ec2..238174a4e4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,4 @@ language: csharp sudo: false -mono: - - 3.12.0 script: - - ./build.sh --quiet verify \ No newline at end of file + - ./build.sh --quiet verify From 2faf31727ed5cdded889239892fa3ce25966ee36 Mon Sep 17 00:00:00 2001 From: Hisham Abdullah Bin Ateya Date: Wed, 5 Aug 2015 12:46:55 +0300 Subject: [PATCH 067/390] Fix namespace typo --- .../ResourceManagerStringLocalizerTest.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/Microsoft.Framework.Localization.Tests/ResourceManagerStringLocalizerTest.cs b/test/Microsoft.Framework.Localization.Tests/ResourceManagerStringLocalizerTest.cs index 0bb208bdf8..f52b3aed2d 100644 --- a/test/Microsoft.Framework.Localization.Tests/ResourceManagerStringLocalizerTest.cs +++ b/test/Microsoft.Framework.Localization.Tests/ResourceManagerStringLocalizerTest.cs @@ -9,7 +9,7 @@ using System.Resources; using Microsoft.Framework.Localization.Internal; using Xunit; -namespace Microsoft.Framework.Localization.Test +namespace Microsoft.Framework.Localization.Tests { public class ResourceManagerStringLocalizerTest { From 3d2c013a4834e85512a1205571c6ff1b55be4e18 Mon Sep 17 00:00:00 2001 From: Pranav K Date: Thu, 10 Sep 2015 18:20:42 -0700 Subject: [PATCH 068/390] Adding NeutralResourcesLanguageAttribute --- .../Properties/AssemblyInfo.cs | 8 ++++++++ .../Properties/AssemblyInfo.cs | 8 ++++++++ .../Properties/AssemblyInfo.cs | 8 ++++++++ .../Properties/AssemblyInfo.cs | 4 ++++ 4 files changed, 28 insertions(+) create mode 100644 src/Microsoft.AspNet.Localization/Properties/AssemblyInfo.cs create mode 100644 src/Microsoft.Framework.Globalization.CultureInfoCache/Properties/AssemblyInfo.cs create mode 100644 src/Microsoft.Framework.Localization.Abstractions/Properties/AssemblyInfo.cs diff --git a/src/Microsoft.AspNet.Localization/Properties/AssemblyInfo.cs b/src/Microsoft.AspNet.Localization/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..aab4e278a4 --- /dev/null +++ b/src/Microsoft.AspNet.Localization/Properties/AssemblyInfo.cs @@ -0,0 +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.Reflection; +using System.Resources; + +[assembly: AssemblyMetadata("Serviceable", "True")] +[assembly: NeutralResourcesLanguage("en-us")] diff --git a/src/Microsoft.Framework.Globalization.CultureInfoCache/Properties/AssemblyInfo.cs b/src/Microsoft.Framework.Globalization.CultureInfoCache/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..aab4e278a4 --- /dev/null +++ b/src/Microsoft.Framework.Globalization.CultureInfoCache/Properties/AssemblyInfo.cs @@ -0,0 +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.Reflection; +using System.Resources; + +[assembly: AssemblyMetadata("Serviceable", "True")] +[assembly: NeutralResourcesLanguage("en-us")] diff --git a/src/Microsoft.Framework.Localization.Abstractions/Properties/AssemblyInfo.cs b/src/Microsoft.Framework.Localization.Abstractions/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..aab4e278a4 --- /dev/null +++ b/src/Microsoft.Framework.Localization.Abstractions/Properties/AssemblyInfo.cs @@ -0,0 +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.Reflection; +using System.Resources; + +[assembly: AssemblyMetadata("Serviceable", "True")] +[assembly: NeutralResourcesLanguage("en-us")] diff --git a/src/Microsoft.Framework.Localization/Properties/AssemblyInfo.cs b/src/Microsoft.Framework.Localization/Properties/AssemblyInfo.cs index e3c2061307..0174621c9b 100644 --- a/src/Microsoft.Framework.Localization/Properties/AssemblyInfo.cs +++ b/src/Microsoft.Framework.Localization/Properties/AssemblyInfo.cs @@ -1,6 +1,10 @@ // 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.Reflection; +using System.Resources; using System.Runtime.CompilerServices; [assembly: InternalsVisibleTo("Microsoft.Framework.Localization.Test")] +[assembly: AssemblyMetadata("Serviceable", "True")] +[assembly: NeutralResourcesLanguage("en-us")] From efcbb2a664edc45c3d70d2bdcc5d535e8d551718 Mon Sep 17 00:00:00 2001 From: Hisham Abdullah Bin Ateya Date: Wed, 5 Aug 2015 23:13:54 +0300 Subject: [PATCH 069/390] Add 'Microsoft.AspNet.Localization' Unit Tests --- Localization.sln | 9 +- ...anguageHeaderRequestCultureProviderTest.cs | 48 +++++ .../CookieRequestCultureProviderTest.cs | 96 ++++++++++ .../CustomRequestCultureProviderTest.cs | 61 +++++++ .../Microsoft.AspNet.Localization.Tests.xproj | 20 ++ .../QueryStringRequestCultureProviderTest.cs | 172 ++++++++++++++++++ .../project.json | 15 ++ 7 files changed, 420 insertions(+), 1 deletion(-) create mode 100644 test/Microsoft.AspNet.Localization.Tests/AccessLanguageHeaderRequestCultureProviderTest.cs create mode 100644 test/Microsoft.AspNet.Localization.Tests/CookieRequestCultureProviderTest.cs create mode 100644 test/Microsoft.AspNet.Localization.Tests/CustomRequestCultureProviderTest.cs create mode 100644 test/Microsoft.AspNet.Localization.Tests/Microsoft.AspNet.Localization.Tests.xproj create mode 100644 test/Microsoft.AspNet.Localization.Tests/QueryStringRequestCultureProviderTest.cs create mode 100644 test/Microsoft.AspNet.Localization.Tests/project.json diff --git a/Localization.sln b/Localization.sln index db586522a9..78aa0eb0d7 100644 --- a/Localization.sln +++ b/Localization.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 14 -VisualStudioVersion = 14.0.22823.1 +VisualStudioVersion = 14.0.23107.0 MinimumVisualStudioVersion = 10.0.40219.1 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{FB313677-BAB3-4E49-8CDB-4FA4A9564767}" EndProject @@ -28,6 +28,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{B723DB83-A EndProject Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.Framework.Localization.Tests", "test\Microsoft.Framework.Localization.Tests\Microsoft.Framework.Localization.Tests.xproj", "{287AD58D-DF34-4F16-8616-FD78FA1CADF9}" EndProject +Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.AspNet.Localization.Tests", "test\Microsoft.AspNet.Localization.Tests\Microsoft.AspNet.Localization.Tests.xproj", "{19A2A931-5C60-47A0-816A-0DC9C4CE5736}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -62,6 +64,10 @@ Global {287AD58D-DF34-4F16-8616-FD78FA1CADF9}.Debug|Any CPU.Build.0 = Debug|Any CPU {287AD58D-DF34-4F16-8616-FD78FA1CADF9}.Release|Any CPU.ActiveCfg = Release|Any CPU {287AD58D-DF34-4F16-8616-FD78FA1CADF9}.Release|Any CPU.Build.0 = Release|Any CPU + {19A2A931-5C60-47A0-816A-0DC9C4CE5736}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {19A2A931-5C60-47A0-816A-0DC9C4CE5736}.Debug|Any CPU.Build.0 = Debug|Any CPU + {19A2A931-5C60-47A0-816A-0DC9C4CE5736}.Release|Any CPU.ActiveCfg = Release|Any CPU + {19A2A931-5C60-47A0-816A-0DC9C4CE5736}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -74,5 +80,6 @@ Global {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} EndGlobalSection EndGlobal diff --git a/test/Microsoft.AspNet.Localization.Tests/AccessLanguageHeaderRequestCultureProviderTest.cs b/test/Microsoft.AspNet.Localization.Tests/AccessLanguageHeaderRequestCultureProviderTest.cs new file mode 100644 index 0000000000..b9ccd53707 --- /dev/null +++ b/test/Microsoft.AspNet.Localization.Tests/AccessLanguageHeaderRequestCultureProviderTest.cs @@ -0,0 +1,48 @@ +// 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; +using Microsoft.AspNet.Localization; +using Microsoft.AspNet.TestHost; +using Microsoft.Framework.DependencyInjection; +using Xunit; + +namespace Microsoft.Framework.Localization.Tests +{ + public class AccessLanguageHeaderRequestCultureProviderTest + { + [Fact] + public async void GetFallbackLanguage() + { + using (var server = TestServer.Create(app => + { + var options = new RequestLocalizationOptions + { + SupportedCultures = new List + { + new CultureInfo("ar-SA"), + new CultureInfo("en-US") + } + }; + app.UseRequestLocalization(options); + app.Run(context => + { + var requestCultureFeature = context.Features.Get(); + var requestCulture = requestCultureFeature.RequestCulture; + Assert.Equal("ar-SA", requestCulture.Culture.Name); + return Task.FromResult(0); + }); + })) + { + var client = server.CreateClient(); + client.DefaultRequestHeaders.AcceptLanguage.ParseAdd("jp,ar-SA,en-US"); + var count = client.DefaultRequestHeaders.AcceptLanguage.Count; + var response = await client.GetAsync(string.Empty); + } + } + } +} \ No newline at end of file diff --git a/test/Microsoft.AspNet.Localization.Tests/CookieRequestCultureProviderTest.cs b/test/Microsoft.AspNet.Localization.Tests/CookieRequestCultureProviderTest.cs new file mode 100644 index 0000000000..2a14092b8c --- /dev/null +++ b/test/Microsoft.AspNet.Localization.Tests/CookieRequestCultureProviderTest.cs @@ -0,0 +1,96 @@ +// 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.Globalization; +using System.Threading.Tasks; +using Microsoft.AspNet.Builder; +using Microsoft.AspNet.Http.Features; +using Microsoft.AspNet.Localization; +using Microsoft.AspNet.TestHost; +using Microsoft.Framework.DependencyInjection; +using Microsoft.Net.Http.Headers; +using Xunit; + +namespace Microsoft.Framework.Localization.Tests +{ + public class CookieRequestCultureProviderTest + { + [Fact] + public async void GetCultureInfoFromPersistentCookie() + { + using (var server = TestServer.Create(app => + { + var options = new RequestLocalizationOptions(); + var provider = new CookieRequestCultureProvider(); + provider.CookieName = "Preferences"; + options.RequestCultureProviders.Insert(0, provider); + app.UseRequestLocalization(options); + app.Run(context => + { + var requestCultureFeature = context.Features.Get(); + var requestCulture = requestCultureFeature.RequestCulture; + Assert.Equal("ar-SA", requestCulture.Culture.Name); + return Task.FromResult(0); + }); + })) + { + var client = server.CreateClient(); + var culture = new CultureInfo("ar-SA"); + var requestCulture = new RequestCulture(culture); + var value = CookieRequestCultureProvider.MakeCookieValue(requestCulture); + client.DefaultRequestHeaders.Add("Cookie", new CookieHeaderValue("Preferences", value).ToString()); + var response = await client.GetAsync(string.Empty); + Assert.Equal("c=ar-SA|uic=ar-SA",value); + } + } + + [Fact] + public async void GetDefaultCultureInfoIfCultureKeysAreMissingOrInvalid() + { + using (var server = TestServer.Create(app => + { + var options = new RequestLocalizationOptions(); + var provider = new CookieRequestCultureProvider(); + provider.CookieName = "Preferences"; + options.RequestCultureProviders.Insert(0, provider); + app.UseRequestLocalization(options); + app.Run(context => + { + var requestCultureFeature = context.Features.Get(); + var requestCulture = requestCultureFeature.RequestCulture; + Assert.Equal(options.DefaultRequestCulture.Culture.Name, requestCulture.Culture.Name); + return Task.FromResult(0); + }); + })) + { + var client = server.CreateClient(); + client.DefaultRequestHeaders.Add("Cookie", new CookieHeaderValue("Preferences", "uic=ar-SA").ToString()); + var response = await client.GetAsync(string.Empty); + } + } + + [Fact] + public async void GetDefaultCultureInfoIfCookieDoesNotExist() + { + using (var server = TestServer.Create(app => + { + var options = new RequestLocalizationOptions(); + var provider = new CookieRequestCultureProvider(); + provider.CookieName = "Preferences"; + options.RequestCultureProviders.Insert(0, provider); + app.UseRequestLocalization(options); + app.Run(context => + { + var requestCultureFeature = context.Features.Get(); + var requestCulture = requestCultureFeature.RequestCulture; + Assert.Equal(options.DefaultRequestCulture.Culture.Name, requestCulture.Culture.Name); + return Task.FromResult(0); + }); + })) + { + var client = server.CreateClient(); + var response = await client.GetAsync(string.Empty); + } + } + } +} \ No newline at end of file diff --git a/test/Microsoft.AspNet.Localization.Tests/CustomRequestCultureProviderTest.cs b/test/Microsoft.AspNet.Localization.Tests/CustomRequestCultureProviderTest.cs new file mode 100644 index 0000000000..83928c419d --- /dev/null +++ b/test/Microsoft.AspNet.Localization.Tests/CustomRequestCultureProviderTest.cs @@ -0,0 +1,61 @@ +// 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.Globalization; +using System.Threading.Tasks; +using Microsoft.AspNet.Builder; +using Microsoft.AspNet.Http; +using Microsoft.AspNet.Http.Features; +using Microsoft.AspNet.Localization; +using Microsoft.AspNet.TestHost; +using Microsoft.Framework.DependencyInjection; +using Microsoft.Framework.Globalization; +using Xunit; + +namespace Microsoft.Framework.Localization.Tests +{ + public class CustomRequestCultureProviderTest + { + [Fact] + public async void CustomRequestCultureProviderThatGetsCultureInfoFromUrl() + { + using (var server = TestServer.Create(app => + { + var options = new RequestLocalizationOptions(); + options.RequestCultureProviders.Insert(0, new CustomRequestCultureProvider(context => + { + var culture = GetCultureInfoFromUrl(context); + var requestCulture = new RequestCulture(culture); + return Task.FromResult(requestCulture); + })); + app.UseRequestLocalization(options); + app.Run(context => + { + var requestCultureFeature = context.Features.Get(); + var requestCulture = requestCultureFeature.RequestCulture; + Assert.Equal("ar", requestCulture.Culture.Name); + return Task.FromResult(0); + }); + })) + { + var client = server.CreateClient(); + var response = await client.GetAsync("/ar/page"); + } + } + + private CultureInfo GetCultureInfoFromUrl(HttpContext context) + { + 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."); + } + return CultureInfoCache.GetCultureInfo(currentCulture); + } + } +} \ No newline at end of file diff --git a/test/Microsoft.AspNet.Localization.Tests/Microsoft.AspNet.Localization.Tests.xproj b/test/Microsoft.AspNet.Localization.Tests/Microsoft.AspNet.Localization.Tests.xproj new file mode 100644 index 0000000000..859644e3de --- /dev/null +++ b/test/Microsoft.AspNet.Localization.Tests/Microsoft.AspNet.Localization.Tests.xproj @@ -0,0 +1,20 @@ + + + + 14.0 + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) + + + + + 19a2a931-5c60-47a0-816a-0dc9c4ce5736 + Microsoft.AspNet.Localization.Tests + ..\..\artifacts\obj\$(MSBuildProjectName) + ..\..\artifacts\bin\$(MSBuildProjectName)\ + + + + 2.0 + + + diff --git a/test/Microsoft.AspNet.Localization.Tests/QueryStringRequestCultureProviderTest.cs b/test/Microsoft.AspNet.Localization.Tests/QueryStringRequestCultureProviderTest.cs new file mode 100644 index 0000000000..b8915e56bd --- /dev/null +++ b/test/Microsoft.AspNet.Localization.Tests/QueryStringRequestCultureProviderTest.cs @@ -0,0 +1,172 @@ +// 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.Threading.Tasks; +using Microsoft.AspNet.Builder; +using Microsoft.AspNet.Http.Features; +using Microsoft.AspNet.Localization; +using Microsoft.AspNet.TestHost; +using Microsoft.Framework.DependencyInjection; +using Xunit; + +namespace Microsoft.Framework.Localization.Tests +{ + public class QueryStringRequestCultureProviderTest + { + [Fact] + public async void GetCultureInfoFromQueryString() + { + using (var server = TestServer.Create(app => + { + var options = new RequestLocalizationOptions(); + app.UseRequestLocalization(options); + app.Run(context => + { + var requestCultureFeature = context.Features.Get(); + var requestCulture = requestCultureFeature.RequestCulture; + Assert.Equal("ar-SA", requestCulture.Culture.Name); + Assert.Equal("ar-YE", requestCulture.UICulture.Name); + return Task.FromResult(0); + }); + })) + { + var client = server.CreateClient(); + var response = await client.GetAsync("/page?culture=ar-SA&ui-culture=ar-YE"); + } + } + + [Fact] + public async void GetDefaultCultureInfoIfCultureKeysAreMissing() + { + using (var server = TestServer.Create(app => + { + var options = new RequestLocalizationOptions(); + app.UseRequestLocalization(options); + 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); + return Task.FromResult(0); + }); + })) + { + var client = server.CreateClient(); + var response = await client.GetAsync("/page"); + } + } + + [Fact] + public async void GetDefaultCultureInfoIfCultureIsInvalid() + { + using (var server = TestServer.Create(app => + { + var options = new RequestLocalizationOptions(); + app.UseRequestLocalization(options); + app.Run(context => + { + var requestCultureFeature = context.Features.Get(); + var requestCulture = requestCultureFeature.RequestCulture; + Assert.Equal(options.DefaultRequestCulture.Culture.Name, requestCulture.Culture.Name); + return Task.FromResult(0); + }); + })) + { + var client = server.CreateClient(); + var response = await client.GetAsync("/page?culture=ar-XY&ui-culture=ar-SA"); + } + } + + [Fact] + public async void GetDefaultCultureInfoIfUICultureIsInvalid() + { + using (var server = TestServer.Create(app => + { + var options = new RequestLocalizationOptions(); + app.UseRequestLocalization(options); + app.Run(context => + { + var requestCultureFeature = context.Features.Get(); + var requestCulture = requestCultureFeature.RequestCulture; + Assert.Equal(options.DefaultRequestCulture.UICulture.Name, requestCulture.UICulture.Name); + return Task.FromResult(0); + }); + })) + { + var client = server.CreateClient(); + var response = await client.GetAsync("/page?culture=ar-SA&ui-culture=ar-XY"); + } + } + + [Fact] + public async void GetSameCultureInfoIfCultureKeyIsMissing() + { + using (var server = TestServer.Create(app => + { + var options = new RequestLocalizationOptions(); + app.UseRequestLocalization(options); + app.Run(context => + { + var requestCultureFeature = context.Features.Get(); + var requestCulture = requestCultureFeature.RequestCulture; + Assert.Equal("ar-SA", requestCulture.Culture.Name); + Assert.Equal("ar-SA", requestCulture.UICulture.Name); + return Task.FromResult(0); + }); + })) + { + var client = server.CreateClient(); + var response = await client.GetAsync("/page?ui-culture=ar-SA"); + } + } + + [Fact] + public async void GetSameCultureInfoIfUICultureKeyIsMissing() + { + using (var server = TestServer.Create(app => + { + var options = new RequestLocalizationOptions(); + app.UseRequestLocalization(options); + app.Run(context => + { + var requestCultureFeature = context.Features.Get(); + var requestCulture = requestCultureFeature.RequestCulture; + Assert.Equal("ar-SA", requestCulture.Culture.Name); + Assert.Equal("ar-SA", requestCulture.UICulture.Name); + return Task.FromResult(0); + }); + })) + { + var client = server.CreateClient(); + var response = await client.GetAsync("/page?culture=ar-SA"); + } + } + + [Fact] + public async void GetCultureInfoFromQueryStringWithCustomKeys() + { + using (var server = TestServer.Create(app => + { + var options = new RequestLocalizationOptions(); + var provider = new QueryStringRequestCultureProvider(); + provider.QueryStringKey = "c"; + provider.UIQueryStringKey = "uic"; + options.RequestCultureProviders.Insert(0, provider); + app.UseRequestLocalization(options); + app.Run(context => + { + var requestCultureFeature = context.Features.Get(); + var requestCulture = requestCultureFeature.RequestCulture; + Assert.Equal("ar-SA", requestCulture.Culture.Name); + Assert.Equal("ar-YE", requestCulture.UICulture.Name); + return Task.FromResult(0); + }); + })) + { + var client = server.CreateClient(); + var response = await client.GetAsync("/page?c=ar-SA&uic=ar-YE"); + } + } + } +} \ No newline at end of file diff --git a/test/Microsoft.AspNet.Localization.Tests/project.json b/test/Microsoft.AspNet.Localization.Tests/project.json new file mode 100644 index 0000000000..e3aeaad6a6 --- /dev/null +++ b/test/Microsoft.AspNet.Localization.Tests/project.json @@ -0,0 +1,15 @@ +{ + "dependencies": { + "Microsoft.AspNet.Localization": "1.0.0-*", + "Microsoft.AspNet.TestHost": "1.0.0-*", + "xunit": "2.1.0-*", + "xunit.runner.dnx": "2.1.0-*" + }, + "frameworks": { + "dnx451": { }, + "dnxcore50": { } + }, + "commands": { + "test": "xunit.runner.dnx" + } +} \ No newline at end of file From 7b53bdd43e2e605a8eb686f22ceb558f46a1c4f5 Mon Sep 17 00:00:00 2001 From: Kirthi Krishnamraju Date: Mon, 14 Sep 2015 10:42:05 -0700 Subject: [PATCH 070/390] Fix for #38: Fallback to wrong language preference when the top preference is unavailable --- ...eptLanguageHeaderRequestCultureProvider.cs | 2 +- ...anguageHeaderRequestCultureProviderTest.cs | 64 ++++++++++++++++++- 2 files changed, 64 insertions(+), 2 deletions(-) diff --git a/src/Microsoft.AspNet.Localization/AcceptLanguageHeaderRequestCultureProvider.cs b/src/Microsoft.AspNet.Localization/AcceptLanguageHeaderRequestCultureProvider.cs index 46fbed8cc2..1771f5ada2 100644 --- a/src/Microsoft.AspNet.Localization/AcceptLanguageHeaderRequestCultureProvider.cs +++ b/src/Microsoft.AspNet.Localization/AcceptLanguageHeaderRequestCultureProvider.cs @@ -57,7 +57,7 @@ namespace Microsoft.AspNet.Localization requestCulture = ValidateRequestCulture(requestCulture); - if (requestCulture != null) + if (requestCulture.Culture == culture) { return Task.FromResult(requestCulture); } diff --git a/test/Microsoft.AspNet.Localization.Tests/AccessLanguageHeaderRequestCultureProviderTest.cs b/test/Microsoft.AspNet.Localization.Tests/AccessLanguageHeaderRequestCultureProviderTest.cs index b9ccd53707..a93d8f54be 100644 --- a/test/Microsoft.AspNet.Localization.Tests/AccessLanguageHeaderRequestCultureProviderTest.cs +++ b/test/Microsoft.AspNet.Localization.Tests/AccessLanguageHeaderRequestCultureProviderTest.cs @@ -16,7 +16,7 @@ namespace Microsoft.Framework.Localization.Tests public class AccessLanguageHeaderRequestCultureProviderTest { [Fact] - public async void GetFallbackLanguage() + public async void GetFallbackLanguage_ReturnsFirstNonNullCultureFromSupportedCultureList() { using (var server = TestServer.Create(app => { @@ -44,5 +44,67 @@ namespace Microsoft.Framework.Localization.Tests var response = await client.GetAsync(string.Empty); } } + + [Fact] + public async void GetFallbackLanguage_ReturnsFromSupportedCulture_AcceptLanguageListContainsSupportedCultures() + { + using (var server = TestServer.Create(app => + { + 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.Run(context => + { + var requestCultureFeature = context.Features.Get(); + var requestCulture = requestCultureFeature.RequestCulture; + Assert.Equal("ar-SA", requestCulture.Culture.Name); + return Task.FromResult(0); + }); + })) + { + var client = server.CreateClient(); + client.DefaultRequestHeaders.AcceptLanguage.ParseAdd("en-GB,ar-SA,en-US"); + var count = client.DefaultRequestHeaders.AcceptLanguage.Count; + var response = await client.GetAsync(string.Empty); + } + } + + [Fact] + public async void GetFallbackLanguage_ReturnsDefault_AcceptLanguageListDoesnotContainSupportedCultures() + { + using (var server = TestServer.Create(app => + { + 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.Run(context => + { + var requestCultureFeature = context.Features.Get(); + var requestCulture = requestCultureFeature.RequestCulture; + Assert.Equal("fr-FR", requestCulture.Culture.Name); + return Task.FromResult(0); + }); + })) + { + var client = server.CreateClient(); + client.DefaultRequestHeaders.AcceptLanguage.ParseAdd("en-GB,ar-MA,en-US"); + var count = client.DefaultRequestHeaders.AcceptLanguage.Count; + var response = await client.GetAsync(string.Empty); + } + } } } \ No newline at end of file From a5a264d1ad12e55dfbcb569563165ab4475a1d01 Mon Sep 17 00:00:00 2001 From: Kirthi Krishnamraju Date: Mon, 14 Sep 2015 16:46:43 -0700 Subject: [PATCH 071/390] fix #3: Add localized resources to the sample --- samples/LocalizationSample/Startup.cs | 1 + samples/LocalizationSample/Startup.es-ES.resx | 123 ++++++++++++++++++ samples/LocalizationSample/Startup.fr-FR.resx | 123 ++++++++++++++++++ samples/LocalizationSample/Startup.ja-JP.resx | 123 ++++++++++++++++++ samples/LocalizationSample/Startup.zh-CN.resx | 123 ++++++++++++++++++ samples/LocalizationSample/Startup.zh.resx | 123 ++++++++++++++++++ 6 files changed, 616 insertions(+) create mode 100644 samples/LocalizationSample/Startup.es-ES.resx create mode 100644 samples/LocalizationSample/Startup.fr-FR.resx create mode 100644 samples/LocalizationSample/Startup.ja-JP.resx create mode 100644 samples/LocalizationSample/Startup.zh-CN.resx create mode 100644 samples/LocalizationSample/Startup.zh.resx diff --git a/samples/LocalizationSample/Startup.cs b/samples/LocalizationSample/Startup.cs index 155ed6bbca..384c0c7f0f 100644 --- a/samples/LocalizationSample/Startup.cs +++ b/samples/LocalizationSample/Startup.cs @@ -86,6 +86,7 @@ $@" "); await context.Response.WriteAsync($"

{SR["Request Localization Sample"]}

"); + await context.Response.WriteAsync($"

{SR["Hello"]}

"); await context.Response.WriteAsync("
"); await context.Response.WriteAsync($""); await context.Response.WriteAsync("
Winning strategy:{requestCultureFeature.Strategy.GetType().Name}
Winning provider:{requestCultureFeature.Provider.GetType().Name}
{SR["Current request culture:"]}{requestCulture.Culture.DisplayName} ({requestCulture.Culture})
{SR["Current request UI culture:"]}{requestCulture.UICulture.DisplayName} ({requestCulture.UICulture})
{SR["Current thread culture:"]}{CultureInfo.CurrentCulture.DisplayName} ({CultureInfo.CurrentCulture})