DataAnnotations of Enum values use DataAnnotationLocalizerProvider
This commit is contained in:
parent
418aac57f4
commit
edf4e8fd9e
|
|
@ -14,7 +14,7 @@ namespace MvcSandbox
|
|||
// This method gets called by the runtime. Use this method to add services to the container.
|
||||
public void ConfigureServices(IServiceCollection services)
|
||||
{
|
||||
services.AddMvc().SetCompatibilityVersion(Microsoft.AspNetCore.Mvc.CompatibilityVersion.Version_2_1);
|
||||
services.AddMvc().SetCompatibilityVersion(Microsoft.AspNetCore.Mvc.CompatibilityVersion.Version_2_2);
|
||||
}
|
||||
|
||||
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
|
||||
|
|
|
|||
|
|
@ -59,6 +59,18 @@ namespace Microsoft.AspNetCore.Mvc
|
|||
/// </remarks>
|
||||
Version_2_1,
|
||||
|
||||
/// <summary>
|
||||
/// Sets the default value of settings on <see cref="MvcOptions"/> to match the behavior of
|
||||
/// ASP.NET Core MVC 2.2.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// ASP.NET Core MVC 2.2 introduces compatibility switches for the following:
|
||||
/// <list type="bullet">
|
||||
/// <item><description><c>MvcDataAnnotationsLocalizationOptions.AllowDataAnnotationsLocalizationForEnumDisplayAttributes</c></description></item>
|
||||
/// </list>
|
||||
/// </remarks>
|
||||
Version_2_2,
|
||||
|
||||
/// <summary>
|
||||
/// Sets the default value of settings on <see cref="MvcOptions"/> to match the latest release. Use this
|
||||
/// value with care, upgrading minor versions will cause breaking changes when using <see cref="Latest"/>.
|
||||
|
|
|
|||
|
|
@ -27,6 +27,9 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal
|
|||
<IConfigureOptions<MvcDataAnnotationsLocalizationOptions>,
|
||||
MvcDataAnnotationsLocalizationOptionsSetup>());
|
||||
}
|
||||
|
||||
services.TryAddEnumerable(
|
||||
ServiceDescriptor.Transient<IPostConfigureOptions<MvcDataAnnotationsLocalizationOptions>, MvcDataAnnotationsLocalizationConfigureCompatibilityOptions>());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -182,7 +182,19 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal
|
|||
|
||||
var groupedDisplayNamesAndValues = new List<KeyValuePair<EnumGroupAndName, string>>();
|
||||
var namesAndValues = new Dictionary<string, string>();
|
||||
var enumLocalizer = _stringLocalizerFactory?.Create(underlyingType);
|
||||
|
||||
IStringLocalizer enumLocalizer = null;
|
||||
if (_localizationOptions.AllowDataAnnotationsLocalizationForEnumDisplayAttributes)
|
||||
{
|
||||
if (_stringLocalizerFactory != null && _localizationOptions.DataAnnotationLocalizerProvider != null)
|
||||
{
|
||||
enumLocalizer = _localizationOptions.DataAnnotationLocalizerProvider(underlyingType, _stringLocalizerFactory);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
enumLocalizer = _stringLocalizerFactory?.Create(underlyingType);
|
||||
}
|
||||
|
||||
var enumFields = Enum.GetNames(underlyingType)
|
||||
.Select(name => underlyingType.GetField(name))
|
||||
|
|
|
|||
|
|
@ -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.Collections.Generic;
|
||||
using Microsoft.AspNetCore.Mvc.Infrastructure;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.DataAnnotations
|
||||
{
|
||||
internal class MvcDataAnnotationsLocalizationConfigureCompatibilityOptions : ConfigureCompatibilityOptions<MvcDataAnnotationsLocalizationOptions>
|
||||
{
|
||||
public MvcDataAnnotationsLocalizationConfigureCompatibilityOptions(
|
||||
ILoggerFactory loggerFactory,
|
||||
IOptions<MvcCompatibilityOptions> compatibilityOptions)
|
||||
: base(loggerFactory, compatibilityOptions)
|
||||
{
|
||||
}
|
||||
|
||||
protected override IReadOnlyDictionary<string, object> DefaultValues
|
||||
{
|
||||
get
|
||||
{
|
||||
var values = new Dictionary<string, object>();
|
||||
|
||||
if (Version >= CompatibilityVersion.Version_2_2)
|
||||
{
|
||||
values[nameof(MvcDataAnnotationsLocalizationOptions.AllowDataAnnotationsLocalizationForEnumDisplayAttributes)] = true;
|
||||
}
|
||||
|
||||
return values;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2,6 +2,9 @@
|
|||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.AspNetCore.Mvc.Infrastructure;
|
||||
using Microsoft.Extensions.Localization;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.DataAnnotations
|
||||
|
|
@ -9,11 +12,60 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations
|
|||
/// <summary>
|
||||
/// Provides programmatic configuration for DataAnnotations localization in the MVC framework.
|
||||
/// </summary>
|
||||
public class MvcDataAnnotationsLocalizationOptions
|
||||
public class MvcDataAnnotationsLocalizationOptions : IEnumerable<ICompatibilitySwitch>
|
||||
{
|
||||
private readonly CompatibilitySwitch<bool> _allowDataAnnotationsLocalizationForEnumDisplayAttributes;
|
||||
private readonly ICompatibilitySwitch[] _switches;
|
||||
|
||||
/// <summary>
|
||||
/// The delegate to invoke for creating <see cref="IStringLocalizer"/>.
|
||||
/// </summary>
|
||||
public Func<Type, IStringLocalizerFactory, IStringLocalizer> DataAnnotationLocalizerProvider;
|
||||
|
||||
/// <summary>
|
||||
/// Instantiates a new instance of the <see cref="MvcDataAnnotationsLocalizationOptions"/> class.
|
||||
/// </summary>
|
||||
public MvcDataAnnotationsLocalizationOptions()
|
||||
{
|
||||
_allowDataAnnotationsLocalizationForEnumDisplayAttributes = new CompatibilitySwitch<bool>(nameof(AllowDataAnnotationsLocalizationForEnumDisplayAttributes));
|
||||
|
||||
_switches = new ICompatibilitySwitch[]
|
||||
{
|
||||
_allowDataAnnotationsLocalizationForEnumDisplayAttributes
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value that determines if <see cref="MvcDataAnnotationsLocalizationOptions.DataAnnotationLocalizerProvider"/> should be used while localizing <see cref="Enum"/> types.
|
||||
/// If set to <c>true</c> <see cref="MvcDataAnnotationsLocalizationOptions.DataAnnotationLocalizerProvider"/> will be used in localizing <see cref="Enum"/> types.
|
||||
/// If set to <c>false</c> the localization will search for values in resource files for the <see cref="Enum"/>.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// This property is associated with a compatibility switch and can provide a different behavior depending on
|
||||
/// the configured compatibility version for the application. See <see cref="CompatibilityVersion"/> for guidance and examples of setting the application's compatibility version.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// Configuring the desired value of the compatibility switch by calling this property's setter will take precedence
|
||||
/// over the value implied by the application's <see cref="CompatibilityVersion"/>.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// If the application's compatibility version is set to <see cref="CompatibilityVersion.Version_2_0"/> or <see cref="CompatibilityVersion.Version_2_1"/> then
|
||||
/// this setting will have the value <c>false</c> unless explicitly configured.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// If the application's compatibility version is set to <see cref="CompatibilityVersion.Version_2_2"/> or
|
||||
/// higher then this setting will have the value <c>true</c> unless explicitly configured.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public bool AllowDataAnnotationsLocalizationForEnumDisplayAttributes
|
||||
{
|
||||
get => _allowDataAnnotationsLocalizationForEnumDisplayAttributes.Value;
|
||||
set => _allowDataAnnotationsLocalizationForEnumDisplayAttributes.Value = value;
|
||||
}
|
||||
|
||||
public IEnumerator<ICompatibilitySwitch> GetEnumerator() => ((IEnumerable<ICompatibilitySwitch>)_switches).GetEnumerator();
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator() => _switches.GetEnumerator();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,6 +16,14 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
|
|||
{
|
||||
public class TestModelMetadataProvider : DefaultModelMetadataProvider
|
||||
{
|
||||
private static DataAnnotationsMetadataProvider CreateDefaultDataAnnotationsProvider(IStringLocalizerFactory stringLocalizerFactory)
|
||||
{
|
||||
var options = Options.Create(new MvcDataAnnotationsLocalizationOptions());
|
||||
options.Value.DataAnnotationLocalizerProvider = (modelType, localizerFactory) => localizerFactory.Create(modelType);
|
||||
|
||||
return new DataAnnotationsMetadataProvider(options, stringLocalizerFactory);
|
||||
}
|
||||
|
||||
// Creates a provider with all the defaults - includes data annotations
|
||||
public static ModelMetadataProvider CreateDefaultProvider(IStringLocalizerFactory stringLocalizerFactory = null)
|
||||
{
|
||||
|
|
@ -23,9 +31,7 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
|
|||
{
|
||||
new DefaultBindingMetadataProvider(),
|
||||
new DefaultValidationMetadataProvider(),
|
||||
new DataAnnotationsMetadataProvider(
|
||||
Options.Create(new MvcDataAnnotationsLocalizationOptions()),
|
||||
stringLocalizerFactory),
|
||||
CreateDefaultDataAnnotationsProvider(stringLocalizerFactory),
|
||||
new DataMemberRequiredBindingMetadataProvider(),
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ using System.Collections.Generic;
|
|||
using Microsoft.AspNetCore.Mvc.DataAnnotations;
|
||||
using Microsoft.AspNetCore.Mvc.DataAnnotations.Internal;
|
||||
using Microsoft.AspNetCore.Mvc.Internal;
|
||||
using Microsoft.Extensions.Localization;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.ModelBinding.Validation
|
||||
|
|
@ -12,15 +13,18 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Validation
|
|||
public class TestModelValidatorProvider : CompositeModelValidatorProvider
|
||||
{
|
||||
// Creates a provider with all the defaults - includes data annotations
|
||||
public static CompositeModelValidatorProvider CreateDefaultProvider()
|
||||
public static CompositeModelValidatorProvider CreateDefaultProvider(IStringLocalizerFactory stringLocalizerFactory = null)
|
||||
{
|
||||
var options = Options.Create(new MvcDataAnnotationsLocalizationOptions());
|
||||
options.Value.DataAnnotationLocalizerProvider = (modelType, localizerFactory) => localizerFactory.Create(modelType);
|
||||
|
||||
var providers = new IModelValidatorProvider[]
|
||||
{
|
||||
new DefaultModelValidatorProvider(),
|
||||
new DataAnnotationsModelValidatorProvider(
|
||||
new ValidationAttributeAdapterProvider(),
|
||||
Options.Create(new MvcDataAnnotationsLocalizationOptions()),
|
||||
stringLocalizerFactory: null)
|
||||
options,
|
||||
stringLocalizerFactory)
|
||||
};
|
||||
|
||||
return new TestModelValidatorProvider(providers);
|
||||
|
|
@ -31,4 +35,4 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Validation
|
|||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,7 +10,6 @@ using Microsoft.AspNetCore.Mvc.ModelBinding;
|
|||
using Microsoft.AspNetCore.Mvc.ModelBinding.Metadata;
|
||||
using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
|
||||
using Microsoft.AspNetCore.Testing;
|
||||
using Microsoft.Extensions.Internal;
|
||||
using Microsoft.Extensions.Localization;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Moq;
|
||||
|
|
@ -18,6 +17,12 @@ using Xunit;
|
|||
|
||||
namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal
|
||||
{
|
||||
public enum TestEnum
|
||||
{
|
||||
[Display(Name = "DisplayNameValue")]
|
||||
DisplayNameValue
|
||||
}
|
||||
|
||||
public class DataAnnotationsMetadataProviderTest
|
||||
{
|
||||
// Includes attributes with a 'simple' effect on display details.
|
||||
|
|
@ -270,6 +275,94 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal
|
|||
Assert.Equal("DisplayNameAttributeValue", context.DisplayMetadata.DisplayName());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CreateDisplayMetadata_DisplayNameAttribute_OnEnum_CompatSwitchWorks()
|
||||
{
|
||||
// Arrange
|
||||
var unsharedLocalizer = new Mock<IStringLocalizer>(MockBehavior.Strict);
|
||||
unsharedLocalizer
|
||||
.Setup(s => s["DisplayNameValue"])
|
||||
.Returns(new LocalizedString("DisplaynameValue", "didn't use shared"));
|
||||
|
||||
var sharedLocalizer = new Mock<IStringLocalizer>(MockBehavior.Strict);
|
||||
sharedLocalizer
|
||||
.Setup(s => s["DisplayNameValue"])
|
||||
.Returns(() => new LocalizedString("DisplayNameValue", "used shared"));
|
||||
|
||||
var stringLocalizerFactoryMock = new Mock<IStringLocalizerFactory>(MockBehavior.Strict);
|
||||
stringLocalizerFactoryMock
|
||||
.Setup(s => s.Create(typeof(TestEnum)))
|
||||
.Returns(() => unsharedLocalizer.Object);
|
||||
stringLocalizerFactoryMock
|
||||
.Setup(s => s.Create(typeof(EmptyClass)))
|
||||
.Returns(() => sharedLocalizer.Object);
|
||||
|
||||
var localizationOptions = Options.Create(new MvcDataAnnotationsLocalizationOptions());
|
||||
localizationOptions.Value.AllowDataAnnotationsLocalizationForEnumDisplayAttributes = false;
|
||||
localizationOptions.Value.DataAnnotationLocalizerProvider = (type, stringLocalizerFactory) =>
|
||||
{
|
||||
return stringLocalizerFactory.Create(typeof(EmptyClass));
|
||||
};
|
||||
|
||||
var provider = new DataAnnotationsMetadataProvider(
|
||||
localizationOptions,
|
||||
stringLocalizerFactory: stringLocalizerFactoryMock.Object);
|
||||
|
||||
var displayName = new DisplayNameAttribute("DisplayNameValue");
|
||||
|
||||
var attributes = new Attribute[] { displayName };
|
||||
var key = ModelMetadataIdentity.ForType(typeof(TestEnum));
|
||||
var context = new DisplayMetadataProviderContext(key, GetModelAttributes(attributes));
|
||||
|
||||
// Act
|
||||
provider.CreateDisplayMetadata(context);
|
||||
|
||||
// Assert
|
||||
Assert.Collection(context.DisplayMetadata.EnumGroupedDisplayNamesAndValues,
|
||||
(e) => Assert.Equal("didn't use shared", e.Key.Name));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CreateDisplayMetadata_DisplayNameAttribute_OnEnum_CompatShimOn()
|
||||
{
|
||||
// Arrange
|
||||
var sharedLocalizer = new Mock<IStringLocalizer>(MockBehavior.Strict);
|
||||
sharedLocalizer
|
||||
.Setup(s => s["DisplayNameValue"])
|
||||
.Returns(new LocalizedString("DisplayNameValue", "Name from DisplayNameAttribute"));
|
||||
|
||||
var stringLocalizerFactoryMock = new Mock<IStringLocalizerFactory>(MockBehavior.Strict);
|
||||
stringLocalizerFactoryMock
|
||||
.Setup(s => s.Create(typeof(EmptyClass)))
|
||||
.Returns(() => sharedLocalizer.Object);
|
||||
|
||||
var localizationOptions = Options.Create(new MvcDataAnnotationsLocalizationOptions());
|
||||
localizationOptions.Value.AllowDataAnnotationsLocalizationForEnumDisplayAttributes = true;
|
||||
localizationOptions.Value.DataAnnotationLocalizerProvider = (type, stringLocalizerFactory) =>
|
||||
{
|
||||
return stringLocalizerFactory.Create(typeof(EmptyClass));
|
||||
};
|
||||
|
||||
var provider = new DataAnnotationsMetadataProvider(
|
||||
localizationOptions,
|
||||
stringLocalizerFactory: stringLocalizerFactoryMock.Object);
|
||||
|
||||
var displayName = new DisplayNameAttribute("DisplayNameValue");
|
||||
|
||||
var attributes = new Attribute[] { displayName };
|
||||
var key = ModelMetadataIdentity.ForType(typeof(TestEnum));
|
||||
var context = new DisplayMetadataProviderContext(key, GetModelAttributes(attributes));
|
||||
|
||||
// Act
|
||||
provider.CreateDisplayMetadata(context);
|
||||
|
||||
// Assert
|
||||
Assert.Collection(context.DisplayMetadata.EnumGroupedDisplayNamesAndValues, (e) =>
|
||||
{
|
||||
Assert.Equal("Name from DisplayNameAttribute", e.Key.Name);
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CreateDisplayMetadata_DisplayNameAttribute_LocalizesDisplayName()
|
||||
{
|
||||
|
|
@ -568,16 +661,13 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal
|
|||
stringLocalizerFactoryMock
|
||||
.Setup(f => f.Create(It.IsAny<Type>()))
|
||||
.Returns(stringLocalizer.Object);
|
||||
|
||||
var options = Options.Create(new MvcDataAnnotationsLocalizationOptions());
|
||||
options.Value.DataAnnotationLocalizerProvider = (type, stringLocalizerFactory) =>
|
||||
{
|
||||
return stringLocalizerFactory.Create(type);
|
||||
};
|
||||
|
||||
var provider = new DataAnnotationsMetadataProvider(
|
||||
options,
|
||||
stringLocalizerFactoryMock.Object);
|
||||
var provider = new DataAnnotationsMetadataProvider(options, stringLocalizerFactoryMock.Object);
|
||||
|
||||
var display = new DisplayAttribute()
|
||||
{
|
||||
|
|
@ -594,13 +684,13 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal
|
|||
provider.CreateDisplayMetadata(context);
|
||||
|
||||
// Assert
|
||||
using (new CultureReplacer("en-US", "en-US"))
|
||||
using(new CultureReplacer("en-US", "en-US"))
|
||||
{
|
||||
Assert.Equal("name from localizer en-US", context.DisplayMetadata.DisplayName());
|
||||
Assert.Equal("description from localizer en-US", context.DisplayMetadata.Description());
|
||||
Assert.Equal("prompt from localizer en-US", context.DisplayMetadata.Placeholder());
|
||||
}
|
||||
using (new CultureReplacer("fr-FR", "fr-FR"))
|
||||
using(new CultureReplacer("fr-FR", "fr-FR"))
|
||||
{
|
||||
Assert.Equal("name from localizer fr-FR", context.DisplayMetadata.DisplayName());
|
||||
Assert.Equal("description from localizer fr-FR", context.DisplayMetadata.Description());
|
||||
|
|
@ -840,14 +930,14 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal
|
|||
.Setup(s => s[It.IsAny<string>()])
|
||||
.Returns<string>((index) => new LocalizedString(index, index + " value"));
|
||||
|
||||
var stringLocalizerFactory = new Mock<IStringLocalizerFactory>(MockBehavior.Strict);
|
||||
stringLocalizerFactory
|
||||
var stringLocalizerFactoryMock = new Mock<IStringLocalizerFactory>(MockBehavior.Strict);
|
||||
stringLocalizerFactoryMock
|
||||
.Setup(f => f.Create(It.IsAny<Type>()))
|
||||
.Returns(stringLocalizer.Object);
|
||||
|
||||
var provider = new DataAnnotationsMetadataProvider(
|
||||
Options.Create(new MvcDataAnnotationsLocalizationOptions()),
|
||||
stringLocalizerFactory.Object);
|
||||
var options = Options.Create(new MvcDataAnnotationsLocalizationOptions());
|
||||
options.Value.DataAnnotationLocalizerProvider = (modelType, stringLocalizerFactory) => stringLocalizerFactory.Create(modelType);
|
||||
var provider = new DataAnnotationsMetadataProvider(options, stringLocalizerFactoryMock.Object);
|
||||
|
||||
// Act
|
||||
provider.CreateDisplayMetadata(context);
|
||||
|
|
@ -1036,12 +1126,12 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal
|
|||
// Assert
|
||||
var groupTwo = Assert.Single(enumNameAndGroup, e => e.Value.Equals("2", StringComparison.Ordinal));
|
||||
|
||||
using (new CultureReplacer("en-US", "en-US"))
|
||||
using(new CultureReplacer("en-US", "en-US"))
|
||||
{
|
||||
Assert.Equal("Loc_Two_Name", groupTwo.Key.Name);
|
||||
}
|
||||
|
||||
using (new CultureReplacer("fr-FR", "fr-FR"))
|
||||
using(new CultureReplacer("fr-FR", "fr-FR"))
|
||||
{
|
||||
Assert.Equal("Loc_Two_Name", groupTwo.Key.Name);
|
||||
}
|
||||
|
|
@ -1056,12 +1146,12 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal
|
|||
// Assert
|
||||
var groupTwo = Assert.Single(enumNameAndGroup, e => e.Value.Equals("2", StringComparison.Ordinal));
|
||||
|
||||
using (new CultureReplacer("en-US", "en-US"))
|
||||
using(new CultureReplacer("en-US", "en-US"))
|
||||
{
|
||||
Assert.Equal("Loc_Two_Name en-US", groupTwo.Key.Name);
|
||||
}
|
||||
|
||||
using (new CultureReplacer("fr-FR", "fr-FR"))
|
||||
using(new CultureReplacer("fr-FR", "fr-FR"))
|
||||
{
|
||||
Assert.Equal("Loc_Two_Name fr-FR", groupTwo.Key.Name);
|
||||
}
|
||||
|
|
@ -1076,12 +1166,12 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal
|
|||
// Assert
|
||||
var groupThree = Assert.Single(enumNameAndGroup, e => e.Value.Equals("3", StringComparison.Ordinal));
|
||||
|
||||
using (new CultureReplacer("en-US", "en-US"))
|
||||
using(new CultureReplacer("en-US", "en-US"))
|
||||
{
|
||||
Assert.Equal("type three name en-US", groupThree.Key.Name);
|
||||
}
|
||||
|
||||
using (new CultureReplacer("fr-FR", "fr-FR"))
|
||||
using(new CultureReplacer("fr-FR", "fr-FR"))
|
||||
{
|
||||
Assert.Equal("type three name fr-FR", groupThree.Key.Name);
|
||||
}
|
||||
|
|
@ -1096,12 +1186,12 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal
|
|||
var groupThree = Assert.Single(enumNameAndGroup, e => e.Value.Equals("3", StringComparison.Ordinal));
|
||||
|
||||
// Assert
|
||||
using (new CultureReplacer("en-US", "en-US"))
|
||||
using(new CultureReplacer("en-US", "en-US"))
|
||||
{
|
||||
Assert.Equal("type three name en-US", groupThree.Key.Name);
|
||||
}
|
||||
|
||||
using (new CultureReplacer("fr-FR", "fr-FR"))
|
||||
using(new CultureReplacer("fr-FR", "fr-FR"))
|
||||
{
|
||||
Assert.Equal("type three name fr-FR", groupThree.Key.Name);
|
||||
}
|
||||
|
|
@ -1269,8 +1359,11 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal
|
|||
.Setup(factory => factory.Create(typeof(EnumWithLocalizedDisplayNames)))
|
||||
.Returns(stringLocalizer.Object);
|
||||
|
||||
var options = Options.Create(new MvcDataAnnotationsLocalizationOptions());
|
||||
options.Value.DataAnnotationLocalizerProvider = (modelType, localizerFactory) => localizerFactory.Create(modelType);
|
||||
|
||||
return new DataAnnotationsMetadataProvider(
|
||||
Options.Create(new MvcDataAnnotationsLocalizationOptions()),
|
||||
options,
|
||||
useStringLocalizer ? stringLocalizerFactory.Object : null);
|
||||
}
|
||||
|
||||
|
|
@ -1304,7 +1397,7 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal
|
|||
|
||||
public bool Equals(KeyValuePair<EnumGroupAndName, string> x, KeyValuePair<EnumGroupAndName, string> y)
|
||||
{
|
||||
using (new CultureReplacer(string.Empty, string.Empty))
|
||||
using(new CultureReplacer(string.Empty, string.Empty))
|
||||
{
|
||||
return x.Key.Name.Equals(y.Key.Name, StringComparison.Ordinal)
|
||||
&& x.Key.Group.Equals(y.Key.Group, StringComparison.Ordinal);
|
||||
|
|
@ -1313,7 +1406,7 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal
|
|||
|
||||
public int GetHashCode(KeyValuePair<EnumGroupAndName, string> obj)
|
||||
{
|
||||
using (new CultureReplacer(string.Empty, string.Empty))
|
||||
using(new CultureReplacer(string.Empty, string.Empty))
|
||||
{
|
||||
return obj.Key.GetHashCode();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,38 @@
|
|||
using System.Net;
|
||||
using System.Threading.Tasks;
|
||||
using RazorWebSite;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Xunit;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.FunctionalTests
|
||||
{
|
||||
public class DataAnnotationTests : IClassFixture<MvcTestFixture<StartupDataAnnotations>>
|
||||
{
|
||||
private HttpClient Client { get; set; }
|
||||
|
||||
public DataAnnotationTests(MvcTestFixture<StartupDataAnnotations> fixture)
|
||||
{
|
||||
var factory = fixture.Factories.FirstOrDefault() ?? fixture.WithWebHostBuilder(builder =>
|
||||
{
|
||||
builder.UseStartup<StartupDataAnnotations>();
|
||||
});
|
||||
Client = factory.CreateDefaultClient();
|
||||
}
|
||||
|
||||
private const string EnumUrl = "http://localhost/Enum/Enum";
|
||||
|
||||
[Fact]
|
||||
public async Task DataAnnotationLocalizionOfEnums_FromDataAnnotationLocalizerProvider()
|
||||
{
|
||||
// Arrange & Act
|
||||
var response = await Client.GetAsync(EnumUrl);
|
||||
var content = await response.Content.ReadAsStringAsync();
|
||||
|
||||
// Assert
|
||||
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||
Assert.Contains("FirstOptionDisplay from singletype", content);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -8,6 +8,8 @@ using Microsoft.AspNetCore.Mvc.DataAnnotations;
|
|||
using Microsoft.AspNetCore.Mvc.Razor;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Localization;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Logging.Abstractions;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Xunit;
|
||||
|
||||
|
|
@ -104,6 +106,9 @@ namespace Microsoft.AspNetCore.Mvc.Localization.Test
|
|||
{
|
||||
// Arrange
|
||||
var builder = new TestMvcBuilder();
|
||||
|
||||
builder.Services.AddSingleton<ILoggerFactory>(NullLoggerFactory.Instance);
|
||||
|
||||
var dataAnnotationLocalizerProvider = new Func<Type, IStringLocalizerFactory, IStringLocalizer>((type, factory) =>
|
||||
{
|
||||
return null;
|
||||
|
|
|
|||
|
|
@ -8,6 +8,8 @@ using Microsoft.AspNetCore.Mvc.DataAnnotations;
|
|||
using Microsoft.AspNetCore.Mvc.Razor;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Localization;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Logging.Abstractions;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Xunit;
|
||||
|
||||
|
|
@ -104,6 +106,9 @@ namespace Microsoft.AspNetCore.Mvc.Localization.Test
|
|||
{
|
||||
// Arrange
|
||||
var builder = new TestMvcCoreBuilder();
|
||||
|
||||
builder.Services.AddSingleton<ILoggerFactory>(NullLoggerFactory.Instance);
|
||||
|
||||
var dataAnnotationLocalizerProvider = new Func<Type, IStringLocalizerFactory, IStringLocalizer>((type, factory) =>
|
||||
{
|
||||
return null;
|
||||
|
|
|
|||
|
|
@ -659,6 +659,7 @@ Environment.NewLine;
|
|||
Mock.Of<IUrlHelper>(),
|
||||
viewEngine.Object,
|
||||
provider,
|
||||
localizerFactory: null,
|
||||
innerHelper => new StubbyHtmlHelper(innerHelper));
|
||||
helper.ViewData["Property1"] = "True";
|
||||
|
||||
|
|
@ -701,6 +702,7 @@ Environment.NewLine;
|
|||
Mock.Of<IUrlHelper>(),
|
||||
viewEngine.Object,
|
||||
provider,
|
||||
localizerFactory: null,
|
||||
innerHelper => new StubbyHtmlHelper(innerHelper));
|
||||
|
||||
// TemplateBuilder sets FormattedModelValue before calling TemplateRenderer and it's used in most templates.
|
||||
|
|
@ -742,6 +744,7 @@ Environment.NewLine;
|
|||
Mock.Of<IUrlHelper>(),
|
||||
viewEngine.Object,
|
||||
provider,
|
||||
localizerFactory: null,
|
||||
innerHelper => new StubbyHtmlHelper(innerHelper));
|
||||
helper.ViewData["Property1"] = "True";
|
||||
|
||||
|
|
@ -784,6 +787,7 @@ Environment.NewLine;
|
|||
Mock.Of<IUrlHelper>(),
|
||||
viewEngine.Object,
|
||||
provider,
|
||||
localizerFactory: null,
|
||||
innerHelper => new StubbyHtmlHelper(innerHelper));
|
||||
|
||||
// TemplateBuilder sets FormattedModelValue before calling TemplateRenderer and it's used in most templates.
|
||||
|
|
|
|||
|
|
@ -80,6 +80,7 @@ namespace Microsoft.AspNetCore.Mvc.Rendering
|
|||
CreateUrlHelper(),
|
||||
CreateViewEngine(),
|
||||
metadataProvider,
|
||||
localizerFactory: null,
|
||||
innerHelperWrapper: null,
|
||||
htmlGenerator: htmlGenerator,
|
||||
idAttributeDotReplacement: null);
|
||||
|
|
@ -92,6 +93,7 @@ namespace Microsoft.AspNetCore.Mvc.Rendering
|
|||
CreateUrlHelper(),
|
||||
CreateViewEngine(),
|
||||
TestModelMetadataProvider.CreateDefaultProvider(),
|
||||
localizerFactory: null,
|
||||
innerHelperWrapper: null,
|
||||
htmlGenerator: null,
|
||||
idAttributeDotReplacement: null);
|
||||
|
|
@ -106,6 +108,7 @@ namespace Microsoft.AspNetCore.Mvc.Rendering
|
|||
CreateUrlHelper(),
|
||||
CreateViewEngine(),
|
||||
TestModelMetadataProvider.CreateDefaultProvider(),
|
||||
localizerFactory: null,
|
||||
innerHelperWrapper: null,
|
||||
htmlGenerator: null,
|
||||
idAttributeDotReplacement: idAttributeDotReplacement);
|
||||
|
|
@ -127,6 +130,7 @@ namespace Microsoft.AspNetCore.Mvc.Rendering
|
|||
CreateUrlHelper(),
|
||||
CreateViewEngine(),
|
||||
provider,
|
||||
localizerFactory: null,
|
||||
innerHelperWrapper: null,
|
||||
htmlGenerator: null,
|
||||
idAttributeDotReplacement: idAttributeDotReplacement);
|
||||
|
|
@ -167,7 +171,8 @@ namespace Microsoft.AspNetCore.Mvc.Rendering
|
|||
model,
|
||||
CreateUrlHelper(),
|
||||
viewEngine,
|
||||
TestModelMetadataProvider.CreateDefaultProvider(stringLocalizerFactory));
|
||||
TestModelMetadataProvider.CreateDefaultProvider(stringLocalizerFactory),
|
||||
stringLocalizerFactory);
|
||||
}
|
||||
|
||||
public static HtmlHelper<TModel> GetHtmlHelper<TModel>(
|
||||
|
|
@ -180,6 +185,7 @@ namespace Microsoft.AspNetCore.Mvc.Rendering
|
|||
CreateUrlHelper(),
|
||||
viewEngine,
|
||||
TestModelMetadataProvider.CreateDefaultProvider(),
|
||||
localizerFactory: null,
|
||||
innerHelperWrapper);
|
||||
}
|
||||
|
||||
|
|
@ -187,9 +193,10 @@ namespace Microsoft.AspNetCore.Mvc.Rendering
|
|||
TModel model,
|
||||
IUrlHelper urlHelper,
|
||||
ICompositeViewEngine viewEngine,
|
||||
IModelMetadataProvider provider)
|
||||
IModelMetadataProvider provider,
|
||||
IStringLocalizerFactory localizerFactory = null)
|
||||
{
|
||||
return GetHtmlHelper(model, urlHelper, viewEngine, provider, innerHelperWrapper: null);
|
||||
return GetHtmlHelper(model, urlHelper, viewEngine, provider, localizerFactory, innerHelperWrapper: null);
|
||||
}
|
||||
|
||||
public static HtmlHelper<TModel> GetHtmlHelper<TModel>(
|
||||
|
|
@ -197,6 +204,7 @@ namespace Microsoft.AspNetCore.Mvc.Rendering
|
|||
IUrlHelper urlHelper,
|
||||
ICompositeViewEngine viewEngine,
|
||||
IModelMetadataProvider provider,
|
||||
IStringLocalizerFactory localizerFactory,
|
||||
Func<IHtmlHelper, IHtmlHelper> innerHelperWrapper)
|
||||
{
|
||||
var viewData = new ViewDataDictionary<TModel>(provider);
|
||||
|
|
@ -207,6 +215,7 @@ namespace Microsoft.AspNetCore.Mvc.Rendering
|
|||
urlHelper,
|
||||
viewEngine,
|
||||
provider,
|
||||
localizerFactory,
|
||||
innerHelperWrapper,
|
||||
htmlGenerator: null,
|
||||
idAttributeDotReplacement: null);
|
||||
|
|
@ -217,6 +226,7 @@ namespace Microsoft.AspNetCore.Mvc.Rendering
|
|||
IUrlHelper urlHelper,
|
||||
ICompositeViewEngine viewEngine,
|
||||
IModelMetadataProvider provider,
|
||||
IStringLocalizerFactory localizerFactory,
|
||||
Func<IHtmlHelper, IHtmlHelper> innerHelperWrapper,
|
||||
IHtmlGenerator htmlGenerator,
|
||||
string idAttributeDotReplacement)
|
||||
|
|
@ -229,14 +239,14 @@ namespace Microsoft.AspNetCore.Mvc.Rendering
|
|||
{
|
||||
options.HtmlHelperOptions.IdAttributeDotReplacement = idAttributeDotReplacement;
|
||||
}
|
||||
var localizationOptionsAccesor = new Mock<IOptions<MvcDataAnnotationsLocalizationOptions>>();
|
||||
|
||||
localizationOptionsAccesor.SetupGet(o => o.Value).Returns(new MvcDataAnnotationsLocalizationOptions());
|
||||
var localizationOptions = new MvcDataAnnotationsLocalizationOptions();
|
||||
var localizationOptionsAccesor = Options.Create(localizationOptions);
|
||||
|
||||
options.ClientModelValidatorProviders.Add(new DataAnnotationsClientModelValidatorProvider(
|
||||
new ValidationAttributeAdapterProvider(),
|
||||
localizationOptionsAccesor.Object,
|
||||
stringLocalizerFactory: null));
|
||||
localizationOptionsAccesor,
|
||||
localizerFactory));
|
||||
|
||||
var urlHelperFactory = new Mock<IUrlHelperFactory>();
|
||||
urlHelperFactory
|
||||
|
|
|
|||
|
|
@ -0,0 +1,16 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using RazorWebSite.Models;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace RazorWebSite.Controllers
|
||||
{
|
||||
public class EnumController : Controller
|
||||
{
|
||||
public IActionResult Enum()
|
||||
{
|
||||
return View(new EnumModel{ Id = ModelEnum.FirstOption });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
// 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.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace RazorWebSite.Models
|
||||
{
|
||||
public enum ModelEnum
|
||||
{
|
||||
[Display(Name = "FirstOptionDisplay")]
|
||||
FirstOption,
|
||||
SecondOptions
|
||||
}
|
||||
|
||||
public class EnumModel
|
||||
{
|
||||
[Display(Name = "ModelEnum")]
|
||||
public ModelEnum Id { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
// 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.IO;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
|
||||
namespace RazorWebSite
|
||||
{
|
||||
public class Program
|
||||
{
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
var host = CreateWebHostBuilder(args)
|
||||
.Build();
|
||||
|
||||
host.Run();
|
||||
}
|
||||
|
||||
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
|
||||
new WebHostBuilder()
|
||||
.UseContentRoot(Directory.GetCurrentDirectory())
|
||||
.UseStartup<Startup>()
|
||||
.UseKestrel()
|
||||
.UseIISIntegration();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,123 @@
|
|||
<?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="FirstOptionDisplay" xml:space="preserve">
|
||||
<value>first option from enum resx</value>
|
||||
</data>
|
||||
</root>
|
||||
|
|
@ -0,0 +1,123 @@
|
|||
<?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="FirstOptionDisplay" xml:space="preserve">
|
||||
<value>FirstOptionDisplay from singletype</value>
|
||||
</data>
|
||||
</root>
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
// 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 RazorWebSite
|
||||
{
|
||||
public class SingleType
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -3,10 +3,8 @@
|
|||
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.Localization;
|
||||
using Microsoft.AspNetCore.Mvc.Razor;
|
||||
using Microsoft.AspNetCore.Razor.TagHelpers;
|
||||
|
|
@ -72,21 +70,5 @@ namespace RazorWebSite
|
|||
|
||||
app.UseMvcWithDefaultRoute();
|
||||
}
|
||||
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
var host = CreateWebHostBuilder(args)
|
||||
.Build();
|
||||
|
||||
host.Run();
|
||||
}
|
||||
|
||||
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
|
||||
new WebHostBuilder()
|
||||
.UseContentRoot(Directory.GetCurrentDirectory())
|
||||
.UseStartup<Startup>()
|
||||
.UseKestrel()
|
||||
.UseIISIntegration();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,54 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Localization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.Infrastructure;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace RazorWebSite
|
||||
{
|
||||
public class StartupDataAnnotations
|
||||
{
|
||||
// This method gets called by the runtime. Use this method to add services to the container.
|
||||
public void ConfigureServices(IServiceCollection services)
|
||||
{
|
||||
services.AddLocalization(options => options.ResourcesPath = "Resources");
|
||||
|
||||
services
|
||||
.AddMvc()
|
||||
.AddViewLocalization()
|
||||
.AddDataAnnotationsLocalization((options) =>
|
||||
{
|
||||
options.DataAnnotationLocalizerProvider =
|
||||
(modelType, stringLocalizerFactory) => stringLocalizerFactory.Create(typeof(SingleType));
|
||||
});
|
||||
services.Configure<MvcCompatibilityOptions>(options => options.CompatibilityVersion = CompatibilityVersion.Latest);
|
||||
}
|
||||
|
||||
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
|
||||
public void Configure(IApplicationBuilder app)
|
||||
{
|
||||
app.UseDeveloperExceptionPage();
|
||||
app.UseRequestLocalization(new RequestLocalizationOptions
|
||||
{
|
||||
DefaultRequestCulture = new RequestCulture("en-US", "en-US"),
|
||||
SupportedCultures = new List<CultureInfo>
|
||||
{
|
||||
new CultureInfo("en-US")
|
||||
},
|
||||
SupportedUICultures = new List<CultureInfo>
|
||||
{
|
||||
new CultureInfo("en-US")
|
||||
}
|
||||
});
|
||||
app.UseStaticFiles();
|
||||
|
||||
// Add MVC to the request pipeline
|
||||
app.UseMvcWithDefaultRoute();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
@model RazorWebSite.Models.EnumModel
|
||||
|
||||
<div>
|
||||
@Html.DisplayNameFor(model => model.Id)
|
||||
@Html.DisplayFor(model => model.Id)
|
||||
</div>
|
||||
Loading…
Reference in New Issue