using Microsoft.Extensions.Localization; using System.Globalization; using System.Net.Http; using System.Text.Json; namespace Atomx.Admin.Client.Services { public class JsonStringLocalizer : IStringLocalizer { private readonly string _resourcesPath; private readonly Dictionary> _resourcesCache = new(); private readonly object _lock = new(); private readonly HttpClient? _httpClient; // resourcesPath 应为相对于站点根的“目录”名称,例如 "Localization" // 在 Blazor WebAssembly 场景下,会使用注入的 HttpClient 从 wwwroot/Localization/{culture}.json 获取资源 public JsonStringLocalizer(string resourcesPath, HttpClient? httpClient = null) { _resourcesPath = (resourcesPath ?? "localization").Trim('/'); // 规范化 _httpClient = httpClient; } public LocalizedString this[string name] { get { var value = GetString(name); return new LocalizedString(name, value ?? name, resourceNotFound: value == null); } } public LocalizedString this[string name, params object[] arguments] { get { var format = GetString(name); var value = string.Format(format ?? name, arguments); return new LocalizedString(name, value, resourceNotFound: format == null); } } public IEnumerable GetAllStrings(bool includeParentCultures) { var culture = CultureInfo.CurrentUICulture.Name; var resources = LoadResources(culture); return resources.Select(r => new LocalizedString(r.Key, r.Value, false)); } private string? GetString(string name) { var culture = CultureInfo.CurrentUICulture.Name; var resources = LoadResources(culture); // 尝试当前文化 if (resources.TryGetValue(name, out var value)) return value; // 如果还找不到,尝试英文作为后备 if (culture != "en-US") { var enResources = LoadResources("en-US"); if (enResources.TryGetValue(name, out var enValue)) return enValue; } return null; } private Dictionary LoadResources(string culture) { lock (_lock) { if (_resourcesCache.TryGetValue(culture, out var cachedResources)) return cachedResources; var resources = new Dictionary(StringComparer.OrdinalIgnoreCase); var fileName = $"{culture}.json"; // 在浏览器(WASM)环境下,通过 HttpClient 从静态资源目录获取 if (OperatingSystem.IsBrowser() && _httpClient != null) { try { // 构造相对 URL,例如 "Localization/zh-Hans.json" var relativeUrl = $"{_resourcesPath}/{fileName}"; var json = _httpClient.GetStringAsync(relativeUrl).ConfigureAwait(false).GetAwaiter().GetResult(); var jsonResources = JsonSerializer.Deserialize>(json); if (jsonResources != null) { foreach (var item in jsonResources) { resources[item.Key] = item.Value; } } } catch (Exception ex) { Console.WriteLine($"Error loading localization file {fileName} via HttpClient: {ex.Message}"); } _resourcesCache[culture] = resources; return resources; } // 非浏览器(例如 Server 或在 prerender 阶段)尝试从文件系统读取。 // 尝试几种可能的路径:基路径为 AppContext.BaseDirectory 或当前工作目录,或直接使用传入的路径。 try { var candidates = new[] { Path.Combine(AppContext.BaseDirectory, _resourcesPath, fileName), Path.Combine(Directory.GetCurrentDirectory(), _resourcesPath, fileName), Path.Combine(_resourcesPath, fileName), }; var filePath = candidates.FirstOrDefault(File.Exists); if (!string.IsNullOrEmpty(filePath)) { var json = File.ReadAllText(filePath); var jsonResources = JsonSerializer.Deserialize>(json); if (jsonResources != null) { foreach (var item in jsonResources) { resources[item.Key] = item.Value; } } } } catch (Exception ex) { Console.WriteLine($"Error loading localization file {fileName} from disk: {ex.Message}"); } _resourcesCache[culture] = resources; return resources; } } } }