Tellurian.Localization 1.0.2

Prefix Reserved
There is a newer version of this package available.
See the version list below for details.
dotnet add package Tellurian.Localization --version 1.0.2
                    
NuGet\Install-Package Tellurian.Localization -Version 1.0.2
                    
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="Tellurian.Localization" Version="1.0.2" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Tellurian.Localization" Version="1.0.2" />
                    
Directory.Packages.props
<PackageReference Include="Tellurian.Localization" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Tellurian.Localization --version 1.0.2
                    
#r "nuget: Tellurian.Localization, 1.0.2"
                    
#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.
#:package Tellurian.Localization@1.0.2
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Tellurian.Localization&version=1.0.2
                    
Install as a Cake Addin
#tool nuget:?package=Tellurian.Localization&version=1.0.2
                    
Install as a Cake Tool

Tellurian.Localization

This library is helpful for .NET developers that creates applications intended for an international market.

Objectives

This library is developed to support the follwing scenarios:

  • A unified model for retriving translations from a varity of language resource types.
  • A centralised language translation service. It is posible to have all resources in one project, also accessible from GUI and other usage.
  • Provide consistent ways in code to retrieve translations.
  • Easy to add new language translations, eventually with help from AI.
  • Full control over translations, which is specially important in applications targeting special domain areas where terminology is important.

Cross-platform Considerations

When devloping cross-platform applications, it is important that localisation behaves consistent on each platform.

.NET 10 uses the International Components for Unicode (ICU) which is supportet on both Windows and Linux including macOS. This ensure consistent behaviour. Minor differences may occur depending on what version of ICU that is installed on the machine the app runs.

Invariant Globalisation

In order for applications to use localized resources, invariant globalisation must be turned off. This can be declared in the project file. If omitted,the default behaviour is that invariant globaisation is off, so you don't need to declare it explicit as in the example below.

<PropertyGroup>
    <InvariantGlobalization>false</InvariantGlobalization>
</PropertyGroup>

Resource Providers

This library has an extensible model for adding new sources of language resources, Resource Providers. This library implements three resource providers;

  • ResxResourceProvider for getting resources from .NET RESX-files. This provider uses standard .NET mechanism - the Resource Manager class.
  • MarkdownResourceProvider gets resources in form of markdown files with a naming convenstion resourcename.language/culture.md. Markdown files should be structure in a base-folder, but can contain sub-folders.
  • ObjectResourceProvider uses reflection to find string properties that matches the language, and returns the text of the property. This is useful for example when you load an object from a database with columns for each supported language.

You can add new providers, for example getting trainslations online or in other file formats.

Fallback Behaviour

You must define a default language. This will be the fallback if a translation for the specific language is not found.

When seaching for a translation of a specific resource key, the search uses a fallback mechanism:

  1. Use specific language with culture, example sv-SE.
  2. Else use language, example sv.
  3. Else use default language with culture, example en-GB.
  4. Else use language, example en.
  5. Else if no translation is found, return resource key to indicate that a translation is missing.

Supported Languages

.NET Localization needs to know what languages the application support. This is configured using the Language record:

public record Language(string TwoLetterCode, bool IsFullySupported)
{
    public bool IsFallback { get; internal set; }
    public string? CultureCode { get; init; }
    public bool CapitalizesNouns { get; init; } = false;
}

Properties:

  • TwoLetterCode - The ISO 639-1 two letter language code, e.g. en, sv, de.
  • IsFullySupported - Indicates if the language has complete translations.
  • IsFallback - Marks the default language. Exactly one language must be marked as fallback.
  • CultureCode - Optional culture specifier, e.g. GB for British English (en-GB).
  • CapitalizesNouns - Indicates if the language capitalizes nouns (e.g. German).

Example:

var languages = new List<Language>
{
    new("en", true) { IsFallback = true, CultureCode = "GB" },  // British English (default)
    new("sv", true) { CultureCode = "SE" },                      // Swedish
    new("de", false) { CapitalizesNouns = true },                // German (partial support)
};

Code Structure

Core Interfaces

Interface Description
ILanguageService Provides information about supported languages and the fallback language.
IResourceProvider Retrieves translations from a specific source (RESX, Markdown, Object).
IResourceProviderGroup Manages multiple resource providers of the same type.

Key Classes

Class Description
Language Record representing a supported language with its properties.
LanguageService Implementation of ILanguageService.
TextContent Record containing the translated text, file suffix, and last modified timestamp.
Settings Configuration class for dependency injection setup.
ResxResourceProvider Provides translations from .NET RESX files via ResourceManager.
ResxResourceProviders Groups multiple RESX providers for different resource types.
MarkdownResourceProvider Provides translations from markdown files.
ObjectResourceProvider Provides translations from object properties using reflection.

TextContent

All resource providers return a TextContent record:

public record TextContent(string Text, string FileSuffix, DateTimeOffset? LastModified = null);
  • Text - The translated string.
  • FileSuffix - Source format (.resx, .md, .obj).
  • LastModified - Timestamp for cache invalidation (mainly used by Markdown provider).

Dependency Injection Setup

Configuration

The library uses Settings for configuration:

public class Settings
{
    public IEnumerable<Language> Languages { get; set; } = [];
    public IEnumerable<string> ResxTypeNames { get; set; } = [];
    public string? MarkdownFilesBasePath { get; set; }
}

Registration in Program.cs

using Microsoft.Extensions.Options;
using Tellurian.Localization;
using Tellurian.Localization.DependencyInjection;

var builder = WebApplication.CreateBuilder(args);

// Configure settings
builder.Services.Configure<Settings>(options =>
{
    options.Languages = new List<Language>
    {
        new("en", true) { IsFallback = true, CultureCode = "GB" },
        new("sv", true) { CultureCode = "SE" },
        new("de", false)
    };
    // Type names for RESX resources (fully qualified)
    options.ResxTypeNames = new[]
    {
        "MyApp.Resources.Labels, MyApp",
        "MyApp.Resources.Messages, MyApp"
    };
    // Base path for markdown files
    options.MarkdownFilesBasePath = "Content/Translations";
});

// Register localization services
var options = builder.Services.BuildServiceProvider()
    .GetRequiredService<IOptions<Settings>>();
builder.Services.AddTellurianLocalization(options);

Retrieving Services

The providers are registered as keyed singletons:

// Get language service
var languageService = serviceProvider.GetRequiredService<ILanguageService>();

// Get RESX providers group
var resxProviders = serviceProvider.GetRequiredKeyedService<IResourceProviderGroup>("Resx");

// Get Markdown provider
var markdownProvider = serviceProvider.GetRequiredKeyedService<IResourceProvider>("Markdown");

// Get Object provider
var objectProvider = serviceProvider.GetRequiredKeyedService<IResourceProvider>("Object");

Usage Examples

Using the Language Service

public class MyService(ILanguageService languageService)
{
    public void ShowSupportedLanguages()
    {
        var languages = languageService.GetSupportedLanguages();
        var fallback = languageService.FallbackLangauge;

        foreach (var lang in languages)
        {
            Console.WriteLine($"{lang.TwoLetterCode}: Fully supported = {lang.IsFullySupported}");
        }
    }

    public bool IsLanguageSupported(CultureInfo culture)
    {
        return languageService.SupportsLanguage(culture);
    }
}

Using RESX Resources

RESX files are compiled into satellite assemblies by .NET. The ResxResourceProvider uses the standard ResourceManager to retrieve translations.

public class TranslationService(
    [FromKeyedServices("Resx")] IResourceProviderGroup resxProviders)
{
    public async Task<string> GetLabel(string key)
    {
        // Uses CultureInfo.CurrentUICulture automatically
        var content = await resxProviders.Translated<Labels>(key);
        return content.Text;
    }

    public async Task<string> GetLabelForCulture(string key, CultureInfo culture)
    {
        var content = await resxProviders.Translated<Labels>(key, culture);
        return content.Text;
    }
}

Using Markdown Resources

public class ContentService(
    [FromKeyedServices("Markdown")] IResourceProvider markdownProvider)
{
    public async Task<string> GetPageContent(string resourceKey)
    {
        var content = await markdownProvider.GetTranslationAsync(
            resourceKey,
            CultureInfo.CurrentUICulture);
        return content.Text;
    }
}

Using Object Resources

Useful for objects loaded from a database with language-specific columns:

// Example: Entity with language properties
public class ProductDescription
{
    public int Id { get; set; }
    public string en { get; set; } = "";  // English description
    public string sv { get; set; } = "";  // Swedish description
    public string de { get; set; } = "";  // German description
}

public class ProductService(
    [FromKeyedServices("Object")] IResourceProvider objectProvider)
{
    public async Task<string> GetDescription(ProductDescription product)
    {
        // Returns the property value matching CurrentUICulture.TwoLetterISOLanguageName
        var content = await objectProvider.GetTranslationAsync(
            product,
            CultureInfo.CurrentUICulture);
        return content.Text;
    }
}

Using Extension Methods

The library provides convenient extension methods:

// Uses CultureInfo.CurrentUICulture automatically
var content = await resourceProvider.Translated("MyResourceKey");

// For objects with language properties
var content = await objectProvider.Translate(myDatabaseEntity);

Markdown File Organization

Naming Convention

Markdown files follow the pattern: {resourcekey}.{language}.md

Examples: English is assumed to be the neutral language that is used as fallback if specific translation is missing.

  • welcome.md - English welcome content
  • welcome.sv.md - Swedish welcome content
  • about-us.md - English "about us" content
  • about-us.sv.md - Swedish "about us" content

Path Mapping

Resource keys with hyphens are converted to directory paths:

  • Key help-faq-intro maps to file path help/faq/intro.{lang}.md

Example folder structure:

Content/Translations/
├── welcome.md 
├── welcome.sv.md
├── welcome.de.md
├── about-us.md
├── about-us.sv.md
├── about-us.de.md
└── help/
    └── faq/
        ├── intro.md
        └── intro.sv.md
        └── intro.de.md

Fallback Behavior

The Markdown provider searches in this order:

  1. {path}.{TwoLetterISOLanguageName}.md (e.g., welcome.sv.md)
  2. {path}.md (default file without language suffix)
  3. Returns the resource key if no file is found
Product Compatible and additional computed target framework versions.
.NET net10.0 is compatible.  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. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages (1)

Showing the top 1 NuGet packages that depend on Tellurian.Localization:

Package Downloads
Tellurian.Trains.Schedules.Model

Import operations for layouts, timetables and schedules.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.5.0 102 8/28/2026
1.4.1 355 6/22/2026
1.4.0 120 6/16/2026
1.3.0 113 6/16/2026
1.2.0 131 6/16/2026
1.1.0 128 6/8/2026
1.0.3 141 3/22/2026
1.0.2 121 3/22/2026
1.0.1 136 1/1/2026
1.0.0 476 12/8/2025