using Atomx.Common.Models;
using Microsoft.JSInterop;
using System.Text.Json;
namespace Atomx.Admin.Client.Services
{
public interface ILocalizationService
{
///
/// 根据name获取制定文化语言的译文
///
///
///
///
Task GetStringAsync(string name, string? culture = null);
///
/// 把本地化文化语言加载到内存中
///
///
///
Task LoadResourcesAsync(string culture);
event EventHandler? ResourcesUpdated;
}
public class LocalizationClientService : ILocalizationService, IAsyncDisposable
{
private readonly HttpService _httpService;
private readonly IJSRuntime _jsRuntime;
private readonly ILogger _logger;
private readonly Dictionary> _resources = new();
private readonly Dictionary _versions = new();
private readonly SemaphoreSlim _semaphore = new(1, 1);
public event EventHandler? ResourcesUpdated;
public LocalizationClientService(
HttpService httpService,
IJSRuntime jsRuntime,
ILogger logger)
{
_httpService = httpService;
_jsRuntime = jsRuntime;
_logger = logger;
}
///
/// 根据name获取制定文化语言的译文
///
///
///
///
public async Task GetStringAsync(string name, string? culture = null)
{
culture ??= await GetCurrentCultureAsync();
if (_resources.TryGetValue(culture, out var cultureResources))
{
if (cultureResources.TryGetValue(name, out var value))
{
return value;
}
// 键不存在,触发资源更新检查
_ = Task.Run(async () => await CheckAndUpdateResourcesAsync(culture));
}
else
{
// 资源未加载,立即加载
await LoadResourcesAsync(culture);
// 重试获取
if (_resources.TryGetValue(culture, out cultureResources) &&
cultureResources.TryGetValue(name, out var value))
{
return value;
}
}
_logger.LogWarning("Localization key not found: {Key} for culture: {Culture}", name, culture);
return null;
}
///
/// 把本地化文化语言加载到内存中
///
///
///
public async Task LoadResourcesAsync(string culture)
{
await _semaphore.WaitAsync();
try
{
// 先尝试从localStorage加载
if (await TryLoadFromLocalStorage(culture))
{
// 检查服务器版本,如果需要更新则从服务器加载
if (await CheckAndUpdateFromServer(culture))
{
return true;
}
return true; // 本地版本仍然有效
}
// 从服务器加载
return await LoadFromServer(culture);
}
finally
{
_semaphore.Release();
}
}
///
/// 从单例对象中获取版本记录
///
///
///
private async Task GetResourceVersionAsync(string culture)
{
if (_versions.TryGetValue(culture, out var version))
{
return version;
}
var storedVersion = await GetStoredVersion(culture);
return storedVersion;
}
///
/// 检查更新资源
///
///
///
private async Task CheckAndUpdateResourcesAsync(string culture)
{
return await CheckAndUpdateFromServer(culture);
}
///
/// 从本地加载文化语言数据
///
///
///
private async Task TryLoadFromLocalStorage(string culture)
{
try
{
var resourcesJson = await _jsRuntime.InvokeAsync("localStorage.getItem", $"locales_{culture}");
var version = await _jsRuntime.InvokeAsync("localStorage.getItem", $"locales_version_{culture}");
if (string.IsNullOrEmpty(resourcesJson) || string.IsNullOrEmpty(version))
{
return false;
}
var resources = JsonSerializer.Deserialize>(resourcesJson);
if (resources != null && version != null)
{
_resources[culture] = resources;
_versions[culture] = version;
return true;
}
}
catch (Exception ex)
{
_logger.LogWarning(ex, "Failed to load resources from localStorage for culture: {Culture}", culture);
}
return false;
}
///
/// 从服务器更新获取文化语言数据
///
///
///
private async Task LoadFromServer(string culture)
{
try
{
var response = await _httpService.Get>($"/api/localeresource/resources/{culture}");
if (response.Success)
{
_resources[culture] = response.Data.Translations;
_versions[culture] = response.Data.ResourceVersion;
// 保存到localStorage
await SaveToLocalStorage(culture, response.Data.Translations, response.Data.ResourceVersion);
ResourcesUpdated?.Invoke(this, culture);
return true;
}
}
catch (Exception ex)
{
_logger.LogError(ex, "Failed to load resources from server for culture: {Culture}", culture);
}
return false;
}
///
/// 根据版本信息上从服务器数据对比版本更新数据
///
///
///
private async Task CheckAndUpdateFromServer(string culture)
{
try
{
var currentVersion = await GetResourceVersionAsync(culture);
var serverVersion = await GetServerVersion(culture);
if (serverVersion != null && (currentVersion == null || serverVersion != currentVersion))
{
_logger.LogInformation("Updating resources for culture: {Culture}", culture);
return await LoadFromServer(culture);
}
}
catch (Exception ex)
{
_logger.LogError(ex, "Failed to check and update resources for culture: {Culture}", culture);
}
return false;
}
///
/// 从服务器获取当前多语言文化数据版本信息
///
///
///
private async Task GetServerVersion(string culture)
{
try
{
var api = $"/api/localeresource/version/{culture}";
var result = await _httpService.Get>(api);
if (result.Success)
{
return result.Data;
}
}
catch (Exception ex)
{
_logger.LogWarning(ex, "Failed to get server version for culture: {Culture}", culture);
}
return null;
}
///
/// 获取存储在本地的文化语言版本信息
///
///
///
private async Task GetStoredVersion(string culture)
{
try
{
var version = await _jsRuntime.InvokeAsync("localStorage.getItem", $"locales_version_{culture}");
if (!string.IsNullOrEmpty(version))
{
return version;
}
}
catch (Exception ex)
{
_logger.LogWarning(ex, "Failed to get stored version for culture: {Culture}", culture);
}
return null;
}
///
/// 存储版本和文化语言信息
///
///
///
///
///
private async Task SaveToLocalStorage(string culture, Dictionary resources, string version)
{
try
{
var resourcesJson = JsonSerializer.Serialize(resources);
await _jsRuntime.InvokeVoidAsync("localStorage.setItem", $"locales_{culture}", resourcesJson);
await _jsRuntime.InvokeVoidAsync("localStorage.setItem", $"locales_version_{culture}", version);
}
catch (Exception ex)
{
_logger.LogWarning(ex, "Failed to save resources to localStorage for culture: {Culture}", culture);
}
}
///
/// 获取浏览器上的文化语言信息
///
///
private async Task GetCurrentCultureAsync()
{
try
{
return await _jsRuntime.InvokeAsync("blazorCulture.get");
}
catch
{
return "en-US"; // 默认文化
}
}
public async ValueTask DisposeAsync()
{
_semaphore?.Dispose();
}
}
}