Skip to main content

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:

<?xml version="1.0" encoding="utf-8"?>
<root>
<data name="GreetingText">
<value>Hello!</value>
</data>
</root>
caution

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):

App.axaml.cs
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.

caution

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:Compile ensures that MSBuild generates the class as part of compilation instead of another IDE file generator.
  • StronglyTypedFileName writes the generated file into the intermediate output folder (obj/), which keeps it out of source control.
  • StronglyTypedNamespace must match the namespace your XAML expects. In this example, where the files are in a Lang folder, the namespace is the root followed by .Lang.
  • StronglyTypedClassName sets the class name. In this case, it should be Resources.
  • PublicClass set to true makes the class public.

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:Static references 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 raises PropertyChanged when the culture changes. Bind to these properties instead of using x: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