CdCSharp.Pangea.Localization 1.0.41

There is a newer version of this package available.
See the version list below for details.
dotnet add package CdCSharp.Pangea.Localization --version 1.0.41
                    
NuGet\Install-Package CdCSharp.Pangea.Localization -Version 1.0.41
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="CdCSharp.Pangea.Localization" Version="1.0.41" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="CdCSharp.Pangea.Localization" Version="1.0.41" />
                    
Directory.Packages.props
<PackageReference Include="CdCSharp.Pangea.Localization" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add CdCSharp.Pangea.Localization --version 1.0.41
                    
#r "nuget: CdCSharp.Pangea.Localization, 1.0.41"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package CdCSharp.Pangea.Localization@1.0.41
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=CdCSharp.Pangea.Localization&version=1.0.41
                    
Install as a Cake Addin
#tool nuget:?package=CdCSharp.Pangea.Localization&version=1.0.41
                    
Install as a Cake Tool

๐ŸŒ CdCSharp.Pangea

<div align="center">

NuGet Version NuGet Downloads Build Status License .NET Version Avalonia

An Avalonia toolkit: MVVM with generated bindings, themes as C# classes, storage and localization

๐Ÿ“ฆ Installation โ€ข ๐Ÿš€ Quick start โ€ข ๐Ÿง  Binding โ€ข ๐ŸŽจ Theming โ€ข ๐Ÿ’พ Storage โ€ข ๐ŸŒ Localization โ€ข ๐Ÿค– AI agents

</div>

Status: pre-1.0. The API is still moving and breaking changes land without a deprecation cycle. Pin a version if you depend on it.


๐ŸŽฏ What is Pangea?

Pangea wires up the parts an Avalonia application usually assembles by hand: a DI container, a source generator that turns fields into observable properties, a theme you can restyle from C#, per-platform storage paths, and localization.

Each capability is a feature โ€” a class implementing IPangeaFeature, discovered at startup, registering its own services.


๐Ÿ“ฆ Installation

Start from the template

dotnet new install CdCSharp.Pangea.Templates
dotnet new pangea-app -n MyApp
cd MyApp && dotnet run

You get a running Avalonia application: the startup wiring, a window, and a sample view model and palette showing the toolkit's conventions.

Option Default
--IncludeSkill true Copy the agent skill into the project
--Sample true Include the sample view model and palette
--PangeaVersion matches the template Version of the Pangea packages to reference

Add to an existing project

dotnet add package CdCSharp.Pangea

That is the package to install: besides pulling in every feature, it is where the application model lives โ€” PangeaApplication, UsePangea() and the window manager.

The features are also published on their own, for using a piece of the toolkit as a plain library without the Pangea application model. Each depends only on CdCSharp.Pangea.Core:

dotnet add package CdCSharp.Pangea.Binding       # [Binding] attribute + source generator
dotnet add package CdCSharp.Pangea.Theming       # palettes, themes, theme service
dotnet add package CdCSharp.Pangea.Storage       # per-platform paths and file access
dotnet add package CdCSharp.Pangea.Localization  # cultures and resource strings

Targets .NET 10 and Avalonia 12.1.


๐Ÿš€ Quick start

// App.axaml.cs
public partial class App : PangeaApplication
{
    public override void Configure(IServiceCollection services)
    {
        services.AddSingleton<IDataService, DataService>();
        // View models deriving from ViewModelBase are registered automatically
    }
}

// Program.cs
public static AppBuilder BuildAvaloniaApp()
    => AppBuilder.Configure<App>()
        .UsePlatformDetect()
        .WithInterFont()
        .LogToTrace()
        .UsePangea();

UsePangea() scans the application's assemblies once, registers every feature it finds, registers your view models, and builds the container. PangeaApplication then lets each feature configure the running application and shows the main window.


๐Ÿง  Binding

Fields marked [Binding] become properties that raise change notifications โ€” including for the computed properties and commands that depend on them.

public partial class ProductViewModel : ViewModelBase
{
    public ProductViewModel(IServiceProvider services) : base(services) { }

    [Binding] private string _name = "";
    [Binding] private decimal _price;
    [Binding] private bool _isAvailable = true;
    [Binding(ReadOnly = true)] private string _sku = "";
    [Binding(PropertyName = "Tags")] private ObservableCollection<string> _categories = [];

    public string DisplayName => $"{Name} ({Price:C})";
    public bool CanOrder => IsAvailable && Price > 0;

    public RelayCommand OrderCommand => CreateCommand(Order, () => CanOrder);
    public RelayCommand<string> AddTagCommand => CreateCommand<string>(AddTag);

    private void Order() => IsAvailable = false;

    private void AddTag(string? tag)
    {
        if (!string.IsNullOrEmpty(tag)) Tags.Add(tag);
    }

    // Optional hook, called by the generated setter
    partial void OnNameChanged() { }
}

The generator analyses the class and emits only the notifications that are actually needed:

partial class ProductViewModel
{
    public string Name
    {
        get => _name;
        set
        {
            if (SetProperty(ref _name, value))
            {
                OnNameChanged();

                // Computed property notifications
                OnPropertyChanged(nameof(DisplayName));
            }
        }
    }

    public decimal Price
    {
        get => _price;
        set
        {
            if (SetProperty(ref _price, value))
            {
                OnPriceChanged();

                // Computed property notifications
                OnPropertyChanged(nameof(CanOrder));
                OnPropertyChanged(nameof(DisplayName));

                // Command CanExecute notifications
                OrderCommand.RaiseCanExecuteChanged();
            }
        }
    }

    // ReadOnly: getter only, no change hook, no notifications
    public string Sku
    {
        get => _sku;
    }

    partial void OnNameChanged();
    partial void OnPriceChanged();
}

What it works out for you

Computed properties An expression- or getter-bodied property that reads a binding property is notified when it changes
Transitive chains Price โ†’ DisplayName โ†’ Summary propagates without you listing it
Commands A command whose CanExecute reads a property โ€” directly, through a CanX method, or through a computed property โ€” gets RaiseCanExecuteChanged()
Collections If OnXChanged calls a method that mutates a collection, whatever reads that collection is notified

Commands

CreateCommand builds a RelayCommand bound to the UI dispatcher.

public RelayCommand SaveCommand => CreateCommand(Save, () => CanSave);          // sync
public RelayCommand LoadCommand => CreateCommand(LoadAsync);                    // async
public RelayCommand<Item> RemoveCommand => CreateCommand<Item>(Remove);         // parameterised

A synchronous body runs on the UI thread, marshalled if you invoke the command from elsewhere. Background work belongs in the Func<Task> overload, where leaving the UI thread is explicit.

Failures always reach ViewModelBase.OnCommandError, which you can override. ExecuteAsync also rethrows so an awaiting caller can react; ICommand.Execute cannot, and does not.


๐ŸŽจ Theming

A theme is a pair of palettes, light and dark. A variant is which of the two is showing. They are separate axes: switching theme keeps the variant, and vice versa.

Restyling the application

Override the colours you care about โ€” everything derived from them follows:

using CdCSharp.Pangea.Theming.Palettes;

public sealed class BrandDark : DarkPalette
{
    public override Color ThemeAccentColor => Color.Parse("#FF4F9DDE");
    public override Color ThemeBackgroundColor => Color.Parse("#FF101418");
}

public sealed class BrandLight : PangeaPalette   // the base is the light palette
{
    public override Color ThemeAccentColor => Color.Parse("#FF1B6EC2");
}
services.Configure<ThemingOptions>(options =>
{
    // Replace the toolkit's own theme...
    options.Themes[PangeaTheme.DefaultName] = new PangeaTheme(new BrandLight(), new BrandDark());

    // ...or add more and let the user choose
    options.Themes["Contrast"] = new PangeaTheme(new ContrastLight(), new ContrastDark());
    options.DefaultTheme = "Contrast";

    options.EnableSystemThemeDetection = true;      // follow the OS preference
    options.FallbackVariant = ThemeVariant.Dark;    // when it has none
});

Every colour property name is its resource key, and each one also produces a brush with Color swapped for Brush. Overriding ThemeAccentColor therefore updates ThemeAccentColor, ThemeAccentBrush, and everything derived from them.

Switching at runtime

public class AppearanceViewModel : ViewModelBase
{
    private readonly IThemeService _themes;

    public AppearanceViewModel(IServiceProvider services) : base(services) =>
        _themes = services.GetRequiredService<IThemeService>();

    public IReadOnlyCollection<string> Themes => _themes.AvailableThemes;

    public RelayCommand<string> UseTheme => CreateCommand<string>(name => _themes.SetTheme(name!));
    public RelayCommand UseDark => CreateCommand(() => _themes.SetVariant(ThemeVariant.Dark));
}

The toolkit ships a ThemeSelector control with a view model that drives both axes.

Because the palettes are Avalonia theme variants, a ThemeVariantScope can render part of the UI in the opposite variant to the rest of the application.

Control themes

The control dictionaries under Resources/Controls/Shared are Avalonia's Simple theme, vendored as a starting point. A manifest records which files are still untouched copies and which this repo has taken ownership of, and a test fails when the two drift apart or when the Avalonia version moves without the theme being re-vendored.


๐Ÿ’พ Storage

Per-platform application folders plus file access.

public class SettingsService(IStorageService storage)
{
    private readonly string _path = storage.GetDataFilePath("settings.json");

    public Task<Settings?> LoadAsync() => storage.ReadJsonAsync<Settings>(_path);

    public Task SaveAsync(Settings settings) => storage.WriteJsonAsync(_path, settings);
}
services.Configure<StorageOptions>(options =>
{
    options.ApplicationName = "MyApp";   // folder name under the platform's data directory
    options.UsePortableMode = false;     // true keeps everything next to the executable
    options.CustomDataPath = null;       // or somewhere of your choosing
});

Writes create the folders they need. Reads do not: ReadTextAsync fails on a missing file the way File.ReadAllTextAsync does, while ReadJsonAsync returns null, for state that may not exist yet.


๐ŸŒ Localization

services.Configure<LocalizationOptions>(options =>
{
    options.SupportedCultures = ["en-US", "es-ES"];
    options.DefaultCulture = "en-US";
    options.AutoDetectCulture = true;

    // Assemblies holding the .resx-generated resource classes
    options.ResourceAssemblies.Add(typeof(Strings).Assembly);
});
public class GreetingViewModel : ViewModelBase
{
    private readonly ILocalizationService _localization;

    public string Welcome => _localization.GetString("WelcomeMessage");

    public void SwitchToSpanish() => _localization.SetCulture("es-ES");
}

An unresolved key comes back as itself, so a missing translation is visible rather than blank. SetCulture applies to the whole application, including threads started afterwards, and raises CultureChanged.


๐Ÿ”ง Configuration

public partial class App : PangeaApplication
{
    public override PangeaOptions ConfigurePangeaOptions(PangeaOptions options)
    {
        options.DI.AutoRegisterViewModels = true;
        options.DI.ViewModelLifetime = ServiceLifetime.Transient;

        // Assemblies to scan beyond those reachable from the entry assembly
        options.DI.AdditionalAssemblies.Add(typeof(PluginFeature).Assembly);

        options.Window.AutoDiscoverMainWindow = true;
        options.Window.MainWindowType = typeof(MainWindow);
        options.Window.MainViewModelType = typeof(MainWindowViewModel);

        return options;
    }
}

Writing a feature

public class TelemetryFeature : IPangeaFeature
{
    public string Name => "Telemetry";
    public Version Version => new(1, 0, 0);

    public void ConfigureServices(IServiceCollection services) =>
        services.AddSingleton<ITelemetry, Telemetry>();

    public void ConfigureApplication(IServiceProvider services, IPangeaApplicationContext context)
    {
        // Runs once the container is built, with the application available
    }
}

Discovery is by interface: any non-abstract IPangeaFeature in a scanned assembly is instantiated and registered. A feature that fails to configure aborts startup naming itself, rather than leaving the application half-wired.

Logging

The toolkit logs through ILogger and registers no providers of its own. Add yours and it is picked up:

public override void Configure(IServiceCollection services) =>
    services.AddLogging(builder => builder.AddConsole());

๐Ÿค– AI coding agents

Pangea ships a skill that teaches an AI coding agent to use the toolkit: the mental model, the conventions, the pitfalls, and the full list of theme resource keys.

Every C# sample in it is compiled against the real assemblies as part of the test suite, through the source generator, so the guidance cannot drift away from the code. That check has already caught a generator bug and an API that made the documented approach impossible.

Three ways to get it, most reliable first:

# 1. The template writes it into your project
dotnet new pangea-app -n MyApp                  # --IncludeSkill is on by default
# 2. Download the version-pinned skill from the matching release
curl -L -o pangea-skill.zip \
  https://github.com/smaicas/CdCSharp.Pangea/releases/download/v1.0.0/pangea-skill-1.0.0.zip
unzip pangea-skill.zip -d ~/.claude/skills/       # or wherever your agent keeps skills
  1. The NuGet package carries it too, under tools/pangea-skill/ inside the package. Note this copy lives in the global packages folder rather than your repository, so an agent listing files will not find it โ€” prefer one of the first two.

The skill follows the usual layout: a SKILL.md whose frontmatter says what it is and when to reach for it, with the bulky key reference in references/.

Pin the skill to the Pangea version you use. Guidance from an older release describes an API that has since moved, and an agent will follow it confidently.


๐Ÿงช Tests

dotnet test --project test/CdCSharp.Pangea.Core.Tests/CdCSharp.Pangea.Core.Tests.csproj

Seven suites cover the source generator, the theme (structure, resource resolution per variant, control template smoke tests, drift against upstream Avalonia), commands and threading, startup registries, storage, localization, and the agent skill's samples. test/CdCSharp.Pangea.Tests.Int is a sample application with a control gallery for looking at the theme by eye.

The theming and window tests run on Avalonia.Headless, so they need no display. The template is verified in CI instead: every option combination is generated against the packages just built and compiled for real.


๐Ÿค Contributing

  1. Fork the repository
  2. Create a branch (git checkout -b feature/amazing-feature)
  3. Commit your changes
  4. Push and open a Pull Request

Found a bug? Check existing issues or open a new one.


๐Ÿ“„ License

MIT โ€” see LICENSE.


๐Ÿ™ Acknowledgments

  • Avalonia UI โ€” the cross-platform UI framework, and the Simple theme the control dictionaries started from
  • .NET โ€” source generators and modern C#

<div align="center">

Made with โค๏ธ for the Avalonia and .NET community

๐Ÿ” Back to top

</div>

Product Compatible and additional computed target framework versions.
.NET net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages (1)

Showing the top 1 NuGet packages that depend on CdCSharp.Pangea.Localization:

Package Downloads
CdCSharp.Pangea

Modern Avalonia ToolKit with MVVM, Binding, Theming and Storage features

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.0.53 158 8/23/2026
1.0.52 125 8/22/2026
1.0.48 134 8/18/2026
1.0.47 126 8/18/2026
1.0.46 125 8/18/2026
1.0.45 120 8/17/2026
1.0.44 109 8/17/2026
1.0.42 115 8/17/2026
1.0.41 118 8/17/2026
1.0.37 183 11/29/2025
1.0.36 247 11/27/2025
1.0.34 266 8/30/2025
1.0.33 272 8/29/2025
1.0.32 283 8/26/2025
1.0.31 317 8/26/2025
1.0.30 304 8/26/2025
1.0.29 268 8/25/2025
1.0.28 310 8/24/2025
1.0.27 313 8/24/2025
1.0.26 310 8/24/2025
Loading failed