Slang.Net 1.1.1-preview3

This is a prerelease version of Slang.Net.
dotnet add package Slang.Net --version 1.1.1-preview3                
NuGet\Install-Package Slang.Net -Version 1.1.1-preview3                
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="Slang.Net" Version="1.1.1-preview3" />                
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add Slang.Net --version 1.1.1-preview3                
#r "nuget: Slang.Net, 1.1.1-preview3"                
#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.
// Install Slang.Net as a Cake Addin
#addin nuget:?package=Slang.Net&version=1.1.1-preview3&prerelease

// Install Slang.Net as a Cake Tool
#tool nuget:?package=Slang.Net&version=1.1.1-preview3&prerelease                

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)

{
  "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"},
};

Tools

Translate with GPT

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

Install slang-gpt-cli:

dotnet tool install -g slang.gpt.cli

Then add the following configuration in your slang.json:

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

Then use slang-gpt:

slang-gpt <csproj> --target=ru --api-key=<api-key>

See more: Documentation

Product Compatible and additional computed target framework versions.
.NET net6.0 is compatible.  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 is compatible.  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. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • net6.0

    • No dependencies.
  • net8.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.

Version Downloads Last updated
1.1.1-preview3 41 11/28/2024
1.1.0 83 11/24/2024