Skip to content

GoneDotNet/LocalizationSourceGenerators

Repository files navigation

GoneDotNet.Localization.SourceGenerators

A Roslyn source generator that automatically generates strongly-typed C# classes from .resx resource files at compile time. This eliminates the need for the traditional ResXFileCodeGenerator custom tool and provides a modern, incremental source generation approach.

Features

  • Compile-time code generation - No runtime reflection overhead
  • Incremental generation - Only regenerates when .resx files change
  • Culture-specific file support - Automatically skips culture-specific files (e.g., Strings.fr.resx)
  • Strongly-typed access - Full IntelliSense and compile-time error checking
  • XML documentation - Generated properties include XML comments from .resx file comments
  • Special character handling - Properly escapes special characters in resource names and values
  • C# keyword handling - Automatically prefixes C# keywords with @

TODOs

  • Better Namespace support (use RootNamespace)
  • Support for custom access modifiers (e.g., public, internal)

Installation

Project Reference (for development)

Add a project reference to your consuming project:

<ItemGroup>
    <ProjectReference Include="path\to\GoneDotNet.Localization.SourceGenerators.csproj" 
                      OutputItemType="Analyzer" 
                      ReferenceOutputAssembly="false" />
</ItemGroup>

NuGet Package (when published)

dotnet add package GoneDotNet.Localization.SourceGenerators

Usage

1. Add your .resx files as AdditionalFiles

In your project file (.csproj), add the following to include .resx files for the source generator:

<ItemGroup>
    <AdditionalFiles Include="**/*.resx" />
</ItemGroup>

2. Create a .resx file

Create a resource file (e.g., Resources.resx) with your localized strings:

<?xml version="1.0" encoding="utf-8"?>
<root>
    <data name="Hello" xml:space="preserve">
        <value>Hello World!</value>
        <comment>A greeting message</comment>
    </data>
    <data name="Goodbye" xml:space="preserve">
        <value>Goodbye!</value>
    </data>
    <data name="WelcomeUser" xml:space="preserve">
        <value>Welcome, {0}!</value>
        <comment>A welcome message with a placeholder for the user name</comment>
    </data>
</root>

3. Use the generated class

The source generator automatically creates a strongly-typed class matching your .resx filename:

using YourNamespace;

// Access string resources directly
Console.WriteLine(Resources.Hello);        // Output: Hello World!
Console.WriteLine(Resources.Goodbye);      // Output: Goodbye!

// Use format strings
Console.WriteLine(string.Format(Resources.WelcomeUser, "John"));  // Output: Welcome, John!

Generated Code

For a Resources.resx file, the generator creates a Resources.g.cs file similar to:

namespace YourProjectNamespace
{
    /// <summary>
    ///   A strongly-typed resource class, for looking up localized strings, etc.
    /// </summary>
    [GeneratedCode("GoneDotNet.Localization.SourceGenerators", "1.0.0.0")]
    internal static class Resources
    {
        private static ResourceManager? resourceMan;
        private static CultureInfo? resourceCulture;

        /// <summary>
        ///   Returns the cached ResourceManager instance used by this class.
        /// </summary>
        internal static ResourceManager ResourceManager { get; }

        /// <summary>
        ///   Overrides the current thread's CurrentUICulture property for all
        ///   resource lookups using this strongly typed resource class.
        /// </summary>
        internal static CultureInfo? Culture { get; set; }

        /// <summary>
        ///   A greeting message
        /// </summary>
        internal static string Hello => ResourceManager.GetString("Hello", resourceCulture) ?? string.Empty;

        // ... additional properties
    }
}

Localization Support

To add translations, create culture-specific .resx files following the naming convention:

Resources.resx           # Default/fallback resources
Resources.fr.resx        # French translations
Resources.de.resx        # German translations
Resources.es-MX.resx     # Mexican Spanish translations

The source generator automatically detects and skips culture-specific files (they don't need separate generated classes). The .NET runtime handles loading the appropriate culture-specific resources automatically based on Thread.CurrentThread.CurrentUICulture.

Changing the Culture at Runtime

using System.Globalization;

// Set a specific culture for resource lookups
Resources.Culture = new CultureInfo("fr-FR");
Console.WriteLine(Resources.Hello);  // Output: Bonjour le monde! (if defined in Resources.fr.resx)

// Or change the thread's UI culture
Thread.CurrentThread.CurrentUICulture = new CultureInfo("de-DE");
Console.WriteLine(Resources.Hello);  // Uses German translation

Requirements

  • .NET SDK 6.0 or later (for consuming projects)
  • The source generator targets netstandard2.0 for broad compatibility

Project Structure

GoneDotNet.Localization.SourceGenerators/
├── GoneDotNet.Localization.SourceGenerators/      # The source generator library
│   ├── ResxSourceGenerator.cs                     # Main generator implementation
│   └── GoneDotNet.Localization.SourceGenerators.csproj
├── GoneDotNet.Localization.SourceGenerators.Sample/ # Sample usage project
│   ├── Program.cs
│   ├── Resources.resx
│   └── Strings.resx
└── GoneDotNet.Localization.SourceGenerators.Tests/  # Unit tests
    └── ResxSourceGeneratorTests.cs

License

MIT License

About

Replace the old dinosaur RESX tooling

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages