mirror of
https://gitee.com/LongbowEnterprise/BootstrapBlazor.git
synced 2024-12-05 13:39:39 +08:00
!794 feat(#I29N8T): TableColumn support localization
* docs: BindItem 支持多语言 * docs: 增加 BindItem 多语言示例 * feat: 设计 ColumnNameAttribute 标签用于支持列头多语言
This commit is contained in:
parent
5141d5144a
commit
d26df34bda
@ -29,5 +29,13 @@
|
||||
"IntroText1": "For displaying anchor hyperlinks on page and jumping between them.",
|
||||
"IntroText2": "Click <code>Anchor</code> item try it",
|
||||
"ContentText1": "<p>Cras mattis consectetur purus sit amet fermentum. Cras justo odio, dapibus ac facilisis in, egestas eget quam. Morbi leo risus, porta ac consectetur ac, vestibulum at eros.</p><p>Praesent commodo cursus magna, vel scelerisque nisl consectetur et. Vivamus sagittis lacus vel augue laoreet rutrum faucibus dolor auctor.</p><p>Aenean lacinia bibendum nulla sed consectetur. Praesent commodo cursus magna, vel scelerisque nisl consectetur et. Donec sed odio dui. Donec ullamcorper nulla non metus auctor fringilla.</p><p>Cras mattis consectetur purus sit amet fermentum. Cras justo odio, dapibus ac facilisis in, egestas eget quam. Morbi leo risus, porta ac consectetur ac, vestibulum at eros.</p><p>Praesent commodo cursus magna, vel scelerisque nisl consectetur et. Vivamus sagittis lacus vel augue laoreet rutrum faucibus dolor auctor.</p>"
|
||||
},
|
||||
"BootstrapBlazor.Shared.Pages.BindItem": {
|
||||
"Name": "Name",
|
||||
"DateTime": "DateTime",
|
||||
"Address": "Address",
|
||||
"Count": "Count",
|
||||
"Complete": "Complete",
|
||||
"Education": "Education"
|
||||
}
|
||||
}
|
||||
|
@ -29,5 +29,13 @@
|
||||
"IntroText1": "需要展现当前页面上可供跳转的锚点链接,以及快速在锚点之间跳转",
|
||||
"IntroText2": "点击下面 <code>Anchor</code> 项目,页面滚动到相对应的章节",
|
||||
"ContentText1": "<p>与现实生活一致:与现实生活的流程、逻辑保持一致,遵循用户习惯的语言和概念;在界面中一致:所有的元素和结构需保持一致,比如:设计样式、图标和文本、元素的位置等。</p><p>控制反馈:通过界面样式和交互动效让用户可以清晰的感知自己的操作;</p><p>页面反馈:操作后,通过页面元素的变化清晰地展现当前状态。</p><p>简化流程:设计简洁直观的操作流程;</p><p>清晰明确:语言表达清晰且表意明确,让用户快速理解进而作出决策;</p><p>帮助用户识别:界面简单直白,让用户快速识别而非回忆,减少用户记忆负担。</p>"
|
||||
},
|
||||
"BootstrapBlazor.Shared.Pages.BindItem": {
|
||||
"Name": "姓名",
|
||||
"DateTime": "日期",
|
||||
"Address": "地址",
|
||||
"Count": "数量",
|
||||
"Complete": "是/否",
|
||||
"Education": "学历"
|
||||
}
|
||||
}
|
||||
|
@ -601,6 +601,8 @@ namespace BootstrapBlazor.Shared.Pages
|
||||
[Table("Test")]
|
||||
public class BindItem
|
||||
{
|
||||
// 列头信息支持 ColumnName Display DisplayName 三种标签
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
@ -613,22 +615,22 @@ namespace BootstrapBlazor.Shared.Pages
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
[Display(Name = "姓名")]
|
||||
[Required(ErrorMessage = "{0}不能为空")]
|
||||
[AutoGenerateColumn(Order = 10)]
|
||||
[ColumnName(Name = "姓名", ResourceName = "Name", ResourceType = typeof(BindItem))]
|
||||
public string? Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
[Display(Name = "日期")]
|
||||
[AutoGenerateColumn(Order = 1, FormatString = "yyyy-MM-dd", Width = 180)]
|
||||
[ColumnName(Name = "日期", ResourceName = "DateTime", ResourceType = typeof(BindItem))]
|
||||
public DateTime? DateTime { get; set; }
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
[Display(Name = "地址")]
|
||||
[ColumnName(Name = "地址", ResourceName = "Address", ResourceType = typeof(BindItem))]
|
||||
[Required(ErrorMessage = "{0}不能为空")]
|
||||
[AutoGenerateColumn(Order = 20)]
|
||||
public string? Address { get; set; }
|
||||
@ -636,14 +638,14 @@ namespace BootstrapBlazor.Shared.Pages
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
[Display(Name = "数量")]
|
||||
[ColumnName(Name = "数量", ResourceName = "Count", ResourceType = typeof(BindItem))]
|
||||
[AutoGenerateColumn(Order = 40)]
|
||||
public int Count { get; set; }
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
[Display(Name = "是/否")]
|
||||
[ColumnName(Name = "是/否", ResourceName = "Complete", ResourceType = typeof(BindItem))]
|
||||
[AutoGenerateColumn(Order = 50)]
|
||||
public bool Complete { get; set; }
|
||||
|
||||
@ -651,7 +653,7 @@ namespace BootstrapBlazor.Shared.Pages
|
||||
///
|
||||
/// </summary>
|
||||
[Required(ErrorMessage = "请选择{0}")]
|
||||
[Display(Name = "学历")]
|
||||
[ColumnName(Name = "学历", ResourceName = "Education", ResourceType = typeof(BindItem))]
|
||||
[AutoGenerateColumn(Order = 60)]
|
||||
//[EnumConverter(typeof(EnumEducation))]
|
||||
public EnumEducation? Education { get; set; }
|
||||
|
57
src/BootstrapBlazor/Attributes/ColumnNameAttribute.cs
Normal file
57
src/BootstrapBlazor/Attributes/ColumnNameAttribute.cs
Normal file
@ -0,0 +1,57 @@
|
||||
// **********************************
|
||||
// 框架名称:BootstrapBlazor
|
||||
// 框架作者:Argo Zhang
|
||||
// 开源地址:
|
||||
// Gitee : https://gitee.com/LongbowEnterprise/BootstrapBlazor
|
||||
// GitHub: https://github.com/ArgoZhang/BootstrapBlazor
|
||||
// 开源协议:Apache-2.0 (https://gitee.com/LongbowEnterprise/BootstrapBlazor/blob/dev/LICENSE)
|
||||
// **********************************
|
||||
|
||||
using BootstrapBlazor.Localization.Json;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using System;
|
||||
|
||||
namespace BootstrapBlazor.Components
|
||||
{
|
||||
/// <summary>
|
||||
/// ColumnName 属性标签用于标示列头显示信息支持多语言
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Property)]
|
||||
public class ColumnNameAttribute : Attribute
|
||||
{
|
||||
/// <summary>
|
||||
/// 获得/设置 列名称
|
||||
/// </summary>
|
||||
public string? Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 获得/设置 资源文件键值名称 为空时使用当前属性名
|
||||
/// </summary>
|
||||
public string? ResourceName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 获得/设置 资源文件所在程序集 为空时扫描当前程序集
|
||||
/// </summary>
|
||||
public Type? ResourceType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 获得显示名称方法
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public string? GetName()
|
||||
{
|
||||
var ret = Name;
|
||||
if (ResourceType != null && !string.IsNullOrEmpty(ResourceName))
|
||||
{
|
||||
var options = ServiceProviderHelper.ServiceProvider.GetRequiredService<IOptions<JsonLocalizationOptions>>();
|
||||
var loggerFactory = ServiceProviderHelper.ServiceProvider.GetRequiredService<ILoggerFactory>();
|
||||
var factory = new JsonStringLocalizerFactory(options, loggerFactory);
|
||||
var localizer = factory.Create(ResourceType);
|
||||
ret = localizer[ResourceName];
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
@ -51,6 +51,8 @@ namespace Microsoft.Extensions.DependencyInjection
|
||||
}
|
||||
});
|
||||
|
||||
ServiceProviderHelper.RegisterService(services);
|
||||
|
||||
return services;
|
||||
}
|
||||
}
|
||||
|
@ -12,6 +12,8 @@ using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.ComponentModel;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Globalization;
|
||||
using System.Reflection;
|
||||
|
||||
namespace Microsoft.AspNetCore.Components.Forms
|
||||
@ -21,7 +23,7 @@ namespace Microsoft.AspNetCore.Components.Forms
|
||||
/// </summary>
|
||||
public static class FieldIdentifierExtensions
|
||||
{
|
||||
private static ConcurrentDictionary<(Type ModelType, string FieldName), string> DisplayNameCache { get; } = new ConcurrentDictionary<(Type, string), string>();
|
||||
private static ConcurrentDictionary<(string CultureInfoName, Type ModelType, string FieldName), string> DisplayNameCache { get; } = new ConcurrentDictionary<(string, Type, string), string>();
|
||||
private static ConcurrentDictionary<(Type ModelType, string FieldName), string> PlaceHolderCache { get; } = new ConcurrentDictionary<(Type, string), string>();
|
||||
|
||||
private static ConcurrentDictionary<(Type ModelType, string FieldName), PropertyInfo> PropertyInfoCache { get; } = new ConcurrentDictionary<(Type, string), PropertyInfo>();
|
||||
@ -49,19 +51,27 @@ namespace Microsoft.AspNetCore.Components.Forms
|
||||
/// <returns></returns>
|
||||
public static string GetDisplayName(this Type modelType, string fieldName)
|
||||
{
|
||||
var cacheKey = (Type: modelType, FieldName: fieldName);
|
||||
var cacheKey = (CultureInfoName: CultureInfo.CurrentUICulture.Name, Type: modelType, FieldName: fieldName);
|
||||
if (!DisplayNameCache.TryGetValue(cacheKey, out var dn))
|
||||
{
|
||||
if (TryGetValidatableProperty(cacheKey.Type, cacheKey.FieldName, out var propertyInfo))
|
||||
{
|
||||
var displayNameAttribute = propertyInfo!.GetCustomAttribute<DisplayAttribute>();
|
||||
if (displayNameAttribute != null)
|
||||
var colNameAttribute = propertyInfo.GetCustomAttribute<ColumnNameAttribute>();
|
||||
if (colNameAttribute != null)
|
||||
{
|
||||
dn = displayNameAttribute.Name;
|
||||
dn = colNameAttribute.GetName();
|
||||
}
|
||||
else
|
||||
if (string.IsNullOrEmpty(dn))
|
||||
{
|
||||
var displayAttribute = propertyInfo!.GetCustomAttribute<DisplayNameAttribute>();
|
||||
var displayNameAttribute = propertyInfo.GetCustomAttribute<DisplayAttribute>();
|
||||
if (displayNameAttribute != null)
|
||||
{
|
||||
dn = displayNameAttribute.Name;
|
||||
}
|
||||
}
|
||||
if (string.IsNullOrEmpty(dn))
|
||||
{
|
||||
var displayAttribute = propertyInfo.GetCustomAttribute<DisplayNameAttribute>();
|
||||
if (displayAttribute != null)
|
||||
{
|
||||
dn = displayAttribute.DisplayName;
|
||||
@ -106,7 +116,7 @@ namespace Microsoft.AspNetCore.Components.Forms
|
||||
{
|
||||
if (TryGetValidatableProperty(cacheKey.Type, cacheKey.FieldName, out var propertyInfo))
|
||||
{
|
||||
var placeHolderAttribute = propertyInfo!.GetCustomAttribute<PlaceHolderAttribute>();
|
||||
var placeHolderAttribute = propertyInfo.GetCustomAttribute<PlaceHolderAttribute>();
|
||||
if (placeHolderAttribute != null)
|
||||
{
|
||||
dn = placeHolderAttribute.Text;
|
||||
@ -121,7 +131,7 @@ namespace Microsoft.AspNetCore.Components.Forms
|
||||
return dn;
|
||||
}
|
||||
|
||||
private static bool TryGetValidatableProperty(Type modelType, string fieldName, out PropertyInfo? propertyInfo)
|
||||
private static bool TryGetValidatableProperty(Type modelType, string fieldName, [NotNullWhen(true)] out PropertyInfo? propertyInfo)
|
||||
{
|
||||
var cacheKey = (ModelType: modelType, FieldName: fieldName);
|
||||
if (!PropertyInfoCache.TryGetValue(cacheKey, out propertyInfo))
|
||||
|
38
src/BootstrapBlazor/Extensions/ServiceProviderHelper.cs
Normal file
38
src/BootstrapBlazor/Extensions/ServiceProviderHelper.cs
Normal file
@ -0,0 +1,38 @@
|
||||
// **********************************
|
||||
// 框架名称:BootstrapBlazor
|
||||
// 框架作者:Argo Zhang
|
||||
// 开源地址:
|
||||
// Gitee : https://gitee.com/LongbowEnterprise/BootstrapBlazor
|
||||
// GitHub: https://github.com/ArgoZhang/BootstrapBlazor
|
||||
// 开源协议:Apache-2.0 (https://gitee.com/LongbowEnterprise/BootstrapBlazor/blob/dev/LICENSE)
|
||||
// **********************************
|
||||
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using System;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
namespace BootstrapBlazor.Components
|
||||
{
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
internal static class ServiceProviderHelper
|
||||
{
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public static ServiceProvider ServiceProvider { get { return _lazy.Value; } }
|
||||
|
||||
[NotNull]
|
||||
private static Lazy<ServiceProvider>? _lazy;
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="services"></param>
|
||||
public static void RegisterService(IServiceCollection services)
|
||||
{
|
||||
_lazy = new Lazy<ServiceProvider>(() => services.BuildServiceProvider());
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user