2026-10-15 01:14:44 +02:00
|
|
|
using System.ComponentModel;
|
|
|
|
|
using System.Globalization;
|
2026-10-15 01:18:13 +02:00
|
|
|
using System.Text.Json;
|
2026-10-15 01:14:44 +02:00
|
|
|
|
|
|
|
|
using Avalonia.Data;
|
|
|
|
|
using Avalonia.Markup.Xaml;
|
|
|
|
|
using Avalonia.Markup.Xaml.MarkupExtensions;
|
|
|
|
|
|
|
|
|
|
namespace MeWeb.App.Services;
|
|
|
|
|
|
|
|
|
|
public sealed class Localizer : INotifyPropertyChanged
|
|
|
|
|
{
|
2026-10-15 22:49:40 +02:00
|
|
|
const string Name = "me-web?";
|
|
|
|
|
|
2026-10-15 01:18:13 +02:00
|
|
|
readonly Dictionary<string, string> english = Load(Languages.Fallback.Code);
|
|
|
|
|
Dictionary<string, string> current;
|
|
|
|
|
|
|
|
|
|
public Localizer() => current = english;
|
2026-10-15 01:14:44 +02:00
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
|
2026-10-15 01:18:13 +02:00
|
|
|
public bool IsRightToLeft => Language.Rtl;
|
2026-10-15 01:14:44 +02:00
|
|
|
|
2026-10-15 01:18:13 +02:00
|
|
|
public string this[string key] => current.GetValueOrDefault(key) ?? english.GetValueOrDefault(key) ?? key;
|
2026-10-15 01:14:44 +02:00
|
|
|
|
2026-10-15 04:39:03 +02:00
|
|
|
public string Format(string key, params object[] args)
|
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
return string.Format(Culture, this[key], args);
|
|
|
|
|
}
|
|
|
|
|
catch (FormatException)
|
|
|
|
|
{
|
|
|
|
|
return this[key];
|
|
|
|
|
}
|
|
|
|
|
}
|
2026-10-15 01:14:44 +02:00
|
|
|
|
|
|
|
|
public void SetLanguage(Language language)
|
|
|
|
|
{
|
|
|
|
|
Language = language ?? Languages.Fallback;
|
2026-10-15 01:18:13 +02:00
|
|
|
current = Language.Code == Languages.Fallback.Code ? english : Load(Language.Code);
|
2026-10-15 22:49:40 +02:00
|
|
|
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));
|
|
|
|
|
}
|
2026-10-15 01:18:13 +02:00
|
|
|
Culture = CultureFor(Language.Code);
|
2026-10-15 01:14:44 +02:00
|
|
|
CultureInfo.DefaultThreadCurrentCulture = CultureInfo.DefaultThreadCurrentUICulture = Culture;
|
|
|
|
|
CultureInfo.CurrentCulture = CultureInfo.CurrentUICulture = Culture;
|
2026-10-16 00:21:41 +02:00
|
|
|
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Item"));
|
2026-10-15 01:14:44 +02:00
|
|
|
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(string.Empty));
|
|
|
|
|
}
|
2026-10-15 01:18:13 +02:00
|
|
|
|
|
|
|
|
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<string, string> 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;
|
|
|
|
|
}
|
|
|
|
|
}
|
2026-10-15 01:14:44 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
}
|