using System.ComponentModel; using System.Globalization; using System.Text.Json; using Avalonia.Data; using Avalonia.Markup.Xaml; using Avalonia.Markup.Xaml.MarkupExtensions; namespace MeWeb.App.Services; public sealed class Localizer : INotifyPropertyChanged { readonly Dictionary english = Load(Languages.Fallback.Code); Dictionary current; public Localizer() => current = english; public static Localizer Instance { get; } = new(); public event PropertyChangedEventHandler PropertyChanged; public Language Language { get; private set; } = Languages.Fallback; public CultureInfo Culture { get; private set; } = CultureInfo.GetCultureInfo(Languages.Fallback.Code); public bool IsRightToLeft => Language.Rtl; public string this[string key] => current.GetValueOrDefault(key) ?? english.GetValueOrDefault(key) ?? key; public string Format(string key, params object[] args) => string.Format(Culture, this[key], args); public void SetLanguage(Language language) { Language = language ?? Languages.Fallback; current = Language.Code == Languages.Fallback.Code ? english : Load(Language.Code); Culture = CultureFor(Language.Code); CultureInfo.DefaultThreadCurrentCulture = CultureInfo.DefaultThreadCurrentUICulture = Culture; CultureInfo.CurrentCulture = CultureInfo.CurrentUICulture = Culture; PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Item[]")); PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(string.Empty)); } public static bool Has(string code) => typeof(Localizer).Assembly.GetManifestResourceInfo(ResourceName(code)) is not null; static string ResourceName(string code) => $"strings.{code}.json"; static Dictionary Load(string code) { using var stream = typeof(Localizer).Assembly.GetManifestResourceStream(ResourceName(code)); if (stream is null) return []; return JsonSerializer.Deserialize(stream, AppJsonContext.Default.DictionaryStringString) ?? []; } static CultureInfo CultureFor(string code) { try { return CultureInfo.GetCultureInfo(code); } catch (CultureNotFoundException) { return CultureInfo.InvariantCulture; } } } public sealed class TrExtension : MarkupExtension { public TrExtension() { } public TrExtension(string key) => Key = key; public string Key { get; set; } public override object ProvideValue(IServiceProvider serviceProvider) => new ReflectionBindingExtension($"[{Key}]") { Mode = BindingMode.OneWay, Source = Localizer.Instance }.ProvideValue(serviceProvider); }