# ResourceString.Net
## What is ResourceString.Net?
ResourceString.Net is a powerful .NET library that allows you to work with string resources in a type-safe manner.
It leverages the `resx` file in your project and utilizes a [c# source code generator](https://learn.microsoft.com/en-us/dotnet/csharp/roslyn-sdk/source-generators-overview) to create a comprehensive API. No [`Designer.cs`](https://learn.microsoft.com/en-us/dotnet/framework/tools/resgen-exe-resource-file-generator?source=recommendations) or [`T4 Text Templates`](https://learn.microsoft.com/en-us/visualstudio/modeling/code-generation-and-t4-text-templates?) filer for resources are required any more.
With ResourceString.Net, you can handle resource strings as "multi-language strings" (see [The ResourceString-Classes](#the-resourcestring-classes)) instead of built-in strings.
This provides the ability to switch languages during runtime without the need to rerun string factory methods.
Additionally, ResourceString.Net ensures that formatted strings have methods with the correct number of expected parameters.
## Installation and Setup Instructions
To incorporate ResourceString.Net into your .NET application, follow these simple steps:
1. Install the NuGet package by executing the following command in the NuGet package manager or the dotnet CLI:
```sh
dotnet add package ResourceString.Net
```
2. Once the package is installed, you can start using the APIs in your application.
## Getting Started
To quickly get started with ResourceString.Net, follow the steps below:
1. Run the following script to create a new project and a resource file:
```sh
dotnet new console --name MyTestConsoleApp
cd MyTestConsoleApp
dotnet add package "System.Resources.Extensions"
dotnet add package "ResourceString.Net"
echo "
Hello {0}
0 = name
World
" > Resources.resx
echo "var message = MyTestConsoleApp.Resources.Greetings.From(
MyTestConsoleApp.Resources.World
);
Console.WriteLine(message.Value);
" > Program.cs
```
2. Add the following `PropertyGroup` to the `MyTestConsoleApp.csproj` file to enable ResourceString.Net to handle the project's resource file:
```xml
$(AdditionalFileItemNames);EmbeddedResource
```
3. Run the project using the following command:
```sh
dotnet run
# Expected output: Hello World
```
During compile time, the ResourceString.Net source code generator will automatically add the following code to the `MyTestConsoleApp.csproj` project:
```cs
using ResourceString.Net.Contract;
using System;
using System.Globalization;
using System.Resources;
using System.Threading;
namespace MyTestConsoleApp
{
internal static class Resources
{
#region ResourceManager
private static readonly Type _Type = typeof(Resources);
private static readonly Lazy _ResourceManager = new Lazy(
() => new ResourceManager("MyTestConsoleApp.Resources" ?? string.Empty, _Type.Assembly),
LazyThreadSafetyMode.PublicationOnly
);
public static ResourceManager ResourceManager => _ResourceManager.Value;
private static CultureInfo GetDefaultCulture()
{
return CultureInfo.CurrentCulture;
}
private static IResourceString AddToCultureCache(IResourceString source)
{
return new CultureBasedCachedString(source, GetDefaultCulture);
}
#endregion // ResourceManager
#region Greetings
internal static class Greetings
{
private static readonly Lazy LazyFormat = new Lazy(
() => AddToCultureCache(new ResourceManagerString("Greetings", ResourceManager, GetDefaultCulture)),
LazyThreadSafetyMode.PublicationOnly
);
public static IResourceString Format => LazyFormat.Value;
public static IResourceString From(IResourceString name) => AddToCultureCache(new FormattedResourceString(
Format,
GetDefaultCulture,
name
));
}
#endregion // Greetings
#region World
private static readonly Lazy LazyWorld = new Lazy(
() => AddToCultureCache(new ResourceManagerString("World", ResourceManager, GetDefaultCulture)),
LazyThreadSafetyMode.PublicationOnly
);
public static IResourceString World => LazyWorld.Value;
#endregion // World
}
}
```
### Add Multi-Languages
Run the following script to add a language specific resource file and add a culture change during runtime:
```sh
echo "
Hallo {0}
0 = name
Welt
" > Resources.de.resx
echo "
Thread.CurrentThread.CurrentCulture = System.Globalization.CultureInfo.GetCultureInfo(\"de-DE\");
Console.WriteLine(message.Value);
" >> Program.cs
dotnet run
# Expected output: Hello World \n Hallo Welt
```
In the code from `Program.cs` you can see there is explicit rebuild of the format string required after the culture switch.
The `IResourceString` object themselves are rebuilding the culture specific string.
## How it works
The ResourceString.Net source code generator operates by reading all `AdditionalFiles` with the `.resx` file extension and generating a static class based on the specified `data` elements within the file.
For example, given the following XML element:
```xml
Welt
```
the source code generator transforms it into the following `C#` class members:
```cs
#region World
private static readonly Lazy LazyWorld = new Lazy(
() => AddToCultureCache(new ResourceManagerString("World", ResourceManager, GetDefaultCulture)),
LazyThreadSafetyMode.PublicationOnly
);
public static IResourceString World => LazyWorld.Value;
#endregion // World
```
If an element contains a format string, such as:
```xml
Hello {0} and {1}
```
the generator generates following code to support the formatted string:
```cs
#region Greetings
internal static class Greetings
{
private static readonly Lazy LazyFormat = new Lazy(
() => AddToCultureCache(new ResourceManagerString("Greetings", ResourceManager, GetDefaultCulture)),
LazyThreadSafetyMode.PublicationOnly
);
public static IResourceString Format => LazyFormat.Value;
public static IResourceString From(IResourceString p1, IResourceString p2) => AddToCultureCache(new FormattedResourceString(
Format,
GetDefaultCulture,
p1,
p2
));
}
#endregion // Greetings
```
In cases where the element with the format string includes a comment element like:
```xml
Hello {0} and {1}
0 = name, 1 = otherName
```
the source generator extracts the parameter names from the comment instead of using generic names:
```cs
public static IResourceString From(IResourceString name, IResourceString otherName) => AddToCultureCache(new FormattedResourceString(
Format,
name,
otherName
));
```
This allows for more descriptive parameter names in the generated code.
## The ResourceString-Classes
### IResourceString
- Interface that defines the contract for resource strings.
- Contains properties: `Value`, representing the string value, and `GetValue(CultureInfo cultureInfo)`, for retrieving the value for a specific `CultureInfo`.
### FormattedResourceString
- Implements the `IResourceString` interface.
- Represents a resource string with placeholders for parameters.
- Allows for dynamic formatting of the string by providing a format and an array of parameters.
- Formats the string by replacing the placeholders with the parameter values.
### JoinedResourceString
- Implements the `IResourceString` interface.
- Represents a resource string that joins multiple elements with a separator.
- Useful for constructing strings that involve concatenating multiple resource strings or literal strings together.
- Allows customization of the separator between the elements.
### LiteralString
- Implements the `IResourceString` interface.
- Represents a literal string resource.
- Provides the actual string value as-is without any formatting or localization.
- Can be used for static, non-localized strings.
### ResourceManagerString
- Implements the `IResourceString` interface.
- Represents a resource string retrieved from a `ResourceManager`.
- Provides access to resource strings stored in `resx` files or other resource sources.
- Handles retrieving the localized value for the specified `CultureInfo`.
### CultureBasedCachedString
- Implements the `IResourceString` interface.
- Enhances performance by avoiding redundant resource string lookups and reducing the overhead associated with repeated string generation.
## Console App: ResourceString.Net.App.Console
The ResourceString.Net project also provide a console app, `ResourceString.Net.App.Console`, which allows you to generate a C# class based on a given resource file. This can be useful for automating the creation of resource classes in your projects without usage of the `source generator`.
### Usage
To use the console app, follow these steps:
1. Build the console app from this source repository
2. Open a command prompt or terminal.
3. Navigate to the directory where the `ResourceString.Net.App.Console` executable is located.
#### Syntax
```
ResourceString.Net.App.Console [namespaceString] [className]
```
#### Parameters
- ``: Required. The path to the resource file (e.g., `Resources.resx`) that you want to generate the C# class from.
- `[namespaceString]` (optional): The namespace for the generated C# class. If not provided, the default value is `"Properties"`.
- `[className]` (optional): The name of the generated C# class. If not provided, the default value is `"Resources"`.
#### Examples
Here are some examples of how to use the console app:
- Generate a C# class named `Resources.cs` in the `"Properties"` namespace based on the `Resources.resx` file:
```
ResourceString.Net.App.Console Resources.resx
```
- Generate a C# class named `MyResources.cs` in the `"MyNamespace"` namespace based on the `MyResources.resx` file:
```
ResourceString.Net.App.Console MyResources.resx MyNamespace MyResources
```
### Output
The console app will generate the C# class code based on the provided resource file and output it to the console.
You can redirect the output to a file if desired.
## Third party packages
| Package | Version |
| --------------------------------------------------------------------------------------------------- | ------- |
| [Microsoft.CodeAnalysis.Analyzers](https://www.nuget.org/packages/Microsoft.CodeAnalysis.Analyzers) | 3.3.4 |
| [Microsoft.CodeAnalysis.CSharp](https://www.nuget.org/packages/Microsoft.CodeAnalysis.CSharp) | 4.3.0 |
| [NETStandard.Library](https://www.nuget.org/packages/NETStandard.Library) | 2.0.3 |
| [LanguageExt.Core](https://www.nuget.org/packages/LanguageExt.Core) | 4.4.3 |
| [System.Resources.Extensions](https://www.nuget.org/packages/System.Resources.Extensions) | 7.0.0 |
## Development Notes
Here are some useful aliases for running Nix commands with experimental features:
- `nixe`: This alias runs the `nix` command with the experimental feature flag `nix-command flakes`.
- `nulock`: This alias runs the `nixe` command with the argument `run .#devTasks.updateNugetLock`, which updates the NuGet lock file.
- `fllock`: This alias runs the `nixe` command with the argument `run .#devTasks.updateFlakeLock`, which updates the Flake lock file.
- `ulock`: This alias combines the `nulock` and `fllock` aliases to update both the NuGet and Flake lock files.
To load the `alias.sh` source file from the current folder, use the following command:
```sh
source ./alias.sh
```
By executing the command above, you'll make the aliases available for use in the current terminal session.