Reorganize source code in preparation to move into aspnet/Extensions
Prior to reorganization, this source code was found in https://github.com/aspnet/Localization/tree/dotnet/extensions@0bcac31dd705fb9db60723f5d7eaeffb728358f5
\n\nCommit migrated from c8e0450d7a
This commit is contained in:
commit
e5fee282ab
|
|
@ -0,0 +1,45 @@
|
||||||
|
// 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.Extensions.Localization
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a service that provides localized strings.
|
||||||
|
/// </summary>
|
||||||
|
public interface IStringLocalizer
|
||||||
|
{
|
||||||
|
/// <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 all string resources.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="includeParentCultures">
|
||||||
|
/// A <see cref="System.Boolean"/> indicating whether to include strings from parent cultures.
|
||||||
|
/// </param>
|
||||||
|
/// <returns>The strings.</returns>
|
||||||
|
IEnumerable<LocalizedString> GetAllStrings(bool includeParentCultures);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new <see cref="IStringLocalizer"/> 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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.Extensions.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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,14 @@
|
||||||
|
// 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.Extensions.Localization
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Represents an <see cref="IStringLocalizer"/> that provides strings for <typeparamref name="T"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">The <see cref="System.Type"/> to provide strings for.</typeparam>
|
||||||
|
public interface IStringLocalizer<T> : IStringLocalizer
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,90 @@
|
||||||
|
// 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.Extensions.Localization
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A locale specific string.
|
||||||
|
/// </summary>
|
||||||
|
public class 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 not found in a resource. Set this to <c>true</c> to indicate an alternate string value was used.</param>
|
||||||
|
public LocalizedString(string name, string value, bool resourceNotFound)
|
||||||
|
: this(name, value, resourceNotFound, searchedLocation: null)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <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 not found in a resource. Set this to <c>true</c> to indicate an alternate string value was used.</param>
|
||||||
|
/// <param name="searchedLocation">The location which was searched for a localization value.</param>
|
||||||
|
public LocalizedString(string name, string value, bool resourceNotFound, string searchedLocation)
|
||||||
|
{
|
||||||
|
if (name == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(name));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
Name = name;
|
||||||
|
Value = value;
|
||||||
|
ResourceNotFound = resourceNotFound;
|
||||||
|
SearchedLocation = searchedLocation;
|
||||||
|
}
|
||||||
|
|
||||||
|
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 not found in a resource. If <c>true</c>, an alternate string value was used.
|
||||||
|
/// </summary>
|
||||||
|
public bool ResourceNotFound { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The location which was searched for a localization value.
|
||||||
|
/// </summary>
|
||||||
|
public string SearchedLocation { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the actual string.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>The actual string.</returns>
|
||||||
|
public override string ToString() => Value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<Product>Microsoft .NET Extensions</Product>
|
||||||
|
<Description>Abstractions of application localization services.
|
||||||
|
Commonly used types:
|
||||||
|
Microsoft.Extensions.Localization.IStringLocalizer
|
||||||
|
Microsoft.Extensions.Localization.IStringLocalizer<T></Description>
|
||||||
|
<TargetFramework>netstandard2.0</TargetFramework>
|
||||||
|
<NoWarn>$(NoWarn);CS1591</NoWarn>
|
||||||
|
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||||
|
<PackageTags>localization</PackageTags>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
|
|
@ -0,0 +1,74 @@
|
||||||
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Microsoft.Extensions.Localization
|
||||||
|
{
|
||||||
|
public static class StringLocalizerExtensions
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the string resource with the given name.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="stringLocalizer">The <see cref="IStringLocalizer"/>.</param>
|
||||||
|
/// <param name="name">The name of the string resource.</param>
|
||||||
|
/// <returns>The string resource as a <see cref="LocalizedString"/>.</returns>
|
||||||
|
public static LocalizedString GetString(
|
||||||
|
this IStringLocalizer stringLocalizer,
|
||||||
|
string name)
|
||||||
|
{
|
||||||
|
if (stringLocalizer == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(stringLocalizer));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (name == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(name));
|
||||||
|
}
|
||||||
|
|
||||||
|
return stringLocalizer[name];
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the string resource with the given name and formatted with the supplied arguments.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="stringLocalizer">The <see cref="IStringLocalizer"/>.</param>
|
||||||
|
/// <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>
|
||||||
|
public static LocalizedString GetString(
|
||||||
|
this IStringLocalizer stringLocalizer,
|
||||||
|
string name,
|
||||||
|
params object[] arguments)
|
||||||
|
{
|
||||||
|
if (stringLocalizer == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(stringLocalizer));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (name == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(name));
|
||||||
|
}
|
||||||
|
|
||||||
|
return stringLocalizer[name, arguments];
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets all string resources including those for parent cultures.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="stringLocalizer">The <see cref="IStringLocalizer"/>.</param>
|
||||||
|
/// <returns>The string resources.</returns>
|
||||||
|
public static IEnumerable<LocalizedString> GetAllStrings(this IStringLocalizer stringLocalizer)
|
||||||
|
{
|
||||||
|
if (stringLocalizer == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(stringLocalizer));
|
||||||
|
}
|
||||||
|
|
||||||
|
return stringLocalizer.GetAllStrings(includeParentCultures: true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Globalization;
|
||||||
|
|
||||||
|
namespace Microsoft.Extensions.Localization
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Provides strings for <typeparamref name="TResourceSource"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="TResourceSource">The <see cref="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)
|
||||||
|
{
|
||||||
|
if (factory == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(factory));
|
||||||
|
}
|
||||||
|
|
||||||
|
_localizer = factory.Create(typeof(TResourceSource));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public virtual IStringLocalizer WithCulture(CultureInfo culture) => _localizer.WithCulture(culture);
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public virtual LocalizedString this[string name]
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (name == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(name));
|
||||||
|
}
|
||||||
|
|
||||||
|
return _localizer[name];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public virtual LocalizedString this[string name, params object[] arguments]
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (name == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(name));
|
||||||
|
}
|
||||||
|
|
||||||
|
return _localizer[name, arguments];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public IEnumerable<LocalizedString> GetAllStrings(bool includeParentCultures) =>
|
||||||
|
_localizer.GetAllStrings(includeParentCultures);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,413 @@
|
||||||
|
{
|
||||||
|
"AssemblyIdentity": "Microsoft.Extensions.Localization.Abstractions, Version=2.1.1.0, Culture=neutral, PublicKeyToken=adb9793829ddae60",
|
||||||
|
"Types": [
|
||||||
|
{
|
||||||
|
"Name": "Microsoft.Extensions.Localization.IStringLocalizer",
|
||||||
|
"Visibility": "Public",
|
||||||
|
"Kind": "Interface",
|
||||||
|
"Abstract": true,
|
||||||
|
"ImplementedInterfaces": [],
|
||||||
|
"Members": [
|
||||||
|
{
|
||||||
|
"Kind": "Method",
|
||||||
|
"Name": "get_Item",
|
||||||
|
"Parameters": [
|
||||||
|
{
|
||||||
|
"Name": "name",
|
||||||
|
"Type": "System.String"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"ReturnType": "Microsoft.Extensions.Localization.LocalizedString",
|
||||||
|
"GenericParameter": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Kind": "Method",
|
||||||
|
"Name": "get_Item",
|
||||||
|
"Parameters": [
|
||||||
|
{
|
||||||
|
"Name": "name",
|
||||||
|
"Type": "System.String"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "arguments",
|
||||||
|
"Type": "System.Object[]",
|
||||||
|
"IsParams": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"ReturnType": "Microsoft.Extensions.Localization.LocalizedString",
|
||||||
|
"GenericParameter": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Kind": "Method",
|
||||||
|
"Name": "GetAllStrings",
|
||||||
|
"Parameters": [
|
||||||
|
{
|
||||||
|
"Name": "includeParentCultures",
|
||||||
|
"Type": "System.Boolean"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"ReturnType": "System.Collections.Generic.IEnumerable<Microsoft.Extensions.Localization.LocalizedString>",
|
||||||
|
"GenericParameter": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Kind": "Method",
|
||||||
|
"Name": "WithCulture",
|
||||||
|
"Parameters": [
|
||||||
|
{
|
||||||
|
"Name": "culture",
|
||||||
|
"Type": "System.Globalization.CultureInfo"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"ReturnType": "Microsoft.Extensions.Localization.IStringLocalizer",
|
||||||
|
"GenericParameter": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"GenericParameters": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "Microsoft.Extensions.Localization.IStringLocalizerFactory",
|
||||||
|
"Visibility": "Public",
|
||||||
|
"Kind": "Interface",
|
||||||
|
"Abstract": true,
|
||||||
|
"ImplementedInterfaces": [],
|
||||||
|
"Members": [
|
||||||
|
{
|
||||||
|
"Kind": "Method",
|
||||||
|
"Name": "Create",
|
||||||
|
"Parameters": [
|
||||||
|
{
|
||||||
|
"Name": "resourceSource",
|
||||||
|
"Type": "System.Type"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"ReturnType": "Microsoft.Extensions.Localization.IStringLocalizer",
|
||||||
|
"GenericParameter": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Kind": "Method",
|
||||||
|
"Name": "Create",
|
||||||
|
"Parameters": [
|
||||||
|
{
|
||||||
|
"Name": "baseName",
|
||||||
|
"Type": "System.String"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "location",
|
||||||
|
"Type": "System.String"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"ReturnType": "Microsoft.Extensions.Localization.IStringLocalizer",
|
||||||
|
"GenericParameter": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"GenericParameters": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "Microsoft.Extensions.Localization.IStringLocalizer<T0>",
|
||||||
|
"Visibility": "Public",
|
||||||
|
"Kind": "Interface",
|
||||||
|
"Abstract": true,
|
||||||
|
"ImplementedInterfaces": [
|
||||||
|
"Microsoft.Extensions.Localization.IStringLocalizer"
|
||||||
|
],
|
||||||
|
"Members": [],
|
||||||
|
"GenericParameters": [
|
||||||
|
{
|
||||||
|
"ParameterName": "T",
|
||||||
|
"ParameterPosition": 0,
|
||||||
|
"BaseTypeOrInterfaces": []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "Microsoft.Extensions.Localization.LocalizedString",
|
||||||
|
"Visibility": "Public",
|
||||||
|
"Kind": "Class",
|
||||||
|
"ImplementedInterfaces": [],
|
||||||
|
"Members": [
|
||||||
|
{
|
||||||
|
"Kind": "Method",
|
||||||
|
"Name": "op_Implicit",
|
||||||
|
"Parameters": [
|
||||||
|
{
|
||||||
|
"Name": "localizedString",
|
||||||
|
"Type": "Microsoft.Extensions.Localization.LocalizedString"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"ReturnType": "System.String",
|
||||||
|
"Static": true,
|
||||||
|
"Visibility": "Public",
|
||||||
|
"GenericParameter": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Kind": "Method",
|
||||||
|
"Name": "get_Name",
|
||||||
|
"Parameters": [],
|
||||||
|
"ReturnType": "System.String",
|
||||||
|
"Visibility": "Public",
|
||||||
|
"GenericParameter": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Kind": "Method",
|
||||||
|
"Name": "get_Value",
|
||||||
|
"Parameters": [],
|
||||||
|
"ReturnType": "System.String",
|
||||||
|
"Visibility": "Public",
|
||||||
|
"GenericParameter": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Kind": "Method",
|
||||||
|
"Name": "get_ResourceNotFound",
|
||||||
|
"Parameters": [],
|
||||||
|
"ReturnType": "System.Boolean",
|
||||||
|
"Visibility": "Public",
|
||||||
|
"GenericParameter": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Kind": "Method",
|
||||||
|
"Name": "get_SearchedLocation",
|
||||||
|
"Parameters": [],
|
||||||
|
"ReturnType": "System.String",
|
||||||
|
"Visibility": "Public",
|
||||||
|
"GenericParameter": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Kind": "Method",
|
||||||
|
"Name": "ToString",
|
||||||
|
"Parameters": [],
|
||||||
|
"ReturnType": "System.String",
|
||||||
|
"Virtual": true,
|
||||||
|
"Override": true,
|
||||||
|
"Visibility": "Public",
|
||||||
|
"GenericParameter": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Kind": "Constructor",
|
||||||
|
"Name": ".ctor",
|
||||||
|
"Parameters": [
|
||||||
|
{
|
||||||
|
"Name": "name",
|
||||||
|
"Type": "System.String"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "value",
|
||||||
|
"Type": "System.String"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"Visibility": "Public",
|
||||||
|
"GenericParameter": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Kind": "Constructor",
|
||||||
|
"Name": ".ctor",
|
||||||
|
"Parameters": [
|
||||||
|
{
|
||||||
|
"Name": "name",
|
||||||
|
"Type": "System.String"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "value",
|
||||||
|
"Type": "System.String"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "resourceNotFound",
|
||||||
|
"Type": "System.Boolean"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"Visibility": "Public",
|
||||||
|
"GenericParameter": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Kind": "Constructor",
|
||||||
|
"Name": ".ctor",
|
||||||
|
"Parameters": [
|
||||||
|
{
|
||||||
|
"Name": "name",
|
||||||
|
"Type": "System.String"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "value",
|
||||||
|
"Type": "System.String"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "resourceNotFound",
|
||||||
|
"Type": "System.Boolean"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "searchedLocation",
|
||||||
|
"Type": "System.String"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"Visibility": "Public",
|
||||||
|
"GenericParameter": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"GenericParameters": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "Microsoft.Extensions.Localization.StringLocalizerExtensions",
|
||||||
|
"Visibility": "Public",
|
||||||
|
"Kind": "Class",
|
||||||
|
"Abstract": true,
|
||||||
|
"Static": true,
|
||||||
|
"Sealed": true,
|
||||||
|
"ImplementedInterfaces": [],
|
||||||
|
"Members": [
|
||||||
|
{
|
||||||
|
"Kind": "Method",
|
||||||
|
"Name": "GetString",
|
||||||
|
"Parameters": [
|
||||||
|
{
|
||||||
|
"Name": "stringLocalizer",
|
||||||
|
"Type": "Microsoft.Extensions.Localization.IStringLocalizer"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "name",
|
||||||
|
"Type": "System.String"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"ReturnType": "Microsoft.Extensions.Localization.LocalizedString",
|
||||||
|
"Static": true,
|
||||||
|
"Extension": true,
|
||||||
|
"Visibility": "Public",
|
||||||
|
"GenericParameter": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Kind": "Method",
|
||||||
|
"Name": "GetString",
|
||||||
|
"Parameters": [
|
||||||
|
{
|
||||||
|
"Name": "stringLocalizer",
|
||||||
|
"Type": "Microsoft.Extensions.Localization.IStringLocalizer"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "name",
|
||||||
|
"Type": "System.String"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "arguments",
|
||||||
|
"Type": "System.Object[]",
|
||||||
|
"IsParams": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"ReturnType": "Microsoft.Extensions.Localization.LocalizedString",
|
||||||
|
"Static": true,
|
||||||
|
"Extension": true,
|
||||||
|
"Visibility": "Public",
|
||||||
|
"GenericParameter": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Kind": "Method",
|
||||||
|
"Name": "GetAllStrings",
|
||||||
|
"Parameters": [
|
||||||
|
{
|
||||||
|
"Name": "stringLocalizer",
|
||||||
|
"Type": "Microsoft.Extensions.Localization.IStringLocalizer"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"ReturnType": "System.Collections.Generic.IEnumerable<Microsoft.Extensions.Localization.LocalizedString>",
|
||||||
|
"Static": true,
|
||||||
|
"Extension": true,
|
||||||
|
"Visibility": "Public",
|
||||||
|
"GenericParameter": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"GenericParameters": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "Microsoft.Extensions.Localization.StringLocalizer<T0>",
|
||||||
|
"Visibility": "Public",
|
||||||
|
"Kind": "Class",
|
||||||
|
"ImplementedInterfaces": [
|
||||||
|
"Microsoft.Extensions.Localization.IStringLocalizer<T0>"
|
||||||
|
],
|
||||||
|
"Members": [
|
||||||
|
{
|
||||||
|
"Kind": "Method",
|
||||||
|
"Name": "get_Item",
|
||||||
|
"Parameters": [
|
||||||
|
{
|
||||||
|
"Name": "name",
|
||||||
|
"Type": "System.String"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"ReturnType": "Microsoft.Extensions.Localization.LocalizedString",
|
||||||
|
"Virtual": true,
|
||||||
|
"ImplementedInterface": "Microsoft.Extensions.Localization.IStringLocalizer",
|
||||||
|
"Visibility": "Public",
|
||||||
|
"GenericParameter": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Kind": "Method",
|
||||||
|
"Name": "get_Item",
|
||||||
|
"Parameters": [
|
||||||
|
{
|
||||||
|
"Name": "name",
|
||||||
|
"Type": "System.String"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "arguments",
|
||||||
|
"Type": "System.Object[]",
|
||||||
|
"IsParams": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"ReturnType": "Microsoft.Extensions.Localization.LocalizedString",
|
||||||
|
"Virtual": true,
|
||||||
|
"ImplementedInterface": "Microsoft.Extensions.Localization.IStringLocalizer",
|
||||||
|
"Visibility": "Public",
|
||||||
|
"GenericParameter": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Kind": "Method",
|
||||||
|
"Name": "GetAllStrings",
|
||||||
|
"Parameters": [
|
||||||
|
{
|
||||||
|
"Name": "includeParentCultures",
|
||||||
|
"Type": "System.Boolean"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"ReturnType": "System.Collections.Generic.IEnumerable<Microsoft.Extensions.Localization.LocalizedString>",
|
||||||
|
"Sealed": true,
|
||||||
|
"Virtual": true,
|
||||||
|
"ImplementedInterface": "Microsoft.Extensions.Localization.IStringLocalizer",
|
||||||
|
"Visibility": "Public",
|
||||||
|
"GenericParameter": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Kind": "Method",
|
||||||
|
"Name": "WithCulture",
|
||||||
|
"Parameters": [
|
||||||
|
{
|
||||||
|
"Name": "culture",
|
||||||
|
"Type": "System.Globalization.CultureInfo"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"ReturnType": "Microsoft.Extensions.Localization.IStringLocalizer",
|
||||||
|
"Virtual": true,
|
||||||
|
"ImplementedInterface": "Microsoft.Extensions.Localization.IStringLocalizer",
|
||||||
|
"Visibility": "Public",
|
||||||
|
"GenericParameter": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Kind": "Constructor",
|
||||||
|
"Name": ".ctor",
|
||||||
|
"Parameters": [
|
||||||
|
{
|
||||||
|
"Name": "factory",
|
||||||
|
"Type": "Microsoft.Extensions.Localization.IStringLocalizerFactory"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"Visibility": "Public",
|
||||||
|
"GenericParameter": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"GenericParameters": [
|
||||||
|
{
|
||||||
|
"ParameterName": "TResourceSource",
|
||||||
|
"ParameterPosition": 0,
|
||||||
|
"BaseTypeOrInterfaces": []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,22 @@
|
||||||
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Microsoft.Extensions.Localization
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a cache of string names in resources.
|
||||||
|
/// </summary>
|
||||||
|
public interface IResourceNamesCache
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Adds a set of resource names to the cache by using the specified function, if the name does not already exist.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="name">The resource name to add string names for.</param>
|
||||||
|
/// <param name="valueFactory">The function used to generate the string names for the resource.</param>
|
||||||
|
/// <returns>The string names for the resource.</returns>
|
||||||
|
IList<string> GetOrAdd(string name, Func<string, IList<string>> valueFactory);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
// 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.IO;
|
||||||
|
using System.Reflection;
|
||||||
|
|
||||||
|
namespace Microsoft.Extensions.Localization.Internal
|
||||||
|
{
|
||||||
|
public class AssemblyWrapper
|
||||||
|
{
|
||||||
|
public AssemblyWrapper(Assembly assembly)
|
||||||
|
{
|
||||||
|
if (assembly == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(assembly));
|
||||||
|
}
|
||||||
|
|
||||||
|
Assembly = assembly;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Assembly Assembly { get; }
|
||||||
|
|
||||||
|
public virtual string FullName => Assembly.FullName;
|
||||||
|
|
||||||
|
public virtual Stream GetManifestResourceStream(string name) => Assembly.GetManifestResourceStream(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,13 @@
|
||||||
|
// 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.Extensions.Localization.Internal
|
||||||
|
{
|
||||||
|
public interface IResourceStringProvider
|
||||||
|
{
|
||||||
|
IList<string> GetAllResourceStrings(CultureInfo culture, bool throwOnMissing);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,27 @@
|
||||||
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Globalization;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
|
namespace Microsoft.Extensions.Localization.Internal
|
||||||
|
{
|
||||||
|
internal static class ResourceManagerStringLocalizerLoggerExtensions
|
||||||
|
{
|
||||||
|
private static readonly Action<ILogger, string, string, CultureInfo, Exception> _searchedLocation;
|
||||||
|
|
||||||
|
static ResourceManagerStringLocalizerLoggerExtensions()
|
||||||
|
{
|
||||||
|
_searchedLocation = LoggerMessage.Define<string, string, CultureInfo>(
|
||||||
|
LogLevel.Debug,
|
||||||
|
1,
|
||||||
|
$"{nameof(ResourceManagerStringLocalizer)} searched for '{{Key}}' in '{{LocationSearched}}' with culture '{{Culture}}'.");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void SearchedLocation(this ILogger logger, string key, string searchedLocation, CultureInfo culture)
|
||||||
|
{
|
||||||
|
_searchedLocation(logger, key, searchedLocation, culture, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Globalization;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Resources;
|
||||||
|
|
||||||
|
namespace Microsoft.Extensions.Localization.Internal
|
||||||
|
{
|
||||||
|
public class ResourceManagerStringProvider : IResourceStringProvider
|
||||||
|
{
|
||||||
|
private readonly IResourceNamesCache _resourceNamesCache;
|
||||||
|
private readonly ResourceManager _resourceManager;
|
||||||
|
private readonly Assembly _assembly;
|
||||||
|
private readonly string _resourceBaseName;
|
||||||
|
|
||||||
|
public ResourceManagerStringProvider(
|
||||||
|
IResourceNamesCache resourceCache,
|
||||||
|
ResourceManager resourceManager,
|
||||||
|
Assembly assembly,
|
||||||
|
string baseName)
|
||||||
|
{
|
||||||
|
_resourceManager = resourceManager;
|
||||||
|
_resourceNamesCache = resourceCache;
|
||||||
|
_assembly = assembly;
|
||||||
|
_resourceBaseName = baseName;
|
||||||
|
}
|
||||||
|
|
||||||
|
private string GetResourceCacheKey(CultureInfo culture)
|
||||||
|
{
|
||||||
|
var resourceName = _resourceManager.BaseName;
|
||||||
|
|
||||||
|
return $"Culture={culture.Name};resourceName={resourceName};Assembly={_assembly.FullName}";
|
||||||
|
}
|
||||||
|
|
||||||
|
private string GetResourceName(CultureInfo culture)
|
||||||
|
{
|
||||||
|
var resourceStreamName = _resourceBaseName;
|
||||||
|
if (!string.IsNullOrEmpty(culture.Name))
|
||||||
|
{
|
||||||
|
resourceStreamName += "." + culture.Name;
|
||||||
|
}
|
||||||
|
resourceStreamName += ".resources";
|
||||||
|
|
||||||
|
return resourceStreamName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IList<string> GetAllResourceStrings(CultureInfo culture, bool throwOnMissing)
|
||||||
|
{
|
||||||
|
var cacheKey = GetResourceCacheKey(culture);
|
||||||
|
|
||||||
|
return _resourceNamesCache.GetOrAdd(cacheKey, _ =>
|
||||||
|
{
|
||||||
|
// We purposly don't dispose the ResourceSet because it causes an ObjectDisposedException when you try to read the values later.
|
||||||
|
var resourceSet = _resourceManager.GetResourceSet(culture, createIfNotExists: true, tryParents: false);
|
||||||
|
if (resourceSet == null)
|
||||||
|
{
|
||||||
|
if (throwOnMissing)
|
||||||
|
{
|
||||||
|
throw new MissingManifestResourceException(Resources.FormatLocalization_MissingManifest(GetResourceName(culture)));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var names = new List<string>();
|
||||||
|
foreach (DictionaryEntry entry in resourceSet)
|
||||||
|
{
|
||||||
|
names.Add((string)entry.Key);
|
||||||
|
}
|
||||||
|
|
||||||
|
return names;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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.Extensions.Localization
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Provides programmatic configuration for localization.
|
||||||
|
/// </summary>
|
||||||
|
public class LocalizationOptions
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The relative path under application root where resource files are located.
|
||||||
|
/// </summary>
|
||||||
|
public string ResourcesPath { get; set; } = string.Empty;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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;
|
||||||
|
using Microsoft.Extensions.DependencyInjection.Extensions;
|
||||||
|
using Microsoft.Extensions.Localization;
|
||||||
|
|
||||||
|
namespace Microsoft.Extensions.DependencyInjection
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Extension methods for setting up localization services in an <see cref="IServiceCollection" />.
|
||||||
|
/// </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"/> so that additional calls can be chained.</returns>
|
||||||
|
public static IServiceCollection AddLocalization(this IServiceCollection services)
|
||||||
|
{
|
||||||
|
if (services == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(services));
|
||||||
|
}
|
||||||
|
|
||||||
|
services.AddOptions();
|
||||||
|
|
||||||
|
AddLocalizationServices(services);
|
||||||
|
|
||||||
|
return services;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Adds services required for application localization.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="services">The <see cref="IServiceCollection"/> to add the services to.</param>
|
||||||
|
/// <param name="setupAction">
|
||||||
|
/// An <see cref="Action{LocalizationOptions}"/> to configure the <see cref="LocalizationOptions"/>.
|
||||||
|
/// </param>
|
||||||
|
/// <returns>The <see cref="IServiceCollection"/> so that additional calls can be chained.</returns>
|
||||||
|
public static IServiceCollection AddLocalization(
|
||||||
|
this IServiceCollection services,
|
||||||
|
Action<LocalizationOptions> setupAction)
|
||||||
|
{
|
||||||
|
if (services == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(services));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (setupAction == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(setupAction));
|
||||||
|
}
|
||||||
|
|
||||||
|
AddLocalizationServices(services, setupAction);
|
||||||
|
|
||||||
|
return services;
|
||||||
|
}
|
||||||
|
|
||||||
|
// To enable unit testing
|
||||||
|
internal static void AddLocalizationServices(IServiceCollection services)
|
||||||
|
{
|
||||||
|
services.TryAddSingleton<IStringLocalizerFactory, ResourceManagerStringLocalizerFactory>();
|
||||||
|
services.TryAddTransient(typeof(IStringLocalizer<>), typeof(StringLocalizer<>));
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static void AddLocalizationServices(
|
||||||
|
IServiceCollection services,
|
||||||
|
Action<LocalizationOptions> setupAction)
|
||||||
|
{
|
||||||
|
AddLocalizationServices(services);
|
||||||
|
services.Configure(setupAction);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,19 @@
|
||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<Product>Microsoft .NET Extensions</Product>
|
||||||
|
<Description>Application localization services and default implementation based on ResourceManager to load localized assembly resources.</Description>
|
||||||
|
<TargetFramework>netstandard2.0</TargetFramework>
|
||||||
|
<NoWarn>$(NoWarn);CS1591</NoWarn>
|
||||||
|
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||||
|
<PackageTags>localization</PackageTags>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Reference Include="Microsoft.Extensions.DependencyInjection.Abstractions" />
|
||||||
|
<Reference Include="Microsoft.Extensions.Options" />
|
||||||
|
<Reference Include="Microsoft.Extensions.Localization.Abstractions" />
|
||||||
|
<Reference Include="Microsoft.Extensions.Logging.Abstractions" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
|
[assembly: InternalsVisibleTo("Microsoft.Extensions.Localization.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
|
||||||
|
|
@ -0,0 +1,62 @@
|
||||||
|
// <auto-generated />
|
||||||
|
namespace Microsoft.Extensions.Localization
|
||||||
|
{
|
||||||
|
using System.Globalization;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Resources;
|
||||||
|
|
||||||
|
internal static class Resources
|
||||||
|
{
|
||||||
|
private static readonly ResourceManager _resourceManager
|
||||||
|
= new ResourceManager("Microsoft.Extensions.Localization.Resources", typeof(Resources).GetTypeInfo().Assembly);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The manifest '{0}' was not found.
|
||||||
|
/// </summary>
|
||||||
|
internal static string Localization_MissingManifest
|
||||||
|
{
|
||||||
|
get { return GetString("Localization_MissingManifest"); }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The manifest '{0}' was not found.
|
||||||
|
/// </summary>
|
||||||
|
internal static string FormatLocalization_MissingManifest(object p0)
|
||||||
|
{
|
||||||
|
return string.Format(CultureInfo.CurrentCulture, GetString("Localization_MissingManifest"), p0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// No manifests exist for the current culture.
|
||||||
|
/// </summary>
|
||||||
|
internal static string Localization_MissingManifest_Parent
|
||||||
|
{
|
||||||
|
get { return GetString("Localization_MissingManifest_Parent"); }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// No manifests exist for the current culture.
|
||||||
|
/// </summary>
|
||||||
|
internal static string FormatLocalization_MissingManifest_Parent()
|
||||||
|
{
|
||||||
|
return GetString("Localization_MissingManifest_Parent");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string GetString(string name, params string[] formatterNames)
|
||||||
|
{
|
||||||
|
var value = _resourceManager.GetString(name);
|
||||||
|
|
||||||
|
System.Diagnostics.Debug.Assert(value != null);
|
||||||
|
|
||||||
|
if (formatterNames != null)
|
||||||
|
{
|
||||||
|
for (var i = 0; i < formatterNames.Length; i++)
|
||||||
|
{
|
||||||
|
value = value.Replace("{" + formatterNames[i] + "}", "{" + i + "}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,33 @@
|
||||||
|
// 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.Extensions.Localization
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Provides the location of resources for an Assembly.
|
||||||
|
/// </summary>
|
||||||
|
[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = false, Inherited = false)]
|
||||||
|
public class ResourceLocationAttribute : Attribute
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new <see cref="ResourceLocationAttribute"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="resourceLocation">The location of resources for this Assembly.</param>
|
||||||
|
public ResourceLocationAttribute(string resourceLocation)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(resourceLocation))
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(resourceLocation));
|
||||||
|
}
|
||||||
|
|
||||||
|
ResourceLocation = resourceLocation;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The location of resources for this Assembly.
|
||||||
|
/// </summary>
|
||||||
|
public string ResourceLocation { get; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,274 @@
|
||||||
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Globalization;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Resources;
|
||||||
|
using Microsoft.Extensions.Localization.Internal;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
|
namespace Microsoft.Extensions.Localization
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// An <see cref="IStringLocalizer"/> that uses the <see cref="ResourceManager"/> and
|
||||||
|
/// <see cref="ResourceReader"/> to provide localized strings.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>This type is thread-safe.</remarks>
|
||||||
|
public class ResourceManagerStringLocalizer : IStringLocalizer
|
||||||
|
{
|
||||||
|
private readonly ConcurrentDictionary<string, object> _missingManifestCache = new ConcurrentDictionary<string, object>();
|
||||||
|
private readonly IResourceNamesCache _resourceNamesCache;
|
||||||
|
private readonly ResourceManager _resourceManager;
|
||||||
|
private readonly IResourceStringProvider _resourceStringProvider;
|
||||||
|
private readonly string _resourceBaseName;
|
||||||
|
private readonly ILogger _logger;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new <see cref="ResourceManagerStringLocalizer"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="resourceManager">The <see cref="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 that contains the strings.</param>
|
||||||
|
/// <param name="resourceNamesCache">Cache of the list of strings for a given resource assembly name.</param>
|
||||||
|
/// <param name="logger">The <see cref="ILogger"/>.</param>
|
||||||
|
public ResourceManagerStringLocalizer(
|
||||||
|
ResourceManager resourceManager,
|
||||||
|
Assembly resourceAssembly,
|
||||||
|
string baseName,
|
||||||
|
IResourceNamesCache resourceNamesCache,
|
||||||
|
ILogger logger)
|
||||||
|
: this(
|
||||||
|
resourceManager,
|
||||||
|
new AssemblyWrapper(resourceAssembly),
|
||||||
|
baseName,
|
||||||
|
resourceNamesCache,
|
||||||
|
logger)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Intended for testing purposes only.
|
||||||
|
/// </summary>
|
||||||
|
public ResourceManagerStringLocalizer(
|
||||||
|
ResourceManager resourceManager,
|
||||||
|
AssemblyWrapper resourceAssemblyWrapper,
|
||||||
|
string baseName,
|
||||||
|
IResourceNamesCache resourceNamesCache,
|
||||||
|
ILogger logger)
|
||||||
|
: this(
|
||||||
|
resourceManager,
|
||||||
|
new ResourceManagerStringProvider(
|
||||||
|
resourceNamesCache,
|
||||||
|
resourceManager,
|
||||||
|
resourceAssemblyWrapper.Assembly,
|
||||||
|
baseName),
|
||||||
|
baseName,
|
||||||
|
resourceNamesCache,
|
||||||
|
logger)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Intended for testing purposes only.
|
||||||
|
/// </summary>
|
||||||
|
public ResourceManagerStringLocalizer(
|
||||||
|
ResourceManager resourceManager,
|
||||||
|
IResourceStringProvider resourceStringProvider,
|
||||||
|
string baseName,
|
||||||
|
IResourceNamesCache resourceNamesCache,
|
||||||
|
ILogger logger)
|
||||||
|
{
|
||||||
|
if (resourceManager == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(resourceManager));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (resourceStringProvider == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(resourceStringProvider));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (baseName == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(baseName));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (resourceNamesCache == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(resourceNamesCache));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (logger == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(logger));
|
||||||
|
}
|
||||||
|
|
||||||
|
_resourceStringProvider = resourceStringProvider;
|
||||||
|
_resourceManager = resourceManager;
|
||||||
|
_resourceBaseName = baseName;
|
||||||
|
_resourceNamesCache = resourceNamesCache;
|
||||||
|
_logger = logger;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public virtual LocalizedString this[string name]
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (name == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(name));
|
||||||
|
}
|
||||||
|
|
||||||
|
var value = GetStringSafely(name, null);
|
||||||
|
|
||||||
|
return new LocalizedString(name, value ?? name, resourceNotFound: value == null, searchedLocation: _resourceBaseName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public virtual LocalizedString this[string name, params object[] arguments]
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (name == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(name));
|
||||||
|
}
|
||||||
|
|
||||||
|
var format = GetStringSafely(name, null);
|
||||||
|
var value = string.Format(format ?? name, arguments);
|
||||||
|
|
||||||
|
return new LocalizedString(name, value, resourceNotFound: format == null, searchedLocation: _resourceBaseName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <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,
|
||||||
|
_resourceStringProvider,
|
||||||
|
_resourceBaseName,
|
||||||
|
_resourceNamesCache,
|
||||||
|
_logger)
|
||||||
|
: new ResourceManagerWithCultureStringLocalizer(
|
||||||
|
_resourceManager,
|
||||||
|
_resourceStringProvider,
|
||||||
|
_resourceBaseName,
|
||||||
|
_resourceNamesCache,
|
||||||
|
culture,
|
||||||
|
_logger);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public virtual IEnumerable<LocalizedString> GetAllStrings(bool includeParentCultures) =>
|
||||||
|
GetAllStrings(includeParentCultures, CultureInfo.CurrentUICulture);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns all strings in the specified culture.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="includeParentCultures"></param>
|
||||||
|
/// <param name="culture">The <see cref="CultureInfo"/> to get strings for.</param>
|
||||||
|
/// <returns>The strings.</returns>
|
||||||
|
protected IEnumerable<LocalizedString> GetAllStrings(bool includeParentCultures, CultureInfo culture)
|
||||||
|
{
|
||||||
|
if (culture == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(culture));
|
||||||
|
}
|
||||||
|
|
||||||
|
var resourceNames = includeParentCultures
|
||||||
|
? GetResourceNamesFromCultureHierarchy(culture)
|
||||||
|
: _resourceStringProvider.GetAllResourceStrings(culture, true);
|
||||||
|
|
||||||
|
foreach (var name in resourceNames)
|
||||||
|
{
|
||||||
|
var value = GetStringSafely(name, culture);
|
||||||
|
yield return new LocalizedString(name, value ?? name, resourceNotFound: value == null, searchedLocation: _resourceBaseName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <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)
|
||||||
|
{
|
||||||
|
if (name == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(name));
|
||||||
|
}
|
||||||
|
|
||||||
|
var keyCulture = culture ?? CultureInfo.CurrentUICulture;
|
||||||
|
|
||||||
|
var cacheKey = $"name={name}&culture={keyCulture.Name}";
|
||||||
|
|
||||||
|
_logger.SearchedLocation(name, _resourceBaseName, keyCulture);
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private IEnumerable<string> GetResourceNamesFromCultureHierarchy(CultureInfo startingCulture)
|
||||||
|
{
|
||||||
|
var currentCulture = startingCulture;
|
||||||
|
var resourceNames = new HashSet<string>();
|
||||||
|
|
||||||
|
var hasAnyCultures = false;
|
||||||
|
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
|
||||||
|
var cultureResourceNames = _resourceStringProvider.GetAllResourceStrings(currentCulture, false);
|
||||||
|
|
||||||
|
if (cultureResourceNames != null)
|
||||||
|
{
|
||||||
|
foreach (var resourceName in cultureResourceNames)
|
||||||
|
{
|
||||||
|
resourceNames.Add(resourceName);
|
||||||
|
}
|
||||||
|
hasAnyCultures = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentCulture == currentCulture.Parent)
|
||||||
|
{
|
||||||
|
// currentCulture begat currentCulture, probably time to leave
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
currentCulture = currentCulture.Parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!hasAnyCultures)
|
||||||
|
{
|
||||||
|
throw new MissingManifestResourceException(Resources.Localization_MissingManifest_Parent);
|
||||||
|
}
|
||||||
|
|
||||||
|
return resourceNames;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,270 @@
|
||||||
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
|
using System.IO;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Resources;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Microsoft.Extensions.Options;
|
||||||
|
|
||||||
|
namespace Microsoft.Extensions.Localization
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// An <see cref="IStringLocalizerFactory"/> that creates instances of <see cref="ResourceManagerStringLocalizer"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// <see cref="ResourceManagerStringLocalizerFactory"/> offers multiple ways to set the relative path of
|
||||||
|
/// resources to be used. They are, in order of precedence:
|
||||||
|
/// <see cref="ResourceLocationAttribute"/> -> <see cref="LocalizationOptions.ResourcesPath"/> -> the project root.
|
||||||
|
/// </remarks>
|
||||||
|
public class ResourceManagerStringLocalizerFactory : IStringLocalizerFactory
|
||||||
|
{
|
||||||
|
private readonly IResourceNamesCache _resourceNamesCache = new ResourceNamesCache();
|
||||||
|
private readonly ConcurrentDictionary<string, ResourceManagerStringLocalizer> _localizerCache =
|
||||||
|
new ConcurrentDictionary<string, ResourceManagerStringLocalizer>();
|
||||||
|
private readonly string _resourcesRelativePath;
|
||||||
|
private readonly ILoggerFactory _loggerFactory;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new <see cref="ResourceManagerStringLocalizer"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="localizationOptions">The <see cref="IOptions{LocalizationOptions}"/>.</param>
|
||||||
|
/// <param name="loggerFactory">The <see cref="ILoggerFactory"/>.</param>
|
||||||
|
public ResourceManagerStringLocalizerFactory(
|
||||||
|
IOptions<LocalizationOptions> localizationOptions,
|
||||||
|
ILoggerFactory loggerFactory)
|
||||||
|
{
|
||||||
|
if (localizationOptions == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(localizationOptions));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (loggerFactory == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(loggerFactory));
|
||||||
|
}
|
||||||
|
|
||||||
|
_resourcesRelativePath = localizationOptions.Value.ResourcesPath ?? string.Empty;
|
||||||
|
_loggerFactory = loggerFactory;
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(_resourcesRelativePath))
|
||||||
|
{
|
||||||
|
_resourcesRelativePath = _resourcesRelativePath.Replace(Path.AltDirectorySeparatorChar, '.')
|
||||||
|
.Replace(Path.DirectorySeparatorChar, '.') + ".";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the resource prefix used to look up the resource.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="typeInfo">The type of the resource to be looked up.</param>
|
||||||
|
/// <returns>The prefix for resource lookup.</returns>
|
||||||
|
protected virtual string GetResourcePrefix(TypeInfo typeInfo)
|
||||||
|
{
|
||||||
|
if (typeInfo == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(typeInfo));
|
||||||
|
}
|
||||||
|
|
||||||
|
return GetResourcePrefix(typeInfo, GetRootNamespace(typeInfo.Assembly), GetResourcePath(typeInfo.Assembly));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the resource prefix used to look up the resource.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="typeInfo">The type of the resource to be looked up.</param>
|
||||||
|
/// <param name="baseNamespace">The base namespace of the application.</param>
|
||||||
|
/// <param name="resourcesRelativePath">The folder containing all resources.</param>
|
||||||
|
/// <returns>The prefix for resource lookup.</returns>
|
||||||
|
/// <remarks>
|
||||||
|
/// For the type "Sample.Controllers.Home" if there's a resourceRelativePath return
|
||||||
|
/// "Sample.Resourcepath.Controllers.Home" if there isn't one then it would return "Sample.Controllers.Home".
|
||||||
|
/// </remarks>
|
||||||
|
protected virtual string GetResourcePrefix(TypeInfo typeInfo, string baseNamespace, string resourcesRelativePath)
|
||||||
|
{
|
||||||
|
if (typeInfo == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(typeInfo));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(baseNamespace))
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(baseNamespace));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(resourcesRelativePath))
|
||||||
|
{
|
||||||
|
return typeInfo.FullName;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// This expectation is defined by dotnet's automatic resource storage.
|
||||||
|
// We have to conform to "{RootNamespace}.{ResourceLocation}.{FullTypeName - AssemblyName}".
|
||||||
|
var assemblyName = new AssemblyName(typeInfo.Assembly.FullName).Name;
|
||||||
|
return baseNamespace + "." + resourcesRelativePath + TrimPrefix(typeInfo.FullName, assemblyName + ".");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the resource prefix used to look up the resource.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="baseResourceName">The name of the resource to be looked up</param>
|
||||||
|
/// <param name="baseNamespace">The base namespace of the application.</param>
|
||||||
|
/// <returns>The prefix for resource lookup.</returns>
|
||||||
|
protected virtual string GetResourcePrefix(string baseResourceName, string baseNamespace)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(baseResourceName))
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(baseResourceName));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(baseNamespace))
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(baseNamespace));
|
||||||
|
}
|
||||||
|
|
||||||
|
var assemblyName = new AssemblyName(baseNamespace);
|
||||||
|
var assembly = Assembly.Load(assemblyName);
|
||||||
|
var rootNamespace = GetRootNamespace(assembly);
|
||||||
|
var resourceLocation = GetResourcePath(assembly);
|
||||||
|
var locationPath = rootNamespace + "." + resourceLocation;
|
||||||
|
|
||||||
|
baseResourceName = locationPath + TrimPrefix(baseResourceName, baseNamespace + ".");
|
||||||
|
|
||||||
|
return baseResourceName;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <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)
|
||||||
|
{
|
||||||
|
if (resourceSource == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(resourceSource));
|
||||||
|
}
|
||||||
|
|
||||||
|
var typeInfo = resourceSource.GetTypeInfo();
|
||||||
|
|
||||||
|
var baseName = GetResourcePrefix(typeInfo);
|
||||||
|
|
||||||
|
var assembly = typeInfo.Assembly;
|
||||||
|
|
||||||
|
return _localizerCache.GetOrAdd(baseName, _ => CreateResourceManagerStringLocalizer(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)
|
||||||
|
{
|
||||||
|
if (baseName == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(baseName));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (location == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(location));
|
||||||
|
}
|
||||||
|
|
||||||
|
return _localizerCache.GetOrAdd($"B={baseName},L={location}", _ =>
|
||||||
|
{
|
||||||
|
var assemblyName = new AssemblyName(location);
|
||||||
|
var assembly = Assembly.Load(assemblyName);
|
||||||
|
baseName = GetResourcePrefix(baseName, location);
|
||||||
|
|
||||||
|
return CreateResourceManagerStringLocalizer(assembly, baseName);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Creates a <see cref="ResourceManagerStringLocalizer"/> for the given input.</summary>
|
||||||
|
/// <param name="assembly">The assembly to create a <see cref="ResourceManagerStringLocalizer"/> for.</param>
|
||||||
|
/// <param name="baseName">The base name of the resource to search for.</param>
|
||||||
|
/// <returns>A <see cref="ResourceManagerStringLocalizer"/> for the given <paramref name="assembly"/> and <paramref name="baseName"/>.</returns>
|
||||||
|
/// <remarks>This method is virtual for testing purposes only.</remarks>
|
||||||
|
protected virtual ResourceManagerStringLocalizer CreateResourceManagerStringLocalizer(
|
||||||
|
Assembly assembly,
|
||||||
|
string baseName)
|
||||||
|
{
|
||||||
|
return new ResourceManagerStringLocalizer(
|
||||||
|
new ResourceManager(baseName, assembly),
|
||||||
|
assembly,
|
||||||
|
baseName,
|
||||||
|
_resourceNamesCache,
|
||||||
|
_loggerFactory.CreateLogger<ResourceManagerStringLocalizer>());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the resource prefix used to look up the resource.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="location">The general location of the resource.</param>
|
||||||
|
/// <param name="baseName">The base name of the resource.</param>
|
||||||
|
/// <param name="resourceLocation">The location of the resource within <paramref name="location"/>.</param>
|
||||||
|
/// <returns>The resource prefix used to look up the resource.</returns>
|
||||||
|
protected virtual string GetResourcePrefix(string location, string baseName, string resourceLocation)
|
||||||
|
{
|
||||||
|
// Re-root the base name if a resources path is set
|
||||||
|
return location + "." + resourceLocation + TrimPrefix(baseName, location + ".");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Gets a <see cref="ResourceLocationAttribute"/> from the provided <see cref="Assembly"/>.</summary>
|
||||||
|
/// <param name="assembly">The assembly to get a <see cref="ResourceLocationAttribute"/> from.</param>
|
||||||
|
/// <returns>The <see cref="ResourceLocationAttribute"/> associated with the given <see cref="Assembly"/>.</returns>
|
||||||
|
/// <remarks>This method is protected and virtual for testing purposes only.</remarks>
|
||||||
|
protected virtual ResourceLocationAttribute GetResourceLocationAttribute(Assembly assembly)
|
||||||
|
{
|
||||||
|
return assembly.GetCustomAttribute<ResourceLocationAttribute>();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Gets a <see cref="RootNamespaceAttribute"/> from the provided <see cref="Assembly"/>.</summary>
|
||||||
|
/// <param name="assembly">The assembly to get a <see cref="RootNamespaceAttribute"/> from.</param>
|
||||||
|
/// <returns>The <see cref="RootNamespaceAttribute"/> associated with the given <see cref="Assembly"/>.</returns>
|
||||||
|
/// <remarks>This method is protected and virtual for testing purposes only.</remarks>
|
||||||
|
protected virtual RootNamespaceAttribute GetRootNamespaceAttribute(Assembly assembly)
|
||||||
|
{
|
||||||
|
return assembly.GetCustomAttribute<RootNamespaceAttribute>();
|
||||||
|
}
|
||||||
|
|
||||||
|
private string GetRootNamespace(Assembly assembly)
|
||||||
|
{
|
||||||
|
var rootNamespaceAttribute = GetRootNamespaceAttribute(assembly);
|
||||||
|
|
||||||
|
return rootNamespaceAttribute?.RootNamespace ??
|
||||||
|
new AssemblyName(assembly.FullName).Name;
|
||||||
|
}
|
||||||
|
|
||||||
|
private string GetResourcePath(Assembly assembly)
|
||||||
|
{
|
||||||
|
var resourceLocationAttribute = GetResourceLocationAttribute(assembly);
|
||||||
|
|
||||||
|
// If we don't have an attribute assume all assemblies use the same resource location.
|
||||||
|
var resourceLocation = resourceLocationAttribute == null
|
||||||
|
? _resourcesRelativePath
|
||||||
|
: resourceLocationAttribute.ResourceLocation + ".";
|
||||||
|
resourceLocation = resourceLocation
|
||||||
|
.Replace(Path.DirectorySeparatorChar, '.')
|
||||||
|
.Replace(Path.AltDirectorySeparatorChar, '.');
|
||||||
|
|
||||||
|
return resourceLocation;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string TrimPrefix(string name, string prefix)
|
||||||
|
{
|
||||||
|
if (name.StartsWith(prefix, StringComparison.Ordinal))
|
||||||
|
{
|
||||||
|
return name.Substring(prefix.Length);
|
||||||
|
}
|
||||||
|
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,164 @@
|
||||||
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Globalization;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Resources;
|
||||||
|
using Microsoft.Extensions.Localization.Internal;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
|
namespace Microsoft.Extensions.Localization
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// An <see cref="IStringLocalizer"/> that uses the <see cref="ResourceManager"/> and
|
||||||
|
/// <see cref="ResourceReader"/> to provide localized strings for a specific <see cref="CultureInfo"/>.
|
||||||
|
/// </summary>
|
||||||
|
public class ResourceManagerWithCultureStringLocalizer : ResourceManagerStringLocalizer
|
||||||
|
{
|
||||||
|
private readonly string _resourceBaseName;
|
||||||
|
private readonly CultureInfo _culture;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new <see cref="ResourceManagerWithCultureStringLocalizer"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="resourceManager">The <see cref="ResourceManager"/> to read strings from.</param>
|
||||||
|
/// <param name="resourceStringProvider">The <see cref="IResourceStringProvider"/> that can find the resources.</param>
|
||||||
|
/// <param name="baseName">The base name of the embedded resource that contains the strings.</param>
|
||||||
|
/// <param name="resourceNamesCache">Cache of the list of strings for a given resource assembly name.</param>
|
||||||
|
/// <param name="culture">The specific <see cref="CultureInfo"/> to use.</param>
|
||||||
|
/// <param name="logger">The <see cref="ILogger"/>.</param>
|
||||||
|
internal ResourceManagerWithCultureStringLocalizer(
|
||||||
|
ResourceManager resourceManager,
|
||||||
|
IResourceStringProvider resourceStringProvider,
|
||||||
|
string baseName,
|
||||||
|
IResourceNamesCache resourceNamesCache,
|
||||||
|
CultureInfo culture,
|
||||||
|
ILogger logger)
|
||||||
|
: base(resourceManager, resourceStringProvider, baseName, resourceNamesCache, logger)
|
||||||
|
{
|
||||||
|
if (resourceManager == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(resourceManager));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (resourceStringProvider == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(resourceStringProvider));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (baseName == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(baseName));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (resourceNamesCache == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(resourceNamesCache));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (culture == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(culture));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (logger == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(logger));
|
||||||
|
}
|
||||||
|
|
||||||
|
_resourceBaseName = baseName;
|
||||||
|
_culture = culture;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new <see cref="ResourceManagerWithCultureStringLocalizer"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="resourceManager">The <see cref="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 that contains the strings.</param>
|
||||||
|
/// <param name="resourceNamesCache">Cache of the list of strings for a given resource assembly name.</param>
|
||||||
|
/// <param name="culture">The specific <see cref="CultureInfo"/> to use.</param>
|
||||||
|
/// <param name="logger">The <see cref="ILogger"/>.</param>
|
||||||
|
public ResourceManagerWithCultureStringLocalizer(
|
||||||
|
ResourceManager resourceManager,
|
||||||
|
Assembly resourceAssembly,
|
||||||
|
string baseName,
|
||||||
|
IResourceNamesCache resourceNamesCache,
|
||||||
|
CultureInfo culture,
|
||||||
|
ILogger logger)
|
||||||
|
: base(resourceManager, resourceAssembly, baseName, resourceNamesCache, logger)
|
||||||
|
{
|
||||||
|
if (resourceManager == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(resourceManager));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (resourceAssembly == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(resourceAssembly));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (baseName == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(baseName));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (resourceNamesCache == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(resourceNamesCache));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (culture == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(culture));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (logger == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(logger));
|
||||||
|
}
|
||||||
|
|
||||||
|
_resourceBaseName = baseName;
|
||||||
|
_culture = culture;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override LocalizedString this[string name]
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (name == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(name));
|
||||||
|
}
|
||||||
|
|
||||||
|
var value = GetStringSafely(name, _culture);
|
||||||
|
|
||||||
|
return new LocalizedString(name, value ?? name, resourceNotFound: value == null, searchedLocation: _resourceBaseName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override LocalizedString this[string name, params object[] arguments]
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (name == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(name));
|
||||||
|
}
|
||||||
|
|
||||||
|
var format = GetStringSafely(name, _culture);
|
||||||
|
var value = string.Format(_culture, format ?? name, arguments);
|
||||||
|
|
||||||
|
return new LocalizedString(name, value, resourceNotFound: format == null, searchedLocation: _resourceBaseName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override IEnumerable<LocalizedString> GetAllStrings(bool includeParentCultures) =>
|
||||||
|
GetAllStrings(includeParentCultures, _culture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,23 @@
|
||||||
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Microsoft.Extensions.Localization
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// An implementation of <see cref="IResourceNamesCache"/> backed by a <see cref="ConcurrentDictionary{TKey, TValue}"/>.
|
||||||
|
/// </summary>
|
||||||
|
public class ResourceNamesCache : IResourceNamesCache
|
||||||
|
{
|
||||||
|
private readonly ConcurrentDictionary<string, IList<string>> _cache = new ConcurrentDictionary<string, IList<string>>();
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public IList<string> GetOrAdd(string name, Func<string, IList<string>> valueFactory)
|
||||||
|
{
|
||||||
|
return _cache.GetOrAdd(name, valueFactory);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,126 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<root>
|
||||||
|
<!--
|
||||||
|
Microsoft ResX Schema
|
||||||
|
|
||||||
|
Version 2.0
|
||||||
|
|
||||||
|
The primary goals of this format is to allow a simple XML format
|
||||||
|
that is mostly human readable. The generation and parsing of the
|
||||||
|
various data types are done through the TypeConverter classes
|
||||||
|
associated with the data types.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
... ado.net/XML headers & schema ...
|
||||||
|
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||||
|
<resheader name="version">2.0</resheader>
|
||||||
|
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||||
|
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||||
|
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||||
|
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||||
|
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||||
|
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||||
|
</data>
|
||||||
|
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||||
|
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||||
|
<comment>This is a comment</comment>
|
||||||
|
</data>
|
||||||
|
|
||||||
|
There are any number of "resheader" rows that contain simple
|
||||||
|
name/value pairs.
|
||||||
|
|
||||||
|
Each data row contains a name, and value. The row also contains a
|
||||||
|
type or mimetype. Type corresponds to a .NET class that support
|
||||||
|
text/value conversion through the TypeConverter architecture.
|
||||||
|
Classes that don't support this are serialized and stored with the
|
||||||
|
mimetype set.
|
||||||
|
|
||||||
|
The mimetype is used for serialized objects, and tells the
|
||||||
|
ResXResourceReader how to depersist the object. This is currently not
|
||||||
|
extensible. For a given mimetype the value must be set accordingly:
|
||||||
|
|
||||||
|
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||||
|
that the ResXResourceWriter will generate, however the reader can
|
||||||
|
read any of the formats listed below.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.binary.base64
|
||||||
|
value : The object must be serialized with
|
||||||
|
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.soap.base64
|
||||||
|
value : The object must be serialized with
|
||||||
|
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||||
|
value : The object must be serialized into a byte array
|
||||||
|
: using a System.ComponentModel.TypeConverter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
-->
|
||||||
|
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||||
|
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||||
|
<xsd:element name="root" msdata:IsDataSet="true">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:choice maxOccurs="unbounded">
|
||||||
|
<xsd:element name="metadata">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||||
|
<xsd:attribute name="type" type="xsd:string" />
|
||||||
|
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||||
|
<xsd:attribute ref="xml:space" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="assembly">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:attribute name="alias" type="xsd:string" />
|
||||||
|
<xsd:attribute name="name" type="xsd:string" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="data">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||||
|
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||||
|
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||||
|
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||||
|
<xsd:attribute ref="xml:space" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="resheader">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
</xsd:choice>
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
</xsd:schema>
|
||||||
|
<resheader name="resmimetype">
|
||||||
|
<value>text/microsoft-resx</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="version">
|
||||||
|
<value>2.0</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="reader">
|
||||||
|
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="writer">
|
||||||
|
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||||
|
</resheader>
|
||||||
|
<data name="Localization_MissingManifest" xml:space="preserve">
|
||||||
|
<value>The manifest '{0}' was not found.</value>
|
||||||
|
</data>
|
||||||
|
<data name="Localization_MissingManifest_Parent" xml:space="preserve">
|
||||||
|
<value>No manifests exist for the current culture.</value>
|
||||||
|
</data>
|
||||||
|
</root>
|
||||||
|
|
@ -0,0 +1,35 @@
|
||||||
|
// 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.Extensions.Localization
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Provides the RootNamespace of an Assembly. The RootNamespace of the assembly is used by Localization to
|
||||||
|
/// determine the resource name to look for when RootNamespace differs from the AssemblyName.
|
||||||
|
/// </summary>
|
||||||
|
[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = false, Inherited = false)]
|
||||||
|
public class RootNamespaceAttribute : Attribute
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new <see cref="RootNamespaceAttribute"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="rootNamespace">The RootNamespace for this Assembly.</param>
|
||||||
|
public RootNamespaceAttribute(string rootNamespace)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(rootNamespace))
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(rootNamespace));
|
||||||
|
}
|
||||||
|
|
||||||
|
RootNamespace = rootNamespace;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The RootNamespace of this Assembly. The RootNamespace of the assembly is used by Localization to
|
||||||
|
/// determine the resource name to look for when RootNamespace differs from the AssemblyName.
|
||||||
|
/// </summary>
|
||||||
|
public string RootNamespace { get; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,687 @@
|
||||||
|
{
|
||||||
|
"AssemblyIdentity": "Microsoft.Extensions.Localization, Version=2.1.1.0, Culture=neutral, PublicKeyToken=adb9793829ddae60",
|
||||||
|
"Types": [
|
||||||
|
{
|
||||||
|
"Name": "Microsoft.Extensions.DependencyInjection.LocalizationServiceCollectionExtensions",
|
||||||
|
"Visibility": "Public",
|
||||||
|
"Kind": "Class",
|
||||||
|
"Abstract": true,
|
||||||
|
"Static": true,
|
||||||
|
"Sealed": true,
|
||||||
|
"ImplementedInterfaces": [],
|
||||||
|
"Members": [
|
||||||
|
{
|
||||||
|
"Kind": "Method",
|
||||||
|
"Name": "AddLocalization",
|
||||||
|
"Parameters": [
|
||||||
|
{
|
||||||
|
"Name": "services",
|
||||||
|
"Type": "Microsoft.Extensions.DependencyInjection.IServiceCollection"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"ReturnType": "Microsoft.Extensions.DependencyInjection.IServiceCollection",
|
||||||
|
"Static": true,
|
||||||
|
"Extension": true,
|
||||||
|
"Visibility": "Public",
|
||||||
|
"GenericParameter": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Kind": "Method",
|
||||||
|
"Name": "AddLocalization",
|
||||||
|
"Parameters": [
|
||||||
|
{
|
||||||
|
"Name": "services",
|
||||||
|
"Type": "Microsoft.Extensions.DependencyInjection.IServiceCollection"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "setupAction",
|
||||||
|
"Type": "System.Action<Microsoft.Extensions.Localization.LocalizationOptions>"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"ReturnType": "Microsoft.Extensions.DependencyInjection.IServiceCollection",
|
||||||
|
"Static": true,
|
||||||
|
"Extension": true,
|
||||||
|
"Visibility": "Public",
|
||||||
|
"GenericParameter": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"GenericParameters": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "Microsoft.Extensions.Localization.IResourceNamesCache",
|
||||||
|
"Visibility": "Public",
|
||||||
|
"Kind": "Interface",
|
||||||
|
"Abstract": true,
|
||||||
|
"ImplementedInterfaces": [],
|
||||||
|
"Members": [
|
||||||
|
{
|
||||||
|
"Kind": "Method",
|
||||||
|
"Name": "GetOrAdd",
|
||||||
|
"Parameters": [
|
||||||
|
{
|
||||||
|
"Name": "name",
|
||||||
|
"Type": "System.String"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "valueFactory",
|
||||||
|
"Type": "System.Func<System.String, System.Collections.Generic.IList<System.String>>"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"ReturnType": "System.Collections.Generic.IList<System.String>",
|
||||||
|
"GenericParameter": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"GenericParameters": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "Microsoft.Extensions.Localization.LocalizationOptions",
|
||||||
|
"Visibility": "Public",
|
||||||
|
"Kind": "Class",
|
||||||
|
"ImplementedInterfaces": [],
|
||||||
|
"Members": [
|
||||||
|
{
|
||||||
|
"Kind": "Method",
|
||||||
|
"Name": "get_ResourcesPath",
|
||||||
|
"Parameters": [],
|
||||||
|
"ReturnType": "System.String",
|
||||||
|
"Visibility": "Public",
|
||||||
|
"GenericParameter": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Kind": "Method",
|
||||||
|
"Name": "set_ResourcesPath",
|
||||||
|
"Parameters": [
|
||||||
|
{
|
||||||
|
"Name": "value",
|
||||||
|
"Type": "System.String"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"ReturnType": "System.Void",
|
||||||
|
"Visibility": "Public",
|
||||||
|
"GenericParameter": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Kind": "Constructor",
|
||||||
|
"Name": ".ctor",
|
||||||
|
"Parameters": [],
|
||||||
|
"Visibility": "Public",
|
||||||
|
"GenericParameter": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"GenericParameters": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "Microsoft.Extensions.Localization.ResourceLocationAttribute",
|
||||||
|
"Visibility": "Public",
|
||||||
|
"Kind": "Class",
|
||||||
|
"BaseType": "System.Attribute",
|
||||||
|
"ImplementedInterfaces": [],
|
||||||
|
"Members": [
|
||||||
|
{
|
||||||
|
"Kind": "Method",
|
||||||
|
"Name": "get_ResourceLocation",
|
||||||
|
"Parameters": [],
|
||||||
|
"ReturnType": "System.String",
|
||||||
|
"Visibility": "Public",
|
||||||
|
"GenericParameter": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Kind": "Constructor",
|
||||||
|
"Name": ".ctor",
|
||||||
|
"Parameters": [
|
||||||
|
{
|
||||||
|
"Name": "resourceLocation",
|
||||||
|
"Type": "System.String"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"Visibility": "Public",
|
||||||
|
"GenericParameter": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"GenericParameters": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "Microsoft.Extensions.Localization.ResourceManagerStringLocalizer",
|
||||||
|
"Visibility": "Public",
|
||||||
|
"Kind": "Class",
|
||||||
|
"ImplementedInterfaces": [
|
||||||
|
"Microsoft.Extensions.Localization.IStringLocalizer"
|
||||||
|
],
|
||||||
|
"Members": [
|
||||||
|
{
|
||||||
|
"Kind": "Method",
|
||||||
|
"Name": "get_Item",
|
||||||
|
"Parameters": [
|
||||||
|
{
|
||||||
|
"Name": "name",
|
||||||
|
"Type": "System.String"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"ReturnType": "Microsoft.Extensions.Localization.LocalizedString",
|
||||||
|
"Virtual": true,
|
||||||
|
"ImplementedInterface": "Microsoft.Extensions.Localization.IStringLocalizer",
|
||||||
|
"Visibility": "Public",
|
||||||
|
"GenericParameter": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Kind": "Method",
|
||||||
|
"Name": "get_Item",
|
||||||
|
"Parameters": [
|
||||||
|
{
|
||||||
|
"Name": "name",
|
||||||
|
"Type": "System.String"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "arguments",
|
||||||
|
"Type": "System.Object[]",
|
||||||
|
"IsParams": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"ReturnType": "Microsoft.Extensions.Localization.LocalizedString",
|
||||||
|
"Virtual": true,
|
||||||
|
"ImplementedInterface": "Microsoft.Extensions.Localization.IStringLocalizer",
|
||||||
|
"Visibility": "Public",
|
||||||
|
"GenericParameter": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Kind": "Method",
|
||||||
|
"Name": "WithCulture",
|
||||||
|
"Parameters": [
|
||||||
|
{
|
||||||
|
"Name": "culture",
|
||||||
|
"Type": "System.Globalization.CultureInfo"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"ReturnType": "Microsoft.Extensions.Localization.IStringLocalizer",
|
||||||
|
"Sealed": true,
|
||||||
|
"Virtual": true,
|
||||||
|
"ImplementedInterface": "Microsoft.Extensions.Localization.IStringLocalizer",
|
||||||
|
"Visibility": "Public",
|
||||||
|
"GenericParameter": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Kind": "Method",
|
||||||
|
"Name": "GetAllStrings",
|
||||||
|
"Parameters": [
|
||||||
|
{
|
||||||
|
"Name": "includeParentCultures",
|
||||||
|
"Type": "System.Boolean"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"ReturnType": "System.Collections.Generic.IEnumerable<Microsoft.Extensions.Localization.LocalizedString>",
|
||||||
|
"Virtual": true,
|
||||||
|
"ImplementedInterface": "Microsoft.Extensions.Localization.IStringLocalizer",
|
||||||
|
"Visibility": "Public",
|
||||||
|
"GenericParameter": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Kind": "Method",
|
||||||
|
"Name": "GetAllStrings",
|
||||||
|
"Parameters": [
|
||||||
|
{
|
||||||
|
"Name": "includeParentCultures",
|
||||||
|
"Type": "System.Boolean"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "culture",
|
||||||
|
"Type": "System.Globalization.CultureInfo"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"ReturnType": "System.Collections.Generic.IEnumerable<Microsoft.Extensions.Localization.LocalizedString>",
|
||||||
|
"Visibility": "Protected",
|
||||||
|
"GenericParameter": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Kind": "Method",
|
||||||
|
"Name": "GetStringSafely",
|
||||||
|
"Parameters": [
|
||||||
|
{
|
||||||
|
"Name": "name",
|
||||||
|
"Type": "System.String"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "culture",
|
||||||
|
"Type": "System.Globalization.CultureInfo"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"ReturnType": "System.String",
|
||||||
|
"Visibility": "Protected",
|
||||||
|
"GenericParameter": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Kind": "Constructor",
|
||||||
|
"Name": ".ctor",
|
||||||
|
"Parameters": [
|
||||||
|
{
|
||||||
|
"Name": "resourceManager",
|
||||||
|
"Type": "System.Resources.ResourceManager"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "resourceAssembly",
|
||||||
|
"Type": "System.Reflection.Assembly"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "baseName",
|
||||||
|
"Type": "System.String"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "resourceNamesCache",
|
||||||
|
"Type": "Microsoft.Extensions.Localization.IResourceNamesCache"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "logger",
|
||||||
|
"Type": "Microsoft.Extensions.Logging.ILogger"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"Visibility": "Public",
|
||||||
|
"GenericParameter": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Kind": "Constructor",
|
||||||
|
"Name": ".ctor",
|
||||||
|
"Parameters": [
|
||||||
|
{
|
||||||
|
"Name": "resourceManager",
|
||||||
|
"Type": "System.Resources.ResourceManager"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "resourceAssemblyWrapper",
|
||||||
|
"Type": "Microsoft.Extensions.Localization.Internal.AssemblyWrapper"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "baseName",
|
||||||
|
"Type": "System.String"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "resourceNamesCache",
|
||||||
|
"Type": "Microsoft.Extensions.Localization.IResourceNamesCache"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "logger",
|
||||||
|
"Type": "Microsoft.Extensions.Logging.ILogger"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"Visibility": "Public",
|
||||||
|
"GenericParameter": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Kind": "Constructor",
|
||||||
|
"Name": ".ctor",
|
||||||
|
"Parameters": [
|
||||||
|
{
|
||||||
|
"Name": "resourceManager",
|
||||||
|
"Type": "System.Resources.ResourceManager"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "resourceStringProvider",
|
||||||
|
"Type": "Microsoft.Extensions.Localization.Internal.IResourceStringProvider"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "baseName",
|
||||||
|
"Type": "System.String"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "resourceNamesCache",
|
||||||
|
"Type": "Microsoft.Extensions.Localization.IResourceNamesCache"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "logger",
|
||||||
|
"Type": "Microsoft.Extensions.Logging.ILogger"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"Visibility": "Public",
|
||||||
|
"GenericParameter": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"GenericParameters": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "Microsoft.Extensions.Localization.ResourceManagerStringLocalizerFactory",
|
||||||
|
"Visibility": "Public",
|
||||||
|
"Kind": "Class",
|
||||||
|
"ImplementedInterfaces": [
|
||||||
|
"Microsoft.Extensions.Localization.IStringLocalizerFactory"
|
||||||
|
],
|
||||||
|
"Members": [
|
||||||
|
{
|
||||||
|
"Kind": "Method",
|
||||||
|
"Name": "GetResourcePrefix",
|
||||||
|
"Parameters": [
|
||||||
|
{
|
||||||
|
"Name": "typeInfo",
|
||||||
|
"Type": "System.Reflection.TypeInfo"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"ReturnType": "System.String",
|
||||||
|
"Virtual": true,
|
||||||
|
"Visibility": "Protected",
|
||||||
|
"GenericParameter": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Kind": "Method",
|
||||||
|
"Name": "GetResourcePrefix",
|
||||||
|
"Parameters": [
|
||||||
|
{
|
||||||
|
"Name": "typeInfo",
|
||||||
|
"Type": "System.Reflection.TypeInfo"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "baseNamespace",
|
||||||
|
"Type": "System.String"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "resourcesRelativePath",
|
||||||
|
"Type": "System.String"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"ReturnType": "System.String",
|
||||||
|
"Virtual": true,
|
||||||
|
"Visibility": "Protected",
|
||||||
|
"GenericParameter": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Kind": "Method",
|
||||||
|
"Name": "GetResourcePrefix",
|
||||||
|
"Parameters": [
|
||||||
|
{
|
||||||
|
"Name": "baseResourceName",
|
||||||
|
"Type": "System.String"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "baseNamespace",
|
||||||
|
"Type": "System.String"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"ReturnType": "System.String",
|
||||||
|
"Virtual": true,
|
||||||
|
"Visibility": "Protected",
|
||||||
|
"GenericParameter": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Kind": "Method",
|
||||||
|
"Name": "Create",
|
||||||
|
"Parameters": [
|
||||||
|
{
|
||||||
|
"Name": "resourceSource",
|
||||||
|
"Type": "System.Type"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"ReturnType": "Microsoft.Extensions.Localization.IStringLocalizer",
|
||||||
|
"Sealed": true,
|
||||||
|
"Virtual": true,
|
||||||
|
"ImplementedInterface": "Microsoft.Extensions.Localization.IStringLocalizerFactory",
|
||||||
|
"Visibility": "Public",
|
||||||
|
"GenericParameter": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Kind": "Method",
|
||||||
|
"Name": "Create",
|
||||||
|
"Parameters": [
|
||||||
|
{
|
||||||
|
"Name": "baseName",
|
||||||
|
"Type": "System.String"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "location",
|
||||||
|
"Type": "System.String"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"ReturnType": "Microsoft.Extensions.Localization.IStringLocalizer",
|
||||||
|
"Sealed": true,
|
||||||
|
"Virtual": true,
|
||||||
|
"ImplementedInterface": "Microsoft.Extensions.Localization.IStringLocalizerFactory",
|
||||||
|
"Visibility": "Public",
|
||||||
|
"GenericParameter": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Kind": "Method",
|
||||||
|
"Name": "CreateResourceManagerStringLocalizer",
|
||||||
|
"Parameters": [
|
||||||
|
{
|
||||||
|
"Name": "assembly",
|
||||||
|
"Type": "System.Reflection.Assembly"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "baseName",
|
||||||
|
"Type": "System.String"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"ReturnType": "Microsoft.Extensions.Localization.ResourceManagerStringLocalizer",
|
||||||
|
"Virtual": true,
|
||||||
|
"Visibility": "Protected",
|
||||||
|
"GenericParameter": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Kind": "Method",
|
||||||
|
"Name": "GetResourcePrefix",
|
||||||
|
"Parameters": [
|
||||||
|
{
|
||||||
|
"Name": "location",
|
||||||
|
"Type": "System.String"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "baseName",
|
||||||
|
"Type": "System.String"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "resourceLocation",
|
||||||
|
"Type": "System.String"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"ReturnType": "System.String",
|
||||||
|
"Virtual": true,
|
||||||
|
"Visibility": "Protected",
|
||||||
|
"GenericParameter": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Kind": "Method",
|
||||||
|
"Name": "GetResourceLocationAttribute",
|
||||||
|
"Parameters": [
|
||||||
|
{
|
||||||
|
"Name": "assembly",
|
||||||
|
"Type": "System.Reflection.Assembly"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"ReturnType": "Microsoft.Extensions.Localization.ResourceLocationAttribute",
|
||||||
|
"Virtual": true,
|
||||||
|
"Visibility": "Protected",
|
||||||
|
"GenericParameter": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Kind": "Method",
|
||||||
|
"Name": "GetRootNamespaceAttribute",
|
||||||
|
"Parameters": [
|
||||||
|
{
|
||||||
|
"Name": "assembly",
|
||||||
|
"Type": "System.Reflection.Assembly"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"ReturnType": "Microsoft.Extensions.Localization.RootNamespaceAttribute",
|
||||||
|
"Virtual": true,
|
||||||
|
"Visibility": "Protected",
|
||||||
|
"GenericParameter": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Kind": "Constructor",
|
||||||
|
"Name": ".ctor",
|
||||||
|
"Parameters": [
|
||||||
|
{
|
||||||
|
"Name": "localizationOptions",
|
||||||
|
"Type": "Microsoft.Extensions.Options.IOptions<Microsoft.Extensions.Localization.LocalizationOptions>"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "loggerFactory",
|
||||||
|
"Type": "Microsoft.Extensions.Logging.ILoggerFactory"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"Visibility": "Public",
|
||||||
|
"GenericParameter": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"GenericParameters": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "Microsoft.Extensions.Localization.ResourceManagerWithCultureStringLocalizer",
|
||||||
|
"Visibility": "Public",
|
||||||
|
"Kind": "Class",
|
||||||
|
"BaseType": "Microsoft.Extensions.Localization.ResourceManagerStringLocalizer",
|
||||||
|
"ImplementedInterfaces": [],
|
||||||
|
"Members": [
|
||||||
|
{
|
||||||
|
"Kind": "Method",
|
||||||
|
"Name": "get_Item",
|
||||||
|
"Parameters": [
|
||||||
|
{
|
||||||
|
"Name": "name",
|
||||||
|
"Type": "System.String"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"ReturnType": "Microsoft.Extensions.Localization.LocalizedString",
|
||||||
|
"Virtual": true,
|
||||||
|
"Override": true,
|
||||||
|
"ImplementedInterface": "Microsoft.Extensions.Localization.IStringLocalizer",
|
||||||
|
"Visibility": "Public",
|
||||||
|
"GenericParameter": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Kind": "Method",
|
||||||
|
"Name": "get_Item",
|
||||||
|
"Parameters": [
|
||||||
|
{
|
||||||
|
"Name": "name",
|
||||||
|
"Type": "System.String"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "arguments",
|
||||||
|
"Type": "System.Object[]",
|
||||||
|
"IsParams": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"ReturnType": "Microsoft.Extensions.Localization.LocalizedString",
|
||||||
|
"Virtual": true,
|
||||||
|
"Override": true,
|
||||||
|
"ImplementedInterface": "Microsoft.Extensions.Localization.IStringLocalizer",
|
||||||
|
"Visibility": "Public",
|
||||||
|
"GenericParameter": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Kind": "Method",
|
||||||
|
"Name": "GetAllStrings",
|
||||||
|
"Parameters": [
|
||||||
|
{
|
||||||
|
"Name": "includeParentCultures",
|
||||||
|
"Type": "System.Boolean"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"ReturnType": "System.Collections.Generic.IEnumerable<Microsoft.Extensions.Localization.LocalizedString>",
|
||||||
|
"Virtual": true,
|
||||||
|
"Override": true,
|
||||||
|
"ImplementedInterface": "Microsoft.Extensions.Localization.IStringLocalizer",
|
||||||
|
"Visibility": "Public",
|
||||||
|
"GenericParameter": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Kind": "Constructor",
|
||||||
|
"Name": ".ctor",
|
||||||
|
"Parameters": [
|
||||||
|
{
|
||||||
|
"Name": "resourceManager",
|
||||||
|
"Type": "System.Resources.ResourceManager"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "resourceAssembly",
|
||||||
|
"Type": "System.Reflection.Assembly"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "baseName",
|
||||||
|
"Type": "System.String"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "resourceNamesCache",
|
||||||
|
"Type": "Microsoft.Extensions.Localization.IResourceNamesCache"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "culture",
|
||||||
|
"Type": "System.Globalization.CultureInfo"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "logger",
|
||||||
|
"Type": "Microsoft.Extensions.Logging.ILogger"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"Visibility": "Public",
|
||||||
|
"GenericParameter": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"GenericParameters": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "Microsoft.Extensions.Localization.ResourceNamesCache",
|
||||||
|
"Visibility": "Public",
|
||||||
|
"Kind": "Class",
|
||||||
|
"ImplementedInterfaces": [
|
||||||
|
"Microsoft.Extensions.Localization.IResourceNamesCache"
|
||||||
|
],
|
||||||
|
"Members": [
|
||||||
|
{
|
||||||
|
"Kind": "Method",
|
||||||
|
"Name": "GetOrAdd",
|
||||||
|
"Parameters": [
|
||||||
|
{
|
||||||
|
"Name": "name",
|
||||||
|
"Type": "System.String"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "valueFactory",
|
||||||
|
"Type": "System.Func<System.String, System.Collections.Generic.IList<System.String>>"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"ReturnType": "System.Collections.Generic.IList<System.String>",
|
||||||
|
"Sealed": true,
|
||||||
|
"Virtual": true,
|
||||||
|
"ImplementedInterface": "Microsoft.Extensions.Localization.IResourceNamesCache",
|
||||||
|
"Visibility": "Public",
|
||||||
|
"GenericParameter": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Kind": "Constructor",
|
||||||
|
"Name": ".ctor",
|
||||||
|
"Parameters": [],
|
||||||
|
"Visibility": "Public",
|
||||||
|
"GenericParameter": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"GenericParameters": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "Microsoft.Extensions.Localization.RootNamespaceAttribute",
|
||||||
|
"Visibility": "Public",
|
||||||
|
"Kind": "Class",
|
||||||
|
"BaseType": "System.Attribute",
|
||||||
|
"ImplementedInterfaces": [],
|
||||||
|
"Members": [
|
||||||
|
{
|
||||||
|
"Kind": "Method",
|
||||||
|
"Name": "get_RootNamespace",
|
||||||
|
"Parameters": [],
|
||||||
|
"ReturnType": "System.String",
|
||||||
|
"Visibility": "Public",
|
||||||
|
"GenericParameter": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Kind": "Constructor",
|
||||||
|
"Name": ".ctor",
|
||||||
|
"Parameters": [
|
||||||
|
{
|
||||||
|
"Name": "rootNamespace",
|
||||||
|
"Type": "System.String"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"Visibility": "Public",
|
||||||
|
"GenericParameter": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"GenericParameters": []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,68 @@
|
||||||
|
// 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.Linq;
|
||||||
|
using Microsoft.Extensions.Localization;
|
||||||
|
using Microsoft.Extensions.Options;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Microsoft.Extensions.DependencyInjection
|
||||||
|
{
|
||||||
|
public class LocalizationServiceCollectionExtensionsTest
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void AddLocalization_AddsNeededServices()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var collection = new ServiceCollection();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
LocalizationServiceCollectionExtensions.AddLocalizationServices(collection);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
AssertContainsSingle(collection, typeof(IStringLocalizerFactory), typeof(ResourceManagerStringLocalizerFactory));
|
||||||
|
AssertContainsSingle(collection, typeof(IStringLocalizer<>), typeof(StringLocalizer<>));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void AddLocalizationWithLocalizationOptions_AddsNeededServices()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var collection = new ServiceCollection();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
LocalizationServiceCollectionExtensions.AddLocalizationServices(
|
||||||
|
collection,
|
||||||
|
options => options.ResourcesPath = "Resources");
|
||||||
|
|
||||||
|
AssertContainsSingle(collection, typeof(IStringLocalizerFactory), typeof(ResourceManagerStringLocalizerFactory));
|
||||||
|
AssertContainsSingle(collection, typeof(IStringLocalizer<>), typeof(StringLocalizer<>));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AssertContainsSingle(
|
||||||
|
IServiceCollection services,
|
||||||
|
Type serviceType,
|
||||||
|
Type implementationType)
|
||||||
|
{
|
||||||
|
var matches = services
|
||||||
|
.Where(sd =>
|
||||||
|
sd.ServiceType == serviceType &&
|
||||||
|
sd.ImplementationType == implementationType)
|
||||||
|
.ToArray();
|
||||||
|
|
||||||
|
if (matches.Length == 0)
|
||||||
|
{
|
||||||
|
Assert.True(
|
||||||
|
false,
|
||||||
|
$"Could not find an instance of {implementationType} registered as {serviceType}");
|
||||||
|
}
|
||||||
|
else if (matches.Length > 1)
|
||||||
|
{
|
||||||
|
Assert.True(
|
||||||
|
false,
|
||||||
|
$"Found multiple instances of {implementationType} registered as {serviceType}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,14 @@
|
||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFrameworks>$(StandardTestTfms)</TargetFrameworks>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Reference Include="Microsoft.AspNetCore.Testing" />
|
||||||
|
<Reference Include="Microsoft.Extensions.Localization" />
|
||||||
|
<Reference Include="Microsoft.Extensions.DependencyInjection" />
|
||||||
|
<Reference Include="Microsoft.Extensions.Logging.Testing" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
|
|
@ -0,0 +1,296 @@
|
||||||
|
// 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.IO;
|
||||||
|
using System.Reflection;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Microsoft.Extensions.Logging.Abstractions;
|
||||||
|
using Microsoft.Extensions.Options;
|
||||||
|
using Moq;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
// This namespace intentionally matches the default assembly namespace.
|
||||||
|
namespace Microsoft.Extensions.Localization.Tests
|
||||||
|
{
|
||||||
|
public class TestResourceManagerStringLocalizerFactory : ResourceManagerStringLocalizerFactory
|
||||||
|
{
|
||||||
|
private ResourceLocationAttribute _resourceLocationAttribute;
|
||||||
|
|
||||||
|
private RootNamespaceAttribute _rootNamespaceAttribute;
|
||||||
|
|
||||||
|
public Assembly Assembly { get; private set; }
|
||||||
|
public string BaseName { get; private set; }
|
||||||
|
|
||||||
|
public TestResourceManagerStringLocalizerFactory(
|
||||||
|
IOptions<LocalizationOptions> localizationOptions,
|
||||||
|
ResourceLocationAttribute resourceLocationAttribute,
|
||||||
|
RootNamespaceAttribute rootNamespaceAttribute,
|
||||||
|
ILoggerFactory loggerFactory)
|
||||||
|
: base(localizationOptions, loggerFactory)
|
||||||
|
{
|
||||||
|
_resourceLocationAttribute = resourceLocationAttribute;
|
||||||
|
_rootNamespaceAttribute = rootNamespaceAttribute;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override ResourceLocationAttribute GetResourceLocationAttribute(Assembly assembly)
|
||||||
|
{
|
||||||
|
return _resourceLocationAttribute;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override RootNamespaceAttribute GetRootNamespaceAttribute(Assembly assembly)
|
||||||
|
{
|
||||||
|
return _rootNamespaceAttribute;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override ResourceManagerStringLocalizer CreateResourceManagerStringLocalizer(Assembly assembly, string baseName)
|
||||||
|
{
|
||||||
|
BaseName = baseName;
|
||||||
|
Assembly = assembly;
|
||||||
|
|
||||||
|
return base.CreateResourceManagerStringLocalizer(assembly, baseName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ResourceManagerStringLocalizerFactoryTest
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void Create_OverloadsProduceSameResult()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var locOptions = new LocalizationOptions();
|
||||||
|
var options = new Mock<IOptions<LocalizationOptions>>();
|
||||||
|
options.Setup(o => o.Value).Returns(locOptions);
|
||||||
|
|
||||||
|
var resourceLocationAttribute = new ResourceLocationAttribute(Path.Combine("My", "Resources"));
|
||||||
|
var loggerFactory = NullLoggerFactory.Instance;
|
||||||
|
var typeFactory = new TestResourceManagerStringLocalizerFactory(
|
||||||
|
options.Object,
|
||||||
|
resourceLocationAttribute,
|
||||||
|
rootNamespaceAttribute: null,
|
||||||
|
loggerFactory: loggerFactory);
|
||||||
|
var stringFactory = new TestResourceManagerStringLocalizerFactory(
|
||||||
|
options.Object,
|
||||||
|
resourceLocationAttribute,
|
||||||
|
rootNamespaceAttribute: null,
|
||||||
|
loggerFactory: loggerFactory);
|
||||||
|
var type = typeof(ResourceManagerStringLocalizerFactoryTest);
|
||||||
|
var assemblyName = new AssemblyName(type.GetTypeInfo().Assembly.FullName);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
typeFactory.Create(type);
|
||||||
|
stringFactory.Create(type.Name, assemblyName.Name);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(typeFactory.BaseName, stringFactory.BaseName);
|
||||||
|
Assert.Equal(typeFactory.Assembly.FullName, stringFactory.Assembly.FullName);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Create_FromType_ReturnsCachedResultForSameType()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var locOptions = new LocalizationOptions();
|
||||||
|
var options = new Mock<IOptions<LocalizationOptions>>();
|
||||||
|
options.Setup(o => o.Value).Returns(locOptions);
|
||||||
|
var loggerFactory = NullLoggerFactory.Instance;
|
||||||
|
var factory = new ResourceManagerStringLocalizerFactory(localizationOptions: options.Object, loggerFactory: loggerFactory);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result1 = factory.Create(typeof(ResourceManagerStringLocalizerFactoryTest));
|
||||||
|
var result2 = factory.Create(typeof(ResourceManagerStringLocalizerFactoryTest));
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Same(result1, result2);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Create_FromType_ReturnsNewResultForDifferentType()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var locOptions = new LocalizationOptions();
|
||||||
|
var options = new Mock<IOptions<LocalizationOptions>>();
|
||||||
|
options.Setup(o => o.Value).Returns(locOptions);
|
||||||
|
var loggerFactory = NullLoggerFactory.Instance;
|
||||||
|
var factory = new ResourceManagerStringLocalizerFactory(localizationOptions: options.Object, loggerFactory: loggerFactory);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result1 = factory.Create(typeof(ResourceManagerStringLocalizerFactoryTest));
|
||||||
|
var result2 = factory.Create(typeof(LocalizationOptions));
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.NotSame(result1, result2);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Create_ResourceLocationAttribute_RootNamespaceIgnoredWhenNoLocation()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var locOptions = new LocalizationOptions();
|
||||||
|
var options = new Mock<IOptions<LocalizationOptions>>();
|
||||||
|
options.Setup(o => o.Value).Returns(locOptions);
|
||||||
|
var loggerFactory = NullLoggerFactory.Instance;
|
||||||
|
|
||||||
|
var resourcePath = Path.Combine("My", "Resources");
|
||||||
|
var rootNamespace = "MyNamespace";
|
||||||
|
var rootNamespaceAttribute = new RootNamespaceAttribute(rootNamespace);
|
||||||
|
|
||||||
|
var typeFactory = new TestResourceManagerStringLocalizerFactory(
|
||||||
|
options.Object,
|
||||||
|
resourceLocationAttribute: null,
|
||||||
|
rootNamespaceAttribute: rootNamespaceAttribute,
|
||||||
|
loggerFactory: loggerFactory);
|
||||||
|
|
||||||
|
var type = typeof(ResourceManagerStringLocalizerFactoryTest);
|
||||||
|
// Act
|
||||||
|
typeFactory.Create(type);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal($"Microsoft.Extensions.Localization.Tests.ResourceManagerStringLocalizerFactoryTest", typeFactory.BaseName);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Create_ResourceLocationAttribute_UsesRootNamespace()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var locOptions = new LocalizationOptions();
|
||||||
|
var options = new Mock<IOptions<LocalizationOptions>>();
|
||||||
|
options.Setup(o => o.Value).Returns(locOptions);
|
||||||
|
var loggerFactory = NullLoggerFactory.Instance;
|
||||||
|
|
||||||
|
var resourcePath = Path.Combine("My", "Resources");
|
||||||
|
var rootNamespace = "MyNamespace";
|
||||||
|
var resourceLocationAttribute = new ResourceLocationAttribute(resourcePath);
|
||||||
|
var rootNamespaceAttribute = new RootNamespaceAttribute(rootNamespace);
|
||||||
|
|
||||||
|
var typeFactory = new TestResourceManagerStringLocalizerFactory(
|
||||||
|
options.Object,
|
||||||
|
resourceLocationAttribute,
|
||||||
|
rootNamespaceAttribute,
|
||||||
|
loggerFactory);
|
||||||
|
|
||||||
|
var type = typeof(ResourceManagerStringLocalizerFactoryTest);
|
||||||
|
// Act
|
||||||
|
typeFactory.Create(type);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal($"MyNamespace.My.Resources.ResourceManagerStringLocalizerFactoryTest", typeFactory.BaseName);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Create_FromType_ResourcesPathDirectorySeperatorToDot()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var locOptions = new LocalizationOptions();
|
||||||
|
locOptions.ResourcesPath = Path.Combine("My", "Resources");
|
||||||
|
var options = new Mock<IOptions<LocalizationOptions>>();
|
||||||
|
options.Setup(o => o.Value).Returns(locOptions);
|
||||||
|
var loggerFactory = NullLoggerFactory.Instance;
|
||||||
|
var factory = new TestResourceManagerStringLocalizerFactory(
|
||||||
|
options.Object,
|
||||||
|
resourceLocationAttribute: null,
|
||||||
|
rootNamespaceAttribute: null,
|
||||||
|
loggerFactory: loggerFactory);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
factory.Create(typeof(ResourceManagerStringLocalizerFactoryTest));
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal("Microsoft.Extensions.Localization.Tests.My.Resources." + nameof(ResourceManagerStringLocalizerFactoryTest), factory.BaseName);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Create_FromNameLocation_ReturnsCachedResultForSameNameLocation()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var locOptions = new LocalizationOptions();
|
||||||
|
var options = new Mock<IOptions<LocalizationOptions>>();
|
||||||
|
options.Setup(o => o.Value).Returns(locOptions);
|
||||||
|
var loggerFactory = NullLoggerFactory.Instance;
|
||||||
|
var factory = new ResourceManagerStringLocalizerFactory(localizationOptions: options.Object, loggerFactory: loggerFactory);
|
||||||
|
var location = typeof(ResourceManagerStringLocalizer).GetTypeInfo().Assembly.FullName;
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result1 = factory.Create("baseName", location);
|
||||||
|
var result2 = factory.Create("baseName", location);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Same(result1, result2);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Create_FromNameLocation_ReturnsNewResultForDifferentName()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var locOptions = new LocalizationOptions();
|
||||||
|
var options = new Mock<IOptions<LocalizationOptions>>();
|
||||||
|
options.Setup(o => o.Value).Returns(locOptions);
|
||||||
|
var loggerFactory = NullLoggerFactory.Instance;
|
||||||
|
var factory = new ResourceManagerStringLocalizerFactory(localizationOptions: options.Object, loggerFactory: loggerFactory);
|
||||||
|
var location = typeof(ResourceManagerStringLocalizer).GetTypeInfo().Assembly.FullName;
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result1 = factory.Create("baseName1", location);
|
||||||
|
var result2 = factory.Create("baseName2", location);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.NotSame(result1, result2);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Create_FromNameLocation_ReturnsNewResultForDifferentLocation()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var locOptions = new LocalizationOptions();
|
||||||
|
var options = new Mock<IOptions<LocalizationOptions>>();
|
||||||
|
options.Setup(o => o.Value).Returns(locOptions);
|
||||||
|
var loggerFactory = NullLoggerFactory.Instance;
|
||||||
|
var factory = new ResourceManagerStringLocalizerFactory(localizationOptions: options.Object, loggerFactory: loggerFactory);
|
||||||
|
var location1 = new AssemblyName(typeof(ResourceManagerStringLocalizer).GetTypeInfo().Assembly.FullName).Name;
|
||||||
|
var location2 = new AssemblyName(typeof(ResourceManagerStringLocalizerFactoryTest).GetTypeInfo().Assembly.FullName).Name;
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result1 = factory.Create("baseName", location1);
|
||||||
|
var result2 = factory.Create("baseName", location2);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.NotSame(result1, result2);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Create_FromNameLocation_ResourcesPathDirectorySeparatorToDot()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var locOptions = new LocalizationOptions();
|
||||||
|
locOptions.ResourcesPath = Path.Combine("My", "Resources");
|
||||||
|
var options = new Mock<IOptions<LocalizationOptions>>();
|
||||||
|
options.Setup(o => o.Value).Returns(locOptions);
|
||||||
|
var loggerFactory = NullLoggerFactory.Instance;
|
||||||
|
var factory = new TestResourceManagerStringLocalizerFactory(
|
||||||
|
options.Object,
|
||||||
|
resourceLocationAttribute: null,
|
||||||
|
rootNamespaceAttribute: null,
|
||||||
|
loggerFactory: loggerFactory);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result1 = factory.Create("baseName", location: "Microsoft.Extensions.Localization.Tests");
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal("Microsoft.Extensions.Localization.Tests.My.Resources.baseName", factory.BaseName);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Create_FromNameLocation_NullLocationThrows()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var locOptions = new LocalizationOptions();
|
||||||
|
var options = new Mock<IOptions<LocalizationOptions>>();
|
||||||
|
options.Setup(o => o.Value).Returns(locOptions);
|
||||||
|
var loggerFactory = NullLoggerFactory.Instance;
|
||||||
|
var factory = new ResourceManagerStringLocalizerFactory(localizationOptions: options.Object, loggerFactory: loggerFactory);
|
||||||
|
|
||||||
|
// Act & Assert
|
||||||
|
Assert.Throws<ArgumentNullException>(() => factory.Create("baseName", location: null));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,299 @@
|
||||||
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Globalization;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Resources;
|
||||||
|
using Microsoft.AspNetCore.Testing;
|
||||||
|
using Microsoft.Extensions.Localization.Internal;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Microsoft.Extensions.Logging.Testing;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Microsoft.Extensions.Localization
|
||||||
|
{
|
||||||
|
public class ResourceManagerStringLocalizerTest
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void EnumeratorCachesCultureWalkForSameAssembly()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var resourceNamesCache = new ResourceNamesCache();
|
||||||
|
var baseName = "test";
|
||||||
|
var resourceAssembly = new TestAssemblyWrapper();
|
||||||
|
var resourceManager = new TestResourceManager(baseName, resourceAssembly);
|
||||||
|
var resourceStreamManager = new TestResourceStringProvider(
|
||||||
|
resourceNamesCache,
|
||||||
|
resourceManager,
|
||||||
|
resourceAssembly.Assembly,
|
||||||
|
baseName);
|
||||||
|
var logger = Logger;
|
||||||
|
var localizer1 = new ResourceManagerStringLocalizer(resourceManager,
|
||||||
|
resourceStreamManager,
|
||||||
|
baseName,
|
||||||
|
resourceNamesCache,
|
||||||
|
logger);
|
||||||
|
var localizer2 = new ResourceManagerStringLocalizer(resourceManager,
|
||||||
|
resourceStreamManager,
|
||||||
|
baseName,
|
||||||
|
resourceNamesCache,
|
||||||
|
logger);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
for (var i = 0; i < 5; i++)
|
||||||
|
{
|
||||||
|
localizer1.GetAllStrings().ToList();
|
||||||
|
localizer2.GetAllStrings().ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
var expectedCallCount = GetCultureInfoDepth(CultureInfo.CurrentUICulture);
|
||||||
|
Assert.Equal(expectedCallCount, resourceAssembly.ManifestResourceStreamCallCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void EnumeratorCacheIsScopedByAssembly()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var resourceNamesCache = new ResourceNamesCache();
|
||||||
|
var baseName = "test";
|
||||||
|
var resourceAssembly1 = new TestAssemblyWrapper(typeof(ResourceManagerStringLocalizerTest));
|
||||||
|
var resourceAssembly2 = new TestAssemblyWrapper(typeof(ResourceManagerStringLocalizer));
|
||||||
|
var resourceManager1 = new TestResourceManager(baseName, resourceAssembly1);
|
||||||
|
var resourceManager2 = new TestResourceManager(baseName, resourceAssembly2);
|
||||||
|
var resourceStreamManager1 = new TestResourceStringProvider(resourceNamesCache, resourceManager1, resourceAssembly1.Assembly, baseName);
|
||||||
|
var resourceStreamManager2 = new TestResourceStringProvider(resourceNamesCache, resourceManager2, resourceAssembly2.Assembly, baseName);
|
||||||
|
var logger = Logger;
|
||||||
|
var localizer1 = new ResourceManagerStringLocalizer(
|
||||||
|
resourceManager1,
|
||||||
|
resourceStreamManager1,
|
||||||
|
baseName,
|
||||||
|
resourceNamesCache,
|
||||||
|
logger);
|
||||||
|
var localizer2 = new ResourceManagerStringLocalizer(
|
||||||
|
resourceManager2,
|
||||||
|
resourceStreamManager2,
|
||||||
|
baseName,
|
||||||
|
resourceNamesCache,
|
||||||
|
logger);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
localizer1.GetAllStrings().ToList();
|
||||||
|
localizer2.GetAllStrings().ToList();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
var expectedCallCount = GetCultureInfoDepth(CultureInfo.CurrentUICulture);
|
||||||
|
Assert.Equal(expectedCallCount, resourceAssembly1.ManifestResourceStreamCallCount);
|
||||||
|
Assert.Equal(expectedCallCount, resourceAssembly2.ManifestResourceStreamCallCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void GetString_PopulatesSearchedLocationOnLocalizedString()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var baseName = "Resources.TestResource";
|
||||||
|
var resourceNamesCache = new ResourceNamesCache();
|
||||||
|
var resourceAssembly = new TestAssemblyWrapper();
|
||||||
|
var resourceManager = new TestResourceManager(baseName, resourceAssembly);
|
||||||
|
var resourceStreamManager = new TestResourceStringProvider(resourceNamesCache, resourceManager, resourceAssembly.Assembly, baseName);
|
||||||
|
var logger = Logger;
|
||||||
|
var localizer = new ResourceManagerStringLocalizer(
|
||||||
|
resourceManager,
|
||||||
|
resourceStreamManager,
|
||||||
|
baseName,
|
||||||
|
resourceNamesCache,
|
||||||
|
logger);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var value = localizer["name"];
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal("Resources.TestResource", value.SearchedLocation);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
[ReplaceCulture("en-US", "en-US")]
|
||||||
|
public void GetString_LogsLocationSearched()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var baseName = "Resources.TestResource";
|
||||||
|
var resourceNamesCache = new ResourceNamesCache();
|
||||||
|
var resourceAssembly = new TestAssemblyWrapper();
|
||||||
|
var resourceManager = new TestResourceManager(baseName, resourceAssembly);
|
||||||
|
var resourceStreamManager = new TestResourceStringProvider(resourceNamesCache, resourceManager, resourceAssembly.Assembly, baseName);
|
||||||
|
var logger = Logger;
|
||||||
|
|
||||||
|
var localizer = new ResourceManagerStringLocalizer(
|
||||||
|
resourceManager,
|
||||||
|
resourceStreamManager,
|
||||||
|
baseName,
|
||||||
|
resourceNamesCache,
|
||||||
|
logger);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var value = localizer["a key!"];
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
var write = Assert.Single(Sink.Writes);
|
||||||
|
Assert.Equal("ResourceManagerStringLocalizer searched for 'a key!' in 'Resources.TestResource' with culture 'en-US'.", write.State.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData(true)]
|
||||||
|
[InlineData(false)]
|
||||||
|
public void ResourceManagerStringLocalizer_GetAllStrings_ReturnsExpectedValue(bool includeParentCultures)
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var baseName = "test";
|
||||||
|
var resourceNamesCache = new ResourceNamesCache();
|
||||||
|
var resourceAssembly = new TestAssemblyWrapper();
|
||||||
|
var resourceManager = new TestResourceManager(baseName, resourceAssembly);
|
||||||
|
var resourceStreamManager = new TestResourceStringProvider(resourceNamesCache, resourceManager, resourceAssembly.Assembly, baseName);
|
||||||
|
var logger = Logger;
|
||||||
|
var localizer = new ResourceManagerStringLocalizer(
|
||||||
|
resourceManager,
|
||||||
|
resourceStreamManager,
|
||||||
|
baseName,
|
||||||
|
resourceNamesCache,
|
||||||
|
logger);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
// We have to access the result so it evaluates.
|
||||||
|
var strings = localizer.GetAllStrings(includeParentCultures).ToList();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
var value = Assert.Single(strings);
|
||||||
|
Assert.Equal("TestName", value.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData(true)]
|
||||||
|
[InlineData(false)]
|
||||||
|
public void ResourceManagerStringLocalizer_GetAllStrings_MissingResourceThrows(bool includeParentCultures)
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var resourceNamesCache = new ResourceNamesCache();
|
||||||
|
var baseName = "testington";
|
||||||
|
var resourceAssembly = new TestAssemblyWrapper();
|
||||||
|
resourceAssembly.HasResources = false;
|
||||||
|
var resourceManager = new TestResourceManager(baseName, resourceAssembly);
|
||||||
|
var logger = Logger;
|
||||||
|
|
||||||
|
var localizer = new ResourceManagerWithCultureStringLocalizer(
|
||||||
|
resourceManager,
|
||||||
|
resourceAssembly.Assembly,
|
||||||
|
baseName,
|
||||||
|
resourceNamesCache,
|
||||||
|
CultureInfo.CurrentCulture,
|
||||||
|
logger);
|
||||||
|
|
||||||
|
// Act & Assert
|
||||||
|
var exception = Assert.Throws<MissingManifestResourceException>(() =>
|
||||||
|
{
|
||||||
|
// We have to access the result so it evaluates.
|
||||||
|
localizer.GetAllStrings(includeParentCultures).ToArray();
|
||||||
|
});
|
||||||
|
|
||||||
|
var expectedTries = includeParentCultures ? 3 : 1;
|
||||||
|
var expected = includeParentCultures
|
||||||
|
? "No manifests exist for the current culture."
|
||||||
|
: $"The manifest 'testington.{CultureInfo.CurrentCulture}.resources' was not found.";
|
||||||
|
Assert.Equal(expected, exception.Message);
|
||||||
|
Assert.Equal(expectedTries, resourceAssembly.ManifestResourceStreamCallCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Stream MakeResourceStream()
|
||||||
|
{
|
||||||
|
var stream = new MemoryStream();
|
||||||
|
var resourceWriter = new ResourceWriter(stream);
|
||||||
|
resourceWriter.AddResource("TestName", "value");
|
||||||
|
resourceWriter.Generate();
|
||||||
|
stream.Position = 0;
|
||||||
|
return stream;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int GetCultureInfoDepth(CultureInfo culture)
|
||||||
|
{
|
||||||
|
var result = 0;
|
||||||
|
var currentCulture = culture;
|
||||||
|
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
result++;
|
||||||
|
|
||||||
|
if (currentCulture == currentCulture.Parent)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
currentCulture = currentCulture.Parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private TestSink Sink { get; } = new TestSink();
|
||||||
|
|
||||||
|
private ILogger Logger => new TestLoggerFactory(Sink, enabled: true).CreateLogger<ResourceManagerStringLocalizer>();
|
||||||
|
|
||||||
|
public class TestResourceManager : ResourceManager
|
||||||
|
{
|
||||||
|
private AssemblyWrapper _assemblyWrapper;
|
||||||
|
|
||||||
|
public TestResourceManager(string baseName, AssemblyWrapper assemblyWrapper)
|
||||||
|
: base(baseName, assemblyWrapper.Assembly)
|
||||||
|
{
|
||||||
|
_assemblyWrapper = assemblyWrapper;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string GetString(string name, CultureInfo culture) => null;
|
||||||
|
|
||||||
|
public override ResourceSet GetResourceSet(CultureInfo culture, bool createIfNotExists, bool tryParents)
|
||||||
|
{
|
||||||
|
var resourceStream = _assemblyWrapper.GetManifestResourceStream(BaseName);
|
||||||
|
|
||||||
|
return resourceStream != null ? new ResourceSet(resourceStream) : null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class TestResourceStringProvider : ResourceManagerStringProvider
|
||||||
|
{
|
||||||
|
public TestResourceStringProvider(
|
||||||
|
IResourceNamesCache resourceCache,
|
||||||
|
TestResourceManager resourceManager,
|
||||||
|
Assembly assembly,
|
||||||
|
string resourceBaseName)
|
||||||
|
: base(resourceCache, resourceManager, assembly, resourceBaseName)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class TestAssemblyWrapper : AssemblyWrapper
|
||||||
|
{
|
||||||
|
public TestAssemblyWrapper()
|
||||||
|
: this(typeof(TestAssemblyWrapper))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public TestAssemblyWrapper(Type type)
|
||||||
|
: base(type.GetTypeInfo().Assembly)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool HasResources { get; set; } = true;
|
||||||
|
|
||||||
|
public int ManifestResourceStreamCallCount { get; private set; }
|
||||||
|
|
||||||
|
public override Stream GetManifestResourceStream(string name)
|
||||||
|
{
|
||||||
|
ManifestResourceStreamCallCount++;
|
||||||
|
|
||||||
|
return HasResources ? MakeResourceStream() : null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue