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 { const string Name = "me-web?"; 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) { try { return string.Format(Culture, this[key], args); } catch (FormatException) { return this[key]; } } public void SetLanguage(Language language) { Language = language ?? Languages.Fallback; current = Language.Code == Languages.Fallback.Code ? english : Load(Language.Code); if (Language.Rtl) { var shown = current; current = english.Keys.Union(shown.Keys).ToDictionary(k => k, k => (shown.GetValueOrDefault(k) ?? english[k]).Replace(Name, $"\u2066{Name}\u2069", StringComparison.Ordinal)); } 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); }