feat(i18n): add Avalonia loc:Tr markup extension and LocalizedString

This commit is contained in:
mika kuns
2026-06-03 11:44:16 +02:00
parent 3c40bb5ea3
commit 35ad1715d3
4 changed files with 81 additions and 0 deletions

View File

@@ -2,6 +2,7 @@
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\ClaudeDo.Data\ClaudeDo.Data.csproj" /> <ProjectReference Include="..\ClaudeDo.Data\ClaudeDo.Data.csproj" />
<ProjectReference Include="..\ClaudeDo.Localization\ClaudeDo.Localization.csproj" />
<ProjectReference Include="..\ClaudeDo.Releases\ClaudeDo.Releases.csproj" /> <ProjectReference Include="..\ClaudeDo.Releases\ClaudeDo.Releases.csproj" />
</ItemGroup> </ItemGroup>

View File

@@ -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;
}

View File

@@ -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
};
}
}

View File

@@ -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);
}
}