Skip to content

dotnet-campus/dotnetCampus.SourceLocalizations

Repository files navigation

dotnetCampus.SourceLocalizations

Build NuGet

dotnetCampus.SourceLocalizations is a source generator that can convert text localization files (e.g. .toml) into C# code and provide strong type support for localization keys.

Features

static void Main()
{
    Console.WriteLine(LocalizedText.Current.App.Title);         // "Hello, World!"
    Console.WriteLine(LocalizedText.Current.App.Description);   // "This is a sample application."
    Console.WriteLine(LocalizedText.Current.Cli.Usage);         // "Usage: dotnetCampus.SourceLocalizations [options]"
    Console.WriteLine(LocalizedText.Current.PressAnyKeyToExit); // "Press any key to exit..."
}
  • Source Generators
    • Generate C# codes
    • Generate properties for implementation types (so that reflections on types can get localized properties which is very important for WPF Bindings)
    • Generate localized types for each language item which contains more than one arguments (This fixes different argument orders among different languages.)
  • File formats
    • TOML
    • YAML 🤡 Might be deprecated in the future.
  • UI Frameworks Support
    • Avalonia 😉 We look forward to your better suggestions.
    • MAUI 😶‍🌫️ Not tested yet
    • Uno Platform 😉 We look forward to your better suggestions.
    • Wpf 😉 We look forward to your better suggestions.
  • Diagnostics Analyzers and Code Fixes
    • Detect (and generate) missing localization keys
    • Detect (and remove) unused localization keys
    • Detect arguments mismatch among localized texts (e.g. Hello, {name:string} in en but こんにちは、{errorCode:int} in ja)
    • Detect invalid IETF language tags and report errors

Installation

dotnet add package dotnetCampus.SourceLocalizations

Usage

1. Create localization files

// Localizations/en.toml
App.Title = "Hello, World!"
App.Description = "This is a sample application."
Cli.Usage = "Usage: dotnetCampus.SourceLocalizations [options]"
PressAnyKeyToExit = "Press any key to exit..."
// Localizations/zh-hans.toml
App.Title = "你好,世界!"
App.Description = "这是一个示例应用程序。"
Cli.Usage = "用法:dotnetCampus.SourceLocalizations [选项]"
PressAnyKeyToExit = "按任意键退出..."

The file name must conform to the IETF BCP 47 standard.

2. Write a localization class

// LocalizedText.cs
using dotnetCampus.SourceLocalizations;

namespace SampleApp;

// The default language is used to generate localization interfaces, so it must be the most complete one.
// The current language is optional. If not specified, the current OS UI language will be used.
// The notification is optional. If true, when the current language changes, the UI will be notified to update the localization text.
[LocalizedConfiguration(Default = "en", Current = "zh-hans", SupportsNotification = false)]
public partial class LocalizedText;

3. Use the generated code

Console, library or any other UI framework:

// Program.cs
static void Main()
{
    Console.WriteLine(LocalizedText.Current.App.Title);         // "Hello, World!"
    Console.WriteLine(LocalizedText.Current.App.Description);   // "This is a sample application."
    Console.WriteLine(LocalizedText.Current.Cli.Usage);         // "Usage: dotnetCampus.SourceLocalizations [options]"
    Console.WriteLine(LocalizedText.Current.PressAnyKeyToExit); // "Press any key to exit..."
}

Avalonia:

<!-- Avalonia MainWindow.axaml -->
<TextBlock Text="{Binding App.Title, Source={x:Static l:LocalizedText.Current}}" />
<TextBlock Text="{Binding App.Description, Source={x:Static l:LocalizedText.Current}}" />

WPF:

<!-- WPF MainWindow.xaml -->
<TextBlock Text="{Binding App.Title, Source={x:Static l:LocalizedText.Current}, Mode=OneWay}" />
<TextBlock Text="{Binding App.Description, Source={x:Static l:LocalizedText.Current}, Mode=OneWay}" />

Uno Platform:

<!-- Uno Platform MainPage.xaml -->
<TextBlock Text="{x:Bind l:Lang.Current.App.Title}" />
<TextBlock Text="{x:Bind l:Lang.Current.App.Description}" />
// Uno Platform MainPage.xaml.cs
using dotnetCampus.Localizations;
namespace dotnetCampus.SampleUnoApp;
public sealed partial class MainPage : Page
{
    public MainPage() => InitializeComponent();
    // IMPORTANT: The Lang property must be public.
    public ILocalizedValues Lang => global::dotnetCampus.SampleUnoApp.Localizations.LocalizedText.Current;
}

Advanced Usage

If you want to add real-time language switching support, you can modify the LocalizedText class as follows:

[LocalizedConfiguration(Default = "en-US", SupportsNotification = true)]
public static partial class LocalizedText
{
    public static AppBuilder UseCompiledLang(this AppBuilder appBuilder)
    {
        if (OperatingSystem.IsWindows())
        {
            var language = GetUserProfileLanguage() ?? CultureInfo.CurrentUICulture.Name;
            _ = SetCurrent(language);
            SystemEvents.UserPreferenceChanged += SystemEvents_UserPreferenceChanged;
        }
        else
        {
            // On other operating systems, the current language is automatically set to the current UI culture.
        }
        return appBuilder;
    }

    [SupportedOSPlatform("windows")]
    private static void SystemEvents_UserPreferenceChanged(object sender, UserPreferenceChangedEventArgs e)
    {
        if (e.Category is UserPreferenceCategory.Locale)
        {
            Dispatcher.UIThread.InvokeAsync(async () =>
            {
                var language = GetUserProfileLanguage();
                if (language is not null)
                {
                    await SetCurrent(language);
                }
            }, DispatcherPriority.Background);
        }
    }

    [SupportedOSPlatform("windows")]
    private static string? GetUserProfileLanguage()
    {
        // Retrieve the current language settings from the registry.
        //
        // Compared to CultureInfo.CurrentUICulture.Name or Win32 API's GetUserDefaultUILanguage, the registry can get updated standard language tags,
        // and supports user-defined language preferences without needing to log off.
        // Note: Even restarting the application will get the old settings; only logging off the system will get the new ones.
        var languageNames = RegistryKey.OpenBaseKey(RegistryHive.CurrentUser, RegistryView.Registry64)
            .OpenSubKey(@"Control Panel\International\User Profile", false)?
            .GetValue("Languages", null) as IReadOnlyList<string>;
        return languageNames?.FirstOrDefault();
    }
}

Then, you can use the UseCompiledLang method in your App.xaml.cs file:

public static AppBuilder BuildAvaloniaApp()
    => AppBuilder.Configure<App>()
        .UsePlatformDetect()
        .UseCompiledLang()
        .XxxOthers()
    ;

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Packages

 
 
 

Languages