-
Notifications
You must be signed in to change notification settings - Fork 0
Home
Package version (last review): 3.2.4
The project provides mechanisms for automatic localization of your application content. Using the project's capabilities, instead of hard-coded strings, you can replace text interface elements in the code with their keys and move all string values into separate localization packages. Keys will be automatically resolved in the required language and substituted before display.
- Usage Example
- Model
- Adding localization to your package
- Real Case: How to create localized console interface with supported language changing
- Localizators Performance Comparison
At the moment only JSON packs are available. To create one use next pattern: {lang}*.json
where {lang} is IETF language tag. You can type anything instead of '*' char (ex. en.app.json
).
Language pack contains dictionary with the pattern: "key": "value"
. Values supports {0}
tags for formatting.
Here is example packs content:
en.json
{
"typeLang": "Type language key (or {0}):",
"exitKey": "exit",
"example": "Language switched to English",
"formatMe": "You have typed: \"{0}\"",
"excep.LangNotSupported": "This language is not supported"
}
es.json
{
"typeLang": "Escriba la clave de idioma (o {0}):",
"exitKey": "salir",
"example": "Idioma cambiado a español",
"formatMe": "Has escrito: \"{0}\"",
"excep.LangNotSupported": "El idioma ingresado no es compatible"
}
ru.json
{
"typeLang": "Введите язык (или {0}):",
"exitKey": "выйти",
"example": "Язык сменён на русский",
"formatMe": "Вы ввели: \"{0}\"",
"excep.LangNotSupported": "Введённый язык не поддерживается"
}
To get langpacks initialize a new instance of ILocalizator
. The only implementation at the moment is StoredLocalizator
. Create one, using the path to your saved packs.
var storage = new StoredLocalizator("locals");
In this example your projects' hierarchy could look like this:
- YourProject
- /locals
- en.json
- es.json
- ru.json
- Program.cs
To resolve appropriate string use ILocalizator.ResolveString(...)
or ILocalizator.ResolveStringOrFallback(...)
methods. You can pass params string[]
to format resolved string.
var helloMessage = storage.ResolveString(
lang: LanguageCode.EN,
key: "typeLang",
format: exitKey);
Assuming exitKey = "exit"
this code will return Type language key (or exit):
(based on language pack).
See Program Example section to get the real code for clarity.
Type | Member | Description |
---|---|---|
enum |
LanguageCode | Represents some of the more commonly used primary language subtags. |
interface |
ILocalizator | Provides a specialized mechanism for localizing strings based on the given language key. |
class |
StoredLocalizator |
ILocalizator default implementation. Optimizes performance by preloading all localizations. |
static class |
LangHelper | Provides helper methods for working with language codes. |
Type | Member | Description |
---|---|---|
string |
NotDefinedKey |
The key for the "NotDefined" message. |
LanguageCode |
DefaultLanguage |
The default language code. |
string |
LocalsPath |
The path to the localization resource files. |
string? |
ResolveString(...) |
Resolves the localized string for the specified language key and key identifier. |
string |
ResolveStringOrFallback(...) |
Resolves the localized string for the specified language key and key identifier or returns a fallback string. |
The StoredLocalizator
is ILocalizator
default implementation.
Optimizes performance by preloading all localizations to the Dictionary<string, Dictionary<LanguageCode, string>> Localizations
during initialization. Trading memory consumption for increased speed.
The GateLocalizator
is ILocalizator
default implementation.
Reduces memory load by resolving the required dictionary during run-time. Degrades processing speed.
Not Implemented Yet
Stores part of the most used strings in-memory.
See Localizators Performance Comparison.
Special static class, provides helper methods for working with language codes.
Type | Member | Description |
---|---|---|
LanguageCode |
FromTag(string tag) |
Converts a language tag to a corresponding LanguageCode enumeration value. |
This guide will walk you through the steps to add localization files to your .NET library before publishing it as a NuGet package. We strongly recommend placing your localization files in the Resources/Locals
directory of your project.
Create a Folder for Localization Files:
- In your project directory, create a folders named
Resources/Locals
if it doesn't already exist. - Add your localization files (e.g., 'en.name.json', 'fr.name.json', etc.) to the
Resources/Locals
directory.
Your project structure should look like this:
ProjectRoot/
├── Resources/
│ └── Locals/
│ ├── en.json
│ ├── fr.json
│ └── ...
└── ...
To ensure that the localization files are included in the NuGet package, you need to update your .csproj file. This will ensure that the files are packed correctly and are available in the right location when your package is installed.
Add the following XML snippet to your .csproj file:
<ItemGroup>
<Content Include="Resources/Locals/YOUR-LANGUAGE-PACK.json">
<Pack>true</Pack>
<PackagePath>contentFiles\any\any\Resources\Locals\</PackagePath>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
Explanation:
-
Include="Resources/Locals/*.json"
: This line includes all JSON files in theResources/Locals
folder. -
<Pack>true</Pack>
: This ensures that the files are packed into the NuGet package. -
<PackagePath>contentFiles\any\any\Resources\Locals\</PackagePath>
: This specifies the path inside the NuGet package where the files will be placed. The files will be accessible in theResources/Locals
folder when the package is installed. -
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
: This ensures that the files are copied to the output directory during the build process.
Before publishing, test your package locally to ensure that the localization files are included and correctly placed. You can do this by creating a NuGet package locally and inspecting its contents.
When adding localization to your package, it's crucial to follow best practices to ensure that your package is easy to maintain, integrate, and use. Below are some key recommendations:
We strongly recommend that every package includes an English ('en
') localization file. This ensures that your package can be easily ported and understood by a global audience. English is often the default language for many systems, and providing an English localization can help avoid potential issues when the package is used in different regions.
To avoid conflicts and ensure clarity, we suggest using a standardized naming convention for your localization files. A good naming pattern is:
lang.project.author.json
For example:
- en.myproject.johndoe.json for English localization
- fr.myproject.johndoe.json for French localization
- etc
This pattern helps in clearly identifying the language, project, and author, reducing the chances of file name conflicts with other packages.
To further avoid conflicts, especially when your package might be integrated into larger systems or projects, use fully qualified keys in your localization files. This approach ensures that keys are unique and descriptive.
Instead of using simple keys like "greeting": "Hello"
use complex, namespaced keys like: "johndoe.myproject.greeting": "Hello"
.
This method follows the format: {author}.{project}.{key}
For example:
{
"johndoe.myproject.welcomeMessage": "Welcome to My Project"
}
By using this pattern, you ensure that your localization keys are unique and can coexist with other localization files in the same project, reducing the risk of accidental overwrites or key conflicts.
A localized console interface that allows you to change your language.
This code in a while (true)
loop will ask the user to enter the language code.
After changing the language, the program will notify about this and update the default language.
If the language is not supported, an error will be thrown with a localized message code.
v.3.0.1
internal class Program
{
static void Main(string[] args)
{
// Initialize new ILocalizator
var storage = new StoredLocalizator("locals");
while (true)
{
try
{
// Get localization for the "exit" key
// Use null-lang to use default one
var exitKey = storage.ResolveString(
lang: null,
key: "exitKey",
resolveDefault: true)
?? throw new ArgumentException("excep.LangNotSupported");
// Get Hello-Message and format with "exit" button
var helloMessage = storage.ResolveString(
lang: null,
key: "typeLang",
resolveDefault: true,
format: exitKey);
Console.WriteLine(helloMessage);
// Get input
var input = Console.ReadLine();
if (string.IsNullOrEmpty(input))
continue;
if (input.Equals(exitKey, StringComparison.CurrentCultureIgnoreCase))
break;
// Get lang enum from input tag
var lang = LangHelper.FromTag(input.Replace("+", ""));
string? resolved = null;
if (input.Contains('+'))
{
resolved = storage.ResolveStringOrFallback(lang, "notdef", true);
}
else
{
resolved = storage.ResolveString(lang, "example", false) ?? throw new ArgumentException("excep.LangNotSupported");
// Update default language
storage.DefaultLanguage = lang;
}
Console.WriteLine(resolved);
}
catch (ArgumentException e)
{
var errorMes = storage.ResolveString(LanguageCode.EN, e.Message, true);
Console.WriteLine(errorMes);
}
}
}
}
The graphs below compare speed and memory usage for StoredLocalizator and GateLocalizator depending on the amount of localized strings.