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
<PackageReference Include="PatTech.Localization.Avalonia" Version="1.3.0" />
<PackageVersion Include="PatTech.Localization.Avalonia" Version="1.3.0" />
<PackageReference Include="PatTech.Localization.Avalonia" />
paket add PatTech.Localization.Avalonia --version 1.3.0
#r "nuget: PatTech.Localization.Avalonia, 1.3.0"
#:package PatTech.Localization.Avalonia@1.3.0
#addin nuget:?package=PatTech.Localization.Avalonia&version=1.3.0
#tool nuget:?package=PatTech.Localization.Avalonia&version=1.3.0
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.
Handle Hyperlinks
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 aTextBlockor 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  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 `` 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 byConverterParameter.MarkdownConverter— turns any bound markdown string into rendered inlines (or a wholeTextBlock, when the target wants a control).EnumDescriptionConverter— turns a[Words]-decorated enum value into its display text; the ConverterParameter picks theDescribeformat (tooltip, description, unit…).FlagsDescriptionConverter— the same for[Flags]combinations, as a list of descriptions or one delimited string (AsArray="False").ArrayMultiConverter— gathers aMultiBindinginto the array thatWordsInline.Paramswants.ResourceVisualConverter— turns a resource value (IImage,Geometry,DataTemplate) into a fresh visual; what thestaticres:/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
- PatTech.Localization.Core — the engine:
words.inifiles, lookups by key, languages and fallbacks,{0}/{Name}parameters,{>key}references, a markdown dialect. - PatTech.Localization.WPF — the same as this, for WPF's XAML.
- PatTech.Localization.Analyzer — the
[Localized]attribute and rule PTL001, which flags a localized seam handed a raw string. It arrives with Core. - Wordsmith — the desktop editor for
words.inifiles, published on GitHub Releases.
| Product | Versions 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. |
-
net10.0
- Avalonia (>= 11.0.0 && < 12.0.0)
- PatTech.Localization.Core (>= 1.3.0)
-
net8.0
- Avalonia (>= 11.0.0 && < 12.0.0)
- PatTech.Localization.Core (>= 1.3.0)
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 |