Bunch of changes:

- Implemented pluggable strategies for determining request culture
- Added NotNull on public APIs
- Added support for a default request culture
- Added options class for configuring the middleware
- Improved the query string logic to support separate formatting & language cultures
- Implemented the logic for accept-language header
- Added more doc comments
This commit is contained in:
damianedwards 2015-05-06 16:50:43 -07:00
parent 5795d331bc
commit b11f7d51c3
21 changed files with 396 additions and 64 deletions

View File

@ -0,0 +1,67 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Globalization;
using System.Linq;
using Microsoft.AspNet.Http;
using Microsoft.AspNet.Localization.Internal;
using Microsoft.Framework.Internal;
using Microsoft.Net.Http.Headers;
namespace Microsoft.AspNet.Localization
{
/// <summary>
/// Determines the culture information for a request via the value of the Accept-Language header.
/// </summary>
/// <remarks>
///
/// </remarks>
public class AcceptLanguageHeaderRequestCultureStrategy : IRequestCultureStrategy
{
/// <summary>
/// The maximum number of values in the Accept-Language header to attempt to create a <see cref="CultureInfo"/>
/// from for the current request.
/// Defaults to <c>3</c>.
/// </summary>
public int MaximumAcceptLanguageHeaderValuesToTry { get; set; } = 3;
/// <inheritdoc />
public RequestCulture DetermineRequestCulture([NotNull] HttpContext httpContext)
{
var acceptLanguageHeader = httpContext.Request.GetTypedHeaders().AcceptLanguage;
if (acceptLanguageHeader == null || acceptLanguageHeader.Count == 0)
{
return null;
}
var languages = acceptLanguageHeader.AsEnumerable();
if (MaximumAcceptLanguageHeaderValuesToTry > 0)
{
// We take only the first configured number of languages from the header and then order those that we
// attempt to parse as a CultureInfo to mitigate potentially spinning CPU on lots of parse attempts.
languages = languages.Take(MaximumAcceptLanguageHeaderValuesToTry);
}
var orderedLanguages = languages.OrderByDescending(h => h, StringWithQualityHeaderValueComparer.QualityComparer)
.ToList();
foreach (var language in orderedLanguages)
{
// Allow empty string values as they map to InvariantCulture, whereas null culture values will throw in
// the CultureInfo ctor
if (language.Value != null)
{
try
{
return new RequestCulture(CultureUtilities.GetCultureFromName(language.Value));
}
catch (CultureNotFoundException) { }
}
}
return null;
}
}
}

View File

@ -0,0 +1,18 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using Microsoft.AspNet.Http;
using Microsoft.Framework.Internal;
namespace Microsoft.AspNet.Localization
{
public class CookieRequestCultureStrategy : IRequestCultureStrategy
{
public RequestCulture DetermineRequestCulture([NotNull] HttpContext httpContext)
{
// TODO
return null;
}
}
}

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 System;
using Microsoft.AspNet.Http;
using Microsoft.Framework.Internal;
namespace Microsoft.AspNet.Localization
{
public class CustomRequestCultureStrategy : IRequestCultureStrategy
{
private readonly Func<HttpContext, RequestCulture> _strategy;
public CustomRequestCultureStrategy([NotNull] Func<HttpContext, RequestCulture> strategy)
{
_strategy = strategy;
}
public RequestCulture DetermineRequestCulture([NotNull] HttpContext httpContext)
{
return _strategy(httpContext);
}
}
}

View File

@ -2,6 +2,7 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using Microsoft.AspNet.Localization;
using Microsoft.Framework.Internal;
namespace Microsoft.AspNet.Builder
{
@ -12,13 +13,29 @@ namespace Microsoft.AspNet.Builder
{
/// <summary>
/// Adds the <see cref="RequestLocalizationMiddleware"/> to automatically set culture information for
/// requests based on information provided by the client.
/// requests based on information provided by the client using the default options.
/// </summary>
/// <param name="builder">The <see cref="IApplicationBuilder"/>.</param>
/// <returns>The <see cref="IApplicationBuilder"/>.</returns>
public static IApplicationBuilder UseRequestLocalization(this IApplicationBuilder builder)
public static IApplicationBuilder UseRequestLocalization([NotNull] this IApplicationBuilder builder)
{
return builder.UseMiddleware<RequestLocalizationMiddleware>();
var options = new RequestLocalizationMiddlewareOptions();
return UseRequestLocalization(builder, options);
}
/// <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>
/// <param name="options">The options to configure the middleware with.</param>
/// <returns>The <see cref="IApplicationBuilder"/>.</returns>
public static IApplicationBuilder UseRequestLocalization(
[NotNull] this IApplicationBuilder builder,
[NotNull] RequestLocalizationMiddlewareOptions options)
{
return builder.UseMiddleware<RequestLocalizationMiddleware>(options);
}
}
}

View File

@ -12,5 +12,12 @@ namespace Microsoft.AspNet.Localization
/// The <see cref="Localization.RequestCulture"/> of the request.
/// </summary>
RequestCulture RequestCulture { get; }
/// <summary>
/// The <see cref="IRequestCultureStrategy"/> that determined the request's culture information.
/// If the value is <c>null</c> then no strategy was used and the request's culture was set to the value of
/// <see cref="RequestLocalizationMiddlewareOptions.DefaultRequestCulture"/>.
/// </summary>
IRequestCultureStrategy Strategy { get; }
}
}

View File

@ -0,0 +1,12 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using Microsoft.AspNet.Http;
namespace Microsoft.AspNet.Localization
{
public interface IRequestCultureStrategy
{
RequestCulture DetermineRequestCulture(HttpContext httpContext);
}
}

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.Globalization;
namespace Microsoft.AspNet.Localization.Internal
{
public static class CultureUtilities
{
public static CultureInfo GetCultureFromName(string cultureName)
{
// Allow empty string values as they map to InvariantCulture, whereas null culture values will throw in
// the CultureInfo ctor
if (cultureName == null)
{
return null;
}
try
{
return new CultureInfo(cultureName);
}
catch (CultureNotFoundException)
{
return null;
}
}
}
}

View File

@ -0,0 +1,67 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using Microsoft.AspNet.Http;
using Microsoft.AspNet.Localization.Internal;
using Microsoft.Framework.Internal;
namespace Microsoft.AspNet.Localization
{
/// <summary>
/// Determines the culture information for a request via values in the query string.
/// </summary>
public class QueryStringRequestCultureStrategy : IRequestCultureStrategy
{
/// <summary>
/// The key that contains the culture name.
/// Defaults to "culture".
/// </summary>
public string QueryStringKey { get; set; } = "culture";
/// <summary>
/// The key that contains the UI culture name. If not specified or no value is found,
/// <see cref="QueryStringKey"/> will be used.
/// Defaults to "ui-culture".
/// </summary>
public string UIQueryStringKey { get; set; } = "ui-culture";
/// <inheritdoc />
public RequestCulture DetermineRequestCulture([NotNull] HttpContext httpContext)
{
var request = httpContext.Request;
if (!request.QueryString.HasValue)
{
return null;
}
string queryCulture = null;
string queryUICulture = null;
if (!string.IsNullOrWhiteSpace(QueryStringKey))
{
queryCulture = request.Query[QueryStringKey];
}
if (!string.IsNullOrWhiteSpace(UIQueryStringKey))
{
queryUICulture = request.Query[UIQueryStringKey];
}
if (queryCulture == null && queryUICulture == null)
{
// No values specified for either so no match
return null;
}
if (queryCulture != null && queryUICulture == null)
{
// Value for culture but not for UI culture so default to culture value for both
queryUICulture = queryCulture;
}
return new RequestCulture(
CultureUtilities.GetCultureFromName(queryCulture),
CultureUtilities.GetCultureFromName(queryUICulture));
}
}
}

View File

@ -2,6 +2,7 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Globalization;
using Microsoft.Framework.Internal;
namespace Microsoft.AspNet.Localization
{
@ -15,7 +16,7 @@ namespace Microsoft.AspNet.Localization
/// properties set to the same <see cref="CultureInfo"/> value.
/// </summary>
/// <param name="culture">The <see cref="CultureInfo"/> for the request.</param>
public RequestCulture(CultureInfo culture)
public RequestCulture([NotNull] CultureInfo culture)
: this (culture, culture)
{
@ -27,7 +28,7 @@ namespace Microsoft.AspNet.Localization
/// </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)
public RequestCulture([NotNull] CultureInfo culture, [NotNull] CultureInfo uiCulture)
{
Culture = culture;
UICulture = uiCulture;

View File

@ -1,6 +1,8 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using Microsoft.Framework.Internal;
namespace Microsoft.AspNet.Localization
{
/// <summary>
@ -12,12 +14,16 @@ namespace Microsoft.AspNet.Localization
/// 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)
public RequestCultureFeature([NotNull] RequestCulture requestCulture, IRequestCultureStrategy strategy)
{
RequestCulture = requestCulture;
Strategy = strategy;
}
/// <inheritdoc />
public RequestCulture RequestCulture { get; }
/// <inheritdoc />
public IRequestCultureStrategy Strategy { get; }
}
}

View File

@ -6,6 +6,7 @@ using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNet.Builder;
using Microsoft.AspNet.Http;
using Microsoft.Framework.Internal;
namespace Microsoft.AspNet.Localization
{
@ -16,14 +17,17 @@ namespace Microsoft.AspNet.Localization
public class RequestLocalizationMiddleware
{
private readonly RequestDelegate _next;
private readonly RequestLocalizationMiddlewareOptions _options;
/// <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)
/// <param name="options"></param>
public RequestLocalizationMiddleware([NotNull] RequestDelegate next, [NotNull] RequestLocalizationMiddlewareOptions options)
{
_next = next;
_options = options;
}
/// <summary>
@ -31,42 +35,35 @@ namespace Microsoft.AspNet.Localization
/// </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)
public async Task Invoke([NotNull] HttpContext context)
{
// TODO: Make this read from Accept-Language header, cookie, app-provided delegate, etc.
if (context.Request.QueryString.HasValue)
var requestCulture = _options.DefaultRequestCulture ??
new RequestCulture(CultureInfo.DefaultThreadCurrentCulture, CultureInfo.DefaultThreadCurrentUICulture);
IRequestCultureStrategy winningStrategy = null;
if (_options.RequestCultureStrategies != null)
{
var queryCulture = context.Request.Query["culture"];
if (!string.IsNullOrEmpty(queryCulture))
foreach (var strategy in _options.RequestCultureStrategies)
{
var requestCulture = new RequestCulture(new CultureInfo(queryCulture));
context.SetFeature<IRequestCultureFeature>(new RequestCultureFeature(requestCulture));
var originalCulture = CultureInfo.CurrentCulture;
var originalUICulture = CultureInfo.CurrentUICulture;
SetCurrentCulture(requestCulture);
await _next(context);
return;
var result = strategy.DetermineRequestCulture(context);
if (result != null)
{
requestCulture = result;
winningStrategy = strategy;
break;
}
}
}
else
{
// NOTE: The below doesn't seem to be needed anymore now that DNX is correctly managing culture across
// async calls but we'll need to verify properly.
// Forcibly set thread to en-US as sometimes previous threads have wrong culture across async calls,
// see note above.
//var defaultRequestCulture = new RequestCulture(new CultureInfo("en-US"));
//SetCurrentCulture(defaultRequestCulture);
}
context.SetFeature<IRequestCultureFeature>(new RequestCultureFeature(requestCulture, winningStrategy));
SetCurrentThreadCulture(requestCulture);
await _next(context);
}
private void SetCurrentCulture(RequestCulture requestCulture)
private static void SetCurrentThreadCulture(RequestCulture requestCulture)
{
#if DNX451
Thread.CurrentThread.CurrentCulture = requestCulture.Culture;

View File

@ -0,0 +1,76 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
namespace Microsoft.AspNet.Localization
{
/// <summary>
/// Specifies options for the <see cref="RequestLocalizationMiddleware"/>.
/// </summary>
public class RequestLocalizationMiddlewareOptions
{
/// <summary>
/// Creates a new <see cref="RequestLocalizationMiddlewareOptions"/> with default values.
/// </summary>
public RequestLocalizationMiddlewareOptions()
{
DefaultRequestCulture = new RequestCulture(CultureInfo.DefaultThreadCurrentCulture, CultureInfo.DefaultThreadCurrentUICulture);
RequestCultureStrategies = new List<IRequestCultureStrategy>
{
new QueryStringRequestCultureStrategy(),
new CookieRequestCultureStrategy(),
new AcceptLanguageHeaderRequestCultureStrategy { MaximumAcceptLanguageHeaderValuesToTry = MaximumAcceptLanguageHeaderValuesToTry }
};
}
/// <summary>
/// The maximum number of values in the Accept-Language header to attempt to create a <see cref="CultureInfo"/>
/// from for the current request.
/// Defaults to <c>3</c>.
/// </summary>
public int MaximumAcceptLanguageHeaderValuesToTry { get; set; } = 3;
/// <summary>
/// The default <see cref="RequestCulture"/> to use. This value will be used if none of the configured
/// <see cref="IRequestCultureStrategy"/> options result in a non-<c>null</c> result.
/// Defaults to <see cref="RequestCulture.Culture"/> set to <see cref="CultureInfo.DefaultThreadCurrentCulture"/>
/// and <see cref="RequestCulture.UICulture"/> set to <see cref="CultureInfo.DefaultThreadCurrentUICulture"/>.
/// </summary>
public RequestCulture DefaultRequestCulture { get; set; }
/// <summary>
/// The cultures supported by the application. If this value is non-<c>null</c>, the
/// <see cref="RequestLocalizationMiddleware"/> will only set the current request culture to an entry in this
/// list. A value of <c>null</c> means all cultures are supported.
/// Defaults to <c>null</c>.
/// </summary>
[SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly", Justification = "Improves usability")]
public IList<CultureInfo> SupportedCultures { get; set; }
/// <summary>
/// The UI cultures supported by the application. If this value is non-<c>null</c>, the
/// <see cref="RequestLocalizationMiddleware"/> will only set the current request culture to an entry in this
/// list. A value of <c>null</c> means all cultures are supported.
/// Defaults to <c>null</c>.
/// </summary>
[SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly", Justification = "Improves usability")]
public IList<CultureInfo> SupportedUICultures { get; set; }
/// <summary>
/// An ordered list of strategies used to determine a request's culture information. The first strategy that
/// returns a non-<c>null</c> result for a given request will be used.
/// Defaults to the following:
/// <list type="number">
/// <item><description><see cref="QueryStringRequestCultureStrategy"/></description></item>
/// <item><description><see cref="CookieRequestCultureStrategy"/></description></item>
/// <item><description><see cref="AcceptLanguageHeaderRequestCultureStrategy"/></description></item>
/// </list>
/// </summary>
[SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly", Justification = "Improves usability")]
public IList<IRequestCultureStrategy> RequestCultureStrategies { get; set; }
}
}

View File

@ -4,6 +4,7 @@
"dependencies": {
"Microsoft.Framework.Localization.Abstractions": "1.0.0-*",
"Microsoft.Framework.NotNullAttribute.Internal": { "type": "build", "version": "1.0.0-*" },
"Microsoft.AspNet.Http.Extensions": "1.0.0-*"
},
@ -13,6 +14,7 @@
"dependencies": {
"System.Collections": "4.0.10-*",
"System.Linq": "4.0.0-*",
"System.Globalization": "4.0.10-*",
"System.Threading": "4.0.10-*",
"Microsoft.CSharp": "4.0.0-*"
}

View File

@ -1,6 +1,8 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using Microsoft.Framework.Internal;
namespace Microsoft.Framework.Localization
{
/// <summary>
@ -13,7 +15,7 @@ namespace Microsoft.Framework.Localization
/// </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)
public LocalizedString([NotNull] string name, [NotNull] string value)
: this(name, value, resourceNotFound: false)
{
@ -25,7 +27,7 @@ namespace Microsoft.Framework.Localization
/// <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)
public LocalizedString([NotNull] string name, [NotNull] string value, bool resourceNotFound)
{
Name = name;
Value = value;

View File

@ -4,6 +4,7 @@
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using Microsoft.Framework.Internal;
namespace Microsoft.Framework.Localization
{
@ -19,7 +20,7 @@ namespace Microsoft.Framework.Localization
/// Creates a new <see cref="StringLocalizer{TResourceSource}"/>.
/// </summary>
/// <param name="factory">The <see cref="IStringLocalizerFactory"/> to use.</param>
public StringLocalizer(IStringLocalizerFactory factory)
public StringLocalizer([NotNull] IStringLocalizerFactory factory)
{
_localizer = factory.Create(typeof(TResourceSource));
}
@ -28,16 +29,17 @@ namespace Microsoft.Framework.Localization
public virtual IStringLocalizer WithCulture(CultureInfo culture) => _localizer.WithCulture(culture);
/// <inheritdoc />
public virtual LocalizedString this[string key] => _localizer[key];
public virtual LocalizedString this[[NotNull] string key] => _localizer[key];
/// <inheritdoc />
public virtual LocalizedString this[string key, params object[] arguments] => _localizer[key, arguments];
public virtual LocalizedString this[[NotNull] string key, params object[] arguments] =>
_localizer[key, arguments];
/// <inheritdoc />
public virtual LocalizedString GetString(string key) => _localizer.GetString(key);
public virtual LocalizedString GetString([NotNull] string key) => _localizer.GetString(key);
/// <inheritdoc />
public virtual LocalizedString GetString(string key, params object[] arguments) =>
public virtual LocalizedString GetString([NotNull] string key, params object[] arguments) =>
_localizer.GetString(key, arguments);
/// <inheritdoc />

View File

@ -3,7 +3,7 @@
"description": "Abstractions of application localization services.",
"dependencies": {
"Microsoft.Framework.NotNullAttribute.Internal": { "type": "build", "version": "1.0.0-*" }
},
"frameworks": {

View File

@ -1,6 +1,7 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using Microsoft.Framework.Internal;
using Microsoft.Framework.Localization;
namespace Microsoft.Framework.DependencyInjection
@ -15,7 +16,7 @@ namespace Microsoft.Framework.DependencyInjection
/// </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)
public static IServiceCollection AddLocalization([NotNull] this IServiceCollection services)
{
services.TryAdd(new ServiceDescriptor(
typeof(IStringLocalizerFactory),

View File

@ -8,6 +8,7 @@ using System.Collections.Generic;
using System.Globalization;
using System.Reflection;
using System.Resources;
using Microsoft.Framework.Internal;
namespace Microsoft.Framework.Localization
{
@ -27,9 +28,9 @@ namespace Microsoft.Framework.Localization
/// <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)
[NotNull] ResourceManager resourceManager,
[NotNull] Assembly resourceAssembly,
[NotNull] string baseName)
{
ResourceManager = resourceManager;
ResourceAssembly = resourceAssembly;
@ -52,20 +53,20 @@ namespace Microsoft.Framework.Localization
protected string ResourceBaseName { get; }
/// <inheritdoc />
public virtual LocalizedString this[string name] => GetString(name);
public virtual LocalizedString this[[NotNull] string name] => GetString(name);
/// <inheritdoc />
public virtual LocalizedString this[string name, params object[] arguments] => GetString(name, arguments);
public virtual LocalizedString this[[NotNull] string name, params object[] arguments] => GetString(name, arguments);
/// <inheritdoc />
public virtual LocalizedString GetString(string name)
public virtual LocalizedString GetString([NotNull] string name)
{
var value = GetStringSafely(name, null);
return new LocalizedString(name, value ?? name, resourceNotFound: value == null);
}
/// <inheritdoc />
public virtual LocalizedString GetString(string name, params object[] arguments)
public virtual LocalizedString GetString([NotNull] string name, params object[] arguments)
{
var format = GetStringSafely(name, null);
var value = string.Format(format ?? name, arguments);
@ -95,7 +96,7 @@ namespace Microsoft.Framework.Localization
/// <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)
protected string GetStringSafely([NotNull] string name, [NotNull] CultureInfo culture)
{
var cacheKey = new MissingManifestCacheKey(name, culture);
if (_missingManifestCache.ContainsKey(cacheKey))
@ -133,7 +134,7 @@ namespace Microsoft.Framework.Localization
/// </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)
protected IEnumerator<LocalizedString> GetEnumerator([NotNull] CultureInfo culture)
{
// TODO: I'm sure something here should be cached, probably the whole result
var resourceNames = GetResourceNamesFromCultureHierarchy(culture);

View File

@ -4,6 +4,7 @@
using System;
using System.Reflection;
using System.Resources;
using Microsoft.Framework.Internal;
using Microsoft.Framework.Runtime;
namespace Microsoft.Framework.Localization
@ -19,7 +20,7 @@ namespace Microsoft.Framework.Localization
/// Creates a new <see cref="ResourceManagerStringLocalizer"/>.
/// </summary>
/// <param name="applicationEnvironment"></param>
public ResourceManagerStringLocalizerFactory(IApplicationEnvironment applicationEnvironment)
public ResourceManagerStringLocalizerFactory([NotNull] IApplicationEnvironment applicationEnvironment)
{
_applicationEnvironment = applicationEnvironment;
}
@ -30,7 +31,7 @@ namespace Microsoft.Framework.Localization
/// </summary>
/// <param name="resourceSource">The <see cref="Type"/>.</param>
/// <returns>The <see cref="ResourceManagerStringLocalizer"/>.</returns>
public IStringLocalizer Create(Type resourceSource)
public IStringLocalizer Create([NotNull] Type resourceSource)
{
var typeInfo = resourceSource.GetTypeInfo();
var assembly = typeInfo.Assembly;
@ -44,7 +45,7 @@ namespace Microsoft.Framework.Localization
/// <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)
public IStringLocalizer Create([NotNull] string baseName, [NotNull] string location)
{
var assembly = Assembly.Load(new AssemblyName(location ?? _applicationEnvironment.ApplicationName));

View File

@ -6,6 +6,7 @@ using System.Collections.Generic;
using System.Globalization;
using System.Reflection;
using System.Resources;
using Microsoft.Framework.Internal;
namespace Microsoft.Framework.Localization
{
@ -25,30 +26,30 @@ namespace Microsoft.Framework.Localization
/// <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)
[NotNull] ResourceManager resourceManager,
[NotNull] Assembly assembly,
[NotNull] string baseName,
[NotNull] CultureInfo culture)
: base(resourceManager, assembly, baseName)
{
_culture = culture;
}
/// <inheritdoc />
public override LocalizedString this[string name] => GetString(name);
public override LocalizedString this[[NotNull] string name] => GetString(name);
/// <inheritdoc />
public override LocalizedString this[string name, params object[] arguments] => GetString(name, arguments);
public override LocalizedString this[[NotNull] string name, params object[] arguments] => GetString(name, arguments);
/// <inheritdoc />
public override LocalizedString GetString(string name)
public override LocalizedString GetString([NotNull] string name)
{
var value = GetStringSafely(name, _culture);
return new LocalizedString(name, value ?? name);
}
/// <inheritdoc />
public override LocalizedString GetString(string name, params object[] arguments)
public override LocalizedString GetString([NotNull] string name, params object[] arguments)
{
var format = GetStringSafely(name, _culture);
var value = string.Format(_culture, format ?? name, arguments);

View File

@ -5,6 +5,7 @@
"dependencies": {
"Microsoft.Framework.DependencyInjection.Abstractions": "1.0.0-*",
"Microsoft.Framework.Localization.Abstractions": "1.0.0-*",
"Microsoft.Framework.NotNullAttribute.Internal": { "type": "build", "version": "1.0.0-*" },
"Microsoft.Framework.Runtime.Abstractions": "1.0.0-*",
"System.Resources.ReaderWriter": "4.0.0-*"
},