Slang.Net
1.1.1
dotnet add package Slang.Net --version 1.1.1
NuGet\Install-Package Slang.Net -Version 1.1.1
<PackageReference Include="Slang.Net" Version="1.1.1" />
paket add Slang.Net --version 1.1.1
#r "nuget: Slang.Net, 1.1.1"
// Install Slang.Net as a Cake Addin #addin nuget:?package=Slang.Net&version=1.1.1 // Install Slang.Net as a Cake Tool #tool nuget:?package=Slang.Net&version=1.1.1
Slang.NET
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
- Typed Parameters
- Comments
- String Format
- Pluralization
- Linked Translations
- Lists
- Maps
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
Product | Versions 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. |
-
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.