Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
// Maintainer: Argo Zhang([email protected]) Website: https://www.blazor.zone

using Microsoft.Extensions.Caching.Memory;
using System.Collections.Frozen;
using System.Globalization;

namespace BootstrapBlazor.Server.Extensions;
Expand All @@ -20,7 +21,7 @@ internal static class CacheManagerExtensions
/// <param name="typeName"></param>
/// <param name="options"></param>
/// <returns></returns>
public static IEnumerable<LocalizedString> GetLocalizedStrings(this ICacheManager cache, string typeName, JsonLocalizationOptions options)
public static FrozenSet<LocalizedString> GetLocalizedStrings(this ICacheManager cache, string typeName, JsonLocalizationOptions options)
{
var key = $"Snippet-{CultureInfo.CurrentUICulture.Name}-{nameof(GetLocalizedStrings)}-{typeName}";
return cache.GetOrCreate(key, entry =>
Expand Down
25 changes: 12 additions & 13 deletions src/BootstrapBlazor/Extensions/LocalizationOptionsExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,22 +27,20 @@ public static IEnumerable<IConfigurationSection> GetJsonStringFromAssembly(this
var builder = new ConfigurationBuilder();

// 获取程序集中的资源文件
var assemblies = new List<Assembly>()
{
assembly
};
var assemblies = new List<Assembly>() { assembly };

var entryAssembly = GetAssembly();
if (assembly != entryAssembly)
{
assemblies.Add(entryAssembly);
}

if (option.AdditionalJsonAssemblies != null)
{
assemblies.AddRange(option.AdditionalJsonAssemblies);
}

var streams = assemblies.SelectMany(i => option.GetResourceStream(i, cultureName));
var streams = assemblies.SelectMany(i => option.GetResourceStream(i, cultureName)).ToList();

// 添加 Json 文件流到配置
foreach (var s in streams)
Expand Down Expand Up @@ -120,16 +118,17 @@ void AddStream(string name)

// 开启回落机制并且当前文化信息与回落语言相同
bool EqualFallbackCulture(string name) => option.EnableFallbackCulture && option.FallbackCulture == name;
}

StringSegment GetParentCultureName(StringSegment cultureInfoName)
static StringSegment GetParentCultureName(StringSegment cultureInfoName)
{
var ret = new StringSegment();
var index = cultureInfoName.IndexOf('-');
if (index > 0)
{
var ret = new StringSegment();
var index = cultureInfoName.IndexOf('-');
if (index > 0)
{
ret = cultureInfoName.Subsegment(0, index);
}
return ret;
ret = cultureInfoName.Subsegment(0, index);
}

return ret;
}
}
23 changes: 11 additions & 12 deletions src/BootstrapBlazor/Localization/Json/JsonStringLocalizer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@
using System.Reflection;
using System.Resources;

#if NET8_0_OR_GREATER
using System.Collections.Frozen;
#endif

namespace BootstrapBlazor.Components;

/// <summary>
Expand Down Expand Up @@ -97,14 +101,19 @@ public override LocalizedString this[string name]
}
}

private string? GetValueFromCache(IEnumerable<LocalizedString>? localizerStrings, string name)
#if NET8_0_OR_GREATER
private string? GetValueFromCache(FrozenSet<LocalizedString>? localizerStrings, string name)
#else
private string? GetValueFromCache(HashSet<LocalizedString>? localizerStrings, string name)
#endif
{
string? ret = null;
var cultureName = CultureInfo.CurrentUICulture.Name;
var cacheKey = $"{nameof(GetValueFromCache)}&name={name}&{Assembly.GetUniqueName()}&type={typeName}&culture={cultureName}";
if (!CacheManager.GetMissingLocalizerByKey(cacheKey))
{
var l = GetLocalizedString();
var l = localizerStrings?.FirstOrDefault(i => i.Name == name)
?? CacheManager.GetAllStringsFromResolve().FirstOrDefault(i => i.Name == name);
if (l is { ResourceNotFound: false })
{
ret = l.Value;
Expand All @@ -116,16 +125,6 @@ public override LocalizedString this[string name]
}
}
return ret;

LocalizedString? GetLocalizedString()
{
LocalizedString? localizer = null;
if (localizerStrings != null)
{
localizer = localizerStrings.FirstOrDefault(i => i.Name == name);
}
return localizer ?? CacheManager.GetAllStringsFromResolve().FirstOrDefault(i => i.Name == name);
}
}

private string? GetLocalizerValueFromCache(IStringLocalizer localizer, string name)
Expand Down
86 changes: 52 additions & 34 deletions src/BootstrapBlazor/Services/CacheManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@
using System.Linq.Expressions;
using System.Reflection;

#if NET8_0_OR_GREATER
using System.Collections.Frozen;
#endif

namespace BootstrapBlazor.Components;

/// <summary>
Expand Down Expand Up @@ -118,7 +122,7 @@ public void Clear(object? key)
/// </summary>
private void SetStartTime(DateTimeOffset startDateTimeOffset)
{
GetOrCreate("BootstrapBlazor_StartTime", entry => startDateTimeOffset);
GetOrCreate("BootstrapBlazor_StartTime", _ => startDateTimeOffset);
}

/// <summary>
Expand Down Expand Up @@ -159,7 +163,7 @@ public static int ElementCount(object? value)

#region Localizer
/// <summary>
///
/// 通过 Type 获得 <see cref="IStringLocalizer"/> 实例
/// </summary>
/// <param name="resourceSource"></param>
/// <returns></returns>
Expand All @@ -168,10 +172,10 @@ public static int ElementCount(object? value)
: Instance.Provider.GetRequiredService<IStringLocalizerFactory>().Create(resourceSource);

/// <summary>
///
/// 获得 <see cref="JsonLocalizationOptions"/> 值
/// </summary>
/// <returns></returns>
public static JsonLocalizationOptions GetJsonLocalizationOption()
private static JsonLocalizationOptions GetJsonLocalizationOption()
{
var localizationOptions = Instance.Provider.GetRequiredService<IOptions<JsonLocalizationOptions>>();
return localizationOptions.Value;
Expand All @@ -185,20 +189,17 @@ public static JsonLocalizationOptions GetJsonLocalizationOption()
/// <returns></returns>
public static IStringLocalizer? GetStringLocalizerFromService(Assembly assembly, string typeName) => assembly.IsDynamic
? null
: Instance.GetOrCreate($"{nameof(GetStringLocalizerFromService)}-{CultureInfo.CurrentUICulture.Name}-{assembly.GetUniqueName()}-{typeName}", entry =>
: Instance.GetOrCreate($"{nameof(GetStringLocalizerFromService)}-{CultureInfo.CurrentUICulture.Name}-{assembly.GetUniqueName()}-{typeName}", _ =>
{
IStringLocalizer? ret = null;
var factories = Instance.Provider.GetServices<IStringLocalizerFactory>();
if (factories != null)
var factory = factories?.LastOrDefault(a => a is not JsonStringLocalizerFactory);
if (factory != null)
{
var factory = factories.LastOrDefault(a => a is not JsonStringLocalizerFactory);
if (factory != null)
var type = assembly.GetType(typeName);
if (type != null)
{
var type = assembly.GetType(typeName);
if (type != null)
{
ret = factory.Create(type);
}
ret = factory.Create(type);
}
}
return ret;
Expand All @@ -209,8 +210,12 @@ public static JsonLocalizationOptions GetJsonLocalizationOption()
/// </summary>
/// <param name="assembly"></param>
/// <param name="typeName"></param>
/// <returns></returns>
public static IEnumerable<LocalizedString>? GetAllStringsByTypeName(Assembly assembly, string typeName) => GetJsonStringByTypeName(GetJsonLocalizationOption(), assembly, typeName, CultureInfo.CurrentUICulture.Name);
#if NET8_0_OR_GREATER
public static FrozenSet<LocalizedString>? GetAllStringsByTypeName(Assembly assembly, string typeName)
#else
public static HashSet<LocalizedString>? GetAllStringsByTypeName(Assembly assembly, string typeName)
#endif
=> GetJsonStringByTypeName(GetJsonLocalizationOption(), assembly, typeName, CultureInfo.CurrentUICulture.Name);

/// <summary>
/// 通过指定程序集获取所有本地化信息键值集合
Expand All @@ -221,36 +226,49 @@ public static JsonLocalizationOptions GetJsonLocalizationOption()
/// <param name="cultureName">cultureName 未空时使用 CultureInfo.CurrentUICulture.Name</param>
/// <param name="forceLoad">默认 false 使用缓存值 设置 true 时内部强制重新加载</param>
/// <returns></returns>
public static IEnumerable<LocalizedString>? GetJsonStringByTypeName(JsonLocalizationOptions option, Assembly assembly, string typeName, string? cultureName = null, bool forceLoad = false)
#if NET8_0_OR_GREATER
public static FrozenSet<LocalizedString>? GetJsonStringByTypeName(JsonLocalizationOptions option, Assembly assembly, string typeName, string? cultureName = null, bool forceLoad = false)
#else
public static HashSet<LocalizedString>? GetJsonStringByTypeName(JsonLocalizationOptions option, Assembly assembly, string typeName, string? cultureName = null, bool forceLoad = false)
#endif
{
return assembly.IsDynamic ? null : GetJsonStringByTypeName();
if (assembly.IsDynamic)
{
return null;
}

IEnumerable<LocalizedString>? GetJsonStringByTypeName()
cultureName ??= CultureInfo.CurrentUICulture.Name;
var key = $"{nameof(GetJsonStringByTypeName)}-{assembly.GetUniqueName()}-{cultureName}";
var typeKey = $"{key}-{typeName}";
if (forceLoad)
{
cultureName ??= CultureInfo.CurrentUICulture.Name;
var key = $"{nameof(GetJsonStringByTypeName)}-{assembly.GetUniqueName()}-{cultureName}";
var typeKey = $"{key}-{typeName}";
if (forceLoad)
{
Instance.Cache.Remove(key);
Instance.Cache.Remove(typeKey);
}
return Instance.GetOrCreate(typeKey, entry =>
{
var sections = Instance.GetOrCreate(key, entry => option.GetJsonStringFromAssembly(assembly, cultureName));
return sections.FirstOrDefault(kv => typeName.Equals(kv.Key, StringComparison.OrdinalIgnoreCase))?
.GetChildren()
.SelectMany(kv => new[] { new LocalizedString(kv.Key, kv.Value!, false, typeName) });
});
Instance.Cache.Remove(key);
Instance.Cache.Remove(typeKey);
}
return Instance.GetOrCreate(typeKey, entry =>
{
var sections = Instance.GetOrCreate(key, entry => option.GetJsonStringFromAssembly(assembly, cultureName));
var items = sections.FirstOrDefault(kv => typeName.Equals(kv.Key, StringComparison.OrdinalIgnoreCase))?
.GetChildren()
.SelectMany(kv => new[] { new LocalizedString(kv.Key, kv.Value!, false, typeName) });
#if NET8_0_OR_GREATER
return items?.ToFrozenSet();
#else
return items?.ToHashSet();
#endif
});
}

/// <summary>
/// 通过 ILocalizationResolve 接口实现类获得本地化键值集合
/// </summary>
/// <param name="includeParentCultures"></param>
/// <returns></returns>
public static IEnumerable<LocalizedString> GetAllStringsFromResolve(bool includeParentCultures = true) => Instance.GetOrCreate($"{nameof(GetAllStringsFromResolve)}-{CultureInfo.CurrentUICulture.Name}", entry => Instance.Provider.GetRequiredService<ILocalizationResolve>().GetAllStringsByCulture(includeParentCultures));
#if NET8_0_OR_GREATER
public static FrozenSet<LocalizedString> GetAllStringsFromResolve(bool includeParentCultures = true) => Instance.GetOrCreate($"{nameof(GetAllStringsFromResolve)}-{CultureInfo.CurrentUICulture.Name}", entry => Instance.Provider.GetRequiredService<ILocalizationResolve>().GetAllStringsByCulture(includeParentCultures).ToFrozenSet());
#else
public static HashSet<LocalizedString> GetAllStringsFromResolve(bool includeParentCultures = true) => Instance.GetOrCreate($"{nameof(GetAllStringsFromResolve)}-{CultureInfo.CurrentUICulture.Name}", entry => Instance.Provider.GetRequiredService<ILocalizationResolve>().GetAllStringsByCulture(includeParentCultures).ToHashSet());
#endif

/// <summary>
/// 查询缺失本地化资源项目
Expand Down
18 changes: 12 additions & 6 deletions src/BootstrapBlazor/Utils/Utility.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@
using System.Linq.Expressions;
using System.Reflection;

#if NET8_0_OR_GREATER
using System.Collections.Frozen;
#endif

namespace BootstrapBlazor.Components;

/// <summary>
Expand Down Expand Up @@ -156,8 +160,11 @@ public static class Utility
/// <param name="typeName">类名称</param>
/// <param name="cultureName">cultureName 未空时使用 CultureInfo.CurrentUICulture.Name</param>
/// <param name="forceLoad">默认 false 使用缓存值 设置 true 时内部强制重新加载</param>
/// <returns></returns>
public static IEnumerable<LocalizedString> GetJsonStringByTypeName(JsonLocalizationOptions option, Assembly assembly, string typeName, string? cultureName = null, bool forceLoad = false) => CacheManager.GetJsonStringByTypeName(option, assembly, typeName, cultureName, forceLoad) ?? [];
#if NET8_0_OR_GREATER
public static FrozenSet<LocalizedString> GetJsonStringByTypeName(JsonLocalizationOptions option, Assembly assembly, string typeName, string? cultureName = null, bool forceLoad = false) => CacheManager.GetJsonStringByTypeName(option, assembly, typeName, cultureName, forceLoad) ?? FrozenSet<LocalizedString>.Empty;
#else
public static HashSet<LocalizedString> GetJsonStringByTypeName(JsonLocalizationOptions option, Assembly assembly, string typeName, string? cultureName = null, bool forceLoad = false) => CacheManager.GetJsonStringByTypeName(option, assembly, typeName, cultureName, forceLoad) ?? new HashSet<LocalizedString>();
#endif

/// <summary>
/// 通过指定程序集与类型获得 IStringLocalizer 实例
Expand Down Expand Up @@ -367,7 +374,7 @@ public static IEnumerable<ITableColumn> GetTableColumns(Type type, IEnumerable<I
return defaultOrderCallback?.Invoke(cols) ?? cols;
}

internal static IEnumerable<ITableColumn> OrderFunc(this IEnumerable<ITableColumn> cols) => cols
internal static IEnumerable<ITableColumn> OrderFunc(this List<ITableColumn> cols) => cols
.Where(a => a.Order > 0).OrderBy(a => a.Order)
.Concat(cols.Where(a => a.Order == 0))
.Concat(cols.Where(a => a.Order < 0).OrderBy(a => a.Order));
Expand Down Expand Up @@ -404,7 +411,6 @@ public static void CreateDisplayByFieldType(this RenderTreeBuilder builder, IEdi
builder.AddAttribute(50, "class", col.CssClass);
}
builder.AddMultipleAttributes(60, item.ComponentParameters);
builder.CloseComponent();
}
else if (item.ComponentType == typeof(Textarea) || item.Rows > 0)
{
Expand All @@ -422,7 +428,6 @@ public static void CreateDisplayByFieldType(this RenderTreeBuilder builder, IEdi
builder.AddAttribute(60, "class", col.CssClass);
}
builder.AddMultipleAttributes(70, item.ComponentParameters);
builder.CloseComponent();
}
else
{
Expand All @@ -448,8 +453,9 @@ public static void CreateDisplayByFieldType(this RenderTreeBuilder builder, IEdi
builder.AddAttribute(90, "class", col.CssClass);
}
builder.AddMultipleAttributes(100, item.ComponentParameters);
builder.CloseComponent();
}

builder.CloseComponent();
}

/// <summary>
Expand Down
Loading