diff --git a/src/ClaudeDo.Ui/ClaudeDo.Ui.csproj b/src/ClaudeDo.Ui/ClaudeDo.Ui.csproj index 7a19886..47a97fb 100644 --- a/src/ClaudeDo.Ui/ClaudeDo.Ui.csproj +++ b/src/ClaudeDo.Ui/ClaudeDo.Ui.csproj @@ -2,6 +2,7 @@ + diff --git a/src/ClaudeDo.Ui/Localization/LocalizedString.cs b/src/ClaudeDo.Ui/Localization/LocalizedString.cs new file mode 100644 index 0000000..23907f7 --- /dev/null +++ b/src/ClaudeDo.Ui/Localization/LocalizedString.cs @@ -0,0 +1,22 @@ +using System.ComponentModel; +using ClaudeDo.Localization; + +namespace ClaudeDo.Ui.Localization; + +public sealed class LocalizedString : INotifyPropertyChanged +{ + private readonly ILocalizer _localizer; + private readonly string _key; + + public LocalizedString(ILocalizer localizer, string key) + { + _localizer = localizer; + _key = key; + _localizer.LanguageChanged += (_, _) => + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Value))); + } + + public string Value => _localizer[_key]; + + public event PropertyChangedEventHandler? PropertyChanged; +} diff --git a/src/ClaudeDo.Ui/Localization/TrExtension.cs b/src/ClaudeDo.Ui/Localization/TrExtension.cs new file mode 100644 index 0000000..46dbbbb --- /dev/null +++ b/src/ClaudeDo.Ui/Localization/TrExtension.cs @@ -0,0 +1,25 @@ +using Avalonia.Data; +using Avalonia.Markup.Xaml; +using ClaudeDo.Localization; + +namespace ClaudeDo.Ui.Localization; + +public sealed class TrExtension : MarkupExtension +{ + public TrExtension() { } + public TrExtension(string key) => Key = key; + + public string Key { get; set; } = ""; + + public static ILocalizer? Localizer { get; set; } + + public override object ProvideValue(IServiceProvider serviceProvider) + { + var loc = Localizer ?? throw new InvalidOperationException("TrExtension.Localizer not initialized"); + return new Binding(nameof(LocalizedString.Value)) + { + Source = new LocalizedString(loc, Key), + Mode = BindingMode.OneWay + }; + } +} diff --git a/tests/ClaudeDo.Ui.Tests/LocalizedStringTests.cs b/tests/ClaudeDo.Ui.Tests/LocalizedStringTests.cs new file mode 100644 index 0000000..e9d50a0 --- /dev/null +++ b/tests/ClaudeDo.Ui.Tests/LocalizedStringTests.cs @@ -0,0 +1,33 @@ +using ClaudeDo.Localization; +using ClaudeDo.Ui.Localization; + +namespace ClaudeDo.Ui.Tests; + +public class LocalizedStringTests +{ + private static Localizer Make() + { + var dir = Path.Combine(Path.GetTempPath(), "loc_" + Guid.NewGuid().ToString("N")); + Directory.CreateDirectory(dir); + File.WriteAllText(Path.Combine(dir, "en.json"), + """{ "metadata": { "code": "en", "name": "English" }, "settings": { "save": "Save" } }"""); + File.WriteAllText(Path.Combine(dir, "de.json"), + """{ "metadata": { "code": "de", "name": "Deutsch" }, "settings": { "save": "Speichern" } }"""); + return new Localizer(LocaleStore.Load(dir), "en"); + } + + [Fact] + public void Value_tracks_language_change() + { + var loc = Make(); + var ls = new LocalizedString(loc, "settings.save"); + Assert.Equal("Save", ls.Value); + + string? changed = null; + ls.PropertyChanged += (_, e) => { if (e.PropertyName == nameof(LocalizedString.Value)) changed = ls.Value; }; + + loc.SetLanguage("de"); + Assert.Equal("Speichern", ls.Value); + Assert.Equal("Speichern", changed); + } +}