Allow setting RootNamespace in ResourceLocationAttribute
This commit is contained in:
parent
683ef56b76
commit
bab2a50ec1
|
|
@ -68,7 +68,7 @@ namespace Microsoft.Extensions.Localization
|
||||||
throw new ArgumentNullException(nameof(typeInfo));
|
throw new ArgumentNullException(nameof(typeInfo));
|
||||||
}
|
}
|
||||||
|
|
||||||
return GetResourcePrefix(typeInfo, new AssemblyName(typeInfo.Assembly.FullName).Name, _resourcesRelativePath);
|
return GetResourcePrefix(typeInfo, GetRootNamespace(typeInfo.Assembly), GetResourcePath(typeInfo.Assembly));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -94,9 +94,17 @@ namespace Microsoft.Extensions.Localization
|
||||||
throw new ArgumentNullException(nameof(baseNamespace));
|
throw new ArgumentNullException(nameof(baseNamespace));
|
||||||
}
|
}
|
||||||
|
|
||||||
return string.IsNullOrEmpty(resourcesRelativePath)
|
if (string.IsNullOrEmpty(resourcesRelativePath))
|
||||||
? typeInfo.FullName
|
{
|
||||||
: baseNamespace + "." + resourcesRelativePath + TrimPrefix(typeInfo.FullName, baseNamespace + ".");
|
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>
|
/// <summary>
|
||||||
|
|
@ -119,8 +127,9 @@ namespace Microsoft.Extensions.Localization
|
||||||
|
|
||||||
var assemblyName = new AssemblyName(baseNamespace);
|
var assemblyName = new AssemblyName(baseNamespace);
|
||||||
var assembly = Assembly.Load(assemblyName);
|
var assembly = Assembly.Load(assemblyName);
|
||||||
|
var rootNamespace = GetRootNamespace(assembly);
|
||||||
var resourceLocation = GetResourcePath(assembly);
|
var resourceLocation = GetResourcePath(assembly);
|
||||||
var locationPath = baseNamespace + "." + resourceLocation;
|
var locationPath = rootNamespace + "." + resourceLocation;
|
||||||
|
|
||||||
baseResourceName = locationPath + TrimPrefix(baseResourceName, baseNamespace + ".");
|
baseResourceName = locationPath + TrimPrefix(baseResourceName, baseNamespace + ".");
|
||||||
|
|
||||||
|
|
@ -141,11 +150,10 @@ namespace Microsoft.Extensions.Localization
|
||||||
}
|
}
|
||||||
|
|
||||||
var typeInfo = resourceSource.GetTypeInfo();
|
var typeInfo = resourceSource.GetTypeInfo();
|
||||||
var assembly = typeInfo.Assembly;
|
|
||||||
var assemblyName = new AssemblyName(assembly.FullName);
|
|
||||||
var resourcePath = GetResourcePath(assembly);
|
|
||||||
|
|
||||||
var baseName = GetResourcePrefix(typeInfo, assemblyName.Name, resourcePath);
|
var baseName = GetResourcePrefix(typeInfo);
|
||||||
|
|
||||||
|
var assembly = typeInfo.Assembly;
|
||||||
|
|
||||||
return _localizerCache.GetOrAdd(baseName, _ => CreateResourceManagerStringLocalizer(assembly, baseName));
|
return _localizerCache.GetOrAdd(baseName, _ => CreateResourceManagerStringLocalizer(assembly, baseName));
|
||||||
}
|
}
|
||||||
|
|
@ -217,6 +225,23 @@ namespace Microsoft.Extensions.Localization
|
||||||
return assembly.GetCustomAttribute<ResourceLocationAttribute>();
|
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)
|
private string GetResourcePath(Assembly assembly)
|
||||||
{
|
{
|
||||||
var resourceLocationAttribute = GetResourceLocationAttribute(assembly);
|
var resourceLocationAttribute = GetResourceLocationAttribute(assembly);
|
||||||
|
|
|
||||||
|
|
@ -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; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -17,16 +17,20 @@ namespace Microsoft.Extensions.Localization.Tests
|
||||||
{
|
{
|
||||||
private ResourceLocationAttribute _resourceLocationAttribute;
|
private ResourceLocationAttribute _resourceLocationAttribute;
|
||||||
|
|
||||||
|
private RootNamespaceAttribute _rootNamespaceAttribute;
|
||||||
|
|
||||||
public Assembly Assembly { get; private set; }
|
public Assembly Assembly { get; private set; }
|
||||||
public string BaseName { get; private set; }
|
public string BaseName { get; private set; }
|
||||||
|
|
||||||
public TestResourceManagerStringLocalizerFactory(
|
public TestResourceManagerStringLocalizerFactory(
|
||||||
IOptions<LocalizationOptions> localizationOptions,
|
IOptions<LocalizationOptions> localizationOptions,
|
||||||
ResourceLocationAttribute resourceLocationAttribute,
|
ResourceLocationAttribute resourceLocationAttribute,
|
||||||
|
RootNamespaceAttribute rootNamespaceAttribute,
|
||||||
ILoggerFactory loggerFactory)
|
ILoggerFactory loggerFactory)
|
||||||
: base(localizationOptions, loggerFactory)
|
: base(localizationOptions, loggerFactory)
|
||||||
{
|
{
|
||||||
_resourceLocationAttribute = resourceLocationAttribute;
|
_resourceLocationAttribute = resourceLocationAttribute;
|
||||||
|
_rootNamespaceAttribute = rootNamespaceAttribute;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override ResourceLocationAttribute GetResourceLocationAttribute(Assembly assembly)
|
protected override ResourceLocationAttribute GetResourceLocationAttribute(Assembly assembly)
|
||||||
|
|
@ -34,6 +38,11 @@ namespace Microsoft.Extensions.Localization.Tests
|
||||||
return _resourceLocationAttribute;
|
return _resourceLocationAttribute;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override RootNamespaceAttribute GetRootNamespaceAttribute(Assembly assembly)
|
||||||
|
{
|
||||||
|
return _rootNamespaceAttribute;
|
||||||
|
}
|
||||||
|
|
||||||
protected override ResourceManagerStringLocalizer CreateResourceManagerStringLocalizer(Assembly assembly, string baseName)
|
protected override ResourceManagerStringLocalizer CreateResourceManagerStringLocalizer(Assembly assembly, string baseName)
|
||||||
{
|
{
|
||||||
BaseName = baseName;
|
BaseName = baseName;
|
||||||
|
|
@ -58,11 +67,13 @@ namespace Microsoft.Extensions.Localization.Tests
|
||||||
var typeFactory = new TestResourceManagerStringLocalizerFactory(
|
var typeFactory = new TestResourceManagerStringLocalizerFactory(
|
||||||
options.Object,
|
options.Object,
|
||||||
resourceLocationAttribute,
|
resourceLocationAttribute,
|
||||||
loggerFactory);
|
rootNamespaceAttribute: null,
|
||||||
|
loggerFactory: loggerFactory);
|
||||||
var stringFactory = new TestResourceManagerStringLocalizerFactory(
|
var stringFactory = new TestResourceManagerStringLocalizerFactory(
|
||||||
options.Object,
|
options.Object,
|
||||||
resourceLocationAttribute,
|
resourceLocationAttribute,
|
||||||
loggerFactory);
|
rootNamespaceAttribute: null,
|
||||||
|
loggerFactory: loggerFactory);
|
||||||
var type = typeof(ResourceManagerStringLocalizerFactoryTest);
|
var type = typeof(ResourceManagerStringLocalizerFactoryTest);
|
||||||
var assemblyName = new AssemblyName(type.GetTypeInfo().Assembly.FullName);
|
var assemblyName = new AssemblyName(type.GetTypeInfo().Assembly.FullName);
|
||||||
|
|
||||||
|
|
@ -111,6 +122,61 @@ namespace Microsoft.Extensions.Localization.Tests
|
||||||
Assert.NotSame(result1, result2);
|
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]
|
[Fact]
|
||||||
public void Create_FromType_ResourcesPathDirectorySeperatorToDot()
|
public void Create_FromType_ResourcesPathDirectorySeperatorToDot()
|
||||||
{
|
{
|
||||||
|
|
@ -123,6 +189,7 @@ namespace Microsoft.Extensions.Localization.Tests
|
||||||
var factory = new TestResourceManagerStringLocalizerFactory(
|
var factory = new TestResourceManagerStringLocalizerFactory(
|
||||||
options.Object,
|
options.Object,
|
||||||
resourceLocationAttribute: null,
|
resourceLocationAttribute: null,
|
||||||
|
rootNamespaceAttribute: null,
|
||||||
loggerFactory: loggerFactory);
|
loggerFactory: loggerFactory);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
|
|
@ -202,6 +269,7 @@ namespace Microsoft.Extensions.Localization.Tests
|
||||||
var factory = new TestResourceManagerStringLocalizerFactory(
|
var factory = new TestResourceManagerStringLocalizerFactory(
|
||||||
options.Object,
|
options.Object,
|
||||||
resourceLocationAttribute: null,
|
resourceLocationAttribute: null,
|
||||||
|
rootNamespaceAttribute: null,
|
||||||
loggerFactory: loggerFactory);
|
loggerFactory: loggerFactory);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
|
|
|
||||||
|
|
@ -5,3 +5,4 @@ using System.Reflection;
|
||||||
using Microsoft.Extensions.Localization;
|
using Microsoft.Extensions.Localization;
|
||||||
|
|
||||||
[assembly: ResourceLocation("ResourceFolder")]
|
[assembly: ResourceLocation("ResourceFolder")]
|
||||||
|
[assembly: RootNamespace("Alternate.Namespace")]
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>netstandard2.0</TargetFramework>
|
<TargetFramework>netstandard2.0</TargetFramework>
|
||||||
|
<RootNamespace>Alternate.Namespace</RootNamespace>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue