English | 简体中文
Lang.Avalonia is a plugin-based localization library for Avalonia UI. The core package provides the XAML markup extension, binding pipeline, converters, I18nManager, and the ILangPlugin contract. Resource loading is provided by JSON, XML, and RESX plugins, with an optional source generator for type-safe resource keys.
| Package | NuGet | Downloads |
|---|---|---|
| Lang.Avalonia | ||
| Lang.Avalonia.Json | ||
| Lang.Avalonia.Xml | ||
| Lang.Avalonia.Resx | ||
| Lang.Avalonia.Analysis |
- Unified XAML and C# localization entry points.
- Runtime language switching through
I18nManager.Instance.Culture. - JSON, XML, and RESX resource providers behind the same
ILangPlugincontract. - Default-culture fallback and original-key fallback.
- Type-safe resource key generation with T4 templates or
Lang.Avalonia.Analysis. - Support for fixed-culture preview bindings, formatted strings, and dynamic binding arguments.
| Resource format | Packages | Typical use |
|---|---|---|
| JSON | Lang.Avalonia + Lang.Avalonia.Json |
Editable files, cross-platform tooling, source-generator demos |
| XML | Lang.Avalonia + Lang.Avalonia.Xml |
Structured language files with nested nodes |
| RESX | Lang.Avalonia + Lang.Avalonia.Resx |
.NET ResourceManager and satellite assemblies |
| Type-safe keys | Lang.Avalonia.Analysis |
Compile-time constants from AdditionalFiles |
Install the core package and one provider:
dotnet add package Lang.Avalonia.JsonRegister the plugin during app startup:
using Lang.Avalonia;
using Lang.Avalonia.Json;
using System.Globalization;
I18nManager.Instance.Register(new JsonLangPlugin(), new CultureInfo("en-US"), out var error);
if (!string.IsNullOrWhiteSpace(error))
{
// Log or show the initialization error.
}Use generated constants in AXAML:
xmlns:c="https://codewf.com"
xmlns:mainLangs="clr-namespace:Localization.Main"
<SelectableTextBlock Text="{c:I18n {x:Static mainLangs:MainView.Title}}" />
<SelectableTextBlock Text="{c:I18n {x:Static mainLangs:MainView.Title}, CultureName=ja-JP}" />Use the same keys from C#:
var title = I18nManager.Instance.GetResource(Localization.Main.MainView.Title);
var englishTitle = I18nManager.Instance.GetResource(Localization.Main.MainView.Title, "en-US");Switch language at runtime:
I18nManager.Instance.Culture = new CultureInfo("zh-CN");Install:
dotnet add package Lang.Avalonia.JsonUse one file per culture and copy JSON files to the output directory:
I18n/en-US.json
I18n/zh-CN.json
I18n/zh-Hant.json
I18n/ja-JP.json
<ItemGroup>
<None Update="I18n\*.json" CopyToOutputDirectory="PreserveNewest" />
</ItemGroup>Each JSON file must include language, description, and cultureName metadata:
{
"language": "English",
"description": "English resources",
"cultureName": "en-US",
"Localization": {
"Main": {
"MainView": {
"Title": "Lang.Avalonia localization workspace",
"ChangeLanguage": "Language"
}
}
}
}JsonLangPlugin.LoadDiagnostics contains skipped-file diagnostics if invalid JSON files are found.
Install:
dotnet add package Lang.Avalonia.XmlUse one file per culture and copy XML files to the output directory:
I18n/en-US.xml
I18n/zh-CN.xml
I18n/zh-Hant.xml
I18n/ja-JP.xml
<ItemGroup>
<None Update="I18n\*.xml" CopyToOutputDirectory="PreserveNewest" />
</ItemGroup>Each XML file must include language, description, and cultureName metadata on the root node:
<?xml version="1.0" encoding="utf-8"?>
<Localization language="English" description="English resources" cultureName="en-US">
<Main>
<MainView>
<Title>Lang.Avalonia localization workspace</Title>
<ChangeLanguage>Language</ChangeLanguage>
</MainView>
</Main>
</Localization>Leaf-node paths become resource keys. The example above produces Localization.Main.MainView.Title.
XmlLangPlugin.LoadDiagnostics contains skipped-file diagnostics if invalid XML files are found.
Install:
dotnet add package Lang.Avalonia.ResxUse standard .NET RESX naming:
I18n/Resources.resx
I18n/Resources.zh-CN.resx
I18n/Resources.zh-Hant.resx
I18n/Resources.ja-JP.resx
Use full resource keys as RESX data names:
<data name="Localization.Main.MainView.Title" xml:space="preserve">
<value>Lang.Avalonia localization workspace</value>
</data>ResxLangPlugin syncs resources by culture and exposes them through the same ILangPlugin contract used by JSON and XML providers. For trimmed publishing, pass the generated ResourceManager explicitly so the app does not need a linker root file for Lang.Avalonia.Resx:
using MyApp.I18n;
I18nManager.Instance.Register(
new ResxLangPlugin(Resources.ResourceManager),
new CultureInfo("en-US"),
out var error);You can also pass the generated resource designer type:
I18nManager.Instance.Register(
new ResxLangPlugin(typeof(Resources)),
new CultureInfo("en-US"),
out var error);The default ResxLangPlugin.Mark value is i18n; keep generated resource designer types under a namespace or folder that contains I18n, or set Mark explicitly:
I18nManager.Instance.Register(
new ResxLangPlugin { Mark = "Resources" },
new CultureInfo("en-US"),
out var error);Convention-based discovery is kept for compatibility, but explicit registration is the recommended path for trimmed apps.
Two generation paths are supported:
- Demo T4 templates generate
I18n/Language.csfrom JSON, XML, or RESX resources. Lang.Avalonia.AnalysisgeneratesLanguage.g.csat compile time fromAdditionalFiles.
For application projects, keep Lang.Avalonia.Analysis private because it is a build-time analyzer:
<PackageReference Include="Lang.Avalonia.Analysis" Version="*" PrivateAssets="all" />Register JSON, XML, or RESX language files as AdditionalFiles:
<ItemGroup>
<AdditionalFiles Include="I18n\*.json" />
<AdditionalFiles Include="I18n\*.xml" />
<AdditionalFiles Include="I18n\*.resx" />
</ItemGroup>Generated field values preserve the original resource keys. Only C# identifier names are sanitized.
For a resource key such as:
Localization.Main.MainView.Title
the generator emits constants shaped like:
namespace Localization.Main;
public static class MainView
{
public static readonly string Title = "Localization.Main.MainView.Title";
}The repository contains four demos:
| Demo | Purpose |
|---|---|
Lang.Avalonia.Json.Demo |
JSON files copied to output and loaded by JsonLangPlugin |
Lang.Avalonia.Xml.Demo |
XML files copied to output and loaded by XmlLangPlugin |
Lang.Avalonia.Resx.Demo |
RESX resources loaded through ResourceManager |
Lang.Avalonia.Analysis.Demo |
JSON resources plus source-generated keys |
Design documentation and SVG diagrams are available in docs/design.md.
Resource lookup follows this order:
- Explicit
CultureNamewhen provided; otherwiseI18nManager.Instance.Culture. - The default culture passed to
Register. - The original resource key.
- JSON and XML providers scan
AppDomain.CurrentDomain.BaseDirectoryby default. - JSON and XML providers can also read embedded resources through
AddResource. - RESX provider does not require a Root.xml file for trimmed publishing when registered with an explicit
ResourceManageror resource designer type. - Dynamic Avalonia bindings are supported for keys and format arguments.