Senlinz.Localization 1.0.0

There is a newer version of this package available.
See the version list below for details.
dotnet add package Senlinz.Localization --version 1.0.0
                    
NuGet\Install-Package Senlinz.Localization -Version 1.0.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Senlinz.Localization" Version="1.0.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Senlinz.Localization" Version="1.0.0" />
                    
Directory.Packages.props
<PackageReference Include="Senlinz.Localization" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Senlinz.Localization --version 1.0.0
                    
#r "nuget: Senlinz.Localization, 1.0.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Senlinz.Localization@1.0.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Senlinz.Localization&version=1.0.0
                    
Install as a Cake Addin
#tool nuget:?package=Senlinz.Localization&version=1.0.0
                    
Install as a Cake Tool

Senlinz.Localization

English | Chinese

A JSON-driven localization source generator for .NET that generates strongly typed localization accessors, resource base classes, and enum-to-localization helpers.

Supports .NET 6 and newer consumer projects.

Features

  • Generate L accessors from l.json.
  • Generate LResource base classes for culture-specific resource implementations.
  • Resolve localized text through LString, LStringResolver, and LResourceProvider.
  • Convert enum values to localization keys with [LString] and [LStringKey].
  • Publish Senlinz.Localization and Senlinz.Localization.Abstractions as separate NuGet packages with a shared embedded package icon.

Package selection

Senlinz.Localization

Use this package in consumer projects that need source generation from JSON.

dotnet add package Senlinz.Localization

Senlinz.Localization.Abstractions

Use this package only when you need the shared runtime contracts and helpers without the source generator.

dotnet add package Senlinz.Localization.Abstractions

Quick start

1. Create the localization file

Create l.json in your project root.

{
  "hello": "Hello",
  "sayHelloTo": "Hello {name}!",
  "statusReady": "Ready",
  "UserType_Teacher": "Teacher",
  "UserType_Student": "Student"
}

2. Register the file in the project

<ItemGroup>
  <AdditionalFiles Include="l.json" />
  <None Update="l.json" CopyToOutputDirectory="PreserveNewest" />
</ItemGroup>
  • AdditionalFiles lets the source generator read the file during compilation.
  • CopyToOutputDirectory is useful when the application also wants to ship the JSON file.

3. Use generated members

After build, the generator creates strongly typed members from each JSON key.

Console.WriteLine(L.Hello);
Console.WriteLine(L.SayHelloTo("World"));
  • hello becomes L.Hello.
  • sayHelloTo becomes L.SayHelloTo(string name).

Localization file rules

Key format

  • JSON keys are converted into generated C# member names.
  • Keep keys stable because generated API names depend on them.

Placeholder parameters

Placeholders inside values become method parameters.

{
  "welcomeUser": "Welcome {userName}",
  "orderSummary": "Order {orderId} for {customerName}"
}

Generated usage:

var message1 = L.WelcomeUser("Alice");
var message2 = L.OrderSummary("SO-001", "Alice");

Escaping placeholders

  • If you want to keep braces as literal text instead of generating a parameter, prefix the placeholder name with $.
{
  "templateTip": "Use {$name} as a placeholder in your template."
}
  • The generated default text becomes Use {name} as a placeholder in your template.

Custom file name

If you do not want to use l.json, set SenlinzLocalizationFile in your project file.

<PropertyGroup>
  <SenlinzLocalizationFile>localization.json</SenlinzLocalizationFile>
</PropertyGroup>

<ItemGroup>
  <AdditionalFiles Include="localization.json" />
  <None Update="localization.json" CopyToOutputDirectory="PreserveNewest" />
</ItemGroup>

Generated types

L

  • L contains strongly typed accessors for every key in the localization JSON.
  • Plain values generate properties, and values with placeholders generate methods.

LResource

  • LResource is a generated abstract base class with one protected abstract member per localization key.
  • Implement one derived class per culture.

Example:

using Senlinz.Localization;

public sealed class EnResource : LResource
{
    public override string Culture => "en";

    protected override string Hello => "Hello";
    protected override string SayHelloTo => "Hello {name}!";
    protected override string StatusReady => "Ready";
}

public sealed class ZhResource : LResource
{
    public override string Culture => "zh";

    protected override string Hello => "你好";
    protected override string SayHelloTo => "你好,{name}!";
    protected override string StatusReady => "就绪";
}

LString

  • LString carries the localization key, fallback text, and runtime arguments.
  • You normally get LString values from generated L members or from enum extensions.

Resolve localized values

Use LResourceProvider to hold resources and LStringResolver to resolve text for the current culture.

using Senlinz.Localization;

var currentCulture = "zh";
var provider = new LResourceProvider(new EnResource(), new ZhResource());
var resolver = new LStringResolver(() => currentCulture, provider.GetResource);

Console.WriteLine(resolver[L.Hello]);
Console.WriteLine(resolver[L.SayHelloTo("世界")]);

You can also call the extension method:

var text = resolver.Resolve(L.Hello);

Fallback behavior

  • If no resource exists for the current culture, the default text from l.json is used.
  • If a resource exists but does not contain a key, the default text is also used.

Enum localization

[LString]

Apply [LString] to an enum to generate a ToLString() extension method.

[LString]
public enum UserType
{
    Teacher,
    Student
}
  • This generates UserTypeExtensions.ToLString(this UserType value).
  • By default, the generated key pattern is <EnumName>_<MemberName>.

For the enum above, the expected localization keys are typically:

{
  "UserType_Teacher": "Teacher",
  "UserType_Student": "Student"
}

[LStringKey]

Use [LStringKey] on enum members when you want to map to an existing localization key.

[LString]
public enum UserType
{
    [LStringKey("teacher")]
    Teacher,

    [LStringKey("student")]
    Student
}

[LStringKey] replaces only the enum member portion of the generated key. The enum prefix and separator are still kept.

Matching JSON:

{
  "UserType_teacher": "Teacher",
  "UserType_student": "Student"
}

If you pass the full key explicitly, it is used as-is and the prefix is not duplicated:

[LString]
public enum UserType
{
    [LStringKey("UserType_Teacher")]
    Teacher,

    [LStringKey("UserType_Student")]
    Student
}

Usage:

var text = UserType.Student.ToLString();
Console.WriteLine(resolver[text]);

Custom separator

LStringAttribute accepts an optional separator value.

[LString("_")]
public enum OrderStatus
{
    Pending,
    Completed
}
  • Choose a separator that keeps the generated member name valid in C#, such as _.
  • Use [LStringKey] when you want to customize the enum member segment while keeping the enum prefix.

End-to-end example

l.json

{
  "hello": "Hello",
  "sayHelloTo": "Hello {name}!",
  "UserType_Teacher": "Teacher",
  "UserType_Student": "Student"
}

Enum

[LString]
public enum UserType
{
    Teacher,
    Student
}

Resources and resolver

using Senlinz.Localization;

var currentCulture = "zh";
var provider = new LResourceProvider(new ZhResource());
var resolver = new LStringResolver(() => currentCulture, provider.GetResource);

Console.WriteLine(resolver[L.Hello]);
Console.WriteLine(resolver[L.SayHelloTo("世界")]);
Console.WriteLine(resolver[UserType.Student.ToLString()]);

public sealed class ZhResource : LResource
{
    public override string Culture => "zh";

    protected override string Hello => "你好";
    protected override string SayHelloTo => "你好,{name}!";
    protected override string UserTypeTeacher => "老师";
    protected override string UserTypeStudent => "学生";
}

Expected output:

你好
你好,世界!
学生

Release packages

  • Every push and pull request runs the validation workflow to restore, build, test, and pack the solution, then uploads the generated package artifacts.
  • Create and push a version tag such as v1.0.0 to trigger the publish workflow.
  1. Validate the solution.
    dotnet test Senlinz.Localization.slnx --configuration Release
    
  2. Pack locally if needed.
    dotnet pack Senlinz.Localization.slnx --configuration Release --output artifacts
    
  3. The Validate GitHub Actions workflow should pass before you cut a release tag.
  4. Local and CI pack operations produce .nupkg artifacts and any available .snupkg symbol artifacts, embed the shared package icon, and the validation workflow uploads them for inspection.
  5. The Publish NuGet packages workflow uploads the generated release artifacts, then publishes the primary packages and any generated symbol packages when the tag build succeeds.
Product Compatible and additional computed target framework versions.
.NET net5.0 was computed.  net5.0-windows was computed.  net6.0 was computed.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 was computed.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
.NET Core netcoreapp2.0 was computed.  netcoreapp2.1 was computed.  netcoreapp2.2 was computed.  netcoreapp3.0 was computed.  netcoreapp3.1 was computed. 
.NET Standard netstandard2.0 is compatible.  netstandard2.1 was computed. 
.NET Framework net461 was computed.  net462 was computed.  net463 was computed.  net47 was computed.  net471 was computed.  net472 was computed.  net48 was computed.  net481 was computed. 
MonoAndroid monoandroid was computed. 
MonoMac monomac was computed. 
MonoTouch monotouch was computed. 
Tizen tizen40 was computed.  tizen60 was computed. 
Xamarin.iOS xamarinios was computed. 
Xamarin.Mac xamarinmac was computed. 
Xamarin.TVOS xamarintvos was computed. 
Xamarin.WatchOS xamarinwatchos was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • .NETStandard 2.0

    • No dependencies.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Stable release with bundled Senlinz.Localization.Abstractions support.