PatTech.Localization.Avalonia 1.3.0

dotnet add package PatTech.Localization.Avalonia --version 1.3.0
                    
NuGet\Install-Package PatTech.Localization.Avalonia -Version 1.3.0
                    
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="PatTech.Localization.Avalonia" Version="1.3.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="PatTech.Localization.Avalonia" Version="1.3.0" />
                    
Directory.Packages.props
<PackageReference Include="PatTech.Localization.Avalonia" />
                    
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 PatTech.Localization.Avalonia --version 1.3.0
                    
#r "nuget: PatTech.Localization.Avalonia, 1.3.0"
                    
#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 PatTech.Localization.Avalonia@1.3.0
                    
#: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=PatTech.Localization.Avalonia&version=1.3.0
                    
Install as a Cake Addin
#tool nuget:?package=PatTech.Localization.Avalonia&version=1.3.0
                    
Install as a Cake Tool

Words for Avalonia

Use the Words extension to put Words in the AXAML.

Include Words

public override void Initialize() {
	Words.Builder()
		// Use as many of these as you need.
		.LoadResource("avares://My-Project/Assets/words.ini")
		// Select the language; Digest installs it as Words.Known.
		.Digest("en");
	AvaloniaXamlLoader.Load(this);
}

Digest also sets the thread cultures to the language, so numbers and dates in format parameters follow your words; chain .UseSystemNumbers() before it to keep the system's regional format instead, words unchanged. WordsConverter formats with the culture the binding hands it — CurrentCulture unless a ConverterCulture says otherwise — like any Avalonia converter.

Markdown links render underlined and blue in the traditional manner, carry pointer-placed tooltips from their "title", and route every click through one global handler — custom schemes make handy in-app commands:

public override void OnFrameworkInitializationCompleted() {
	Hyperlink.RegisterGlobalNavigateHandler(uri => {
		if (uri.Scheme is "appcmd") {
			// Handle application command hyperlinks.
		}
		else if (uri.Scheme is "http" or "https" or "mailto") {
			// Only hand the shell schemes you trust to open externally: a rendered
			// value is display text, so never shell-open an arbitrary scheme (file:
			// and friends would run local things). An unlisted scheme is ignored.
			Process.Start(new ProcessStartInfo(uri.ToString()) { UseShellExecute = true });
		}
	});

	// ...

Use Words in AXAML

One namespace gives you everything (pattech.words, the older name, still works):

	xmlns:l="https://github.com/pzahra/words"
	Title="{l:Words main.title}">

	<TextBlock>
      <l:WordsInline Key="main.sample-markdown"/>
    </TextBlock>
  • {l:Words key} — a markup extension that resolves to the localized string.
  • <l:WordsInline Key="key"/> — an inline that renders the value, markdown and all, inside a TextBlock or other flow content.

WordsInline also fills format placeholders from its Params property: bind an array for positional {0} tags, or any other object for {Name} tags read off its public fields and properties. The inlines re-render whenever Key or Params changes.

	<TextBlock>
      <l:WordsInline Key="main.unread" Params="{Binding UnreadParams}"/>
    </TextBlock>

Changing language means restarting

{l:Words} resolves once, when the AXAML loads, and WordsInline re-renders only when its Key or Params change. Neither watches Words.Known, and that is deliberate, not a gap to fill: a live swap would also have to catch every LazyWords, every string a view model composed and kept, every title already set, and it would only hold up in an app that is strict MVVM all the way down. Do not hot-swap the dictionary in a running UI. Save the choice and relaunch the process, with --lang=xx on the command line as the sample does or from a settings file as Wordsmith does, and let the new process load in the new language.

Put pictures in your Words

Markdown images work in any rendered value, with the URI scheme deciding where the picture comes from:

[main.save-hint]
value=Press ![save icon](staticres:SaveIconGeometry?height=16&foreground=DarkGreen) to save.

Out of the box the parser speaks avares: (embedded assets), assets: (files under the application's Assets folder. It's a convenience, not a security boundary: the path is lexically clamped to that folder — ../ and rooted paths resolve to nothing — and the scheme only ever loads images, so a symlink someone planted inside Assets is out of scope), and staticres: and dynres: (a resource by x:Key, found from where the image lands in the tree — the window or user control it is in, then the application — the way {StaticResource} and {DynamicResource} are; dynres: stays live, so a theme variant change re-renders it). A resource renders as a fresh visual every time: an IImage in an Image, a Geometry in a filled Path, a DataTemplate as newly built content — which is how you reuse a control, since one instance can't live under two parents. Any other resource type throws, as it would anywhere else in Avalonia. Query options width, height, background, and foreground apply whatever the scheme; the query carries display options, not asset identity, so resolvers always receive the URI with it already split off. background and foreground take a color, or a brush resource spelled the way the image schemes are — staticres:key or dynres:key, found from where the image lands, and dynres: follows a theme variant change (a Color resource is wrapped in a brush; any other type throws; a missing key leaves the default standing — black fill, no border — rather than going transparent). Raster images render at their natural size unless width or height says otherwise; geometry, having no natural size, defaults to the font height. Anything that fails to resolve degrades to its alt text as [🖼️!alt], because a missing icon should never eat your sentence.

Teach it new schemes by registering an IImageSchemeResolver on the shared parser at startup — say, Material Design icons:

class PackIconResolver : IImageSchemeResolver {
	public Control? Resolve(Uri source, ImageOptions options)
		=> Enum.TryParse<PackIconKind>(source.AbsolutePath.TrimStart('/'), out var kind)
			? new PackIcon { Kind = kind, Foreground = options.Foreground ?? Brushes.Black }
			: null;
}

// at startup:
MarkdownParser.Default.ImageSchemes["md"] = new PackIconResolver();
// and now `![save](md:ContentSave)` gives you Words with icons in them.

Convert Words

For values that only exist at runtime, there are converters:

  • WordsConverter — formats a bound value into the Words template named by ConverterParameter.
  • MarkdownConverter — turns any bound markdown string into rendered inlines (or a whole TextBlock, when the target wants a control).
  • EnumDescriptionConverter — turns a [Words]-decorated enum value into its display text; the ConverterParameter picks the Describe format (tooltip, description, unit…).
  • FlagsDescriptionConverter — the same for [Flags] combinations, as a list of descriptions or one delimited string (AsArray="False").
  • ArrayMultiConverter — gathers a MultiBinding into the array that WordsInline.Params wants.
  • ResourceVisualConverter — turns a resource value (IImage, Geometry, DataTemplate) into a fresh visual; what the staticres:/dynres: image schemes render through, should you want the same from a binding.

None of them need configuring, so the package ships them pre-instantiated in Converters.axaml — merge it once:

<Application.Resources>
	<ResourceDictionary>
		<ResourceDictionary.MergedDictionaries>
			<ResourceInclude Source="avares://PatTech.Localization.Avalonia/Converters.axaml"/>
		</ResourceDictionary.MergedDictionaries>
	</ResourceDictionary>
</Application.Resources>

and every view can say {StaticResource WordsMarkdown}, WordsFormat, WordsEnumDescription, WordsFlagsDescription (joined text), WordsFlagsDescriptionList (one description per flag), WordsParamsArray, or WordsResourceVisual.

See it all at once

The Sample-Ava project is the full tour: formatting, entities and emoji, tooltipped and in-app hyperlinks, every image scheme, live format parameters, a markdown playground, a dark/light switch that shows a dynres: image following the theme while its staticres: twin stays put, and a language dropdown that relaunches the app in the selected language.

The rest of the suite

Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  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

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.3.0 40 9/19/2026