├── .appveyor.yml ├── .gitattributes ├── .github └── ISSUE_TEMPLATE.md ├── .gitignore ├── .travis.yml ├── .vsts-pipelines └── builds │ ├── ci-internal.yml │ └── ci-public.yml ├── CONTRIBUTING.md ├── Directory.Build.props ├── Directory.Build.targets ├── LICENSE.txt ├── Localization.sln ├── NuGet.config ├── NuGetPackageVerifier.json ├── README.md ├── build.cmd ├── build.sh ├── build ├── Key.snk ├── dependencies.props ├── repo.props └── sources.props ├── korebuild-lock.txt ├── korebuild.json ├── run.cmd ├── run.ps1 ├── run.sh ├── samples └── LocalizationSample │ ├── LocalizationSample.csproj │ ├── My │ └── Resources │ │ ├── Startup.es-ES.resx │ │ ├── Startup.fr-FR.resx │ │ ├── Startup.ja-JP.resx │ │ ├── Startup.zh-CN.resx │ │ └── Startup.zh.resx │ └── Startup.cs ├── src ├── Directory.Build.props ├── Microsoft.AspNetCore.Localization.Routing │ ├── Microsoft.AspNetCore.Localization.Routing.csproj │ ├── RouteDataRequestCultureProvider.cs │ └── baseline.netcore.json ├── Microsoft.AspNetCore.Localization │ ├── AcceptLanguageHeaderRequestCultureProvider.cs │ ├── ApplicationBuilderExtensions.cs │ ├── CookieRequestCultureProvider.cs │ ├── CustomRequestCultureProvider.cs │ ├── IRequestCultureFeature.cs │ ├── IRequestCultureProvider.cs │ ├── Internal │ │ └── RequestCultureProviderLoggerExtensions.cs │ ├── Microsoft.AspNetCore.Localization.csproj │ ├── Properties │ │ └── Resources.Designer.cs │ ├── ProviderCultureResult.cs │ ├── QueryStringRequestCultureProvider.cs │ ├── RequestCulture.cs │ ├── RequestCultureFeature.cs │ ├── RequestCultureProvider.cs │ ├── RequestLocalizationMiddleware.cs │ ├── RequestLocalizationOptions.cs │ ├── RequestLocalizationOptionsExtensions.cs │ ├── Resources.resx │ └── baseline.netcore.json ├── Microsoft.Extensions.Localization.Abstractions │ ├── IStringLocalizer.cs │ ├── IStringLocalizerFactory.cs │ ├── IStringLocalizerOfT.cs │ ├── LocalizedString.cs │ ├── Microsoft.Extensions.Localization.Abstractions.csproj │ ├── StringLocalizerExtensions.cs │ ├── StringLocalizerOfT.cs │ └── baseline.netcore.json └── Microsoft.Extensions.Localization │ ├── IResourceNamesCache.cs │ ├── Internal │ ├── AssemblyWrapper.cs │ ├── IResourceStringProvider.cs │ ├── ResourceManagerStringLocalizerLoggerExtensions.cs │ └── ResourceManagerStringProvider.cs │ ├── LocalizationOptions.cs │ ├── LocalizationServiceCollectionExtensions.cs │ ├── Microsoft.Extensions.Localization.csproj │ ├── Properties │ ├── AssemblyInfo.cs │ └── Resources.Designer.cs │ ├── ResourceLocationAttribute.cs │ ├── ResourceManagerStringLocalizer.cs │ ├── ResourceManagerStringLocalizerFactory.cs │ ├── ResourceManagerWithCultureStringLocalizer.cs │ ├── ResourceNamesCache.cs │ ├── Resources.resx │ ├── RootNamespaceAttribute.cs │ └── baseline.netcore.json ├── test ├── Directory.Build.props ├── LocalizationWebsite │ ├── LocalizationWebsite.csproj │ ├── Models │ │ ├── Customer.cs │ │ └── Customer.fr-FR.resx │ ├── Program.cs │ ├── Resources │ │ ├── Models.Customer.fr-FR.resx │ │ ├── StartupCustomCulturePreserved.en-US.resx │ │ ├── StartupResourcesInFolder.fr-FR.resx │ │ └── Test.fr-FR.resx │ ├── StartupBuilderAPIs.cs │ ├── StartupCustomCulturePreserved.cs │ ├── StartupGetAllStrings.cs │ ├── StartupResourcesAtRootFolder.cs │ ├── StartupResourcesAtRootFolder.fr-FR.resx │ ├── StartupResourcesInClassLibrary.cs │ ├── StartupResourcesInFolder.cs │ └── Test.fr-FR.resx ├── Microsoft.AspNetCore.Localization.FunctionalTests │ ├── LocalizationSampleTest.cs │ ├── LocalizationTest.cs │ └── Microsoft.AspNetCore.Localization.FunctionalTests.csproj ├── Microsoft.AspNetCore.Localization.Routing.Tests │ ├── Microsoft.AspNetCore.Localization.Routing.Tests.csproj │ └── RouteDataRequestCultureProviderTest.cs ├── Microsoft.AspNetCore.Localization.Tests │ ├── AcceptLanguageHeaderRequestCultureProviderTest.cs │ ├── CookieRequestCultureProviderTest.cs │ ├── CustomRequestCultureProviderTest.cs │ ├── Microsoft.AspNetCore.Localization.Tests.csproj │ ├── QueryStringRequestCultureProviderTest.cs │ ├── RequestLocalizationOptionsExtensionsTest.cs │ └── RequestLocalizationOptionsTest.cs ├── Microsoft.Extensions.Localization.Tests │ ├── LocalizationServiceCollectionExtensionsTest.cs │ ├── Microsoft.Extensions.Localization.Tests.csproj │ ├── ResourceManagerStringLocalizerFactoryTest.cs │ └── ResourceManagerStringLocalizerTest.cs ├── ResourcesClassLibraryNoAttribute │ ├── Model.cs │ ├── Resources │ │ └── Model.resx │ └── ResourcesClassLibraryNoAttribute.csproj └── ResourcesClassLibraryWithAttribute │ ├── AssemblyInfo.cs │ ├── Model.cs │ ├── ResourceFolder │ └── Model.resx │ └── ResourcesClassLibraryWithAttribute.csproj └── version.props /.appveyor.yml: -------------------------------------------------------------------------------- 1 | init: 2 | - git config --global core.autocrlf true 3 | branches: 4 | only: 5 | - master 6 | - /^release\/.*$/ 7 | - /^(.*\/)?ci-.*$/ 8 | build_script: 9 | - ps: .\run.ps1 default-build 10 | clone_depth: 1 11 | environment: 12 | global: 13 | DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true 14 | DOTNET_CLI_TELEMETRY_OPTOUT: 1 15 | test: 'off' 16 | deploy: 'off' 17 | os: Visual Studio 2017 18 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | *.doc diff=astextplain 2 | *.DOC diff=astextplain 3 | *.docx diff=astextplain 4 | *.DOCX diff=astextplain 5 | *.dot diff=astextplain 6 | *.DOT diff=astextplain 7 | *.pdf diff=astextplain 8 | *.PDF diff=astextplain 9 | *.rtf diff=astextplain 10 | *.RTF diff=astextplain 11 | 12 | *.jpg binary 13 | *.png binary 14 | *.gif binary 15 | 16 | *.cs text=auto diff=csharp 17 | *.vb text=auto 18 | *.resx text=auto 19 | *.c text=auto 20 | *.cpp text=auto 21 | *.cxx text=auto 22 | *.h text=auto 23 | *.hxx text=auto 24 | *.py text=auto 25 | *.rb text=auto 26 | *.java text=auto 27 | *.html text=auto 28 | *.htm text=auto 29 | *.css text=auto 30 | *.scss text=auto 31 | *.sass text=auto 32 | *.less text=auto 33 | *.js text=auto 34 | *.lisp text=auto 35 | *.clj text=auto 36 | *.sql text=auto 37 | *.php text=auto 38 | *.lua text=auto 39 | *.m text=auto 40 | *.asm text=auto 41 | *.erl text=auto 42 | *.fs text=auto 43 | *.fsx text=auto 44 | *.hs text=auto 45 | 46 | *.csproj text=auto 47 | *.vbproj text=auto 48 | *.fsproj text=auto 49 | *.dbproj text=auto 50 | *.sln text=auto eol=crlf 51 | 52 | *.sh eol=lf 53 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | THIS ISSUE TRACKER IS CLOSED - please log new issues here: https://github.com/aspnet/Home/issues 2 | 3 | For information about this change, see https://github.com/aspnet/Announcements/issues/283 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | [Oo]bj/ 2 | [Bb]in/ 3 | TestResults/ 4 | .nuget/ 5 | .build/ 6 | .testPublish/ 7 | *.sln.ide/ 8 | _ReSharper.*/ 9 | packages/ 10 | artifacts/ 11 | PublishProfiles/ 12 | .vs/ 13 | debugSettings.json 14 | project.lock.json 15 | *.user 16 | *.suo 17 | *.cache 18 | *.docstates 19 | _ReSharper.* 20 | nuget.exe 21 | *net45.csproj 22 | *net451.csproj 23 | *k10.csproj 24 | *.psess 25 | *.vsp 26 | *.pidb 27 | *.userprefs 28 | *DS_Store 29 | *.ncrunchsolution 30 | *.*sdf 31 | *.ipch 32 | *.sln.ide 33 | *launchSettings.json 34 | **/Resources/*.Designer.cs 35 | .vscode/ 36 | global.json 37 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: csharp 2 | sudo: false 3 | dist: trusty 4 | env: 5 | global: 6 | - DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true 7 | - DOTNET_CLI_TELEMETRY_OPTOUT: 1 8 | mono: none 9 | os: 10 | - linux 11 | - osx 12 | osx_image: xcode8.2 13 | addons: 14 | apt: 15 | packages: 16 | - libunwind8 17 | branches: 18 | only: 19 | - master 20 | - /^release\/.*$/ 21 | - /^(.*\/)?ci-.*$/ 22 | before_install: 23 | - if test "$TRAVIS_OS_NAME" == "osx"; then brew update; brew install openssl; ln -s 24 | /usr/local/opt/openssl/lib/libcrypto.1.0.0.dylib /usr/local/lib/; ln -s /usr/local/opt/openssl/lib/libssl.1.0.0.dylib 25 | /usr/local/lib/; fi 26 | script: 27 | - ./build.sh 28 | -------------------------------------------------------------------------------- /.vsts-pipelines/builds/ci-internal.yml: -------------------------------------------------------------------------------- 1 | trigger: 2 | - master 3 | - release/* 4 | 5 | resources: 6 | repositories: 7 | - repository: buildtools 8 | type: git 9 | name: aspnet-BuildTools 10 | ref: refs/heads/master 11 | 12 | phases: 13 | - template: .vsts-pipelines/templates/project-ci.yml@buildtools 14 | -------------------------------------------------------------------------------- /.vsts-pipelines/builds/ci-public.yml: -------------------------------------------------------------------------------- 1 | trigger: 2 | - master 3 | - release/* 4 | 5 | # See https://github.com/aspnet/BuildTools 6 | resources: 7 | repositories: 8 | - repository: buildtools 9 | type: github 10 | endpoint: DotNet-Bot GitHub Connection 11 | name: aspnet/BuildTools 12 | ref: refs/heads/master 13 | 14 | phases: 15 | - template: .vsts-pipelines/templates/project-ci.yml@buildtools 16 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | Contributing 2 | ====== 3 | 4 | Information on contributing to this repo is in the [Contributing Guide](https://github.com/aspnet/Home/blob/master/CONTRIBUTING.md) in the Home repo. 5 | -------------------------------------------------------------------------------- /Directory.Build.props: -------------------------------------------------------------------------------- 1 |  2 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | https://github.com/aspnet/Localization 12 | git 13 | $(MSBuildThisFileDirectory) 14 | $(MSBuildThisFileDirectory)build\Key.snk 15 | true 16 | true 17 | 18 | 19 | -------------------------------------------------------------------------------- /Directory.Build.targets: -------------------------------------------------------------------------------- 1 |  2 | 3 | $(MicrosoftNETCoreAppPackageVersion) 4 | $(NETStandardLibrary20PackageVersion) 5 | 6 | 7 | -------------------------------------------------------------------------------- /NuGet.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /NuGetPackageVerifier.json: -------------------------------------------------------------------------------- 1 | { 2 | "Default": { 3 | "rules": [ 4 | "DefaultCompositeRule" 5 | ] 6 | } 7 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Localization [Archived] 2 | ======================= 3 | 4 | **This GitHub project has been archived.** Ongoing development on this project can be found in . 5 | 6 | Localization abstractions and implementations for ASP.NET Core applications. 7 | 8 | This project is part of ASP.NET Core. You can find samples, documentation and getting started instructions for ASP.NET Core at the [AspNetCore](https://github.com/aspnet/AspNetCore) repo. 9 | -------------------------------------------------------------------------------- /build.cmd: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | PowerShell -NoProfile -NoLogo -ExecutionPolicy unrestricted -Command "[System.Threading.Thread]::CurrentThread.CurrentCulture = ''; [System.Threading.Thread]::CurrentThread.CurrentUICulture = '';& '%~dp0run.ps1' default-build %*; exit $LASTEXITCODE" 3 | -------------------------------------------------------------------------------- /build.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -euo pipefail 4 | DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" 5 | 6 | # Call "sync" between "chmod" and execution to prevent "text file busy" error in Docker (aufs) 7 | chmod +x "$DIR/run.sh"; sync 8 | "$DIR/run.sh" default-build "$@" 9 | -------------------------------------------------------------------------------- /build/Key.snk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aspnet/Localization/c283dfb56cd9620edfae644f1229d8231e9cfc0e/build/Key.snk -------------------------------------------------------------------------------- /build/dependencies.props: -------------------------------------------------------------------------------- 1 |  2 | 3 | $(MSBuildAllProjects);$(MSBuildThisFileFullPath) 4 | 5 | 6 | 3.0.0-build-20181114.5 7 | 3.0.0-alpha1-10742 8 | 3.0.0-alpha1-10742 9 | 3.0.0-alpha1-10742 10 | 3.0.0-alpha1-10742 11 | 3.0.0-alpha1-10742 12 | 3.0.0-alpha1-10742 13 | 3.0.0-preview-181113-11 14 | 3.0.0-preview-181113-11 15 | 3.0.0-preview-181113-11 16 | 3.0.0-preview-181113-11 17 | 3.0.0-preview-181113-11 18 | 3.0.0-preview-181113-11 19 | 3.0.0-preview-181113-11 20 | 3.0.0-preview-181113-11 21 | 3.0.0-preview-181113-11 22 | 3.0.0-preview1-26907-05 23 | 15.6.1 24 | 4.10.0 25 | 2.0.3 26 | 2.3.1 27 | 2.4.0 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /build/repo.props: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | Internal.AspNetCore.Universe.Lineup 11 | https://dotnet.myget.org/F/aspnetcore-dev/api/v3/index.json 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /build/sources.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | $(DotNetRestoreSources) 6 | 7 | $(RestoreSources); 8 | https://dotnet.myget.org/F/dotnet-core/api/v3/index.json; 9 | https://dotnet.myget.org/F/aspnetcore-dev/api/v3/index.json; 10 | https://dotnet.myget.org/F/aspnetcore-tools/api/v3/index.json; 11 | 12 | 13 | $(RestoreSources); 14 | https://api.nuget.org/v3/index.json; 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /korebuild-lock.txt: -------------------------------------------------------------------------------- 1 | version:3.0.0-build-20181114.5 2 | commithash:880e9a204d4ee4a18dfd83c9fb05a192a28bca60 3 | -------------------------------------------------------------------------------- /korebuild.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://raw.githubusercontent.com/aspnet/BuildTools/master/tools/korebuild.schema.json", 3 | "channel": "master" 4 | } 5 | -------------------------------------------------------------------------------- /run.cmd: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | PowerShell -NoProfile -NoLogo -ExecutionPolicy unrestricted -Command "[System.Threading.Thread]::CurrentThread.CurrentCulture = ''; [System.Threading.Thread]::CurrentThread.CurrentUICulture = '';& '%~dp0run.ps1' %*; exit $LASTEXITCODE" 3 | -------------------------------------------------------------------------------- /samples/LocalizationSample/LocalizationSample.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netcoreapp3.0 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /samples/LocalizationSample/My/Resources/Startup.es-ES.resx: -------------------------------------------------------------------------------- 1 |  2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | text/microsoft-resx 110 | 111 | 112 | 2.0 113 | 114 | 115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | 121 | Hola 122 | 123 | -------------------------------------------------------------------------------- /samples/LocalizationSample/My/Resources/Startup.fr-FR.resx: -------------------------------------------------------------------------------- 1 |  2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | text/microsoft-resx 110 | 111 | 112 | 2.0 113 | 114 | 115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | 121 | Bonjour 122 | 123 | -------------------------------------------------------------------------------- /samples/LocalizationSample/My/Resources/Startup.ja-JP.resx: -------------------------------------------------------------------------------- 1 |  2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | text/microsoft-resx 110 | 111 | 112 | 2.0 113 | 114 | 115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | 121 | こんにちは 122 | 123 | -------------------------------------------------------------------------------- /samples/LocalizationSample/My/Resources/Startup.zh-CN.resx: -------------------------------------------------------------------------------- 1 |  2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | text/microsoft-resx 110 | 111 | 112 | 2.0 113 | 114 | 115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | 121 | 您好 122 | 123 | -------------------------------------------------------------------------------- /samples/LocalizationSample/My/Resources/Startup.zh.resx: -------------------------------------------------------------------------------- 1 |  2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | text/microsoft-resx 110 | 111 | 112 | 2.0 113 | 114 | 115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | 121 | 您好 122 | 123 | -------------------------------------------------------------------------------- /src/Directory.Build.props: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/Microsoft.AspNetCore.Localization.Routing/Microsoft.AspNetCore.Localization.Routing.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Microsoft ASP.NET Core 5 | Provides a request culture provider which gets culture and ui-culture from request's route data. 6 | netcoreapp3.0 7 | $(NoWarn);CS1591 8 | true 9 | aspnetcore;localization 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /src/Microsoft.AspNetCore.Localization.Routing/RouteDataRequestCultureProvider.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) .NET Foundation. All rights reserved. 2 | // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 3 | 4 | using System; 5 | using System.Threading.Tasks; 6 | using Microsoft.AspNetCore.Http; 7 | using Microsoft.AspNetCore.Routing; 8 | using Microsoft.Extensions.Internal; 9 | 10 | namespace Microsoft.AspNetCore.Localization.Routing 11 | { 12 | /// 13 | /// Determines the culture information for a request via values in the route data. 14 | /// 15 | public class RouteDataRequestCultureProvider : RequestCultureProvider 16 | { 17 | /// 18 | /// The key that contains the culture name. 19 | /// Defaults to "culture". 20 | /// 21 | public string RouteDataStringKey { get; set; } = "culture"; 22 | 23 | /// 24 | /// The key that contains the UI culture name. If not specified or no value is found, 25 | /// will be used. 26 | /// Defaults to "ui-culture". 27 | /// 28 | public string UIRouteDataStringKey { get; set; } = "ui-culture"; 29 | 30 | /// 31 | public override Task DetermineProviderCultureResult(HttpContext httpContext) 32 | { 33 | if (httpContext == null) 34 | { 35 | throw new ArgumentNullException(nameof(httpContext)); 36 | } 37 | 38 | string culture = null; 39 | string uiCulture = null; 40 | 41 | if (!string.IsNullOrEmpty(RouteDataStringKey)) 42 | { 43 | culture = httpContext.GetRouteValue(RouteDataStringKey)?.ToString(); 44 | } 45 | 46 | if (!string.IsNullOrEmpty(UIRouteDataStringKey)) 47 | { 48 | uiCulture = httpContext.GetRouteValue(UIRouteDataStringKey)?.ToString(); 49 | } 50 | 51 | if (culture == null && uiCulture == null) 52 | { 53 | // No values specified for either so no match 54 | return NullProviderCultureResult; 55 | } 56 | 57 | if (culture != null && uiCulture == null) 58 | { 59 | // Value for culture but not for UI culture so default to culture value for both 60 | uiCulture = culture; 61 | } 62 | 63 | if (culture == null && uiCulture != null) 64 | { 65 | // Value for UI culture but not for culture so default to UI culture value for both 66 | culture = uiCulture; 67 | } 68 | 69 | var providerResultCulture = new ProviderCultureResult(culture, uiCulture); 70 | 71 | return Task.FromResult(providerResultCulture); 72 | } 73 | } 74 | } -------------------------------------------------------------------------------- /src/Microsoft.AspNetCore.Localization.Routing/baseline.netcore.json: -------------------------------------------------------------------------------- 1 | { 2 | "AssemblyIdentity": "Microsoft.AspNetCore.Localization.Routing, Version=2.1.1.0, Culture=neutral, PublicKeyToken=adb9793829ddae60", 3 | "Types": [ 4 | { 5 | "Name": "Microsoft.AspNetCore.Localization.Routing.RouteDataRequestCultureProvider", 6 | "Visibility": "Public", 7 | "Kind": "Class", 8 | "BaseType": "Microsoft.AspNetCore.Localization.RequestCultureProvider", 9 | "ImplementedInterfaces": [], 10 | "Members": [ 11 | { 12 | "Kind": "Method", 13 | "Name": "DetermineProviderCultureResult", 14 | "Parameters": [ 15 | { 16 | "Name": "httpContext", 17 | "Type": "Microsoft.AspNetCore.Http.HttpContext" 18 | } 19 | ], 20 | "ReturnType": "System.Threading.Tasks.Task", 21 | "Virtual": true, 22 | "Override": true, 23 | "ImplementedInterface": "Microsoft.AspNetCore.Localization.IRequestCultureProvider", 24 | "Visibility": "Public", 25 | "GenericParameter": [] 26 | }, 27 | { 28 | "Kind": "Method", 29 | "Name": "get_RouteDataStringKey", 30 | "Parameters": [], 31 | "ReturnType": "System.String", 32 | "Visibility": "Public", 33 | "GenericParameter": [] 34 | }, 35 | { 36 | "Kind": "Method", 37 | "Name": "set_RouteDataStringKey", 38 | "Parameters": [ 39 | { 40 | "Name": "value", 41 | "Type": "System.String" 42 | } 43 | ], 44 | "ReturnType": "System.Void", 45 | "Visibility": "Public", 46 | "GenericParameter": [] 47 | }, 48 | { 49 | "Kind": "Method", 50 | "Name": "get_UIRouteDataStringKey", 51 | "Parameters": [], 52 | "ReturnType": "System.String", 53 | "Visibility": "Public", 54 | "GenericParameter": [] 55 | }, 56 | { 57 | "Kind": "Method", 58 | "Name": "set_UIRouteDataStringKey", 59 | "Parameters": [ 60 | { 61 | "Name": "value", 62 | "Type": "System.String" 63 | } 64 | ], 65 | "ReturnType": "System.Void", 66 | "Visibility": "Public", 67 | "GenericParameter": [] 68 | }, 69 | { 70 | "Kind": "Constructor", 71 | "Name": ".ctor", 72 | "Parameters": [], 73 | "Visibility": "Public", 74 | "GenericParameter": [] 75 | } 76 | ], 77 | "GenericParameters": [] 78 | } 79 | ] 80 | } -------------------------------------------------------------------------------- /src/Microsoft.AspNetCore.Localization/AcceptLanguageHeaderRequestCultureProvider.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) .NET Foundation. All rights reserved. 2 | // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 3 | 4 | using System; 5 | using System.Linq; 6 | using System.Threading.Tasks; 7 | using Microsoft.AspNetCore.Http; 8 | using Microsoft.Extensions.Internal; 9 | using Microsoft.Net.Http.Headers; 10 | 11 | namespace Microsoft.AspNetCore.Localization 12 | { 13 | /// 14 | /// Determines the culture information for a request via the value of the Accept-Language header. 15 | /// 16 | public class AcceptLanguageHeaderRequestCultureProvider : RequestCultureProvider 17 | { 18 | /// 19 | /// The maximum number of values in the Accept-Language header to attempt to create a 20 | /// from for the current request. 21 | /// Defaults to 3. 22 | /// 23 | public int MaximumAcceptLanguageHeaderValuesToTry { get; set; } = 3; 24 | 25 | /// 26 | public override Task DetermineProviderCultureResult(HttpContext httpContext) 27 | { 28 | if (httpContext == null) 29 | { 30 | throw new ArgumentNullException(nameof(httpContext)); 31 | } 32 | 33 | var acceptLanguageHeader = httpContext.Request.GetTypedHeaders().AcceptLanguage; 34 | 35 | if (acceptLanguageHeader == null || acceptLanguageHeader.Count == 0) 36 | { 37 | return NullProviderCultureResult; 38 | } 39 | 40 | var languages = acceptLanguageHeader.AsEnumerable(); 41 | 42 | if (MaximumAcceptLanguageHeaderValuesToTry > 0) 43 | { 44 | // We take only the first configured number of languages from the header and then order those that we 45 | // attempt to parse as a CultureInfo to mitigate potentially spinning CPU on lots of parse attempts. 46 | languages = languages.Take(MaximumAcceptLanguageHeaderValuesToTry); 47 | } 48 | 49 | var orderedLanguages = languages.OrderByDescending(h => h, StringWithQualityHeaderValueComparer.QualityComparer) 50 | .Select(x => x.Value).ToList(); 51 | 52 | if (orderedLanguages.Count > 0) 53 | { 54 | return Task.FromResult(new ProviderCultureResult(orderedLanguages)); 55 | } 56 | 57 | return NullProviderCultureResult; 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/Microsoft.AspNetCore.Localization/ApplicationBuilderExtensions.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) .NET Foundation. All rights reserved. 2 | // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 3 | 4 | using System; 5 | using Microsoft.AspNetCore.Localization; 6 | using Microsoft.Extensions.Options; 7 | 8 | namespace Microsoft.AspNetCore.Builder 9 | { 10 | /// 11 | /// Extension methods for adding the to an application. 12 | /// 13 | public static class ApplicationBuilderExtensions 14 | { 15 | /// 16 | /// Adds the to automatically set culture information for 17 | /// requests based on information provided by the client. 18 | /// 19 | /// The . 20 | /// The . 21 | public static IApplicationBuilder UseRequestLocalization(this IApplicationBuilder app) 22 | { 23 | if (app == null) 24 | { 25 | throw new ArgumentNullException(nameof(app)); 26 | } 27 | 28 | return app.UseMiddleware(); 29 | } 30 | 31 | /// 32 | /// Adds the to automatically set culture information for 33 | /// requests based on information provided by the client. 34 | /// 35 | /// The . 36 | /// The to configure the middleware with. 37 | /// The . 38 | public static IApplicationBuilder UseRequestLocalization( 39 | this IApplicationBuilder app, 40 | RequestLocalizationOptions options) 41 | { 42 | if (app == null) 43 | { 44 | throw new ArgumentNullException(nameof(app)); 45 | } 46 | 47 | if (options == null) 48 | { 49 | throw new ArgumentNullException(nameof(options)); 50 | } 51 | 52 | return app.UseMiddleware(Options.Create(options)); 53 | } 54 | 55 | /// 56 | /// Adds the to automatically set culture information for 57 | /// requests based on information provided by the client. 58 | /// 59 | /// The . 60 | /// 61 | /// 62 | /// This will going to instantiate a new that doesn't come from the services. 63 | /// 64 | /// The . 65 | public static IApplicationBuilder UseRequestLocalization( 66 | this IApplicationBuilder app, 67 | Action optionsAction) 68 | { 69 | if (app == null) 70 | { 71 | throw new ArgumentNullException(nameof(app)); 72 | } 73 | 74 | if (optionsAction == null) 75 | { 76 | throw new ArgumentNullException(nameof(optionsAction)); 77 | } 78 | 79 | var options = new RequestLocalizationOptions(); 80 | optionsAction.Invoke(options); 81 | 82 | return app.UseMiddleware(Options.Create(options)); 83 | } 84 | 85 | /// 86 | /// Adds the to automatically set culture information for 87 | /// requests based on information provided by the client. 88 | /// 89 | /// The . 90 | /// The culture names to be added by the application, which is represents both supported cultures and UI cultures. 91 | /// The . 92 | /// 93 | /// Note that the first culture is the default culture name. 94 | /// 95 | public static IApplicationBuilder UseRequestLocalization( 96 | this IApplicationBuilder app, 97 | params string[] cultures) 98 | { 99 | if (app == null) 100 | { 101 | throw new ArgumentNullException(nameof(app)); 102 | } 103 | 104 | if (cultures == null) 105 | { 106 | throw new ArgumentNullException(nameof(cultures)); 107 | } 108 | 109 | if (cultures.Length == 0) 110 | { 111 | throw new ArgumentException(Resources.Exception_CulturesShouldNotBeEmpty); 112 | } 113 | 114 | var options = new RequestLocalizationOptions() 115 | .AddSupportedCultures(cultures) 116 | .AddSupportedUICultures(cultures) 117 | .SetDefaultCulture(cultures[0]); 118 | 119 | return app.UseMiddleware(Options.Create(options)); 120 | } 121 | } 122 | } -------------------------------------------------------------------------------- /src/Microsoft.AspNetCore.Localization/CookieRequestCultureProvider.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) .NET Foundation. All rights reserved. 2 | // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 3 | 4 | using System; 5 | using System.Threading.Tasks; 6 | using Microsoft.AspNetCore.Http; 7 | using Microsoft.Extensions.Internal; 8 | 9 | namespace Microsoft.AspNetCore.Localization 10 | { 11 | /// 12 | /// Determines the culture information for a request via the value of a cookie. 13 | /// 14 | public class CookieRequestCultureProvider : RequestCultureProvider 15 | { 16 | private static readonly char[] _cookieSeparator = new[] { '|' }; 17 | private static readonly string _culturePrefix = "c="; 18 | private static readonly string _uiCulturePrefix = "uic="; 19 | 20 | /// 21 | /// Represent the default cookie name used to track the user's preferred culture information, which is ".AspNetCore.Culture". 22 | /// 23 | public static readonly string DefaultCookieName = ".AspNetCore.Culture"; 24 | 25 | /// 26 | /// The name of the cookie that contains the user's preferred culture information. 27 | /// Defaults to . 28 | /// 29 | public string CookieName { get; set; } = DefaultCookieName; 30 | 31 | /// 32 | public override Task DetermineProviderCultureResult(HttpContext httpContext) 33 | { 34 | if (httpContext == null) 35 | { 36 | throw new ArgumentNullException(nameof(httpContext)); 37 | } 38 | 39 | var cookie = httpContext.Request.Cookies[CookieName]; 40 | 41 | if (string.IsNullOrEmpty(cookie)) 42 | { 43 | return NullProviderCultureResult; 44 | } 45 | 46 | var providerResultCulture = ParseCookieValue(cookie); 47 | 48 | return Task.FromResult(providerResultCulture); 49 | } 50 | 51 | /// 52 | /// Creates a string representation of a for placement in a cookie. 53 | /// 54 | /// The . 55 | /// The cookie value. 56 | public static string MakeCookieValue(RequestCulture requestCulture) 57 | { 58 | if (requestCulture == null) 59 | { 60 | throw new ArgumentNullException(nameof(requestCulture)); 61 | } 62 | 63 | var seperator = _cookieSeparator[0].ToString(); 64 | 65 | return string.Join(seperator, 66 | $"{_culturePrefix}{requestCulture.Culture.Name}", 67 | $"{_uiCulturePrefix}{requestCulture.UICulture.Name}"); 68 | } 69 | 70 | /// 71 | /// Parses a from the specified cookie value. 72 | /// Returns null if parsing fails. 73 | /// 74 | /// The cookie value to parse. 75 | /// The or null if parsing fails. 76 | public static ProviderCultureResult ParseCookieValue(string value) 77 | { 78 | if (string.IsNullOrWhiteSpace(value)) 79 | { 80 | return null; 81 | } 82 | 83 | var parts = value.Split(_cookieSeparator, StringSplitOptions.RemoveEmptyEntries); 84 | 85 | if (parts.Length != 2) 86 | { 87 | return null; 88 | } 89 | 90 | var potentialCultureName = parts[0]; 91 | var potentialUICultureName = parts[1]; 92 | 93 | if (!potentialCultureName.StartsWith(_culturePrefix) || !potentialUICultureName.StartsWith(_uiCulturePrefix)) 94 | { 95 | return null; 96 | } 97 | 98 | var cultureName = potentialCultureName.Substring(_culturePrefix.Length); 99 | var uiCultureName = potentialUICultureName.Substring(_uiCulturePrefix.Length); 100 | 101 | if (cultureName == null && uiCultureName == null) 102 | { 103 | // No values specified for either so no match 104 | return null; 105 | } 106 | 107 | if (cultureName != null && uiCultureName == null) 108 | { 109 | // Value for culture but not for UI culture so default to culture value for both 110 | uiCultureName = cultureName; 111 | } 112 | 113 | if (cultureName == null && uiCultureName != null) 114 | { 115 | // Value for UI culture but not for culture so default to UI culture value for both 116 | cultureName = uiCultureName; 117 | } 118 | 119 | return new ProviderCultureResult(cultureName, uiCultureName); 120 | } 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /src/Microsoft.AspNetCore.Localization/CustomRequestCultureProvider.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) .NET Foundation. All rights reserved. 2 | // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 3 | 4 | using System; 5 | using System.Threading.Tasks; 6 | using Microsoft.AspNetCore.Http; 7 | 8 | namespace Microsoft.AspNetCore.Localization 9 | { 10 | /// 11 | /// Determines the culture information for a request via the configured delegate. 12 | /// 13 | public class CustomRequestCultureProvider : RequestCultureProvider 14 | { 15 | private readonly Func> _provider; 16 | 17 | /// 18 | /// Creates a new using the specified delegate. 19 | /// 20 | /// The provider delegate. 21 | public CustomRequestCultureProvider(Func> provider) 22 | { 23 | if (provider == null) 24 | { 25 | throw new ArgumentNullException(nameof(provider)); 26 | } 27 | 28 | _provider = provider; 29 | } 30 | 31 | /// 32 | public override Task DetermineProviderCultureResult(HttpContext httpContext) 33 | { 34 | if (httpContext == null) 35 | { 36 | throw new ArgumentNullException(nameof(httpContext)); 37 | } 38 | 39 | return _provider(httpContext); 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/Microsoft.AspNetCore.Localization/IRequestCultureFeature.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) .NET Foundation. All rights reserved. 2 | // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 3 | 4 | namespace Microsoft.AspNetCore.Localization 5 | { 6 | /// 7 | /// Represents the feature that provides the current request's culture information. 8 | /// 9 | public interface IRequestCultureFeature 10 | { 11 | /// 12 | /// The of the request. 13 | /// 14 | RequestCulture RequestCulture { get; } 15 | 16 | /// 17 | /// The that determined the request's culture information. 18 | /// If the value is null then no provider was used and the request's culture was set to the value of 19 | /// . 20 | /// 21 | IRequestCultureProvider Provider { get; } 22 | } 23 | } -------------------------------------------------------------------------------- /src/Microsoft.AspNetCore.Localization/IRequestCultureProvider.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) .NET Foundation. All rights reserved. 2 | // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 3 | 4 | using System.Threading.Tasks; 5 | using Microsoft.AspNetCore.Http; 6 | 7 | namespace Microsoft.AspNetCore.Localization 8 | { 9 | /// 10 | /// Represents a provider for determining the culture information of an . 11 | /// 12 | public interface IRequestCultureProvider 13 | { 14 | /// 15 | /// Implements the provider to determine the culture of the given request. 16 | /// 17 | /// The for the request. 18 | /// 19 | /// The determined . 20 | /// Returns null if the provider couldn't determine a . 21 | /// 22 | Task DetermineProviderCultureResult(HttpContext httpContext); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/Microsoft.AspNetCore.Localization/Internal/RequestCultureProviderLoggerExtensions.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) .NET Foundation. All rights reserved. 2 | // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 3 | 4 | using System; 5 | using System.Collections.Generic; 6 | using Microsoft.Extensions.Logging; 7 | using Microsoft.Extensions.Primitives; 8 | 9 | namespace Microsoft.AspNetCore.Localization.Internal 10 | { 11 | internal static class RequestCultureProviderLoggerExtensions 12 | { 13 | private static readonly Action, Exception> _unsupportedCulture; 14 | private static readonly Action, Exception> _unsupportedUICulture; 15 | 16 | static RequestCultureProviderLoggerExtensions() 17 | { 18 | _unsupportedCulture = LoggerMessage.Define>( 19 | LogLevel.Warning, 20 | 1, 21 | "{requestCultureProvider} returned the following unsupported cultures '{cultures}'."); 22 | _unsupportedUICulture = LoggerMessage.Define>( 23 | LogLevel.Warning, 24 | 2, 25 | "{requestCultureProvider} returned the following unsupported UI Cultures '{uiCultures}'."); 26 | } 27 | 28 | public static void UnsupportedCultures(this ILogger logger, string requestCultureProvider, IList cultures) 29 | { 30 | _unsupportedCulture(logger, requestCultureProvider, cultures, null); 31 | } 32 | 33 | public static void UnsupportedUICultures(this ILogger logger, string requestCultureProvider, IList uiCultures) 34 | { 35 | _unsupportedUICulture(logger, requestCultureProvider, uiCultures, null); 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/Microsoft.AspNetCore.Localization/Microsoft.AspNetCore.Localization.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Microsoft ASP.NET Core 5 | ASP.NET Core middleware for automatically applying culture information to HTTP requests. Culture information can be specified in the HTTP header, query string, cookie, or custom source. 6 | netcoreapp3.0 7 | $(NoWarn);CS1591 8 | true 9 | aspnetcore;localization 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /src/Microsoft.AspNetCore.Localization/Properties/Resources.Designer.cs: -------------------------------------------------------------------------------- 1 | // 2 | namespace Microsoft.AspNetCore.Localization 3 | { 4 | using System.Reflection; 5 | using System.Resources; 6 | 7 | internal static class Resources 8 | { 9 | private static readonly ResourceManager _resourceManager 10 | = new ResourceManager("Microsoft.AspNetCore.Localization.Resources", typeof(Resources).GetTypeInfo().Assembly); 11 | 12 | /// 13 | /// Please provide at least one culture. 14 | /// 15 | internal static string Exception_CulturesShouldNotBeEmpty 16 | { 17 | get { return GetString("Exception_CulturesShouldNotBeEmpty"); } 18 | } 19 | 20 | private static string GetString(string name, params string[] formatterNames) 21 | { 22 | var value = _resourceManager.GetString(name); 23 | 24 | System.Diagnostics.Debug.Assert(value != null); 25 | 26 | if (formatterNames != null) 27 | { 28 | for (var i = 0; i < formatterNames.Length; i++) 29 | { 30 | value = value.Replace("{" + formatterNames[i] + "}", "{" + i + "}"); 31 | } 32 | } 33 | 34 | return value; 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/Microsoft.AspNetCore.Localization/ProviderCultureResult.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) .NET Foundation. All rights reserved. 2 | // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 3 | 4 | using System.Collections.Generic; 5 | using Microsoft.Extensions.Primitives; 6 | 7 | namespace Microsoft.AspNetCore.Localization 8 | { 9 | /// 10 | /// Details about the cultures obtained from . 11 | /// 12 | public class ProviderCultureResult 13 | { 14 | /// 15 | /// Creates a new object that has its and 16 | /// properties set to the same culture value. 17 | /// 18 | /// The name of the culture to be used for formatting, text, i.e. language. 19 | public ProviderCultureResult(StringSegment culture) 20 | : this(new List { culture }, new List { culture }) 21 | { 22 | } 23 | 24 | /// 25 | /// Creates a new object has its and 26 | /// properties set to the respective culture values provided. 27 | /// 28 | /// The name of the culture to be used for formatting. 29 | /// The name of the ui culture to be used for text, i.e. language. 30 | public ProviderCultureResult(StringSegment culture, StringSegment uiCulture) 31 | : this(new List { culture }, new List { uiCulture }) 32 | { 33 | } 34 | 35 | /// 36 | /// Creates a new object that has its and 37 | /// properties set to the same culture value. 38 | /// 39 | /// The list of cultures to be used for formatting, text, i.e. language. 40 | public ProviderCultureResult(IList cultures) 41 | : this(cultures, cultures) 42 | { 43 | } 44 | 45 | /// 46 | /// Creates a new object has its and 47 | /// properties set to the respective culture values provided. 48 | /// 49 | /// The list of cultures to be used for formatting. 50 | /// The list of ui cultures to be used for text, i.e. language. 51 | public ProviderCultureResult(IList cultures, IList uiCultures) 52 | { 53 | Cultures = cultures; 54 | UICultures = uiCultures; 55 | } 56 | 57 | /// 58 | /// Gets the list of cultures to be used for formatting. 59 | /// 60 | public IList Cultures { get; } 61 | 62 | /// 63 | /// Gets the list of ui cultures to be used for text, i.e. language; 64 | /// 65 | public IList UICultures { get; } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/Microsoft.AspNetCore.Localization/QueryStringRequestCultureProvider.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) .NET Foundation. All rights reserved. 2 | // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 3 | 4 | using System; 5 | using System.Threading.Tasks; 6 | using Microsoft.AspNetCore.Http; 7 | using Microsoft.Extensions.Internal; 8 | 9 | namespace Microsoft.AspNetCore.Localization 10 | { 11 | /// 12 | /// Determines the culture information for a request via values in the query string. 13 | /// 14 | public class QueryStringRequestCultureProvider : RequestCultureProvider 15 | { 16 | /// 17 | /// The key that contains the culture name. 18 | /// Defaults to "culture". 19 | /// 20 | public string QueryStringKey { get; set; } = "culture"; 21 | 22 | /// 23 | /// The key that contains the UI culture name. If not specified or no value is found, 24 | /// will be used. 25 | /// Defaults to "ui-culture". 26 | /// 27 | public string UIQueryStringKey { get; set; } = "ui-culture"; 28 | 29 | /// 30 | public override Task DetermineProviderCultureResult(HttpContext httpContext) 31 | { 32 | if (httpContext == null) 33 | { 34 | throw new ArgumentNullException(nameof(httpContext)); 35 | } 36 | 37 | var request = httpContext.Request; 38 | if (!request.QueryString.HasValue) 39 | { 40 | return NullProviderCultureResult; 41 | } 42 | 43 | string queryCulture = null; 44 | string queryUICulture = null; 45 | 46 | if (!string.IsNullOrWhiteSpace(QueryStringKey)) 47 | { 48 | queryCulture = request.Query[QueryStringKey]; 49 | } 50 | 51 | if (!string.IsNullOrWhiteSpace(UIQueryStringKey)) 52 | { 53 | queryUICulture = request.Query[UIQueryStringKey]; 54 | } 55 | 56 | if (queryCulture == null && queryUICulture == null) 57 | { 58 | // No values specified for either so no match 59 | return NullProviderCultureResult; 60 | } 61 | 62 | if (queryCulture != null && queryUICulture == null) 63 | { 64 | // Value for culture but not for UI culture so default to culture value for both 65 | queryUICulture = queryCulture; 66 | } 67 | 68 | if (queryCulture == null && queryUICulture != null) 69 | { 70 | // Value for UI culture but not for culture so default to UI culture value for both 71 | queryCulture = queryUICulture; 72 | } 73 | 74 | var providerResultCulture = new ProviderCultureResult(queryCulture, queryUICulture); 75 | 76 | return Task.FromResult(providerResultCulture); 77 | } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/Microsoft.AspNetCore.Localization/RequestCulture.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) .NET Foundation. All rights reserved. 2 | // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 3 | 4 | using System; 5 | using System.Globalization; 6 | 7 | namespace Microsoft.AspNetCore.Localization 8 | { 9 | /// 10 | /// Details about the culture for an . 11 | /// 12 | public class RequestCulture 13 | { 14 | /// 15 | /// Creates a new object has its and 16 | /// properties set to the same value. 17 | /// 18 | /// The for the request. 19 | public RequestCulture(CultureInfo culture) 20 | : this(culture, culture) 21 | { 22 | } 23 | 24 | /// 25 | /// Creates a new object has its and 26 | /// properties set to the same value. 27 | /// 28 | /// The culture for the request. 29 | public RequestCulture(string culture) 30 | : this(culture, culture) 31 | { 32 | } 33 | 34 | /// 35 | /// Creates a new object has its and 36 | /// properties set to the respective values provided. 37 | /// 38 | /// The culture for the request to be used for formatting. 39 | /// The culture for the request to be used for text, i.e. language. 40 | public RequestCulture(string culture, string uiCulture) 41 | : this (new CultureInfo(culture), new CultureInfo(uiCulture)) 42 | { 43 | } 44 | 45 | /// 46 | /// Creates a new object has its and 47 | /// properties set to the respective values provided. 48 | /// 49 | /// The for the request to be used for formatting. 50 | /// The for the request to be used for text, i.e. language. 51 | public RequestCulture(CultureInfo culture, CultureInfo uiCulture) 52 | { 53 | if (culture == null) 54 | { 55 | throw new ArgumentNullException(nameof(culture)); 56 | } 57 | 58 | if (uiCulture == null) 59 | { 60 | throw new ArgumentNullException(nameof(uiCulture)); 61 | } 62 | 63 | Culture = culture; 64 | UICulture = uiCulture; 65 | } 66 | 67 | /// 68 | /// Gets the for the request to be used for formatting. 69 | /// 70 | public CultureInfo Culture { get; } 71 | 72 | /// 73 | /// Gets the for the request to be used for text, i.e. language; 74 | /// 75 | public CultureInfo UICulture { get; } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/Microsoft.AspNetCore.Localization/RequestCultureFeature.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) .NET Foundation. All rights reserved. 2 | // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 3 | 4 | using System; 5 | 6 | namespace Microsoft.AspNetCore.Localization 7 | { 8 | /// 9 | /// Provides the current request's culture information. 10 | /// 11 | public class RequestCultureFeature : IRequestCultureFeature 12 | { 13 | /// 14 | /// Creates a new with the specified . 15 | /// 16 | /// The . 17 | /// The . 18 | public RequestCultureFeature(RequestCulture requestCulture, IRequestCultureProvider provider) 19 | { 20 | if (requestCulture == null) 21 | { 22 | throw new ArgumentNullException(nameof(requestCulture)); 23 | } 24 | 25 | RequestCulture = requestCulture; 26 | Provider = provider; 27 | } 28 | 29 | /// 30 | public RequestCulture RequestCulture { get; } 31 | 32 | /// 33 | public IRequestCultureProvider Provider { get; } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/Microsoft.AspNetCore.Localization/RequestCultureProvider.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) .NET Foundation. All rights reserved. 2 | // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 3 | 4 | using System.Threading.Tasks; 5 | using Microsoft.AspNetCore.Builder; 6 | using Microsoft.AspNetCore.Http; 7 | 8 | namespace Microsoft.AspNetCore.Localization 9 | { 10 | /// 11 | /// An abstract base class provider for determining the culture information of an . 12 | /// 13 | public abstract class RequestCultureProvider : IRequestCultureProvider 14 | { 15 | /// 16 | /// Result that indicates that this instance of could not determine the 17 | /// request culture. 18 | /// 19 | protected static readonly Task NullProviderCultureResult = Task.FromResult(default(ProviderCultureResult)); 20 | 21 | /// 22 | /// The current options for the . 23 | /// 24 | public RequestLocalizationOptions Options { get; set; } 25 | 26 | /// 27 | public abstract Task DetermineProviderCultureResult(HttpContext httpContext); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/Microsoft.AspNetCore.Localization/RequestLocalizationOptionsExtensions.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) .NET Foundation. All rights reserved. 2 | // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 3 | 4 | using System; 5 | using Microsoft.AspNetCore.Localization; 6 | 7 | namespace Microsoft.AspNetCore.Builder 8 | { 9 | /// 10 | /// Extension methods for the . 11 | /// 12 | public static class RequestLocalizationOptionsExtensions 13 | { 14 | /// 15 | /// Adds a new to the . 16 | /// 17 | /// The cultures to be added. 18 | /// The cultures to be added. 19 | /// The . 20 | /// This method ensures that has priority over other instances in . 21 | public static RequestLocalizationOptions AddInitialRequestCultureProvider( 22 | this RequestLocalizationOptions requestLocalizationOptions, 23 | RequestCultureProvider requestCultureProvider) 24 | { 25 | if (requestLocalizationOptions == null) 26 | { 27 | throw new ArgumentNullException(nameof(requestLocalizationOptions)); 28 | } 29 | 30 | if (requestCultureProvider == null) 31 | { 32 | throw new ArgumentNullException(nameof(requestCultureProvider)); 33 | } 34 | 35 | requestLocalizationOptions.RequestCultureProviders.Insert(0, requestCultureProvider); 36 | 37 | return requestLocalizationOptions; 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/Microsoft.Extensions.Localization.Abstractions/IStringLocalizer.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) .NET Foundation. All rights reserved. 2 | // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 3 | 4 | using System.Collections.Generic; 5 | using System.Globalization; 6 | 7 | namespace Microsoft.Extensions.Localization 8 | { 9 | /// 10 | /// Represents a service that provides localized strings. 11 | /// 12 | public interface IStringLocalizer 13 | { 14 | /// 15 | /// Gets the string resource with the given name. 16 | /// 17 | /// The name of the string resource. 18 | /// The string resource as a . 19 | LocalizedString this[string name] { get; } 20 | 21 | /// 22 | /// Gets the string resource with the given name and formatted with the supplied arguments. 23 | /// 24 | /// The name of the string resource. 25 | /// The values to format the string with. 26 | /// The formatted string resource as a . 27 | LocalizedString this[string name, params object[] arguments] { get; } 28 | 29 | /// 30 | /// Gets all string resources. 31 | /// 32 | /// 33 | /// A indicating whether to include strings from parent cultures. 34 | /// 35 | /// The strings. 36 | IEnumerable GetAllStrings(bool includeParentCultures); 37 | 38 | /// 39 | /// Creates a new for a specific . 40 | /// 41 | /// The to use. 42 | /// A culture-specific . 43 | IStringLocalizer WithCulture(CultureInfo culture); 44 | } 45 | } -------------------------------------------------------------------------------- /src/Microsoft.Extensions.Localization.Abstractions/IStringLocalizerFactory.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) .NET Foundation. All rights reserved. 2 | // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 3 | 4 | using System; 5 | 6 | namespace Microsoft.Extensions.Localization 7 | { 8 | /// 9 | /// Represents a factory that creates instances. 10 | /// 11 | public interface IStringLocalizerFactory 12 | { 13 | /// 14 | /// Creates an using the and 15 | /// of the specified . 16 | /// 17 | /// The . 18 | /// The . 19 | IStringLocalizer Create(Type resourceSource); 20 | 21 | /// 22 | /// Creates an . 23 | /// 24 | /// The base name of the resource to load strings from. 25 | /// The location to load resources from. 26 | /// The . 27 | IStringLocalizer Create(string baseName, string location); 28 | } 29 | } -------------------------------------------------------------------------------- /src/Microsoft.Extensions.Localization.Abstractions/IStringLocalizerOfT.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) .NET Foundation. All rights reserved. 2 | // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 3 | 4 | namespace Microsoft.Extensions.Localization 5 | { 6 | /// 7 | /// Represents an that provides strings for . 8 | /// 9 | /// The to provide strings for. 10 | public interface IStringLocalizer : IStringLocalizer 11 | { 12 | 13 | } 14 | } -------------------------------------------------------------------------------- /src/Microsoft.Extensions.Localization.Abstractions/LocalizedString.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) .NET Foundation. All rights reserved. 2 | // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 3 | 4 | using System; 5 | 6 | namespace Microsoft.Extensions.Localization 7 | { 8 | /// 9 | /// A locale specific string. 10 | /// 11 | public class LocalizedString 12 | { 13 | /// 14 | /// Creates a new . 15 | /// 16 | /// The name of the string in the resource it was loaded from. 17 | /// The actual string. 18 | public LocalizedString(string name, string value) 19 | : this(name, value, resourceNotFound: false) 20 | { 21 | } 22 | 23 | /// 24 | /// Creates a new . 25 | /// 26 | /// The name of the string in the resource it was loaded from. 27 | /// The actual string. 28 | /// Whether the string was not found in a resource. Set this to true to indicate an alternate string value was used. 29 | public LocalizedString(string name, string value, bool resourceNotFound) 30 | : this(name, value, resourceNotFound, searchedLocation: null) 31 | { 32 | } 33 | 34 | /// 35 | /// Creates a new . 36 | /// 37 | /// The name of the string in the resource it was loaded from. 38 | /// The actual string. 39 | /// Whether the string was not found in a resource. Set this to true to indicate an alternate string value was used. 40 | /// The location which was searched for a localization value. 41 | public LocalizedString(string name, string value, bool resourceNotFound, string searchedLocation) 42 | { 43 | if (name == null) 44 | { 45 | throw new ArgumentNullException(nameof(name)); 46 | } 47 | 48 | if (value == null) 49 | { 50 | throw new ArgumentNullException(nameof(value)); 51 | } 52 | 53 | Name = name; 54 | Value = value; 55 | ResourceNotFound = resourceNotFound; 56 | SearchedLocation = searchedLocation; 57 | } 58 | 59 | public static implicit operator string(LocalizedString localizedString) 60 | { 61 | return localizedString?.Value; 62 | } 63 | 64 | /// 65 | /// The name of the string in the resource it was loaded from. 66 | /// 67 | public string Name { get; } 68 | 69 | /// 70 | /// The actual string. 71 | /// 72 | public string Value { get; } 73 | 74 | /// 75 | /// Whether the string was not found in a resource. If true, an alternate string value was used. 76 | /// 77 | public bool ResourceNotFound { get; } 78 | 79 | /// 80 | /// The location which was searched for a localization value. 81 | /// 82 | public string SearchedLocation { get; } 83 | 84 | /// 85 | /// Returns the actual string. 86 | /// 87 | /// The actual string. 88 | public override string ToString() => Value; 89 | } 90 | } -------------------------------------------------------------------------------- /src/Microsoft.Extensions.Localization.Abstractions/Microsoft.Extensions.Localization.Abstractions.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Microsoft .NET Extensions 5 | Abstractions of application localization services. 6 | Commonly used types: 7 | Microsoft.Extensions.Localization.IStringLocalizer 8 | Microsoft.Extensions.Localization.IStringLocalizer<T> 9 | netstandard2.0 10 | $(NoWarn);CS1591 11 | true 12 | localization 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /src/Microsoft.Extensions.Localization.Abstractions/StringLocalizerExtensions.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) .NET Foundation. All rights reserved. 2 | // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 3 | 4 | using System; 5 | using System.Collections.Generic; 6 | 7 | namespace Microsoft.Extensions.Localization 8 | { 9 | public static class StringLocalizerExtensions 10 | { 11 | /// 12 | /// Gets the string resource with the given name. 13 | /// 14 | /// The . 15 | /// The name of the string resource. 16 | /// The string resource as a . 17 | public static LocalizedString GetString( 18 | this IStringLocalizer stringLocalizer, 19 | string name) 20 | { 21 | if (stringLocalizer == null) 22 | { 23 | throw new ArgumentNullException(nameof(stringLocalizer)); 24 | } 25 | 26 | if (name == null) 27 | { 28 | throw new ArgumentNullException(nameof(name)); 29 | } 30 | 31 | return stringLocalizer[name]; 32 | } 33 | 34 | /// 35 | /// Gets the string resource with the given name and formatted with the supplied arguments. 36 | /// 37 | /// The . 38 | /// The name of the string resource. 39 | /// The values to format the string with. 40 | /// The formatted string resource as a . 41 | public static LocalizedString GetString( 42 | this IStringLocalizer stringLocalizer, 43 | string name, 44 | params object[] arguments) 45 | { 46 | if (stringLocalizer == null) 47 | { 48 | throw new ArgumentNullException(nameof(stringLocalizer)); 49 | } 50 | 51 | if (name == null) 52 | { 53 | throw new ArgumentNullException(nameof(name)); 54 | } 55 | 56 | return stringLocalizer[name, arguments]; 57 | } 58 | 59 | /// 60 | /// Gets all string resources including those for parent cultures. 61 | /// 62 | /// The . 63 | /// The string resources. 64 | public static IEnumerable GetAllStrings(this IStringLocalizer stringLocalizer) 65 | { 66 | if (stringLocalizer == null) 67 | { 68 | throw new ArgumentNullException(nameof(stringLocalizer)); 69 | } 70 | 71 | return stringLocalizer.GetAllStrings(includeParentCultures: true); 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/Microsoft.Extensions.Localization.Abstractions/StringLocalizerOfT.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) .NET Foundation. All rights reserved. 2 | // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 3 | 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Globalization; 7 | 8 | namespace Microsoft.Extensions.Localization 9 | { 10 | /// 11 | /// Provides strings for . 12 | /// 13 | /// The to provide strings for. 14 | public class StringLocalizer : IStringLocalizer 15 | { 16 | private IStringLocalizer _localizer; 17 | 18 | /// 19 | /// Creates a new . 20 | /// 21 | /// The to use. 22 | public StringLocalizer(IStringLocalizerFactory factory) 23 | { 24 | if (factory == null) 25 | { 26 | throw new ArgumentNullException(nameof(factory)); 27 | } 28 | 29 | _localizer = factory.Create(typeof(TResourceSource)); 30 | } 31 | 32 | /// 33 | public virtual IStringLocalizer WithCulture(CultureInfo culture) => _localizer.WithCulture(culture); 34 | 35 | /// 36 | public virtual LocalizedString this[string name] 37 | { 38 | get 39 | { 40 | if (name == null) 41 | { 42 | throw new ArgumentNullException(nameof(name)); 43 | } 44 | 45 | return _localizer[name]; 46 | } 47 | } 48 | 49 | /// 50 | public virtual LocalizedString this[string name, params object[] arguments] 51 | { 52 | get 53 | { 54 | if (name == null) 55 | { 56 | throw new ArgumentNullException(nameof(name)); 57 | } 58 | 59 | return _localizer[name, arguments]; 60 | } 61 | } 62 | 63 | /// 64 | public IEnumerable GetAllStrings(bool includeParentCultures) => 65 | _localizer.GetAllStrings(includeParentCultures); 66 | } 67 | } -------------------------------------------------------------------------------- /src/Microsoft.Extensions.Localization/IResourceNamesCache.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) .NET Foundation. All rights reserved. 2 | // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 3 | 4 | using System; 5 | using System.Collections.Generic; 6 | 7 | namespace Microsoft.Extensions.Localization 8 | { 9 | /// 10 | /// Represents a cache of string names in resources. 11 | /// 12 | public interface IResourceNamesCache 13 | { 14 | /// 15 | /// Adds a set of resource names to the cache by using the specified function, if the name does not already exist. 16 | /// 17 | /// The resource name to add string names for. 18 | /// The function used to generate the string names for the resource. 19 | /// The string names for the resource. 20 | IList GetOrAdd(string name, Func> valueFactory); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/Microsoft.Extensions.Localization/Internal/AssemblyWrapper.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) .NET Foundation. All rights reserved. 2 | // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 3 | 4 | using System; 5 | using System.IO; 6 | using System.Reflection; 7 | 8 | namespace Microsoft.Extensions.Localization.Internal 9 | { 10 | public class AssemblyWrapper 11 | { 12 | public AssemblyWrapper(Assembly assembly) 13 | { 14 | if (assembly == null) 15 | { 16 | throw new ArgumentNullException(nameof(assembly)); 17 | } 18 | 19 | Assembly = assembly; 20 | } 21 | 22 | public Assembly Assembly { get; } 23 | 24 | public virtual string FullName => Assembly.FullName; 25 | 26 | public virtual Stream GetManifestResourceStream(string name) => Assembly.GetManifestResourceStream(name); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/Microsoft.Extensions.Localization/Internal/IResourceStringProvider.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) .NET Foundation. All rights reserved. 2 | // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 3 | 4 | using System.Collections.Generic; 5 | using System.Globalization; 6 | 7 | namespace Microsoft.Extensions.Localization.Internal 8 | { 9 | public interface IResourceStringProvider 10 | { 11 | IList GetAllResourceStrings(CultureInfo culture, bool throwOnMissing); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/Microsoft.Extensions.Localization/Internal/ResourceManagerStringLocalizerLoggerExtensions.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) .NET Foundation. All rights reserved. 2 | // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 3 | 4 | using System; 5 | using System.Globalization; 6 | using Microsoft.Extensions.Logging; 7 | 8 | namespace Microsoft.Extensions.Localization.Internal 9 | { 10 | internal static class ResourceManagerStringLocalizerLoggerExtensions 11 | { 12 | private static readonly Action _searchedLocation; 13 | 14 | static ResourceManagerStringLocalizerLoggerExtensions() 15 | { 16 | _searchedLocation = LoggerMessage.Define( 17 | LogLevel.Debug, 18 | 1, 19 | $"{nameof(ResourceManagerStringLocalizer)} searched for '{{Key}}' in '{{LocationSearched}}' with culture '{{Culture}}'."); 20 | } 21 | 22 | public static void SearchedLocation(this ILogger logger, string key, string searchedLocation, CultureInfo culture) 23 | { 24 | _searchedLocation(logger, key, searchedLocation, culture, null); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/Microsoft.Extensions.Localization/Internal/ResourceManagerStringProvider.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) .NET Foundation. All rights reserved. 2 | // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 3 | 4 | using System.Collections; 5 | using System.Collections.Generic; 6 | using System.Globalization; 7 | using System.Reflection; 8 | using System.Resources; 9 | 10 | namespace Microsoft.Extensions.Localization.Internal 11 | { 12 | public class ResourceManagerStringProvider : IResourceStringProvider 13 | { 14 | private readonly IResourceNamesCache _resourceNamesCache; 15 | private readonly ResourceManager _resourceManager; 16 | private readonly Assembly _assembly; 17 | private readonly string _resourceBaseName; 18 | 19 | public ResourceManagerStringProvider( 20 | IResourceNamesCache resourceCache, 21 | ResourceManager resourceManager, 22 | Assembly assembly, 23 | string baseName) 24 | { 25 | _resourceManager = resourceManager; 26 | _resourceNamesCache = resourceCache; 27 | _assembly = assembly; 28 | _resourceBaseName = baseName; 29 | } 30 | 31 | private string GetResourceCacheKey(CultureInfo culture) 32 | { 33 | var resourceName = _resourceManager.BaseName; 34 | 35 | return $"Culture={culture.Name};resourceName={resourceName};Assembly={_assembly.FullName}"; 36 | } 37 | 38 | private string GetResourceName(CultureInfo culture) 39 | { 40 | var resourceStreamName = _resourceBaseName; 41 | if (!string.IsNullOrEmpty(culture.Name)) 42 | { 43 | resourceStreamName += "." + culture.Name; 44 | } 45 | resourceStreamName += ".resources"; 46 | 47 | return resourceStreamName; 48 | } 49 | 50 | public IList GetAllResourceStrings(CultureInfo culture, bool throwOnMissing) 51 | { 52 | var cacheKey = GetResourceCacheKey(culture); 53 | 54 | return _resourceNamesCache.GetOrAdd(cacheKey, _ => 55 | { 56 | // We purposly don't dispose the ResourceSet because it causes an ObjectDisposedException when you try to read the values later. 57 | var resourceSet = _resourceManager.GetResourceSet(culture, createIfNotExists: true, tryParents: false); 58 | if (resourceSet == null) 59 | { 60 | if (throwOnMissing) 61 | { 62 | throw new MissingManifestResourceException(Resources.FormatLocalization_MissingManifest(GetResourceName(culture))); 63 | } 64 | else 65 | { 66 | return null; 67 | } 68 | } 69 | 70 | var names = new List(); 71 | foreach (DictionaryEntry entry in resourceSet) 72 | { 73 | names.Add((string)entry.Key); 74 | } 75 | 76 | return names; 77 | }); 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/Microsoft.Extensions.Localization/LocalizationOptions.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) .NET Foundation. All rights reserved. 2 | // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 3 | 4 | namespace Microsoft.Extensions.Localization 5 | { 6 | /// 7 | /// Provides programmatic configuration for localization. 8 | /// 9 | public class LocalizationOptions 10 | { 11 | /// 12 | /// The relative path under application root where resource files are located. 13 | /// 14 | public string ResourcesPath { get; set; } = string.Empty; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/Microsoft.Extensions.Localization/LocalizationServiceCollectionExtensions.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) .NET Foundation. All rights reserved. 2 | // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 3 | 4 | using System; 5 | using Microsoft.Extensions.DependencyInjection.Extensions; 6 | using Microsoft.Extensions.Localization; 7 | 8 | namespace Microsoft.Extensions.DependencyInjection 9 | { 10 | /// 11 | /// Extension methods for setting up localization services in an . 12 | /// 13 | public static class LocalizationServiceCollectionExtensions 14 | { 15 | /// 16 | /// Adds services required for application localization. 17 | /// 18 | /// The to add the services to. 19 | /// The so that additional calls can be chained. 20 | public static IServiceCollection AddLocalization(this IServiceCollection services) 21 | { 22 | if (services == null) 23 | { 24 | throw new ArgumentNullException(nameof(services)); 25 | } 26 | 27 | services.AddOptions(); 28 | 29 | AddLocalizationServices(services); 30 | 31 | return services; 32 | } 33 | 34 | /// 35 | /// Adds services required for application localization. 36 | /// 37 | /// The to add the services to. 38 | /// 39 | /// An to configure the . 40 | /// 41 | /// The so that additional calls can be chained. 42 | public static IServiceCollection AddLocalization( 43 | this IServiceCollection services, 44 | Action setupAction) 45 | { 46 | if (services == null) 47 | { 48 | throw new ArgumentNullException(nameof(services)); 49 | } 50 | 51 | if (setupAction == null) 52 | { 53 | throw new ArgumentNullException(nameof(setupAction)); 54 | } 55 | 56 | AddLocalizationServices(services, setupAction); 57 | 58 | return services; 59 | } 60 | 61 | // To enable unit testing 62 | internal static void AddLocalizationServices(IServiceCollection services) 63 | { 64 | services.TryAddSingleton(); 65 | services.TryAddTransient(typeof(IStringLocalizer<>), typeof(StringLocalizer<>)); 66 | } 67 | 68 | internal static void AddLocalizationServices( 69 | IServiceCollection services, 70 | Action setupAction) 71 | { 72 | AddLocalizationServices(services); 73 | services.Configure(setupAction); 74 | } 75 | } 76 | } -------------------------------------------------------------------------------- /src/Microsoft.Extensions.Localization/Microsoft.Extensions.Localization.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Microsoft .NET Extensions 5 | Application localization services and default implementation based on ResourceManager to load localized assembly resources. 6 | netstandard2.0 7 | $(NoWarn);CS1591 8 | true 9 | localization 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /src/Microsoft.Extensions.Localization/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) .NET Foundation. All rights reserved. 2 | // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 3 | 4 | using System.Runtime.CompilerServices; 5 | 6 | [assembly: InternalsVisibleTo("Microsoft.Extensions.Localization.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")] 7 | -------------------------------------------------------------------------------- /src/Microsoft.Extensions.Localization/Properties/Resources.Designer.cs: -------------------------------------------------------------------------------- 1 | // 2 | namespace Microsoft.Extensions.Localization 3 | { 4 | using System.Globalization; 5 | using System.Reflection; 6 | using System.Resources; 7 | 8 | internal static class Resources 9 | { 10 | private static readonly ResourceManager _resourceManager 11 | = new ResourceManager("Microsoft.Extensions.Localization.Resources", typeof(Resources).GetTypeInfo().Assembly); 12 | 13 | /// 14 | /// The manifest '{0}' was not found. 15 | /// 16 | internal static string Localization_MissingManifest 17 | { 18 | get { return GetString("Localization_MissingManifest"); } 19 | } 20 | 21 | /// 22 | /// The manifest '{0}' was not found. 23 | /// 24 | internal static string FormatLocalization_MissingManifest(object p0) 25 | { 26 | return string.Format(CultureInfo.CurrentCulture, GetString("Localization_MissingManifest"), p0); 27 | } 28 | 29 | /// 30 | /// No manifests exist for the current culture. 31 | /// 32 | internal static string Localization_MissingManifest_Parent 33 | { 34 | get { return GetString("Localization_MissingManifest_Parent"); } 35 | } 36 | 37 | /// 38 | /// No manifests exist for the current culture. 39 | /// 40 | internal static string FormatLocalization_MissingManifest_Parent() 41 | { 42 | return GetString("Localization_MissingManifest_Parent"); 43 | } 44 | 45 | private static string GetString(string name, params string[] formatterNames) 46 | { 47 | var value = _resourceManager.GetString(name); 48 | 49 | System.Diagnostics.Debug.Assert(value != null); 50 | 51 | if (formatterNames != null) 52 | { 53 | for (var i = 0; i < formatterNames.Length; i++) 54 | { 55 | value = value.Replace("{" + formatterNames[i] + "}", "{" + i + "}"); 56 | } 57 | } 58 | 59 | return value; 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/Microsoft.Extensions.Localization/ResourceLocationAttribute.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) .NET Foundation. All rights reserved. 2 | // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 3 | 4 | using System; 5 | 6 | namespace Microsoft.Extensions.Localization 7 | { 8 | /// 9 | /// Provides the location of resources for an Assembly. 10 | /// 11 | [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = false, Inherited = false)] 12 | public class ResourceLocationAttribute : Attribute 13 | { 14 | /// 15 | /// Creates a new . 16 | /// 17 | /// The location of resources for this Assembly. 18 | public ResourceLocationAttribute(string resourceLocation) 19 | { 20 | if (string.IsNullOrEmpty(resourceLocation)) 21 | { 22 | throw new ArgumentNullException(nameof(resourceLocation)); 23 | } 24 | 25 | ResourceLocation = resourceLocation; 26 | } 27 | 28 | /// 29 | /// The location of resources for this Assembly. 30 | /// 31 | public string ResourceLocation { get; } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/Microsoft.Extensions.Localization/ResourceNamesCache.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) .NET Foundation. All rights reserved. 2 | // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 3 | 4 | using System; 5 | using System.Collections.Concurrent; 6 | using System.Collections.Generic; 7 | 8 | namespace Microsoft.Extensions.Localization 9 | { 10 | /// 11 | /// An implementation of backed by a . 12 | /// 13 | public class ResourceNamesCache : IResourceNamesCache 14 | { 15 | private readonly ConcurrentDictionary> _cache = new ConcurrentDictionary>(); 16 | 17 | /// 18 | public IList GetOrAdd(string name, Func> valueFactory) 19 | { 20 | return _cache.GetOrAdd(name, valueFactory); 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/Microsoft.Extensions.Localization/RootNamespaceAttribute.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) .NET Foundation. All rights reserved. 2 | // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 3 | 4 | using System; 5 | 6 | namespace Microsoft.Extensions.Localization 7 | { 8 | /// 9 | /// Provides the RootNamespace of an Assembly. The RootNamespace of the assembly is used by Localization to 10 | /// determine the resource name to look for when RootNamespace differs from the AssemblyName. 11 | /// 12 | [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = false, Inherited = false)] 13 | public class RootNamespaceAttribute : Attribute 14 | { 15 | /// 16 | /// Creates a new . 17 | /// 18 | /// The RootNamespace for this Assembly. 19 | public RootNamespaceAttribute(string rootNamespace) 20 | { 21 | if (string.IsNullOrEmpty(rootNamespace)) 22 | { 23 | throw new ArgumentNullException(nameof(rootNamespace)); 24 | } 25 | 26 | RootNamespace = rootNamespace; 27 | } 28 | 29 | /// 30 | /// The RootNamespace of this Assembly. The RootNamespace of the assembly is used by Localization to 31 | /// determine the resource name to look for when RootNamespace differs from the AssemblyName. 32 | /// 33 | public string RootNamespace { get; } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /test/Directory.Build.props: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /test/LocalizationWebsite/LocalizationWebsite.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netcoreapp3.0 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /test/LocalizationWebsite/Models/Customer.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) .NET Foundation. All rights reserved. 2 | // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 3 | 4 | namespace LocalizationWebsite.Models 5 | { 6 | public class Customer 7 | { 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /test/LocalizationWebsite/Models/Customer.fr-FR.resx: -------------------------------------------------------------------------------- 1 |  2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | text/microsoft-resx 110 | 111 | 112 | 2.0 113 | 114 | 115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | 121 | Bonjour from Customer in Models folder 122 | 123 | -------------------------------------------------------------------------------- /test/LocalizationWebsite/Program.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) .NET Foundation. All rights reserved. 2 | // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 3 | 4 | using Microsoft.AspNetCore.Hosting; 5 | using Microsoft.Extensions.Configuration; 6 | using Microsoft.Extensions.Logging; 7 | 8 | namespace LocalizationWebsite 9 | { 10 | public static class Program 11 | { 12 | public static void Main(string[] args) 13 | { 14 | var config = new ConfigurationBuilder() 15 | .AddCommandLine(args) 16 | .Build(); 17 | 18 | var host = new WebHostBuilder() 19 | .ConfigureLogging((_, factory) => 20 | { 21 | factory.AddConsole(); 22 | factory.AddFilter("Console", level => level >= LogLevel.Warning); 23 | }) 24 | .UseKestrel() 25 | .UseConfiguration(config) 26 | .UseStartup("LocalizationWebsite") 27 | .Build(); 28 | 29 | host.Run(); 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /test/LocalizationWebsite/Resources/Models.Customer.fr-FR.resx: -------------------------------------------------------------------------------- 1 |  2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | text/microsoft-resx 110 | 111 | 112 | 2.0 113 | 114 | 115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | 121 | Bonjour from Customer in resources folder 122 | 123 | -------------------------------------------------------------------------------- /test/LocalizationWebsite/Resources/StartupCustomCulturePreserved.en-US.resx: -------------------------------------------------------------------------------- 1 |  2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | text/microsoft-resx 110 | 111 | 112 | 2.0 113 | 114 | 115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | 121 | We shouldn't get the english hello! 122 | 123 | -------------------------------------------------------------------------------- /test/LocalizationWebsite/Resources/StartupResourcesInFolder.fr-FR.resx: -------------------------------------------------------------------------------- 1 |  2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | text/microsoft-resx 110 | 111 | 112 | 2.0 113 | 114 | 115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | 121 | Bonjour from StartupResourcesInFolder 122 | 123 | -------------------------------------------------------------------------------- /test/LocalizationWebsite/Resources/Test.fr-FR.resx: -------------------------------------------------------------------------------- 1 |  2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | text/microsoft-resx 110 | 111 | 112 | 2.0 113 | 114 | 115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | 121 | Bonjour from Test in resources folder 122 | 123 | -------------------------------------------------------------------------------- /test/LocalizationWebsite/StartupBuilderAPIs.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) .NET Foundation. All rights reserved. 2 | // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 3 | 4 | using LocalizationWebsite.Models; 5 | using Microsoft.AspNetCore.Builder; 6 | using Microsoft.AspNetCore.Http; 7 | using Microsoft.AspNetCore.Localization; 8 | using Microsoft.Extensions.DependencyInjection; 9 | using Microsoft.Extensions.Localization; 10 | using Microsoft.Extensions.Logging; 11 | 12 | namespace LocalizationWebsite 13 | { 14 | public class StartupBuilderAPIs 15 | { 16 | public void ConfigureServices(IServiceCollection services) 17 | { 18 | services.AddLocalization(options => options.ResourcesPath = "Resources"); 19 | } 20 | 21 | public void Configure( 22 | IApplicationBuilder app, 23 | ILoggerFactory loggerFactory, 24 | IStringLocalizer customerStringLocalizer) 25 | { 26 | var supportedCultures = new[] { "en-US", "fr-FR" }; 27 | app.UseRequestLocalization(options => 28 | options 29 | .AddSupportedCultures(supportedCultures) 30 | .AddSupportedUICultures(supportedCultures) 31 | .SetDefaultCulture("ar-YE") 32 | ); 33 | 34 | app.Run(async (context) => 35 | { 36 | var requestCultureFeature = context.Features.Get(); 37 | var requestCulture = requestCultureFeature.RequestCulture; 38 | await context.Response.WriteAsync(customerStringLocalizer["Hello"]); 39 | }); 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /test/LocalizationWebsite/StartupCustomCulturePreserved.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) .NET Foundation. All rights reserved. 2 | // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 3 | 4 | using System.Collections.Generic; 5 | using System.Globalization; 6 | using Microsoft.AspNetCore.Builder; 7 | using Microsoft.AspNetCore.Http; 8 | using Microsoft.AspNetCore.Localization; 9 | using Microsoft.Extensions.DependencyInjection; 10 | 11 | namespace LocalizationWebsite 12 | { 13 | public class StartupCustomCulturePreserved 14 | { 15 | public void ConfigureServices(IServiceCollection services) 16 | { 17 | services.AddLocalization(); 18 | } 19 | 20 | public void Configure( 21 | IApplicationBuilder app) 22 | { 23 | app.UseRequestLocalization(new RequestLocalizationOptions 24 | { 25 | DefaultRequestCulture = new RequestCulture("en-US"), 26 | SupportedCultures = new List() 27 | { 28 | new CultureInfo("en-US") { NumberFormat= { CurrencySymbol = "kr" } } 29 | }, 30 | SupportedUICultures = new List() 31 | { 32 | new CultureInfo("en-US") { NumberFormat= { CurrencySymbol = "kr" } } 33 | } 34 | }); 35 | 36 | app.Run(async (context) => 37 | { 38 | await context.Response.WriteAsync(10.ToString("C")); 39 | }); 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /test/LocalizationWebsite/StartupGetAllStrings.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) .NET Foundation. All rights reserved. 2 | // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 3 | 4 | using System.Collections.Generic; 5 | using System.Globalization; 6 | using System.Linq; 7 | using LocalizationWebsite.Models; 8 | using Microsoft.AspNetCore.Builder; 9 | using Microsoft.AspNetCore.Http; 10 | using Microsoft.AspNetCore.Localization; 11 | using Microsoft.Extensions.DependencyInjection; 12 | using Microsoft.Extensions.Localization; 13 | using Microsoft.Extensions.Logging; 14 | 15 | namespace LocalizationWebsite 16 | { 17 | public class StartupGetAllStrings 18 | { 19 | public void ConfigureServices(IServiceCollection services) 20 | { 21 | services.AddLocalization(options => options.ResourcesPath = "Resources"); 22 | } 23 | 24 | public void Configure( 25 | IApplicationBuilder app, 26 | ILoggerFactory loggerFactory, 27 | IStringLocalizer customerStringLocalizer) 28 | { 29 | app.UseRequestLocalization(new RequestLocalizationOptions 30 | { 31 | DefaultRequestCulture = new RequestCulture("en-US"), 32 | SupportedCultures = new List() 33 | { 34 | new CultureInfo("fr-FR") 35 | }, 36 | SupportedUICultures = new List() 37 | { 38 | new CultureInfo("fr-FR") 39 | } 40 | }); 41 | 42 | app.Run(async (context) => 43 | { 44 | var strings = customerStringLocalizer.GetAllStrings(); 45 | 46 | await context.Response.WriteAsync(strings.Count().ToString()); 47 | await context.Response.WriteAsync(" "); 48 | await context.Response.WriteAsync(string.Join(" ", strings)); 49 | }); 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /test/LocalizationWebsite/StartupResourcesAtRootFolder.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) .NET Foundation. All rights reserved. 2 | // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 3 | 4 | using System.Collections.Generic; 5 | using System.Globalization; 6 | using System.Reflection; 7 | using LocalizationWebsite.Models; 8 | using Microsoft.AspNetCore.Builder; 9 | using Microsoft.AspNetCore.Http; 10 | using Microsoft.AspNetCore.Localization; 11 | using Microsoft.Extensions.DependencyInjection; 12 | using Microsoft.Extensions.Localization; 13 | using Microsoft.Extensions.Logging; 14 | 15 | namespace LocalizationWebsite 16 | { 17 | public class StartupResourcesAtRootFolder 18 | { 19 | public void ConfigureServices(IServiceCollection services) 20 | { 21 | services.AddLocalization(); 22 | } 23 | 24 | public void Configure( 25 | IApplicationBuilder app, 26 | ILoggerFactory loggerFactory, 27 | IStringLocalizerFactory stringLocalizerFactory, 28 | IStringLocalizer startupStringLocalizer, 29 | IStringLocalizer customerStringLocalizer) 30 | { 31 | app.UseRequestLocalization(new RequestLocalizationOptions 32 | { 33 | DefaultRequestCulture = new RequestCulture("en-US"), 34 | SupportedCultures = new List() 35 | { 36 | new CultureInfo("fr-FR") 37 | }, 38 | SupportedUICultures = new List() 39 | { 40 | new CultureInfo("fr-FR") 41 | } 42 | }); 43 | 44 | var location = typeof(LocalizationWebsite.StartupResourcesAtRootFolder).GetTypeInfo().Assembly.GetName().Name; 45 | var stringLocalizer = stringLocalizerFactory.Create("Test", location: location); 46 | 47 | app.Run(async (context) => 48 | { 49 | await context.Response.WriteAsync(startupStringLocalizer["Hello"]); 50 | await context.Response.WriteAsync(" "); 51 | await context.Response.WriteAsync(stringLocalizer["Hello"]); 52 | await context.Response.WriteAsync(" "); 53 | await context.Response.WriteAsync(customerStringLocalizer["Hello"]); 54 | }); 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /test/LocalizationWebsite/StartupResourcesAtRootFolder.fr-FR.resx: -------------------------------------------------------------------------------- 1 |  2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | text/microsoft-resx 110 | 111 | 112 | 2.0 113 | 114 | 115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | 121 | Bonjour from StartupResourcesAtRootFolder 122 | 123 | -------------------------------------------------------------------------------- /test/LocalizationWebsite/StartupResourcesInClassLibrary.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) .NET Foundation. All rights reserved. 2 | // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 3 | 4 | using System.Collections.Generic; 5 | using System.Globalization; 6 | using System.Reflection; 7 | using Microsoft.AspNetCore.Builder; 8 | using Microsoft.AspNetCore.Http; 9 | using Microsoft.AspNetCore.Localization; 10 | using Microsoft.Extensions.DependencyInjection; 11 | using Microsoft.Extensions.Localization; 12 | using Microsoft.Extensions.Logging; 13 | 14 | namespace LocalizationWebsite 15 | { 16 | public class StartupResourcesInClassLibrary 17 | { 18 | public void ConfigureServices(IServiceCollection services) 19 | { 20 | services.AddLocalization(options => options.ResourcesPath = "Resources"); 21 | } 22 | 23 | public void Configure( 24 | IApplicationBuilder app, 25 | ILoggerFactory loggerFactory, 26 | IStringLocalizerFactory stringLocalizerFactory) 27 | { 28 | var supportedCultures = new List() 29 | { 30 | new CultureInfo("en-US"), 31 | new CultureInfo("fr-FR") 32 | }; 33 | 34 | app.UseRequestLocalization(new RequestLocalizationOptions 35 | { 36 | DefaultRequestCulture = new RequestCulture("en-US"), 37 | SupportedCultures = supportedCultures, 38 | SupportedUICultures = supportedCultures 39 | }); 40 | 41 | var noAttributeStringLocalizer = stringLocalizerFactory.Create(typeof(ResourcesClassLibraryNoAttribute.Model)); 42 | var withAttributeStringLocalizer = stringLocalizerFactory.Create(typeof(ResourcesClassLibraryWithAttribute.Model)); 43 | 44 | var noAttributeAssembly = typeof(ResourcesClassLibraryNoAttribute.Model).GetTypeInfo().Assembly; 45 | var noAttributeName = new AssemblyName(noAttributeAssembly.FullName).Name; 46 | var noAttributeNameStringLocalizer = stringLocalizerFactory.Create( 47 | nameof(ResourcesClassLibraryNoAttribute.Model), 48 | noAttributeName); 49 | 50 | var withAttributeAssembly = typeof(ResourcesClassLibraryWithAttribute.Model).GetTypeInfo().Assembly; 51 | var withAttributeName = new AssemblyName(withAttributeAssembly.FullName).Name; 52 | var withAttributeNameStringLocalizer = stringLocalizerFactory.Create( 53 | nameof(ResourcesClassLibraryWithAttribute.Model), 54 | withAttributeName); 55 | 56 | app.Run(async (context) => 57 | { 58 | await context.Response.WriteAsync(noAttributeNameStringLocalizer["Hello"]); 59 | await context.Response.WriteAsync(" "); 60 | await context.Response.WriteAsync(noAttributeStringLocalizer["Hello"]); 61 | await context.Response.WriteAsync(" "); 62 | await context.Response.WriteAsync(withAttributeNameStringLocalizer["Hello"]); 63 | await context.Response.WriteAsync(" "); 64 | await context.Response.WriteAsync(withAttributeStringLocalizer["Hello"]); 65 | }); 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /test/LocalizationWebsite/StartupResourcesInFolder.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) .NET Foundation. All rights reserved. 2 | // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 3 | 4 | using System.Collections.Generic; 5 | using System.Globalization; 6 | using System.Reflection; 7 | using LocalizationWebsite.Models; 8 | using Microsoft.AspNetCore.Builder; 9 | using Microsoft.AspNetCore.Http; 10 | using Microsoft.AspNetCore.Localization; 11 | using Microsoft.Extensions.DependencyInjection; 12 | using Microsoft.Extensions.Localization; 13 | using Microsoft.Extensions.Logging; 14 | 15 | namespace LocalizationWebsite 16 | { 17 | public class StartupResourcesInFolder 18 | { 19 | public void ConfigureServices(IServiceCollection services) 20 | { 21 | services.AddLocalization(options => options.ResourcesPath = "Resources"); 22 | } 23 | 24 | public void Configure( 25 | IApplicationBuilder app, 26 | ILoggerFactory loggerFactory, 27 | IStringLocalizerFactory stringLocalizerFactory, 28 | IStringLocalizer startupStringLocalizer, 29 | IStringLocalizer custromerStringLocalizer, 30 | // This localizer is used in tests to prevent a regression of https://github.com/aspnet/Localization/issues/293 31 | // Namely that english was always being returned if it existed. 32 | IStringLocalizer customCultureLocalizer) 33 | { 34 | app.UseRequestLocalization(new RequestLocalizationOptions 35 | { 36 | DefaultRequestCulture = new RequestCulture("en-US"), 37 | SupportedCultures = new List() 38 | { 39 | new CultureInfo("fr-FR") 40 | }, 41 | SupportedUICultures = new List() 42 | { 43 | new CultureInfo("fr-FR") 44 | } 45 | }); 46 | 47 | var assemblyName = typeof(StartupResourcesInFolder).GetTypeInfo().Assembly.GetName().Name; 48 | var stringLocalizer = stringLocalizerFactory.Create("Test", assemblyName); 49 | 50 | app.Run(async (context) => 51 | { 52 | await context.Response.WriteAsync(startupStringLocalizer["Hello"]); 53 | await context.Response.WriteAsync(" "); 54 | await context.Response.WriteAsync(stringLocalizer["Hello"]); 55 | await context.Response.WriteAsync(" "); 56 | await context.Response.WriteAsync(custromerStringLocalizer["Hello"]); 57 | await context.Response.WriteAsync(" "); 58 | await context.Response.WriteAsync(customCultureLocalizer["Hello"]); 59 | }); 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /test/LocalizationWebsite/Test.fr-FR.resx: -------------------------------------------------------------------------------- 1 |  2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | text/microsoft-resx 110 | 111 | 112 | 2.0 113 | 114 | 115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | 121 | Bonjour from Test in root folder 122 | 123 | -------------------------------------------------------------------------------- /test/Microsoft.AspNetCore.Localization.FunctionalTests/LocalizationSampleTest.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) .NET Foundation. All rights reserved. 2 | // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 3 | 4 | using System; 5 | using System.IO; 6 | using System.Net; 7 | using System.Net.Http; 8 | using System.Threading.Tasks; 9 | using LocalizationSample; 10 | using Microsoft.AspNetCore.Hosting; 11 | using Microsoft.AspNetCore.TestHost; 12 | using Xunit; 13 | 14 | namespace Microsoft.AspNetCore.Localization.FunctionalTests 15 | { 16 | public class LocalizationSampleTest 17 | { 18 | [Fact] 19 | public async Task LocalizationSampleSmokeTest() 20 | { 21 | // Arrange 22 | var webHostBuilder = new WebHostBuilder().UseStartup(typeof(Startup)); 23 | var testHost = new TestServer(webHostBuilder); 24 | var locale = "fr-FR"; 25 | var client = testHost.CreateClient(); 26 | var request = new HttpRequestMessage(HttpMethod.Get, "My/Resources"); 27 | var cookieValue = $"c={locale}|uic={locale}"; 28 | request.Headers.Add("Cookie", $"{CookieRequestCultureProvider.DefaultCookieName}={cookieValue}"); 29 | 30 | // Act 31 | var response = await client.SendAsync(request); 32 | 33 | // Assert 34 | Assert.Equal(HttpStatusCode.OK, response.StatusCode); 35 | Assert.Contains("

Bonjour

", await response.Content.ReadAsStringAsync()); 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /test/Microsoft.AspNetCore.Localization.FunctionalTests/LocalizationTest.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) .NET Foundation. All rights reserved. 2 | // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 3 | 4 | using System; 5 | using System.Net; 6 | using System.Net.Http; 7 | using System.Threading.Tasks; 8 | using LocalizationWebsite; 9 | using Microsoft.AspNetCore.Hosting; 10 | using Microsoft.AspNetCore.TestHost; 11 | using Xunit; 12 | 13 | namespace Microsoft.AspNetCore.Localization.FunctionalTests 14 | { 15 | public class LocalizationTest 16 | { 17 | [Fact] 18 | public Task Localization_CustomCulture() 19 | { 20 | return RunTest( 21 | typeof(StartupCustomCulturePreserved), 22 | "en-US", 23 | "kr10.00"); 24 | } 25 | 26 | [Fact] 27 | public Task Localization_GetAllStrings() 28 | { 29 | return RunTest( 30 | typeof(StartupGetAllStrings), 31 | "fr-FR", 32 | "1 Bonjour from Customer in resources folder"); 33 | } 34 | 35 | [Fact] 36 | public Task Localization_ResourcesInClassLibrary_ReturnLocalizedValue() 37 | { 38 | return RunTest( 39 | typeof(StartupResourcesInClassLibrary), 40 | "fr-FR", 41 | "Bonjour from ResourcesClassLibraryNoAttribute Bonjour from ResourcesClassLibraryNoAttribute Bonjour from ResourcesClassLibraryWithAttribute Bonjour from ResourcesClassLibraryWithAttribute"); 42 | } 43 | 44 | [Fact] 45 | public Task Localization_ResourcesInFolder_ReturnLocalizedValue() 46 | { 47 | return RunTest( 48 | typeof(StartupResourcesInFolder), 49 | "fr-FR", 50 | "Bonjour from StartupResourcesInFolder Bonjour from Test in resources folder Bonjour from Customer in resources folder Hello"); 51 | } 52 | 53 | [Fact] 54 | public Task Localization_ResourcesInFolder_ReturnLocalizedValue_WithCultureFallback() 55 | { 56 | return RunTest( 57 | typeof(StartupResourcesInFolder), 58 | "fr-FR-test", 59 | "Bonjour from StartupResourcesInFolder Bonjour from Test in resources folder Bonjour from Customer in resources folder Hello"); 60 | } 61 | 62 | [Fact] 63 | public Task Localization_ResourcesInFolder_ReturnNonLocalizedValue_CultureHierarchyTooDeep() 64 | { 65 | return RunTest( 66 | typeof(StartupResourcesInFolder), 67 | "fr-FR-test-again-too-deep-to-work", 68 | "Hello Hello Hello Hello"); 69 | } 70 | 71 | [Fact] 72 | public Task Localization_ResourcesAtRootFolder_ReturnLocalizedValue() 73 | { 74 | return RunTest( 75 | typeof(StartupResourcesAtRootFolder), 76 | "fr-FR", 77 | "Bonjour from StartupResourcesAtRootFolder Bonjour from Test in root folder Bonjour from Customer in Models folder"); 78 | } 79 | 80 | [Fact] 81 | public Task Localization_BuilderAPIs() 82 | { 83 | return RunTest( 84 | typeof(StartupBuilderAPIs), 85 | "ar-YE", 86 | "Hello"); 87 | } 88 | 89 | private async Task RunTest(Type startupType, string culture, string expected) 90 | { 91 | var webHostBuilder = new WebHostBuilder().UseStartup(startupType); 92 | var testHost = new TestServer(webHostBuilder); 93 | 94 | var client = testHost.CreateClient(); 95 | var request = new HttpRequestMessage(); 96 | var cookieValue = $"c={culture}|uic={culture}"; 97 | request.Headers.Add("Cookie", $"{CookieRequestCultureProvider.DefaultCookieName}={cookieValue}"); 98 | 99 | var response = await client.SendAsync(request); 100 | 101 | Assert.Equal(HttpStatusCode.OK, response.StatusCode); 102 | Assert.Equal(expected, await response.Content.ReadAsStringAsync()); 103 | } 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /test/Microsoft.AspNetCore.Localization.FunctionalTests/Microsoft.AspNetCore.Localization.FunctionalTests.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netcoreapp3.0 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /test/Microsoft.AspNetCore.Localization.Routing.Tests/Microsoft.AspNetCore.Localization.Routing.Tests.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netcoreapp3.0 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /test/Microsoft.AspNetCore.Localization.Tests/CustomRequestCultureProviderTest.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) .NET Foundation. All rights reserved. 2 | // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 3 | 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Globalization; 7 | using System.Threading.Tasks; 8 | using Microsoft.AspNetCore.Builder; 9 | using Microsoft.AspNetCore.Hosting; 10 | using Microsoft.AspNetCore.Http; 11 | using Microsoft.AspNetCore.Localization; 12 | using Microsoft.AspNetCore.TestHost; 13 | using Xunit; 14 | 15 | namespace Microsoft.Extensions.Localization 16 | { 17 | public class CustomRequestCultureProviderTest 18 | { 19 | [Fact] 20 | public async Task CustomRequestCultureProviderThatGetsCultureInfoFromUrl() 21 | { 22 | var builder = new WebHostBuilder() 23 | .Configure(app => 24 | { 25 | var options = new RequestLocalizationOptions 26 | { 27 | DefaultRequestCulture = new RequestCulture("en-US"), 28 | SupportedCultures = new List 29 | { 30 | new CultureInfo("ar") 31 | }, 32 | SupportedUICultures = new List 33 | { 34 | new CultureInfo("ar") 35 | } 36 | }; 37 | options.RequestCultureProviders.Insert(0, new CustomRequestCultureProvider(context => 38 | { 39 | var culture = GetCultureInfoFromUrl(context, options.SupportedCultures); 40 | var requestCulture = new ProviderCultureResult(culture); 41 | return Task.FromResult(requestCulture); 42 | })); 43 | app.UseRequestLocalization(options); 44 | app.Run(context => 45 | { 46 | var requestCultureFeature = context.Features.Get(); 47 | var requestCulture = requestCultureFeature.RequestCulture; 48 | Assert.Equal("ar", requestCulture.Culture.Name); 49 | return Task.FromResult(0); 50 | }); 51 | }); 52 | 53 | using (var server = new TestServer(builder)) 54 | { 55 | var client = server.CreateClient(); 56 | var response = await client.GetAsync("/ar/page"); 57 | } 58 | } 59 | 60 | private string GetCultureInfoFromUrl(HttpContext context, IList supportedCultures) 61 | { 62 | var currentCulture = "en"; 63 | var segments = context.Request.Path.Value.Split(new char[] { '/' }, StringSplitOptions.RemoveEmptyEntries); 64 | if (segments.Length > 1 && segments[0].Length == 2) 65 | { 66 | currentCulture = segments[0]; 67 | } 68 | 69 | return currentCulture; 70 | } 71 | } 72 | } -------------------------------------------------------------------------------- /test/Microsoft.AspNetCore.Localization.Tests/Microsoft.AspNetCore.Localization.Tests.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netcoreapp3.0 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /test/Microsoft.AspNetCore.Localization.Tests/RequestLocalizationOptionsExtensionsTest.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) .NET Foundation. All rights reserved. 2 | // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 3 | 4 | using System.Threading.Tasks; 5 | using Microsoft.AspNetCore.Builder; 6 | using Xunit; 7 | 8 | namespace Microsoft.AspNetCore.Localization 9 | { 10 | public class RequestLocalizationOptionsExtensionsTest 11 | { 12 | [Fact] 13 | public void AddInitialRequestCultureProvider_ShouldBeInsertedAtFirstPostion() 14 | { 15 | // Arrange 16 | var options = new RequestLocalizationOptions(); 17 | var provider = new CustomRequestCultureProvider(context => Task.FromResult(new ProviderCultureResult("ar-YE"))); 18 | 19 | // Act 20 | options.AddInitialRequestCultureProvider(provider); 21 | 22 | // Assert 23 | Assert.Same(provider, options.RequestCultureProviders[0]); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /test/Microsoft.AspNetCore.Localization.Tests/RequestLocalizationOptionsTest.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) .NET Foundation. All rights reserved. 2 | // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 3 | 4 | using System; 5 | using System.Globalization; 6 | using System.Linq; 7 | using Microsoft.AspNetCore.Builder; 8 | using Xunit; 9 | 10 | namespace Microsoft.AspNetCore.Localization 11 | { 12 | public class RequestLocalizationOptionsTest : IDisposable 13 | { 14 | private readonly CultureInfo _initialCulture; 15 | private readonly CultureInfo _initialUICulture; 16 | 17 | public RequestLocalizationOptionsTest() 18 | { 19 | _initialCulture = CultureInfo.CurrentCulture; 20 | _initialUICulture = CultureInfo.CurrentUICulture; 21 | } 22 | 23 | [Fact] 24 | public void DefaultRequestCulture_DefaultsToCurrentCulture() 25 | { 26 | // Arrange/Act 27 | var options = new RequestLocalizationOptions(); 28 | 29 | // Assert 30 | Assert.NotNull(options.DefaultRequestCulture); 31 | Assert.Equal(CultureInfo.CurrentCulture, options.DefaultRequestCulture.Culture); 32 | Assert.Equal(CultureInfo.CurrentUICulture, options.DefaultRequestCulture.UICulture); 33 | } 34 | 35 | [Fact] 36 | public void DefaultRequestCulture_DefaultsToCurrentCultureWhenExplicitlySet() 37 | { 38 | // Arrange 39 | var explicitCulture = new CultureInfo("fr-FR"); 40 | CultureInfo.CurrentCulture = explicitCulture; 41 | CultureInfo.CurrentUICulture = explicitCulture; 42 | 43 | // Act 44 | var options = new RequestLocalizationOptions(); 45 | 46 | // Assert 47 | Assert.Equal(explicitCulture, options.DefaultRequestCulture.Culture); 48 | Assert.Equal(explicitCulture, options.DefaultRequestCulture.UICulture); 49 | } 50 | 51 | [Fact] 52 | public void DefaultRequestCulture_ThrowsWhenTryingToSetToNull() 53 | { 54 | // Arrange 55 | var options = new RequestLocalizationOptions(); 56 | 57 | // Act/Assert 58 | Assert.Throws(() => options.DefaultRequestCulture = null); 59 | } 60 | 61 | [Fact] 62 | public void SupportedCultures_DefaultsToCurrentCulture() 63 | { 64 | // Arrange/Act 65 | var options = new RequestLocalizationOptions(); 66 | 67 | // Assert 68 | Assert.Collection(options.SupportedCultures, item => Assert.Equal(CultureInfo.CurrentCulture, item)); 69 | Assert.Collection(options.SupportedUICultures, item => Assert.Equal(CultureInfo.CurrentUICulture, item)); 70 | } 71 | 72 | [Fact] 73 | public void SupportedCultures_DefaultsToCurrentCultureWhenExplicitlySet() 74 | { 75 | // Arrange 76 | var explicitCulture = new CultureInfo("fr-FR"); 77 | CultureInfo.CurrentCulture = explicitCulture; 78 | CultureInfo.CurrentUICulture = explicitCulture; 79 | 80 | // Act 81 | var options = new RequestLocalizationOptions(); 82 | 83 | // Assert 84 | Assert.Collection(options.SupportedCultures, item => Assert.Equal(explicitCulture, item)); 85 | Assert.Collection(options.SupportedUICultures, item => Assert.Equal(explicitCulture, item)); 86 | } 87 | 88 | [Fact] 89 | public void BuilderAPIs_AddSupportedCultures() 90 | { 91 | // Arrange 92 | var supportedCultures = new[] { "en-US", "ar-YE" }; 93 | 94 | // Act 95 | var options = new RequestLocalizationOptions() 96 | .AddSupportedCultures(supportedCultures); 97 | 98 | // Assert 99 | Assert.Equal(supportedCultures, options.SupportedCultures.Select(c => c.Name)); 100 | } 101 | 102 | [Fact] 103 | public void BuilderAPIs_AddSupportedUICultures() 104 | { 105 | // Arrange 106 | var supportedUICultures = new[] { "en-US", "ar-YE" }; 107 | 108 | // Act 109 | var options = new RequestLocalizationOptions() 110 | .AddSupportedUICultures(supportedUICultures); 111 | 112 | // Assert 113 | Assert.Equal(supportedUICultures, options.SupportedUICultures.Select(c => c.Name)); 114 | } 115 | 116 | [Fact] 117 | public void BuilderAPIs_SetDefaultCulture() 118 | { 119 | // Arrange 120 | var defaultCulture = "ar-YE"; 121 | 122 | // Act 123 | var options = new RequestLocalizationOptions() 124 | .SetDefaultCulture(defaultCulture); 125 | 126 | // Assert 127 | Assert.Equal(defaultCulture, options.DefaultRequestCulture.Culture.Name); 128 | } 129 | 130 | public void Dispose() 131 | { 132 | CultureInfo.CurrentCulture = _initialCulture; 133 | CultureInfo.CurrentUICulture = _initialUICulture; 134 | } 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /test/Microsoft.Extensions.Localization.Tests/LocalizationServiceCollectionExtensionsTest.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) .NET Foundation. All rights reserved. 2 | // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 3 | 4 | using System; 5 | using System.Linq; 6 | using Microsoft.Extensions.Localization; 7 | using Microsoft.Extensions.Options; 8 | using Xunit; 9 | 10 | namespace Microsoft.Extensions.DependencyInjection 11 | { 12 | public class LocalizationServiceCollectionExtensionsTest 13 | { 14 | [Fact] 15 | public void AddLocalization_AddsNeededServices() 16 | { 17 | // Arrange 18 | var collection = new ServiceCollection(); 19 | 20 | // Act 21 | LocalizationServiceCollectionExtensions.AddLocalizationServices(collection); 22 | 23 | // Assert 24 | AssertContainsSingle(collection, typeof(IStringLocalizerFactory), typeof(ResourceManagerStringLocalizerFactory)); 25 | AssertContainsSingle(collection, typeof(IStringLocalizer<>), typeof(StringLocalizer<>)); 26 | } 27 | 28 | [Fact] 29 | public void AddLocalizationWithLocalizationOptions_AddsNeededServices() 30 | { 31 | // Arrange 32 | var collection = new ServiceCollection(); 33 | 34 | // Act 35 | LocalizationServiceCollectionExtensions.AddLocalizationServices( 36 | collection, 37 | options => options.ResourcesPath = "Resources"); 38 | 39 | AssertContainsSingle(collection, typeof(IStringLocalizerFactory), typeof(ResourceManagerStringLocalizerFactory)); 40 | AssertContainsSingle(collection, typeof(IStringLocalizer<>), typeof(StringLocalizer<>)); 41 | } 42 | 43 | private void AssertContainsSingle( 44 | IServiceCollection services, 45 | Type serviceType, 46 | Type implementationType) 47 | { 48 | var matches = services 49 | .Where(sd => 50 | sd.ServiceType == serviceType && 51 | sd.ImplementationType == implementationType) 52 | .ToArray(); 53 | 54 | if (matches.Length == 0) 55 | { 56 | Assert.True( 57 | false, 58 | $"Could not find an instance of {implementationType} registered as {serviceType}"); 59 | } 60 | else if (matches.Length > 1) 61 | { 62 | Assert.True( 63 | false, 64 | $"Found multiple instances of {implementationType} registered as {serviceType}"); 65 | } 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /test/Microsoft.Extensions.Localization.Tests/Microsoft.Extensions.Localization.Tests.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netcoreapp3.0;net461 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /test/ResourcesClassLibraryNoAttribute/Model.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) .NET Foundation. All rights reserved. 2 | // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 3 | 4 | namespace ResourcesClassLibraryNoAttribute 5 | { 6 | public class Model 7 | { 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /test/ResourcesClassLibraryNoAttribute/Resources/Model.resx: -------------------------------------------------------------------------------- 1 |  2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | text/microsoft-resx 110 | 111 | 112 | 2.0 113 | 114 | 115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | 121 | Bonjour from ResourcesClassLibraryNoAttribute 122 | 123 | -------------------------------------------------------------------------------- /test/ResourcesClassLibraryNoAttribute/ResourcesClassLibraryNoAttribute.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netstandard2.0 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /test/ResourcesClassLibraryWithAttribute/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) .NET Foundation. All rights reserved. 2 | // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 3 | 4 | using System.Reflection; 5 | using Microsoft.Extensions.Localization; 6 | 7 | [assembly: ResourceLocation("ResourceFolder")] 8 | [assembly: RootNamespace("Alternate.Namespace")] -------------------------------------------------------------------------------- /test/ResourcesClassLibraryWithAttribute/Model.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) .NET Foundation. All rights reserved. 2 | // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 3 | 4 | namespace ResourcesClassLibraryWithAttribute 5 | { 6 | public class Model 7 | { 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /test/ResourcesClassLibraryWithAttribute/ResourceFolder/Model.resx: -------------------------------------------------------------------------------- 1 |  2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | text/microsoft-resx 110 | 111 | 112 | 2.0 113 | 114 | 115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | 121 | Bonjour from ResourcesClassLibraryWithAttribute 122 | 123 | -------------------------------------------------------------------------------- /test/ResourcesClassLibraryWithAttribute/ResourcesClassLibraryWithAttribute.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netstandard2.0 5 | Alternate.Namespace 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /version.props: -------------------------------------------------------------------------------- 1 |  2 | 3 | 3.0.0 4 | alpha1 5 | $(VersionPrefix) 6 | $(VersionPrefix)-$(VersionSuffix)-final 7 | t000 8 | a- 9 | $(FeatureBranchVersionPrefix)$(VersionSuffix)-$([System.Text.RegularExpressions.Regex]::Replace('$(FeatureBranchVersionSuffix)', '[^\w-]', '-')) 10 | $(VersionSuffix)-$(BuildNumber) 11 | 12 | 13 | --------------------------------------------------------------------------------