Changelog
All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog and this project adheres to Semantic Versioning.
Unreleased
Added
- A BenchmarkDotNet suite under
benchmarks/that measures code lookups, ICU MessageFormat and the Avalonia and WPF markup extensions against the.resxdesigner class. - A benchmark results page in
the documentation: what each lookup, markup extension and culture switch costs compared with the
.resxdesigner class, with the latest results and their history onmain.
2.0.0 - 2026-10-20
Added
IMessageFormatterandILocalizer.MessageFormatter: one replaceable formatter that every resolved value passes through, with or without arguments. The default,StringFormatMessageFormatter, usesstring.Formatand does not format a value without arguments.MessageArgument, one argument of a lookup: its position, an optional name and its value.ILocalizer.LookupFailedwithLookupFailedEventArgs, raised for every failed lookup: a key that no resource file contains, or a value the formatter cannot format.LookupFailureandLookupFailureReason, which describe a failed lookup toLookupFailedand toILocalizer.LookupFailureTextBuilder.ILocalizer.RefreshTriggeringRevision, which changes on every change that can change a lookup's result (culture, formatter, lookup-failure text builder, enum key builder, registered resource managers).MessageArguments, the arguments of a lookup, built withFromValues,FromTuples,FromDictionaryorFromObjectand checked when they are built: positional, named or mixed.- Enum lookups with format arguments:
Get(sortOrder, MessageArguments.FromValues(count)). ILocalizer.EnumKeyBuilder, one replaceable enum key convention for code and XAML.- Typed keys for every neutral
.resx, with or without a.Designer.cs: files without a designer, neutral files with a dot in their name and projects usingGenerateResxSourceproduce keys classes. Without a designer, the keys class takes its namespace from the file'sCustomToolNamespacewhen it is set. - The generated
<Name>Keys.ResourceManagerproperty: the resource manager of that.resx, which every key of the class uses. Register it for search-all lookups and pass it for scoped lookups. - Generator warnings
RXLGEN003(a.resxreached the generator without its MSBuild resource name) andRXLGEN004(two neutral.resxfiles would produce the same keys class). - Format arguments on the binding, in both UI packages:
<l:Argument>children of{l:Localize}, each with a constant or boundValueand an optionalName. There is no upper limit, each localized property has its own arguments, and arguments work in style setters, resources and templates. {l:Localize}re-resolves on every change ofILocalizer.RefreshTriggeringRevision- a new formatter, a new lookup-failure text builder, a registered or removed resource manager - not only on a language switch.- Format arguments for enum labels in XAML (issue 11):
<l:Argument>children of{l:LocalizeEnum}, andMultiBindingchildren ofLocalizeEnumConverterfrom index 2 on. LocalizeEnumExtension.Value: the enum value to localize, a binding or a fixed member (Value={x:Static m:FileSortOrder.Ascending}), and the shorthand{l:LocalizeEnum {Binding SortOrder}}through the new constructorLocalizeEnumExtension(object value).- A bound key for
{l:Localize}, in both UI packages:{l:Localize {Binding Key}}orKey="{Binding Key}"takes the key from the view model, as aResourceKeyor astring. A bound value that gives no key, such asnull, renders an empty string instead of throwing. LocalizeEnumConverter.ArgumentNames: comma-separated names for the converter's arguments, for a formatter that understands named arguments.{l:LocalizeEnum}re-resolves on every change ofILocalizer.RefreshTriggeringRevision, and so doesLocalizeEnumConverterwith its new refresh-trigger child.- The opt-in package
ResXLocalization.MessageFormat: ICU MessageFormat (plural,select) throughLocalizer.Current.UseMessageFormat(), which parses every resolved value as an ICU message, with or without arguments, optionally with a configuredMessageFormatterof theMessageFormatlibrary. See ICU MessageFormat. DesignTime.Culture, an attached property in both UI packages that selects the language of XAML previewers (<Window ... l:DesignTime.Culture="de">). It setsLocalizer.Current.CurrentCultureinside a previewer and has no effect at run time. See Design time and previewers.- A workaround for a defect in Rider's WPF XAML previewer (RIDER-143207): localized strings, which
rendered nothing there, now render their raw resource pattern under the default formatter, for example
{0} people invited. See Design time and previewers.
Changed
- BREAKING: A formatting failure in
Get(key, args)and its scoped and typed siblings returns the failure text and raisesILocalizer.LookupFailed; it no longer throwsFormatException. See the migration guide. - BREAKING: In XAML, a formatting failure in
{l:Localize}renders the failure text instead of an Avalonia binding error or an empty WPF text. See the migration guide. - BREAKING:
Get(key, args)with an empty array returns the resource value unformatted:{{x}}stays{{x}}. See the migration guide. - BREAKING: A custom
ILocalizerimplementation must implementLookupFailed,MessageFormatter,RefreshTriggeringRevisionandLookupFailureTextBuilder. See the migration guide. - BREAKING:
ILocalizer.MissingTranslationFormatis replaced byILocalizer.LookupFailureTextBuilder, aFunc<LookupFailure, string>that also produces the failure text for a formatting failure. See the migration guide. - BREAKING:
ILocalizer.TranslationNotFoundandTranslationNotFoundEventArgsare replaced byILocalizer.LookupFailedandLookupFailedEventArgs, which report formatting failures too. See the migration guide. - BREAKING: Format arguments are built with
MessageArguments:Get("Key", a, b)becomesGet("Key", MessageArguments.FromValues(a, b)); theparams object?[]overloads are removed. See the migration guide. - BREAKING: A scoped lookup names the manager:
Get("Key", rm)becomesGet("Key", resourceManager: rm), andGet("Key", rm, a, b)becomesGet("Key", MessageArguments.FromValues(a, b), rm). See the migration guide. - BREAKING: An enum key prefix is named:
Get(sortOrder, "Display_")becomesGet(sortOrder, keyPrefix: "Display_"). See the migration guide. - BREAKING: A
nullresource manager means search-all and anullkey prefix the default ofILocalizer.EnumKeyBuilder, instead of throwingArgumentNullException;LocalizeEnumExtension.KeyPrefixandLocalizeEnumConverter.KeyPrefixarestring?and default tonull. See the migration guide. - BREAKING: Every 1.x
Getsignature is removed, includingGet(string)andGet(ResourceKey); assemblies compiled against 1.x throwMissingMethodExceptionuntil recompiled. See the migration guide. - BREAKING: A custom
ILocalizerimplementation has exactly threeGetoverloads - string,ResourceKeyand enum keys, each with an optionalMessageArguments- and implementsEnumKeyBuilder. See the migration guide. - BREAKING: Typed keys use the generated
<Name>Keys.ResourceManager, no longer the designer's<Name>.ResourceManager;ReferenceEquals(AppStringsKeys.Greeting.Manager, AppStrings.ResourceManager)is nowfalse. See the migration guide. - BREAKING: Files without a designer, neutral files with a dot in their name and projects using
GenerateResxSourcenow produce keys classes, which can collide with types of your own. See the migration guide. - BREAKING: Two neutral
.resxfiles that produce the same keys class reportRXLGEN004and generate neither class, instead of merging into one partial class. See the migration guide. - BREAKING:
Localizer.RegisterResourceManagerthrowsArgumentExceptionnaming the base name for a plainResourceManagerfor which no resources exist under its base name, instead of registering it and letting every search-all lookup that reaches it throwMissingManifestResourceException. Fix the base name, or register<Name>Keys.ResourceManager. See the migration guide. - BREAKING:
LocalizeExtension.Keyis anobject?that takes astringor aResourceKey; a key that is neither throwsInvalidOperationExceptionwhen the view loads instead of producing an empty key. See the migration guide. - BREAKING: Avalonia:
{l:Localize}with arguments in aStylesetter or a resource is formatted; it resolved unformatted. See the migration guide. - BREAKING:
LocalizeEnumConverterreads child 0 of itsMultiBindingas the refresh trigger (RefreshTriggeringRevisionofLocalizer.Current), child 1 as the enum value and children from index 2 on as format arguments; aMultiBindingwith the enum first renders an empty string until it is rewritten. See the migration guide.
Removed
- The generator warning
RXLGEN002(a.resxwithout a recognized classic designer): a designer is no longer required. - BREAKING:
LocalizeArgsand its attached propertiesArg0...Arg8, in both UI packages; give{l:Localize}<l:Argument>children instead. See the migration guide. - BREAKING:
LocalizeExtension.ResourceKey; setKeyto theResourceKeyinstead. See the migration guide. - BREAKING: The
DataContextmode of{l:LocalizeEnum}: it no longer reads the enum value from the target'sDataContext; setValue, asValue={Binding}in item templates. See the migration guide.
1.1.0 - 2026-07-20
Added
- Dynamic composite-format arguments for
{l:Localize}in both UI packages: bind the newLocalizeArgs.Arg0…Arg8attached properties on the target element to supplyString.Formatarguments for the resolved resource value (e.g.{0} people invited). The rendered text re-formats live when a bound argument or the culture changes; elements without arguments resolve exactly as before.
1.0.0 - 2026-07-13
Added
- Initial release of
ResXLocalization.AvaloniaandResXLocalization.WPF(targeting .NET 8 LTS and .NET 10), both depending on the shared Core package and carrying the Roslyn source generator plus MSBuild wiring:- Live, no-reload language switching via
Localizer.Current.CurrentCulture; theCultureChangedevent carries aCultureChangedEventArgsreporting the previous and new culture. - Typed, compile-checked keys generated for string entries in eligible neutral
.resxfiles with sibling classic designers ({l:Localize {x:Static …Keys.…}}). - Search-all, scoped, and typed lookup modes over any number of registered resource files, with
UnregisterResourceManager/ClearResourceManagersfor dynamic scenarios. - Enum localization by convention (
{l:LocalizeEnum},LocalizeEnumConverter). - Format-argument overloads:
Get(key, args…)formats the resolved value in the current culture. - Miss diagnostics: the
TranslationNotFoundevent and the configurableMissingTranslationFormatsentinel (default!key!). - Available-culture discovery for language pickers:
GetAvailableCultures(). - Native AOT / trimming support for the Avalonia package and the shared engine.
- Live, no-reload language switching via