Initial commit

This commit is contained in:
damianedwards 2015-05-05 23:27:25 -07:00
commit 9eede89418
32 changed files with 1134 additions and 0 deletions

50
.gitattributes vendored Normal file
View File

@ -0,0 +1,50 @@
*.doc diff=astextplain
*.DOC diff=astextplain
*.docx diff=astextplain
*.DOCX diff=astextplain
*.dot diff=astextplain
*.DOT diff=astextplain
*.pdf diff=astextplain
*.PDF diff=astextplain
*.rtf diff=astextplain
*.RTF diff=astextplain
*.jpg binary
*.png binary
*.gif binary
*.cs text=auto diff=csharp
*.vb text=auto
*.resx text=auto
*.c text=auto
*.cpp text=auto
*.cxx text=auto
*.h text=auto
*.hxx text=auto
*.py text=auto
*.rb text=auto
*.java text=auto
*.html text=auto
*.htm text=auto
*.css text=auto
*.scss text=auto
*.sass text=auto
*.less text=auto
*.js text=auto
*.lisp text=auto
*.clj text=auto
*.sql text=auto
*.php text=auto
*.lua text=auto
*.m text=auto
*.asm text=auto
*.erl text=auto
*.fs text=auto
*.fsx text=auto
*.hs text=auto
*.csproj text=auto
*.vbproj text=auto
*.fsproj text=auto
*.dbproj text=auto
*.sln text=auto eol=crlf

28
.gitignore vendored Normal file
View File

@ -0,0 +1,28 @@
[Oo]bj/
[Bb]in/
TestResults/
.nuget/
_ReSharper.*/
packages/
artifacts/
PublishProfiles/
*.user
*.suo
*.cache
*.docstates
_ReSharper.*
nuget.exe
*net45.csproj
*net451.csproj
*k10.csproj
*.psess
*.vsp
*.pidb
*.userprefs
*DS_Store
*.ncrunchsolution
*.*sdf
*.ipch
*.sln.ide
debugSettings.json
project.lock.json

4
.travis.yml Normal file
View File

@ -0,0 +1,4 @@
language: csharp
sudo: false
script:
- ./build.sh --quiet verify

4
CONTRIBUTING.md Normal file
View File

@ -0,0 +1,4 @@
Contributing
======
Information on contributing to this repo is in the [Contributing Guide](https://github.com/aspnet/Home/blob/master/CONTRIBUTING.md) in the Home repo.

12
LICENSE.txt Normal file
View File

@ -0,0 +1,12 @@
Copyright (c) .NET Foundation. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License"); you may not use
these files except in compliance with the License. You may obtain a copy of the
License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software distributed
under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied. See the License for the
specific language governing permissions and limitations under the License.

41
Localization.sln Normal file
View File

@ -0,0 +1,41 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 14
VisualStudioVersion = 14.0.22823.1
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{FB313677-BAB3-4E49-8CDB-4FA4A9564767}"
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.Framework.Localization", "src\Microsoft.Framework.Localization\Microsoft.Framework.Localization.xproj", "{29743CFF-120E-40EB-9B89-5818425946FA}"
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.AspNet.Localization", "src\Microsoft.AspNet.Localization\Microsoft.AspNet.Localization.xproj", "{23E3BC23-3464-4D9B-BF78-02CB2182BEF0}"
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.Framework.Localization.Abstractions", "src\Microsoft.Framework.Localization.Abstractions\Microsoft.Framework.Localization.Abstractions.xproj", "{A1FCF259-70F6-4605-AA2D-E4B356BE771A}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{29743CFF-120E-40EB-9B89-5818425946FA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{29743CFF-120E-40EB-9B89-5818425946FA}.Debug|Any CPU.Build.0 = Debug|Any CPU
{29743CFF-120E-40EB-9B89-5818425946FA}.Release|Any CPU.ActiveCfg = Release|Any CPU
{29743CFF-120E-40EB-9B89-5818425946FA}.Release|Any CPU.Build.0 = Release|Any CPU
{23E3BC23-3464-4D9B-BF78-02CB2182BEF0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{23E3BC23-3464-4D9B-BF78-02CB2182BEF0}.Debug|Any CPU.Build.0 = Debug|Any CPU
{23E3BC23-3464-4D9B-BF78-02CB2182BEF0}.Release|Any CPU.ActiveCfg = Release|Any CPU
{23E3BC23-3464-4D9B-BF78-02CB2182BEF0}.Release|Any CPU.Build.0 = Release|Any CPU
{A1FCF259-70F6-4605-AA2D-E4B356BE771A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A1FCF259-70F6-4605-AA2D-E4B356BE771A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A1FCF259-70F6-4605-AA2D-E4B356BE771A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A1FCF259-70F6-4605-AA2D-E4B356BE771A}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{29743CFF-120E-40EB-9B89-5818425946FA} = {FB313677-BAB3-4E49-8CDB-4FA4A9564767}
{23E3BC23-3464-4D9B-BF78-02CB2182BEF0} = {FB313677-BAB3-4E49-8CDB-4FA4A9564767}
{A1FCF259-70F6-4605-AA2D-E4B356BE771A} = {FB313677-BAB3-4E49-8CDB-4FA4A9564767}
EndGlobalSection
EndGlobal

7
NuGet.Config Normal file
View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<packageSources>
<add key="AspNetVNext" value="https://www.myget.org/F/aspnetvnext/api/v2" />
<add key="NuGet" value="https://nuget.org/api/v2/" />
</packageSources>
</configuration>

9
README.md Normal file
View File

@ -0,0 +1,9 @@
Localization
==========
AppVeyor: TODO
Travis: TODO
Localization abstractions and implementations for ASP.NET 5 applications.
This project is part of ASP.NET 5. You can find samples, documentation and getting started instructions for ASP.NET 5 at the [Home](https://github.com/aspnet/home) repo.

7
appveyor.yml Normal file
View File

@ -0,0 +1,7 @@
init:
- git config --global core.autocrlf true
build_script:
- build.cmd --quiet verify
clone_depth: 1
test: off
deploy: off

28
build.cmd Normal file
View File

@ -0,0 +1,28 @@
@echo off
cd %~dp0
SETLOCAL
SET CACHED_NUGET=%LocalAppData%\NuGet\NuGet.exe
IF EXIST %CACHED_NUGET% goto copynuget
echo Downloading latest version of NuGet.exe...
IF NOT EXIST %LocalAppData%\NuGet md %LocalAppData%\NuGet
@powershell -NoProfile -ExecutionPolicy unrestricted -Command "$ProgressPreference = 'SilentlyContinue'; Invoke-WebRequest 'https://www.nuget.org/nuget.exe' -OutFile '%CACHED_NUGET%'"
:copynuget
IF EXIST .nuget\nuget.exe goto restore
md .nuget
copy %CACHED_NUGET% .nuget\nuget.exe > nul
:restore
IF EXIST packages\KoreBuild goto run
.nuget\NuGet.exe install KoreBuild -ExcludeVersion -o packages -nocache -pre
.nuget\NuGet.exe install Sake -version 0.2 -o packages -ExcludeVersion
IF "%SKIP_DNX_INSTALL%"=="1" goto run
CALL packages\KoreBuild\build\dnvm upgrade -runtime CLR -arch x86
CALL packages\KoreBuild\build\dnvm install default -runtime CoreCLR -arch x86
:run
CALL packages\KoreBuild\build\dnvm use default -runtime CLR -arch x86
packages\Sake\tools\Sake.exe -I packages\KoreBuild\build -f makefile.shade %*

39
build.sh Normal file
View File

@ -0,0 +1,39 @@
#!/bin/bash
if test `uname` = Darwin; then
cachedir=~/Library/Caches/KBuild
else
if [ -z $XDG_DATA_HOME ]; then
cachedir=$HOME/.local/share
else
cachedir=$XDG_DATA_HOME;
fi
fi
mkdir -p $cachedir
url=https://www.nuget.org/nuget.exe
if test ! -f $cachedir/nuget.exe; then
wget -O $cachedir/nuget.exe $url 2>/dev/null || curl -o $cachedir/nuget.exe --location $url /dev/null
fi
if test ! -e .nuget; then
mkdir .nuget
cp $cachedir/nuget.exe .nuget/nuget.exe
fi
if test ! -d packages/KoreBuild; then
mono .nuget/nuget.exe install KoreBuild -ExcludeVersion -o packages -nocache -pre
mono .nuget/nuget.exe install Sake -version 0.2 -o packages -ExcludeVersion
fi
if ! type dnvm > /dev/null 2>&1; then
source packages/KoreBuild/build/dnvm.sh
fi
if ! type dnx > /dev/null 2>&1; then
dnvm upgrade
fi
mono packages/Sake/tools/Sake.exe -I packages/KoreBuild/build -f makefile.shade "$@"

7
makefile.shade Normal file
View File

@ -0,0 +1,7 @@
var VERSION='0.1'
var FULL_VERSION='0.1'
var AUTHORS='Microsoft Open Technologies, Inc.'
use-standard-lifecycle
k-standard-goals

View File

@ -0,0 +1,24 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using Microsoft.AspNet.Localization;
namespace Microsoft.AspNet.Builder
{
/// <summary>
/// Extension methods for adding the <see cref="RequestLocalizationMiddleware"/> to an application.
/// </summary>
public static class IApplicationBuilderExtensions
{
/// <summary>
/// Adds the <see cref="RequestLocalizationMiddleware"/> to automatically set culture information for
/// requests based on information provided by the client.
/// </summary>
/// <param name="builder">The <see cref="IApplicationBuilder"/>.</param>
/// <returns>The <see cref="IApplicationBuilder"/>.</returns>
public static IApplicationBuilder UseRequestLocalization(this IApplicationBuilder builder)
{
return builder.UseMiddleware<RequestLocalizationMiddleware>();
}
}
}

View File

@ -0,0 +1,16 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
namespace Microsoft.AspNet.Localization
{
/// <summary>
/// Represents the feature that provides the current request's culture information.
/// </summary>
public interface IRequestCultureFeature
{
/// <summary>
/// The <see cref="Localization.RequestCulture"/> of the request.
/// </summary>
RequestCulture RequestCulture { get; }
}
}

View File

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">14.0</VisualStudioVersion>
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
</PropertyGroup>
<Import Project="$(VSToolsPath)\DNX\Microsoft.DNX.Props" Condition="'$(VSToolsPath)' != ''" />
<PropertyGroup Label="Globals">
<ProjectGuid>23e3bc23-3464-4d9b-bf78-02cb2182bef0</ProjectGuid>
<RootNamespace>Microsoft.AspNet.Localization</RootNamespace>
<BaseIntermediateOutputPath Condition="'$(BaseIntermediateOutputPath)'=='' ">..\..\artifacts\obj\$(MSBuildProjectName)</BaseIntermediateOutputPath>
<OutputPath Condition="'$(OutputPath)'=='' ">..\..\artifacts\bin\$(MSBuildProjectName)\</OutputPath>
</PropertyGroup>
<PropertyGroup>
<SchemaVersion>2.0</SchemaVersion>
</PropertyGroup>
<Import Project="$(VSToolsPath)\DNX\Microsoft.DNX.targets" Condition="'$(VSToolsPath)' != ''" />
</Project>

View File

@ -0,0 +1,46 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Globalization;
namespace Microsoft.AspNet.Localization
{
/// <summary>
/// Details about the culture for an <see cref="Http.HttpRequest"/>.
/// </summary>
public class RequestCulture
{
/// <summary>
/// Creates a new <see cref="RequestCulture"/> object has its <see cref="Culture"/> and <see cref="UICulture"/>
/// properties set to the same <see cref="CultureInfo"/> value.
/// </summary>
/// <param name="culture">The <see cref="CultureInfo"/> for the request.</param>
public RequestCulture(CultureInfo culture)
: this (culture, culture)
{
}
/// <summary>
/// Creates a new <see cref="RequestCulture"/> object has its <see cref="Culture"/> and <see cref="UICulture"/>
/// properties set to the respective <see cref="CultureInfo"/> values provided.
/// </summary>
/// <param name="culture">The <see cref="CultureInfo"/> for the request to be used for formatting.</param>
/// <param name="uiCulture">The <see cref="CultureInfo"/> for the request to be used for text, i.e. language.</param>
public RequestCulture(CultureInfo culture, CultureInfo uiCulture)
{
Culture = culture;
UICulture = uiCulture;
}
/// <summary>
/// Gets the <see cref="CultureInfo"/> for the request to be used for formatting.
/// </summary>
public CultureInfo Culture { get; }
/// <summary>
/// Gets the <see cref="CultureInfo"/> for the request to be used for text, i.e. language;
/// </summary>
public CultureInfo UICulture { get; }
}
}

View File

@ -0,0 +1,23 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
namespace Microsoft.AspNet.Localization
{
/// <summary>
/// Provides the current request's culture information.
/// </summary>
public class RequestCultureFeature : IRequestCultureFeature
{
/// <summary>
/// Creates a new <see cref="RequestCultureFeature"/> with the specified <see cref="Localization.RequestCulture"/>.
/// </summary>
/// <param name="requestCulture">The <see cref="Localization.RequestCulture"/>.</param>
public RequestCultureFeature(RequestCulture requestCulture)
{
RequestCulture = requestCulture;
}
/// <inheritdoc />
public RequestCulture RequestCulture { get; }
}
}

View File

@ -0,0 +1,80 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Globalization;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNet.Builder;
using Microsoft.AspNet.Http;
namespace Microsoft.AspNet.Localization
{
/// <summary>
/// Enables automatic setting of the culture for <see cref="Http.HttpRequest"/>s based on information
/// sent by the client in headers and logic provided by the application.
/// </summary>
public class RequestLocalizationMiddleware
{
private readonly RequestDelegate _next;
/// <summary>
/// Creates a new <see cref="RequestLocalizationMiddleware"/>.
/// </summary>
/// <param name="next">The <see cref="RequestDelegate"/> representing the next middleware in the pipeline.</param>
public RequestLocalizationMiddleware(RequestDelegate next)
{
_next = next;
}
/// <summary>
/// Invokes the logic of the middleware.
/// </summary>
/// <param name="context">The <see cref="HttpContext"/>.</param>
/// <returns>A <see cref="Task"/> that completes when the middleware has completed processing.</returns>
public async Task Invoke(HttpContext context)
{
// TODO: Make this read from Accept-Language header, cookie, app-provided delegate, etc.
if (context.Request.QueryString.HasValue)
{
var queryCulture = context.Request.Query["culture"];
if (!string.IsNullOrEmpty(queryCulture))
{
var requestCulture = new RequestCulture(new CultureInfo(queryCulture));
context.SetFeature<IRequestCultureFeature>(new RequestCultureFeature(requestCulture));
var originalCulture = CultureInfo.CurrentCulture;
var originalUICulture = CultureInfo.CurrentUICulture;
SetCurrentCulture(requestCulture);
await _next(context);
return;
}
}
else
{
// NOTE: The below doesn't seem to be needed anymore now that DNX is correctly managing culture across
// async calls but we'll need to verify properly.
// Forcibly set thread to en-US as sometimes previous threads have wrong culture across async calls,
// see note above.
//var defaultRequestCulture = new RequestCulture(new CultureInfo("en-US"));
//SetCurrentCulture(defaultRequestCulture);
}
await _next(context);
}
private void SetCurrentCulture(RequestCulture requestCulture)
{
#if DNX451
Thread.CurrentThread.CurrentCulture = requestCulture.Culture;
Thread.CurrentThread.CurrentUICulture = requestCulture.UICulture;
#else
CultureInfo.CurrentCulture = requestCulture.Culture;
CultureInfo.CurrentUICulture = requestCulture.UICulture;
#endif
}
}
}

View File

@ -0,0 +1,21 @@
{
"version": "1.0.0-*",
"description": "",
"dependencies": {
"Microsoft.Framework.Localization.Abstractions": "1.0.0-*",
"Microsoft.AspNet.Http.Extensions": "1.0.0-*"
},
"frameworks": {
"dnx451": { },
"dnxcore50": {
"dependencies": {
"System.Collections": "4.0.10-*",
"System.Linq": "4.0.0-*",
"System.Threading": "4.0.10-*",
"Microsoft.CSharp": "4.0.0-*"
}
}
}
}

View File

@ -0,0 +1,51 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Collections.Generic;
using System.Globalization;
namespace Microsoft.Framework.Localization
{
/// <summary>
/// Represents a service that provides localized strings.
/// </summary>
public interface IStringLocalizer : IEnumerable<LocalizedString>
{
/// <summary>
/// Gets the string resource with the given name.
/// </summary>
/// <param name="name">The name of the string resource.</param>
/// <returns>The string resource as a <see cref="LocalizedString"/>.</returns>
LocalizedString this[string name] { get; }
/// <summary>
/// Gets the string resource with the given name and formatted with the supplied arguments.
/// </summary>
/// <param name="name">The name of the string resource.</param>
/// <param name="arguments">The values to format the string with.</param>
/// <returns>The formatted string resource as a <see cref="LocalizedString"/>.</returns>
LocalizedString this[string name, params object[] arguments] { get; }
/// <summary>
/// Gets the string resource with the given name.
/// </summary>
/// <param name="name">The name of the string resource.</param>
/// <returns>The string resource as a <see cref="LocalizedString"/>.</returns>
LocalizedString GetString(string name);
/// <summary>
/// Gets the string resource with the given name and formatted with the supplied arguments.
/// </summary>
/// <param name="name">The name of the string resource.</param>
/// <param name="arguments">The values to format the string with.</param>
/// <returns>The formatted string resource as a <see cref="LocalizedString"/>.</returns>
LocalizedString GetString(string name, params object[] arguments);
/// <summary>
/// Creates a new <see cref="ResourceManagerStringLocalizer"/> for a specific <see cref="CultureInfo"/>.
/// </summary>
/// <param name="culture">The <see cref="CultureInfo"/> to use.</param>
/// <returns>A culture-specific <see cref="IStringLocalizer"/>.</returns>
IStringLocalizer WithCulture(CultureInfo culture);
}
}

View File

@ -0,0 +1,29 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
namespace Microsoft.Framework.Localization
{
/// <summary>
/// Represents a factory that creates <see cref="IStringLocalizer"/> instances.
/// </summary>
public interface IStringLocalizerFactory
{
/// <summary>
/// Creates an <see cref="IStringLocalizer"/> using the <see cref="System.Reflection.Assembly"/> and
/// <see cref="Type.FullName"/> of the specified <see cref="Type"/>.
/// </summary>
/// <param name="resourceSource">The <see cref="Type"/>.</param>
/// <returns>The <see cref="IStringLocalizer"/>.</returns>
IStringLocalizer Create(Type resourceSource);
/// <summary>
/// Creates an <see cref="IStringLocalizer"/>.
/// </summary>
/// <param name="baseName">The base name of the resource to load strings from.</param>
/// <param name="location">The location to load resources from.</param>
/// <returns>The <see cref="IStringLocalizer"/>.</returns>
IStringLocalizer Create(string baseName, string location);
}
}

View File

@ -0,0 +1,16 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
namespace Microsoft.Framework.Localization
{
/// <summary>
/// Represents an <see cref="IStringLocalizer"/> that provides strings for <see cref="T"/>.
/// </summary>
/// <typeparam name="T">The <see cref="Type"/> to provide strings for.</typeparam>
public interface IStringLocalizer<T> : IStringLocalizer
{
}
}

View File

@ -0,0 +1,61 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
namespace Microsoft.Framework.Localization
{
/// <summary>
/// A locale specific string.
/// </summary>
public struct LocalizedString
{
/// <summary>
/// Creates a new <see cref="LocalizedString"/>.
/// </summary>
/// <param name="name">The name of the string in the resource it was loaded from.</param>
/// <param name="value">The actual string.</param>
public LocalizedString(string name, string value)
: this(name, value, resourceNotFound: false)
{
}
/// <summary>
/// Creates a new <see cref="LocalizedString"/>.
/// </summary>
/// <param name="name">The name of the string in the resource it was loaded from.</param>
/// <param name="value">The actual string.</param>
/// <param name="resourceNotFound">Whether the string was found in a resource. Set this to <c>false</c> to indicate an alternate string value was used.</param>
public LocalizedString(string name, string value, bool resourceNotFound)
{
Name = name;
Value = value;
ResourceNotFound = resourceNotFound;
}
public static implicit operator string (LocalizedString localizedString)
{
return localizedString.Value;
}
/// <summary>
/// The name of the string in the resource it was loaded from.
/// </summary>
public string Name { get; }
/// <summary>
/// The actual string.
/// </summary>
public string Value { get; }
/// <summary>
/// Whether the string was found in a resource. If <c>false</c>, an alternate string value was used.
/// </summary>
public bool ResourceNotFound { get; }
/// <summary>
/// Returns the actual string.
/// </summary>
/// <returns>The actual string.</returns>
public override string ToString() => Value;
}
}

View File

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">14.0</VisualStudioVersion>
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
</PropertyGroup>
<Import Project="$(VSToolsPath)\DNX\Microsoft.DNX.Props" Condition="'$(VSToolsPath)' != ''" />
<PropertyGroup Label="Globals">
<ProjectGuid>a1fcf259-70f6-4605-aa2d-e4b356be771a</ProjectGuid>
<RootNamespace>Microsoft.Framework.Localization.Abstractions</RootNamespace>
<BaseIntermediateOutputPath Condition="'$(BaseIntermediateOutputPath)'=='' ">..\..\artifacts\obj\$(MSBuildProjectName)</BaseIntermediateOutputPath>
<OutputPath Condition="'$(OutputPath)'=='' ">..\..\artifacts\bin\$(MSBuildProjectName)\</OutputPath>
</PropertyGroup>
<PropertyGroup>
<SchemaVersion>2.0</SchemaVersion>
</PropertyGroup>
<Import Project="$(VSToolsPath)\DNX\Microsoft.DNX.targets" Condition="'$(VSToolsPath)' != ''" />
</Project>

View File

@ -0,0 +1,48 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
namespace Microsoft.Framework.Localization
{
/// <summary>
/// Provides strings for <see cref="TResourceSource"/>.
/// </summary>
/// <typeparam name="TResourceSource">The <see cref="System.Type"/> to provide strings for.</typeparam>
public class StringLocalizer<TResourceSource> : IStringLocalizer<TResourceSource>
{
private IStringLocalizer _localizer;
/// <summary>
/// Creates a new <see cref="StringLocalizer{TResourceSource}"/>.
/// </summary>
/// <param name="factory">The <see cref="IStringLocalizerFactory"/> to use.</param>
public StringLocalizer(IStringLocalizerFactory factory)
{
_localizer = factory.Create(typeof(TResourceSource));
}
/// <inheritdoc />
public virtual IStringLocalizer WithCulture(CultureInfo culture) => _localizer.WithCulture(culture);
/// <inheritdoc />
public virtual LocalizedString this[string key] => _localizer[key];
/// <inheritdoc />
public virtual LocalizedString this[string key, params object[] arguments] => _localizer[key, arguments];
/// <inheritdoc />
public virtual LocalizedString GetString(string key) => _localizer.GetString(key);
/// <inheritdoc />
public virtual LocalizedString GetString(string key, params object[] arguments) =>
_localizer.GetString(key, arguments);
/// <inheritdoc />
public IEnumerator<LocalizedString> GetEnumerator() => _localizer.GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => _localizer.GetEnumerator();
}
}

View File

@ -0,0 +1,21 @@
{
"version": "1.0.0-*",
"description": "Abstractions of application localization services.",
"dependencies": {
},
"frameworks": {
"dnx451": { },
"dnxcore50": {
"dependencies": {
"System.Collections": "4.0.10-beta-*",
"System.Globalization": "4.0.10-beta-*",
"System.Reflection": "4.0.10-beta-*",
"System.Runtime": "4.0.20-beta-22904",
"Microsoft.CSharp": "4.0.0-beta-*"
}
}
}
}

View File

@ -0,0 +1,32 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using Microsoft.Framework.Localization;
namespace Microsoft.Framework.DependencyInjection
{
/// <summary>
/// Extension methods for adding localization servics to the DI container.
/// </summary>
public static class LocalizationServiceCollectionExtensions
{
/// <summary>
/// Adds services required for application localization.
/// </summary>
/// <param name="services">The <see cref="IServiceCollection"/> to add the services to.</param>
/// <returns>The <see cref="IServiceCollection"/>.</returns>
public static IServiceCollection AddLocalization(this IServiceCollection services)
{
services.TryAdd(new ServiceDescriptor(
typeof(IStringLocalizerFactory),
typeof(ResourceManagerStringLocalizerFactory),
ServiceLifetime.Singleton));
services.TryAdd(new ServiceDescriptor(
typeof(IStringLocalizer<>),
typeof(StringLocalizer<>),
ServiceLifetime.Transient));
return services;
}
}
}

View File

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">14.0</VisualStudioVersion>
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
</PropertyGroup>
<Import Project="$(VSToolsPath)\DNX\Microsoft.DNX.Props" Condition="'$(VSToolsPath)' != ''" />
<PropertyGroup Label="Globals">
<ProjectGuid>29743cff-120e-40eb-9b89-5818425946fa</ProjectGuid>
<RootNamespace>Microsoft.Framework.Localization</RootNamespace>
<BaseIntermediateOutputPath Condition="'$(BaseIntermediateOutputPath)'=='' ">..\..\artifacts\obj\$(MSBuildProjectName)</BaseIntermediateOutputPath>
<OutputPath Condition="'$(OutputPath)'=='' ">..\..\artifacts\bin\$(MSBuildProjectName)\</OutputPath>
</PropertyGroup>
<PropertyGroup>
<SchemaVersion>2.0</SchemaVersion>
</PropertyGroup>
<Import Project="$(VSToolsPath)\DNX\Microsoft.DNX.targets" Condition="'$(VSToolsPath)' != ''" />
</Project>

View File

@ -0,0 +1,226 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Globalization;
using System.Reflection;
using System.Resources;
namespace Microsoft.Framework.Localization
{
/// <summary>
/// An <see cref="IStringLocalizer"/> that uses the <see cref="System.Resources.ResourceManager"/> and
/// <see cref="System.Resources.ResourceReader"/> to provide localized strings.
/// </summary>
public class ResourceManagerStringLocalizer : IStringLocalizer
{
private readonly ConcurrentDictionary<MissingManifestCacheKey, object> _missingManifestCache =
new ConcurrentDictionary<MissingManifestCacheKey, object>();
/// <summary>
/// Creates a new <see cref="ResourceManagerStringLocalizer"/>.
/// </summary>
/// <param name="resourceManager">The <see cref="System.Resources.ResourceManager"/> to read strings from.</param>
/// <param name="resourceAssembly">The <see cref="Assembly"/> that contains the strings as embedded resources.</param>
/// <param name="baseName">The base name of the embedded resource in the <see cref="Assembly"/> that contains the strings.</param>
public ResourceManagerStringLocalizer(
ResourceManager resourceManager,
Assembly resourceAssembly,
string baseName)
{
ResourceManager = resourceManager;
ResourceAssembly = resourceAssembly;
ResourceBaseName = baseName;
}
/// <summary>
/// The <see cref="System.Resources.ResourceManager"/> to read strings from.
/// </summary>
protected ResourceManager ResourceManager { get; }
/// <summary>
/// The <see cref="Assembly"/> that contains the strings as embedded resources.
/// </summary>
protected Assembly ResourceAssembly { get; }
/// <summary>
/// The base name of the embedded resource in the <see cref="Assembly"/> that contains the strings.
/// </summary>
protected string ResourceBaseName { get; }
/// <inheritdoc />
public virtual LocalizedString this[string name] => GetString(name);
/// <inheritdoc />
public virtual LocalizedString this[string name, params object[] arguments] => GetString(name, arguments);
/// <inheritdoc />
public virtual LocalizedString GetString(string name)
{
var value = GetStringSafely(name, null);
return new LocalizedString(name, value ?? name, resourceNotFound: value == null);
}
/// <inheritdoc />
public virtual LocalizedString GetString(string name, params object[] arguments)
{
var format = GetStringSafely(name, null);
var value = string.Format(format ?? name, arguments);
return new LocalizedString(name, value, resourceNotFound: format == null);
}
/// <summary>
/// Creates a new <see cref="ResourceManagerStringLocalizer"/> for a specific <see cref="CultureInfo"/>.
/// </summary>
/// <param name="culture">The <see cref="CultureInfo"/> to use.</param>
/// <returns>A culture-specific <see cref="ResourceManagerStringLocalizer"/>.</returns>
public IStringLocalizer WithCulture(CultureInfo culture)
{
return culture == null
? new ResourceManagerStringLocalizer(ResourceManager, ResourceAssembly, ResourceBaseName)
: new ResourceManagerWithCultureStringLocalizer(
ResourceManager,
ResourceAssembly,
ResourceBaseName,
culture);
}
/// <summary>
/// Gets a resource string from the <see cref="ResourceManager"/> and returns <c>null</c> instead of
/// throwing exceptions if a match isn't found.
/// </summary>
/// <param name="name">The name of the string resource.</param>
/// <param name="culture">The <see cref="CultureInfo"/> to get the string for.</param>
/// <returns>The resource string, or <c>null</c> if none was found.</returns>
protected string GetStringSafely(string name, CultureInfo culture)
{
var cacheKey = new MissingManifestCacheKey(name, culture);
if (_missingManifestCache.ContainsKey(cacheKey))
{
return null;
}
try
{
return culture == null ? ResourceManager.GetString(name) : ResourceManager.GetString(name, culture);
}
catch (MissingManifestResourceException)
{
_missingManifestCache.TryAdd(cacheKey, null);
return null;
}
}
/// <summary>
/// Returns an <see cref="IEnumerator{LocalizedString}"/> for all strings in the current culture.
/// </summary>
/// <returns>The <see cref="IEnumerator{LocalizedString}"/>.</returns>
public virtual IEnumerator<LocalizedString> GetEnumerator()
{
return GetEnumerator(CultureInfo.CurrentUICulture);
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
/// <summary>
/// Returns an <see cref="IEnumerator{LocalizedString}"/> for all strings in the specified culture.
/// </summary>
/// <param name="culture">The <see cref="CultureInfo"/> to get strings for.</param>
/// <returns>The <see cref="IEnumerator{LocalizedString}"/>.</returns>
protected IEnumerator<LocalizedString> GetEnumerator(CultureInfo culture)
{
// TODO: I'm sure something here should be cached, probably the whole result
var resourceNames = GetResourceNamesFromCultureHierarchy(culture);
foreach (var name in resourceNames)
{
var value = GetStringSafely(name, culture);
yield return new LocalizedString(name, value ?? name, resourceNotFound: value == null);
}
}
private IEnumerable<string> GetResourceNamesFromCultureHierarchy(CultureInfo startingCulture)
{
var currentCulture = startingCulture;
var resourceNames = new HashSet<string>();
while (true)
{
try
{
var resourceStreamName = ResourceBaseName;
if (!string.IsNullOrEmpty(currentCulture.Name))
{
resourceStreamName += "." + currentCulture.Name;
}
resourceStreamName += ".resources";
using (var cultureResourceStream = ResourceAssembly.GetManifestResourceStream(resourceStreamName))
using (var resources = new ResourceReader(cultureResourceStream))
{
foreach (DictionaryEntry entry in resources)
{
var resourceName = (string)entry.Key;
resourceNames.Add(resourceName);
}
}
}
catch (MissingManifestResourceException) { }
if (currentCulture == currentCulture.Parent)
{
// currentCulture begat currentCulture, probably time to leave
break;
}
currentCulture = currentCulture.Parent;
}
return resourceNames;
}
private class MissingManifestCacheKey : IEquatable<MissingManifestCacheKey>
{
private readonly int _hashCode;
public MissingManifestCacheKey(string name, CultureInfo culture)
{
Name = name;
CultureInfo = culture;
_hashCode = new { Name, CultureInfo }.GetHashCode();
}
public string Name { get; }
public CultureInfo CultureInfo { get; }
public bool Equals(MissingManifestCacheKey other)
{
return string.Equals(Name, other.Name, StringComparison.Ordinal)
&& CultureInfo == other.CultureInfo;
}
public override bool Equals(object obj)
{
var other = obj as MissingManifestCacheKey;
if (other != null)
{
return Equals(other);
}
return base.Equals(obj);
}
public override int GetHashCode()
{
return _hashCode;
}
}
}
}

View File

@ -0,0 +1,54 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Reflection;
using System.Resources;
using Microsoft.Framework.Runtime;
namespace Microsoft.Framework.Localization
{
/// <summary>
/// An <see cref="IStringLocalizerFactory"/> that creates instances of <see cref="ResourceManagerStringLocalizer"/>.
/// </summary>
public class ResourceManagerStringLocalizerFactory : IStringLocalizerFactory
{
private readonly IApplicationEnvironment _applicationEnvironment;
/// <summary>
/// Creates a new <see cref="ResourceManagerStringLocalizer"/>.
/// </summary>
/// <param name="applicationEnvironment"></param>
public ResourceManagerStringLocalizerFactory(IApplicationEnvironment applicationEnvironment)
{
_applicationEnvironment = applicationEnvironment;
}
/// <summary>
/// Creates a <see cref="ResourceManagerStringLocalizer"/> using the <see cref="Assembly"/> and
/// <see cref="Type.FullName"/> of the specified <see cref="Type"/>.
/// </summary>
/// <param name="resourceSource">The <see cref="Type"/>.</param>
/// <returns>The <see cref="ResourceManagerStringLocalizer"/>.</returns>
public IStringLocalizer Create(Type resourceSource)
{
var typeInfo = resourceSource.GetTypeInfo();
var assembly = typeInfo.Assembly;
var baseName = typeInfo.FullName;
return new ResourceManagerStringLocalizer(new ResourceManager(resourceSource), assembly, baseName);
}
/// <summary>
/// Creates a <see cref="ResourceManagerStringLocalizer"/>.
/// </summary>
/// <param name="baseName">The base name of the resource to load strings from.</param>
/// <param name="location">The location to load resources from.</param>
/// <returns>The <see cref="ResourceManagerStringLocalizer"/>.</returns>
public IStringLocalizer Create(string baseName, string location)
{
var assembly = Assembly.Load(new AssemblyName(location ?? _applicationEnvironment.ApplicationName));
return new ResourceManagerStringLocalizer(new ResourceManager(baseName, assembly), assembly, baseName);
}
}
}

View File

@ -0,0 +1,64 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Reflection;
using System.Resources;
namespace Microsoft.Framework.Localization
{
/// <summary>
/// An <see cref="IStringLocalizer"/> that uses the <see cref="System.Resources.ResourceManager"/> and
/// <see cref="System.Resources.ResourceReader"/> to provide localized strings for a specific <see cref="CultureInfo"/>.
/// </summary>
public class ResourceManagerWithCultureStringLocalizer : ResourceManagerStringLocalizer
{
private readonly CultureInfo _culture;
/// <summary>
/// Creates a new <see cref="ResourceManagerWithCultureStringLocalizer"/>.
/// </summary>
/// <param name="resourceManager">The <see cref="System.Resources.ResourceManager"/> to read strings from.</param>
/// <param name="resourceAssembly">The <see cref="Assembly"/> that contains the strings as embedded resources.</param>
/// <param name="baseName">The base name of the embedded resource in the <see cref="Assembly"/> that contains the strings.</param>
/// <param name="culture">The specific <see cref="CultureInfo"/> to use.</param>
public ResourceManagerWithCultureStringLocalizer(
ResourceManager resourceManager,
Assembly assembly,
string baseName,
CultureInfo culture)
: base(resourceManager, assembly, baseName)
{
_culture = culture;
}
/// <inheritdoc />
public override LocalizedString this[string name] => GetString(name);
/// <inheritdoc />
public override LocalizedString this[string name, params object[] arguments] => GetString(name, arguments);
/// <inheritdoc />
public override LocalizedString GetString(string name)
{
var value = GetStringSafely(name, _culture);
return new LocalizedString(name, value ?? name);
}
/// <inheritdoc />
public override LocalizedString GetString(string name, params object[] arguments)
{
var format = GetStringSafely(name, _culture);
var value = string.Format(_culture, format ?? name, arguments);
return new LocalizedString(name, value ?? name, resourceNotFound: format == null);
}
/// <inheritdoc />
public override IEnumerator<LocalizedString> GetEnumerator()
{
return GetEnumerator(_culture);
}
}
}

View File

@ -0,0 +1,26 @@
{
"version": "1.0.0-*",
"description": "",
"dependencies": {
"Microsoft.Framework.DependencyInjection.Abstractions": "1.0.0-*",
"Microsoft.Framework.Localization.Abstractions": "1.0.0-*",
"Microsoft.Framework.Runtime.Abstractions": "1.0.0-*",
"System.Resources.ReaderWriter": "4.0.0-*"
},
"frameworks": {
"dnx451": { },
"dnxcore50": {
"dependencies": {
"System.Collections": "4.0.10-*",
"System.Collections.Concurrent": "4.0.10-*",
"System.Globalization": "4.0.10-*",
"System.Linq": "4.0.0-*",
"System.Resources.ResourceManager": "4.0.0-*",
"System.Threading": "4.0.10-*",
"Microsoft.CSharp": "4.0.0-*"
}
}
}
}