Skip to main content

Slang.Net by Egor Zheludkov

NuGet / site data

Nuget GitHub last commit GitHub Repo stars

Details

Info

info

Name: Slang.Net

Type-safe i18n for .NET

Author: Egor Zheludkov

NuGet: https://www.nuget.org/packages/Slang.Net/

You can find more details at https://github.com/egorozh/Slang.NET/

Source: https://github.com/egorozh/Slang.NET/

Author

note

Egor Zheludkov Alt text

Original Readme

note

Slang.NET

Slang.NET

Nuget Nuget License: MIT

Type-safe i18n for .NET

About this library

Slang.NET is a .NET port of the slang from the Dart/Flutter community with new features (like string format).

You can view how the generated files general, en, and de look

Getting Started:

Install the library as a NuGet package:

Install-Package dotnet add package Slang.Net

######### Add JSON files:

Important file must end with ".i18n.json". This is necessary so that the SourceGenerator does not track changes to other AdditionalFiles.

i18n/strings_en.i18n.json or i18n/strings_en-US.i18n.json or i18n/strings.i18n.json (for base culture)

Note The locale part of the file name is language[-Script][-COUNTRY], so a script subtag is supported as well: i18n/strings_sr-Cyrl-RS.i18n.json, i18n/strings_sr-Latn-RS.i18n.json, i18n/strings_zh-Hant.i18n.json.

{
"screen": {
"locale1": "Locale 1"
}
}

i18n/strings_ru.i18n.json or i18n/strings_ru-RU.i18n.json

{
"screen": {
"locale1": "Локаль 1"
}
}

slang.json

{
"base_culture": "en" // or "en-EN"
}

Recommendation It is recommended to specify the country code, such as "en-US," for proper functionality when formatting strings, especially if you will be retrieving the list of cultures from SupportedCultures.

######### Include JSON files as AdditionalFiles:


<ItemGroup>
<AdditionalFiles Include="i18n\*.i18n.json" />
<AdditionalFiles Include="slang.json" />
</ItemGroup>

######### Add a partial class:

[Translations(InputFileName = "strings")]
public partial class Strings;

######### Done!

Strings.SetCulture(new CultureInfo("ru-RU")); 

Console.WriteLine(Strings.Instance.Root.Screen.Locale1); // Локаль 1

Strings.SetCulture(new CultureInfo("en-US"));

Console.WriteLine(Strings.Instance.Root.Screen.Locale1); // Locale 1

or

  <MenuItem  Header="{Binding Root.Screen.Locale1, Source={x:Static localization:Strings.Instance}}" />
Features

######### String Interpolation

You can specify parameters passed at runtime..

{
"Hello": "Hello {name}"
}

The generated code will look like this:

/// In en, this message translates to:
/// **"Hello {name}"**
public virtual string Hello(object name) => $"Hello {name}";

######### Typed Parameters

Parameters are typed as object by default. This is convenient because it offers maximum flexibility.

You can specify the type using two syntax options: 1 - Simple:

{
"greet": "Hello {name: string}, you are {age: int} years old"
}

2 - Using a placeholders, which allows you to specify a type or format string (see String Format).

{
"greet2": "Hello {name}, you are {age} years old",
"@greet2": {
"placeholders": {
"name": {
"type": "string"
},
"age": {
"type": "int"
}
}
}
}

The generated code will look like this:

/// In ru, this message translates to:
/// **"Hello {name}, you are {age} years old"**
public virtual string Greet(string name, int age) => $"Hello {name}, you are {age} years old";

######### Comments

You can add comments to your translation files.

{
"@@locale": "en", // fully ignored
"mainScreen": {
"button": "Submit",
// ignored as translation but rendered as a comment
"@button": "The submit button shown at the bottom",
// or use
"button2": "Submit",
"@button2": {
"description": "The submit button shown at the bottom"
}
}
}

The generated code will look like this:

/// The submit button shown at the bottom
///
/// In ru, this message translates to:
/// **"Submit"**
public virtual string Button => "Submit";

######### String Format

This library supports embedding format via ToString(format) for the following types: int, long, double, decimal, float, DateTime, DateOnly, TimeOnly, TimeSpan. For other types, the format string is passed through string.Format(format, locale).

{
"dateExample": "Date {date}",
"@dateExample": {
"placeholders": {
"date": {
"type": "DateTime",
"format": "dd MMMM HH:mm"
}
}
}
}
String s = Strings.Instance.Root.DateExample(DateTime.Now); // Date 17 October 22:25

The generated code will look like this:

/// In ru, this message translates to:
/// **"Date {date}"**
public virtual string DateExample(DateTime date)
{
string dateString = date.ToString("dd MMMM HH:mm");
return $"Date {dateString}";
}

######### Pluralization

This library uses the concept defined here.

Some languages have support out of the box. See here.

Plurals are detected by the following keywords: zero, one, two, few, many, other.

{
"someKey": {
"apple": {
"one": "I have {n} apple.",
"other": "I have {n} apples."
}
}
}
String a = Strings.Instance.Root.SomeKey.Apple(n: 1); // I have 1 apple.
String b = Strings.Instance.Root.SomeKey.Apple(n: 2); // I have 2 apples.

The generated code will look like this:

public virtual string Apple(int n) => PluralResolvers.Cardinal("en")(n,
one: $"I have {n} apple.",
other: $"I have {n} apples.");

The detected plurals are cardinals by default.

To specify ordinals, you need to add the (ordinal) modifier.

{
"someKey": {
"apple(cardinal)": {
"one": "I have {n} apple.",
"other": "I have {n} apples."
},
"place(ordinal)": {
"one": "{n}st place.",
"two": "{n}nd place.",
"few": "{n}rd place.",
"other": "{n}th place."
}
}
}

By default, the parameter name is n. You can change that by adding a modifier.

{
"someKey": {
"apple(param=appleCount)": {
"one": "I have one apple.",
"other": "I have multiple apples."
}
}
}
String a = Strings.Instance.Root.SomeKey.Apple(appleCount: 1); // notice 'appleCount' instead of 'n'

You can set the default parameter globally using PluralParameter.

[Translations(
InputFileName = "strings",
PluralParameter = "count")]
internal partial class Strings;

######### Linked Translations

You can link one translation to another. Add the prefix @: followed by the absolute path to the desired translation.

{
"fields": {
"name": "my name is {firstName}",
"age": "I am {age} years old"
},
"introduce": "Hello, @:fields.name and @:fields.age"
}
String s = Strings.Instance.Root.Introduce(firstName: "Tom", age: 27); // Hello, my name is Tom and I am 27 years old.

The generated code will look like this:

/// In ru, this message translates to:
/// **"Hello, {_root.Fields.Name(firstName: firstName)} and {_root.Fields.Age(age: age)}"**
public virtual string Introduce(object firstName, object age) => $"Hello, {_root.Fields.Name(firstName: firstName)} and {_root.Fields.Age(age: age)}";

Optionally, you can escape linked translations by surrounding the path with {}:

{
"fields": {
"name": "my name is {firstName}"
},
"introduce": "Hello, @:{fields.name}inator"
}

######### Lists

You can also place lists inside lists!

{
"niceList": [
"hello",
"nice",
[
"first item in nested list",
"second item in nested list"
],
{
"wow": "WOW!",
"ok": "OK!"
},
{
"aMapEntry": "access via key",
"anotherEntry": "access via second key"
}
]
}
String a = Strings.Instance.Root.NiceList[1]; // "nice"
String b = Strings.Instance.Root.NiceList[2][0]; // "first item in nested list"
String c = Strings.Instance.Root.NiceList[3].Ok; // "OK!"
String d = Strings.Instance.Root.NiceList[4].AMapEntry; // "access via key"

The generated code will look like this:

public virtual List<dynamic> NiceList => [
"hello",
"nice",
new[]{
"first item in nested list",
"second item in nested list",
},
new Feature1NiceList0i3Ru(_root),
new Feature1NiceList0i4Ru(_root),
];

######### Maps

You can access each translation using string keys.

Add the (map) modifier.

{
"a(map)": {
"helloWorld": "hello"
},
"b": {
"b0": "hey",
"b1(map)": {
"hiThere": "hi"
}
}
}

Now you can access translations using keys:

String a = Strings.Instance.Root.A["helloWorld"]; // "hello"
String b = Strings.Instance.Root.B.B0; // "hey"
String c = Strings.Instance.Root.B.B1["hiThere"]; // "hi"

The generated code will look like this:

/// In ru, this message translates to:
/// **"hey"**
public virtual string B0 => "hey";
public virtual IReadOnlyDictionary<string, string> B1 => new Dictionary<string, string> {
{"hiThere", "hi"},
};
Slang CLI

######### Translate with GPT

Take advantage of GPT to internationalize your app with context-aware translations.

Install Slang CLI:

dotnet tool install --global Slang.CLI

Then add the following configuration in your slang.json:

{
"base_culture": "en",
"gpt": {
"base_culture": "ru",
"model": "gpt-4o-mini",
"description": "Showcase for Slang.Net.Gpt"
}
}

Then use slang-gpt:

slang gpt --target=en --api-key=<api-key>

See more: [Documentation](https://github.com/egorozh/Slang.NET/Utilities/Gpt/README.md

Additional Materials

######### Articles

######### Videos

About

note

Generate localization files from strings

How to use

Example (source csproj, source files)

This is the CSharp Project that references Slang.Net

<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<PropertyGroup>
<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
<CompilerGeneratedFilesOutputPath>$(BaseIntermediateOutputPath)\GX</CompilerGeneratedFilesOutputPath>
</PropertyGroup>

<ItemGroup>
<AdditionalFiles Include="i18n\*.i18n.json" />
<AdditionalFiles Remove="i18n\AllText_ro-Ro.i18n.json" />
<AdditionalFiles Include="slang.json" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="Slang.Net" Version="10.1.1" />
</ItemGroup>
</Project>

Generated Files

Those are taken from $(BaseIntermediateOutputPath)\GX

// Generated file. Do not edit.
//
//
// Locales: 2
//
// Built on 20.09.2026 at 16:12 UTC

#nullable enable

using Slang;
using EmbedDemo;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Globalization;
using System.Linq;
using System.Threading;

namespace EmbedDemo
{
partial class TranslateAllText
{
private static readonly CultureInfo _en_US = new CultureInfo("en-US");
private static readonly CultureInfo _ro = new CultureInfo("ro");

private static readonly Dictionary<CultureInfo, TranslateAllText> _translations =
new Dictionary<CultureInfo, TranslateAllText>(capacity: 2)
{
{_en_US, new TranslateAllText() },
{_ro, new TranslateAllTextRo() }
};


public static CultureInfo BaseCulture => _en_US;

public static IReadOnlyList<CultureInfo> SupportedCultures => _translations.Keys.ToList();

public static TranslationsInstance Instance \{ get; \} = new TranslationsInstance();

public static void SetCulture(CultureInfo culture, bool uiOnly = false)
{
if (!uiOnly)
CultureInfo.CurrentCulture = culture;

CultureInfo.CurrentUICulture = culture;

Instance.OnCultureChanged();
}

public class TranslationsInstance : INotifyPropertyChanged
{
public event PropertyChangedEventHandler? PropertyChanged;

public TranslateAllText Root
{
get
{
var culture = CultureInfo.CurrentUICulture;

if (_translations.TryGetValue(culture, out var translation))
return translation;

var sameCulture = SupportedCultures.FirstOrDefault(c => c.TwoLetterISOLanguageName == culture.TwoLetterISOLanguageName);

if (sameCulture != null)
return _translations[sameCulture];

return _translations[BaseCulture];
}
}

public void OnCultureChanged()
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Root)));
}
}
}
}

Useful

Download Example (.NET C#)

Share Slang.Net

https://ignatandrei.github.io/RSCG_Examples/v2/docs/Slang.Net

Category "FilesToCode" has the following generators:

1 Chorn.EmbeddedResourceAccessGenerator Nuget GitHub Repo stars 2024-01-21

2 corecraft Nuget GitHub Repo stars 2024-02-17

3 Datacute.EmbeddedResourcePropertyGenerator Nuget GitHub Repo stars 2024-11-03

4 DotnetYang Nuget GitHub Repo stars 2024-06-29

5 EmbedResourceCSharp Nuget GitHub Repo stars 2023-04-16

6 kli.Localize Nuget GitHub Repo stars 2025-10-01

7 LingoGen Nuget GitHub Repo stars 2024-02-23

8 Maestria.TypeProviders Nuget GitHub Repo stars 2026-04-09

9 MagicConstants Nuget GitHub Repo stars 2026-09-04

10 NFH.FileEmbed Nuget GitHub Repo stars 2025-08-08

11 NotNotAppSettings Nuget GitHub Repo stars 2024-01-26

12 Pinecone.TypedPath Nuget GitHub Repo stars 2026-08-24

13 Podimo.ConstEmbed Nuget GitHub Repo stars 2023-04-16

14 ResXGenerator Nuget GitHub Repo stars 2023-10-02

15 RSCG_JSON2Class Nuget GitHub Repo stars 2024-02-29

16 RSCG_Utils Nuget GitHub Repo stars 2023-04-16

17 Slang.Net Nuget GitHub Repo stars 2026-09-11

18 Strings.ResourceGenerator Nuget GitHub Repo stars 2025-07-06

19 SvgIconGenerator Nuget GitHub Repo stars 2026-04-04

20 ThisAssembly_Resources Nuget GitHub Repo stars 2023-09-16

21 ThisAssembly.Strings Nuget GitHub Repo stars 2024-07-21

22 TypedPaths Nuget GitHub Repo stars 2026-04-01

23 Weave Nuget GitHub Repo stars 2024-01-27

See category

FilesToCode