Files
MeWeb/src/MeWeb.App/Services/Localizer.cs
T
thepraandClaude Opus 5.5 7e3be9ed41
Build / Desktop, server and tools (push) Successful in 1m7s
Release / Every platform, the server, the release (push) Successful in 8m0s
"me-web?" 0.7.1: changing the language changes all of it
- Choosing another language now changes every text on the screen at once. Before, the titles, the question about the test from outside and most labels stayed in the previous language until the app was restarted.
- On narrow phones the name at the top shrinks to fit instead of slipping under the info button.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012CzABvBkbcFqoHdmi8b9WB
2026-10-16 00:21:41 +02:00

97 lines
2.9 KiB
C#

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<string, string> english = Load(Languages.Fallback.Code);
Dictionary<string, string> 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<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;
}
}
}
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);
}