Localizing using ResX
Localization is a crucial part of developing a user experience for a global audience. In .NET, the ResXResourceReader and ResXResourceWriter classes are used to read and write resources in an XML-based format (.resx). This guide walks you through localizing an Avalonia application using ResX.
Add ResX files to the project
Before localizing, you need to include ResX files for each language you want to support. As an example, you might have these three files saved in a folder called Lang:
Resources.fil-PH.resx(Filipino)Resources.ja-JP.resx(Japanese)Resources.resx(English, default language)
Each ResX file contains translated text that corresponds to the keys used in the application. The set of three ResX files used in this example might look something like this:
- English (default)
- Filipino
- Japanese
<?xml version="1.0" encoding="utf-8"?>
<root>
<data name="GreetingText">
<value>Hello!</value>
</data>
</root>
<?xml version="1.0" encoding="utf-8"?>
<root>
<data name="GreetingText">
<value>Kumusta!</value>
</data>
</root>
<?xml version="1.0" encoding="utf-8"?>
<root>
<data name="GreetingText">
<value>こんにちは!</value>
</data>
</root>
If you add ResX files to the Assets folder, make sure to switch your "Build action" to "Embedded resource", or the code generation may fail.
Set the culture
To use a specific language in the application, you need to set the current culture. This is done in the App.axaml.cs file, by adding CultureInfo along with a using statement for System.Globalization.
The following example sets the culture to Filipino (fil-PH):
using System.Globalization;
namespace MyApp;
public partial class App : Application
{
public override void Initialize()
{
AvaloniaXamlLoader.Load(this);
}
public override void OnFrameworkInitializationCompleted()
{
Lang.Resources.Culture = new CultureInfo("fil-PH");
if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
{
desktop.MainWindow = new MainWindow
{
DataContext = new MainWindowViewModel(),
};
}
base.OnFrameworkInitializationCompleted();
}
}
Replace "fil-PH" with another culture code as required.
Use localized text in the view
To use the localized text in a view, declare the namespace of the localization resources, then refer to them statically in XAML.
<Window xmlns:lang="using:MyApp.Lang">
<TextBlock Text="{x:Static lang:Resources.GreetingText}"/>
In the above sample, GreetingText is the key for a string in the ResX files shown previously. The {x:Static} markup extension is used to reference a static property that has been defined in a .NET class, which, in this case, is the resources file (lang:Resources.GreetingText).
So long as the corresponding keys are available, setting the culture to a different locale displays the entire user interface in the selected language.
Generating a public Resources class
For the {x:Static lang:Resources.GreetingText} reference to work, the Resources class must be publicly accessible. By default, this class is generated by ResXFileCodeGenerator as internal, meaning it is unreachable from XAML.
You must ensure a public resource class is available. Otherwise, your {x:Static} references may fail. (Because an internal class still compiles.) How you generate the Resources class depends on the tooling you use: Visual Studio or other tools.
Only the default resource file (Resources.resx) needs to generate the Resources class. Culture-specific files (e.g., Resources.fil-PH.resx, Resources.ja-JP.resx) supply the translated text only and do not need their own class.
Visual Studio
In Visual Studio, you can change the generator tool from ResXFileCodeGenerator to PublicResXFileCodeGenerator. To do so, add the following to your .csproj file:
<ItemGroup>
<EmbeddedResource Update="Lang\Resources.resx">
<Generator>PublicResXFileCodeGenerator</Generator>
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
</EmbeddedResource>
</ItemGroup>
<ItemGroup>
<Compile Update="Lang\Resources.Designer.cs">
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
<DependentUpon>Resources.resx</DependentUpon>
</Compile>
</ItemGroup>
Other tools (Rider, VS Code, CLI)
In non-Visual Studio setups, the MSBuild resource generator is recommended instead, which runs on every platform and every IDE. Add this to your .csproj file:
<ItemGroup>
<EmbeddedResource Update="Lang\Resources.resx">
<Generator>MSBuild:Compile</Generator>
<StronglyTypedFileName>$(IntermediateOutputPath)Resources.Designer.cs</StronglyTypedFileName>
<StronglyTypedLanguage>CSharp</StronglyTypedLanguage>
<StronglyTypedNamespace>MyApp.Lang</StronglyTypedNamespace>
<StronglyTypedClassName>Resources</StronglyTypedClassName>
<PublicClass>true</PublicClass>
</EmbeddedResource>
</ItemGroup>
MSBuild:Compileensures that MSBuild generates the class as part of compilation instead of another IDE file generator.StronglyTypedFileNamewrites the generated file into the intermediate output folder (obj/), which keeps it out of source control.StronglyTypedNamespacemust match the namespace your XAML expects. In this example, where the files are in aLangfolder, the namespace is the root followed by.Lang.StronglyTypedClassNamesets the class name. In this case, it should beResources.PublicClassset totruemakes the classpublic.
Runtime language switching
Change the culture code at runtime to allow users to switch languages without restarting the application:
public void SwitchLanguage(string cultureCode)
{
Lang.Resources.Culture = new CultureInfo(cultureCode);
// Raise PropertyChanged for all localized properties
// or reload the view to pick up new strings
}
Note that x:Static bindings do not automatically update when the culture changes. Because x:Static resolves its value once at load time, the UI will not reflect the new language until the view is refreshed. To work around this, use one of the following approaches:
- Reload the view or window. Close and recreate the window so that all
x:Staticreferences are re-evaluated against the new culture code. - Use a localization service with
INotifyPropertyChanged. Create a service class that exposes localized strings as properties and raisesPropertyChangedwhen the culture changes. Bind to these properties instead of usingx:Static.
Right-to-left (RTL) support
Avalonia supports RTL through the FlowDirection property. Setting FlowDirection to RightToLeft mirrors the layout of child controls, which is essential for languages such as Arabic, Hebrew, and Persian.
<Window FlowDirection="RightToLeft">
<!-- All child controls mirror their layout -->
</Window>
You can also set FlowDirection dynamically based on the current culture:
var culture = new CultureInfo("ar-SA");
if (culture.TextInfo.IsRightToLeft)
{
mainWindow.FlowDirection = FlowDirection.RightToLeft;
}
Some controls can adjust their layout according to FlowDirection, e.g.,:
StackPanel(reverses the order of horizontal items)Grid(mirrors column ordering)DockPanel(swaps left and right docking)TextBlock(adjusts text alignment)
Culture-aware formatting
When using StringFormat in data bindings, formatting can follow the culture of the current thread. For example, to adapt currency formatting for a value named Price:
<TextBlock Text="{Binding Price, StringFormat='{}{0:C}'}" />
To control which culture is used for formatting, set Thread.CurrentThread.CurrentCulture in your application startup.
Thread.CurrentThread.CurrentCulture = new CultureInfo("de-DE");
In the above example, setting culture to de-DE displays a price of 1234.56 as 1.234,56 € instead of $1,234.56.
Platform considerations
Avalonia's localization features work consistently across all supported platforms. For system locale detection, use CultureInfo.CurrentCulture on any supported platform.
See also
- Resources: Application resources.
- Custom Fonts: Loading fonts for different scripts.