Hi, I have just released version 2.0.0 of ResXLocalization, my localization library for WPF and Avalonia (MIT-licensed).
In essence:
Create your .resx files.
Install ResXLocalization.Avalonia or ResXLocalization.WPF.
Let it generate the typed resource keys (source generator) and use them in XAML and code:
<TextBlock Text="{l:Localize {x:Static res:SettingsViewKeys.SaveSetting}}" />
this.SaveButton.Content = Localizer.Current.Get(SettingsViewKeys.SaveSetting);
And live switch the language/culture:
Localizer.Current.CurrentCulture = new CultureInfo("de-DE");
Typed keys mean you can reference resources like objects and get compile time checking.
This avoids having typos in resource names go unnoticed.
When you change Localizer.Current.CurrentCulture it updates all localization bindings in place. If you assign a string directly in code as above, you need to call Get again after changing the culture.
The biggest new feature of V2 is the optional ICU MessageFormat support (thank you livonium for the feature request that sparked the work on V2!):
For something like “1 file deleted” / “5 files deleted”, you can use a plural rule in your resource text:
{Count, plural, one {# file deleted} other {# files deleted}}
(You need to install the NuGet package ResXLocalization.MessageFormat and call Localizer.Current.UseMessageFormat() at startup for this to work)
Then just bind the count like this:
<TextBlock>
<TextBlock.Text>
<l:Localize Key="{x:Static res:AppStringsKeys.FilesDeleted}">
<l:Argument Name="Count" Value="{Binding DeletedCount}" />
</l:Localize>
</TextBlock.Text>
</TextBlock>
The text block is automatically updated when either the count or the language changes.
Of course you can also bind multiple values to a localized text/message.
Each translation can use its language/culture plural rules.
The ICU message format support is opt-in. If you don't use it, the default formatter (based on string.Format) is used.
A few other changes in this release:
- Typed keys now finally work also without a
.Designer.cs file.
- Formatting arguments are now placed via
<Argument> children of the Localize/LocalizeEnum markup extension, which replaces the old Arg0…Arg8 attached properties, so arguments now also work in styles and templates.
- Resource keys are bindable, so the view model can dynamically decide which resource to use.
- You can change the preview language (for VS XAML Designer/Rider XAML previewer) with
DesignTime.Culture. However, ResXLocalization does not work correctly in Rider's WPF previewer due to a number (there are quite a few) of bugs in it. JetBrains has really made quite a few unfortunate design decisions for their previewer. I tried to work around them as far as possible, but it's nowhere near perfect.
The library supports .NET 8 and later with Avalonia (12+) or WPF.
For Avalonia it is completely native AOT compatible and trim safe (for WPF, native AOT is not supported, because WPF doesn't have support for it).
If you are upgrading from V1, please read the migration guide, because there are quite a few breaking changes to make it all work.
The repository has runnable sample apps (both for Avalonia and WPF):
https://github.com/rent-a-developer/ResXLocalization
AI attribution:
I used AI, mainly Claude, for brainstorming, planning, experiments, and parts of the implementation. It helped me save a great deal of time finding good solutions for a couple of quirks of Avalonia and (especially) WPF and the source generator’s MSBuild integration. I reviewed the generated code and I use ResXLocalization in my own projects, so I would not settle for some half-baked vibe coded garbage. Of course, if you don't like projects where AI is involved, then just ignore this project and that's fine too.
I hope this helps someone with the localization of their app(s)!
If you find this library useful I would appreciate a star on GitHub.
Of course feature request and (reasonable) criticism is welcome too.