
In today's wave of global software development, application internationalization (i18n) and localization (L10n) are particularly important. Avalonia UI, as a powerful cross-platform UI framework, provides developers with multiple ways to achieve internationalization. Among them, using traditional Resx resource files for internationalization not only aligns with the habits from Winform, WPF, ASP.NET Core, and other development scenarios but also leverages practical tools and specific development workflows to make internationalization efficient and well-organized.
1. Introduction: The Marriage of Resx Resource Files and Avalonia UI Internationalization
In the field of software development, internationalization is key to ensuring applications can cross language and cultural boundaries, reaching users worldwide. Avalonia UI stands out in cross-platform application development with its flexible architecture and rich features. Resx resource files, a time-tested method of managing localization resources, have found new relevance in Avalonia UI. By combining the two, developers can equip their applications with multi-language support in a familiar development model, easily catering to the needs of users from different regions.
The following screenshot shows the management of Resx resource files using the VS extension ResXManager:

2. Detailed Usage Steps: Building the Foundation of Multi-language Applications
2.1. Strategic Layout of Resx Resource Files
2.1.1. Project Directory Planning and Creating the Base Resource File
Begin your Avalonia UI project journey—whether it's an existing mature project or a brand new one. First, add a directory in the project to store internationalization resources; let's name it I18n (you can customize the directory name based on your project's needs). In this directory, create the default English language resource file Resource.resx. This file will serve as the foundation of the entire internationalization resource system, carrying all text resources for the application in an English environment.

2.1.2. Expanding Multi-language Resource Files
- Once the English resource file is ready, we can further expand with other language resource files. Taking Simplified Chinese, Traditional Chinese, and Japanese as examples, their file names must follow a specific naming convention: the file name prefix matches the default language resource file name, i.e.,
Resource, with the correspondingCultureNamesuffix added. For instance, the Simplified Chinese resource file should be namedResource.zh-CN.resx, Traditional ChineseResource.zh-Hant.resx, and JapaneseResource.ja-JP.resx. This naming convention helps Avalonia UI accurately identify and load resources of different languages at runtime. - With the powerful ResXManager tool, we can easily open these resource files for multi-language text editing. During editing, special attention must be paid to the naming of language Keys; they must satisfy C# variable syntax, because subsequent development processes will generate corresponding language Key classes based on these Keys, ensuring accurate reference and manipulation of these resources at the code level.
2.2. Introducing NuGet Packages: A Handy Assistant for Enhancing Internationalization Features
Install-Package AvaloniaExtensions.Axaml
This package brings a series of practical APIs to our project, including multi-language switching functionality, convenient methods for obtaining the translated string corresponding to a Key, and support for language markers in the axaml frontend. These features greatly simplify our code writing and interface design during internationalization development.
2.3. T4 Files: The Bridge from Resource Files to Strongly-Typed Resource Classes
2.3.1. Creating and Configuring T4 Files
With resource files in place, although internationalization can be achieved to some extent, directly using string Keys to reference resources in code is error-prone and unintuitive. Therefore, we introduce T4 files to generate strongly-typed resource classes based on resource files. In the previously created I18n directory, add a T4 file, e.g., Language.tt (the file name can be adjusted according to project requirements).

2.3.2. T4 File Content Parsing and Generation Logic
Open the Language.tt file; its content includes a series of directives and code snippets. First, multiple namespaces are imported via #import directives, providing necessary functional support for subsequent code operations, such as XML data processing and file operations.
<#@ template debug="false" hostspecific="true" language="C#" #>
<#@ assembly name="System.Core" #>
<#@ assembly name="System.Xml" #>
<#@ assembly name="System.Xml.Linq" #>
<#@ import namespace="System.Linq" #>
<#@ import namespace="System.Text" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ import namespace="System.Xml.Linq" #>
<#@ import namespace="System.IO" #>
<#@ output extension=".cs" #>
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
<#
const string ResourceFileName = "Resources.resx";
#>
namespace <#=System.Runtime.Remoting.Messaging.CallContext.LogicalGetData("NamespaceHint").ToString()#>;
public static class Language
{
<#
var resourceKeys = XElement.Load(this.Host.ResolvePath(ResourceFileName))
.Elements("data")
.Select(item => item.Attribute("name")?.Value)
.Where(item => item != null);
var resourceDesignerName = Path.GetFileNameWithoutExtension(ResourceFileName);
foreach (string resourceKey in resourceKeys)
{
#>
public static readonly string <#= resourceKey #> = "<#= resourceKey #>";
<#
}
#>
}
Here, the ResourceFileName variable specifies the default Resx resource file name created earlier; this is the basis for the T4 file to generate the strongly-typed resource class. In the main body of the T4 file, the specified resource file is loaded via XElement.Load, and all language Keys are extracted from the XML structure of the resource file using a LINQ query expression. Then, for each extracted Key, a corresponding public static read-only string field is generated, with the field name being the Key and the initial value also the Key. When the T4 file is saved (usually via Ctrl + S), a C# file named Language.cs is generated in the same directory, containing the strongly-typed resource class. For example:
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace CodeWF.Toolbox.I18n;
public static class Language
{
public static readonly string AppName = "AppName";
public static readonly string Home = "Home";
public static readonly string SearchToolTip = "SearchToolTip";
public static readonly string Setting = "Setting";
public static readonly string DesiredAvailabilityNotification = "DesiredAvailabilityNotification";
public static readonly string AccessToolbox = "AccessToolbox";
public static readonly string MissingTool = "MissingTool";
public static readonly string InterfaceStyleSettings = "InterfaceStyleSettings";
public static readonly string GeneralSettings = "GeneralSettings";
public static readonly string Theme = "Theme";
public static readonly string FollowingSystem = "FollowingSystem";
public static readonly string LightMode = "LightMode";
public static readonly string DarkMode = "DarkMode";
public static readonly string LanguageKey = "LanguageKey";
public static readonly string AutoOpenToolboxAtStartup = "AutoOpenToolboxAtStartup";
public static readonly string HideTrayIconOnClose = "HideTrayIconOnClose";
public static readonly string TurnOn = "TurnOn";
public static readonly string TurnOff = "TurnOff";
public static readonly string Exit = "Exit";
public static readonly string SureExit = "SureExit";
public static readonly string FindInTrayIcon = "FindInTrayIcon";
public static readonly string ShowMainWindow = "ShowMainWindow";
public static readonly string DisplayPromptWhenClosing = "DisplayPromptWhenClosing";
public static readonly string NoMorePrompts = "NoMorePrompts";
public static readonly string About = "About";
public static readonly string AboutMessage = "AboutMessage";
}
This generated resource class allows us to reference resource Keys in a strongly-typed manner in the code, greatly improving code readability and maintainability.
2.4. Specific Application in the Project: Bringing Multi-language Features to Life
2.4.1. Resource References in Code
In C# code, we can use the I18nManager class to obtain the translated string corresponding to a specific language Key. For example:
I18nManager.GetString(Language.Setting)
Here, Language.Setting is a field from the strongly-typed resource class generated by the T4 file. Through this approach, we can conveniently obtain and use multi-language resources anywhere in the code, ensuring the application displays correctly in different language environments.
2.4.2. Language Binding in Axaml Interfaces
In the axaml frontend, we first need to import the corresponding namespaces:
xmlns:i18n="https://codewf.com"
xmlns:language="clr-namespace:CodeWF.Toolbox.I18n"
Then, use data binding to associate the text properties of interface elements with language resources. For example:
<TextBlock Text="{i18n:I18n {x:Static language:Language.AppName}}" />
In this way, when the application's language environment changes, the text of the interface elements automatically updates to the corresponding translated text, achieving dynamic internationalization of the interface.
3. Implementing Language Switching
Implementing language switching is also very simple: just set the I18nManager.Instance.Culture property by passing the target language's CultureInfo object. For example:
I18nManager.Instance.Culture = new CultureInfo(language);
Here, the language variable can be any valid language code, such as zh-CN, ja-JP, etc. After setting a new language culture, the entire application's language display updates immediately, providing users with a seamless multi-language switching experience.
3. Summary: Pros and Cons of the Resx Resource File Internationalization Approach
Using Resx resource files to internationalize Avalonia UI applications undoubtedly offers a familiar and convenient path for traditional developers. It fully leverages existing development experience and tool ecosystems, such as ResXManager and T4 file technology, enabling the internationalization development process to efficiently integrate into existing project workflows. However, this approach is not perfect. Its maintenance from the perspective of ordinary users may present difficulties. Ordinary users may not be familiar with the structure and editing methods of Resx files, nor understand the logic of T4 code generation. This requires developers to fully consider ongoing maintenance costs during project design and implementation, perhaps providing simple and easy-to-use interfaces or tools to assist ordinary users in updating and managing internationalization resources. Nevertheless, for development teams and projects with a certain technical foundation, the Resx resource file internationalization approach remains a recommended choice, offering excellent performance in terms of functionality, efficiency, and compatibility.
I hope this article provides a helpful reference and guidance for Avalonia UI developers in their internationalization practices, enabling your applications to shine on the global stage.
Bonus: The next article introduces internationalization using XML files.