The Path to Avalonia Internationalization: Deep Application and Exploration of Resx Resource Files

The Path to Avalonia Internationalization: Deep Application and Exploration of Resx Resource Files

In the current wave of globalized software development, internationalization (i18n) and localization (L10n) of applications have become particularly important. Avalonia UI, as a powerful cross-platform UI framework, offers developers multiple ways to achieve internationalization. Among them, using traditional Resx resource files for internationalization not only aligns with the usage habits in WinForms, WPF, ASP.NET Core, and other development scenarios, but also, with the help of some practical tools and specific development processes, makes the implementation of internationalization efficient and structured.

Last updated 12/5/2024 9:34 PM
沙漠尽头的狼
10 min read
Category
Avalonia UI
Tags
.NET C# ASP.NET Core WPF Avalonia UI

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 corresponding CultureName suffix added. For instance, the Simplified Chinese resource file should be named Resource.zh-CN.resx, Traditional Chinese Resource.zh-Hant.resx, and Japanese Resource.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.

Keep Exploring

Related Reading

More Articles
Same category / Same tag 9/13/2025

Migration Series from WPF to Avalonia: Why I Must Migrate My WPF Application to Avalonia

In the past few years, our host computer software has mainly been developed using WPF and WinForm . These technologies work well on the Windows platform and have accompanied us from small-scale trial production to the current stage of large-scale delivery. However, with business development and changes in customer requirements, the single Windows technology stack has gradually become a hurdle we must overcome.

Continue Reading