Senlinz.Localization
3.0.0
See the version list below for details.
dotnet add package Senlinz.Localization --version 3.0.0
NuGet\Install-Package Senlinz.Localization -Version 3.0.0
<PackageReference Include="Senlinz.Localization" Version="3.0.0" />
<PackageVersion Include="Senlinz.Localization" Version="3.0.0" />
<PackageReference Include="Senlinz.Localization" />
paket add Senlinz.Localization --version 3.0.0
#r "nuget: Senlinz.Localization, 3.0.0"
#:package Senlinz.Localization@3.0.0
#addin nuget:?package=Senlinz.Localization&version=3.0.0
#tool nuget:?package=Senlinz.Localization&version=3.0.0
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.
- Documentation site: https://gui-xie.github.io/Senlinz.Localization/
- Current package version:
3.0.0
Quick navigation
Features
- Generate
Laccessors from a primary culture JSON file. - Generate
LResourceplus one concrete resource class for every discovered culture JSON file. - Resolve localized text through
LString,LStringResolver, and generatedLResourcetypes. - Convert enum values to localization keys with
[LString]and[LStringKey]. - Publish
Senlinz.LocalizationandSenlinz.Localization.Abstractionsas separate NuGet packages.
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 localization contracts without the source generator.
dotnet add package Senlinz.Localization.Abstractions
Quick start
1. Create the localization files
Place localization JSON files under the L/ folder. en.json is the default primary file unless you override it with SenlinzLocalizationFile.
Example layout:
MyProject/
├── L/
│ ├── en.json
│ └── zh.json
└── MyProject.csproj
// en.json
{
"hello": "Hello",
"sayHelloTo": "Hello {name}!",
"statusReady": "Ready",
"userType": {
"teacher": "Teacher",
"student": "Student"
}
}
// zh.json
{
"hello": "你好",
"sayHelloTo": "你好,{name}!",
"statusReady": "就绪",
"userType": {
"teacher": "老师",
"student": "学生"
}
}
2. Register the files in the project
<ItemGroup>
<AdditionalFiles Include="L/*.json" />
</ItemGroup>
AdditionalFileslets the source generator read the localization files underL/.- If you later place files into subfolders, just widen the glob pattern; folders do not affect the generated namespace.
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"));
hellobecomesL.Hello.sayHelloTobecomesL.SayHelloTo(string name).
4. Use generated culture resources and resolve text
using Senlinz.Localization;
var currentCulture = "zh";
var resolver = new LStringResolver(() => currentCulture);
Console.WriteLine(resolver[L.Hello]);
Console.WriteLine(resolver[L.SayHelloTo("世界")]);
new LStringResolver(() => currentCulture)uses the generated resolver and automatically includes all discovered resources.- If you need runtime overrides, pass your own
LResourceinstances to the overload that accepts resources explicitly.
Localization file rules
Key format
- JSON keys are converted into generated C# member names.
- Keep keys stable because generated API names depend on them.
- Generated member names follow the JSON shape directly and only capitalize the leading letter to fit Pascal-style naming, so
user_statusbecomesL.User_status. - Nested JSON objects generate nested accessors, so
exception -> user -> notFoundbecomesL.Exception.User.NotFound(...). - Nested JSON paths use dotted keys internally, so the example above resolves as
exception.user.notFound. - Enum keys use a nested path based on the enum name and member name, so
UserType.Teacherresolves touserType.teacherandL.UserType.Teacher.
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.
Primary localization file
SenlinzLocalizationFile selects which JSON file generates L and which generated resource acts as the default resource. The default is en.json.
<PropertyGroup>
<SenlinzLocalizationFile>zh.json</SenlinzLocalizationFile>
</PropertyGroup>
<ItemGroup>
<AdditionalFiles Include="L/*.json" />
</ItemGroup>
Generated types
L
Lcontains strongly typed accessors for every key in the localization JSON.- Plain values generate properties, and values with placeholders generate methods.
LResource
LResourceis a generated abstract base class whoseGetResource()method returns the primary localization dictionary.- The generator also emits one concrete internal
*Resourceclass per discovered culture JSON file, such asEnResourceandZhResource. new LStringResolver(() => currentCulture)uses the generated resolver and wires in every discovered resource automatically.- You can still derive your own custom resources from
LResourceand overrideGetResource()when you need runtime overrides.
LString
LStringcarries the localization key, fallback text, and runtime arguments.- You normally get
LStringvalues from generatedLmembers or from enum extensions.
Resolve localized values
Use LStringResolver to resolve text for the current culture. For most applications, new LStringResolver(...) is the simplest setup.
using Senlinz.Localization;
var currentCulture = "zh";
var resolver = new LStringResolver(() => currentCulture);
Console.WriteLine(resolver[L.Hello]);
Console.WriteLine(resolver[L.SayHelloTo("世界")]);
If you need runtime overrides, derive from LResource, override GetResource(), and pass your own resources explicitly.
You can also call the instance method:
var text = resolver.Resolve(L.Hello);
Fallback behavior
- If no resource exists for the current culture, the default text from the primary JSON file is used.
- If a resource exists but does not contain a key, the default text is also used.
- Resource dictionaries are cached per resolver instance and culture.
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). - Enum values always use the nested key pattern
<enumNameCamelCase>.<memberNameCamelCase>. - For
UserType.Teacher, the generated key isuserType.teacher, so the accessor isL.UserType.Teacher.
For the enum above, the expected localization keys are typically:
{
"userType": {
"teacher": "Teacher",
"student": "Student"
}
}
[LStringKey]
Use [LStringKey] on enum members when you want to override only the enum member segment of the key.
[LString]
public enum UserType
{
[LStringKey("teacher")]
Teacher,
[LStringKey("student")]
Student
}
[LStringKey] only replaces the final enum member segment. The enum prefix segment stays derived from the enum name.
Matching JSON:
{
"userType": {
"teacher": "Teacher",
"student": "Student"
}
}
Passing a dotted or legacy full key still only changes the final member segment:
[LString]
public enum UserType
{
[LStringKey("userType.teacher")]
Teacher,
[LStringKey("legacy.student")]
Student
}
Usage:
var text = UserType.Student.ToLString();
Console.WriteLine(resolver[text]);
End-to-end example
en.json
{
"hello": "Hello",
"sayHelloTo": "Hello {name}!",
"userType": {
"teacher": "Teacher",
"student": "Student"
}
}
zh.json
{
"hello": "你好",
"sayHelloTo": "你好,{name}!",
"userType": {
"teacher": "老师",
"student": "学生"
}
}
Enum
[LString]
public enum UserType
{
Teacher,
Student
}
Resolver
using Senlinz.Localization;
var currentCulture = "zh";
var resolver = new LStringResolver(() => currentCulture);
Console.WriteLine(resolver[L.Hello]);
Console.WriteLine(resolver[L.SayHelloTo("世界")]);
Console.WriteLine(resolver[UserType.Student.ToLString()]);
Expected output:
你好
你好,世界!
学生
| Product | Versions 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. |
-
.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.
Moves generated localization resources to a dictionary-only fallback model and keeps resolver wiring in consumer projects.