Ecng.Localization 1.0.244

dotnet add package Ecng.Localization --version 1.0.244
                    
NuGet\Install-Package Ecng.Localization -Version 1.0.244
                    
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="Ecng.Localization" Version="1.0.244" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Ecng.Localization" Version="1.0.244" />
                    
Directory.Packages.props
<PackageReference Include="Ecng.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 Ecng.Localization --version 1.0.244
                    
#r "nuget: Ecng.Localization, 1.0.244"
                    
#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 Ecng.Localization@1.0.244
                    
#: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=Ecng.Localization&version=1.0.244
                    
Install as a Cake Addin
#tool nuget:?package=Ecng.Localization&version=1.0.244
                    
Install as a Cake Tool

Ecng.Localization

A lightweight and flexible localization engine for .NET applications that provides centralized string resource management with support for multiple cultures and easy resource overrides.

Table of Contents

Overview

Ecng.Localization provides a simple yet powerful localization infrastructure that allows you to:

  • Centralize all localizable strings in your application
  • Support multiple languages and cultures
  • Implement custom translation providers
  • Integrate seamlessly with .NET data annotations
  • Maintain type-safe resource keys

The library uses a provider pattern through the ILocalizer interface, making it easy to plug in custom localization strategies ranging from simple in-memory dictionaries to database-backed translation systems.

Key Features

  • Pluggable Architecture: Implement custom localizers via ILocalizer interface
  • Two Localization Modes: Translate by English text or by resource key
  • Resource Type Support: Works with DisplayAttribute for declarative localization
  • Extension Methods: Convenient string extension methods for inline localization
  • Thread-Safe: Can be safely used in multi-threaded applications
  • No External Dependencies: Minimal footprint with no third-party dependencies
  • Fallback Behavior: Returns original text when translation is not found
  • Type-Safe Keys: Const keys prevent typos in resource references

Installation

Add a reference to the Ecng.Localization project or NuGet package in your application:

<ItemGroup>
  <ProjectReference Include="path\to\Ecng.Localization.csproj" />
</ItemGroup>

Quick Start

Basic Usage

using Ecng.Localization;

// Use the default localizer (returns input as-is)
string text = "Hello".Localize();
Console.WriteLine(text); // Output: Hello

// Use predefined resource keys
string name = LocalizedStrings.Name;
Console.WriteLine(name); // Output: Name (in English by default)

Implementing Custom Localization

using Ecng.Localization;

// 1. Create a custom localizer
public class RussianLocalizer : ILocalizer
{
    private readonly Dictionary<string, string> _translations = new()
    {
        ["Name"] = "Имя",
        ["Warnings"] = "Предупреждения",
        ["Errors"] = "Ошибки",
        ["Info"] = "Информация"
    };

    public string Localize(string enStr)
    {
        // Translate by English text
        return _translations.GetValueOrDefault(enStr, enStr);
    }

    public string LocalizeByKey(string key)
    {
        // Translate by resource key
        return _translations.GetValueOrDefault(key, key);
    }
}

// 2. Register the localizer globally
LocalizedStrings.Localizer = new RussianLocalizer();

// 3. Use localized strings throughout your application
Console.WriteLine(LocalizedStrings.Name);      // Output: Имя
Console.WriteLine(LocalizedStrings.Warnings);  // Output: Предупреждения
Console.WriteLine("Name".Localize());          // Output: Имя

Core Concepts

ILocalizer Interface

The ILocalizer interface is the foundation of the localization system:

public interface ILocalizer
{
    /// <summary>
    /// Localizes a string by its English text.
    /// </summary>
    string Localize(string enStr);

    /// <summary>
    /// Localizes a string by its resource key.
    /// </summary>
    string LocalizeByKey(string key);
}

Two Translation Approaches:

  1. Localize(enStr): Uses English text as the key. Good for dynamic strings.
  2. LocalizeByKey(key): Uses a constant key. Better for compile-time safety.

LocalizedStrings Class

The static LocalizedStrings class provides:

  • Global localizer instance via Localizer property
  • Predefined resource keys (e.g., NameKey, IdKey, WarningsKey)
  • Predefined localized string properties (e.g., Name, Id, Warnings)
  • Extension methods for string localization

Default Behavior

By default, the library uses a null localizer that returns strings as-is:

// Default behavior (no translation)
"Hello".Localize();           // Returns: "Hello"
"MyKey".LocalizeByKey();      // Returns: "MyKey"
LocalizedStrings.Name;        // Returns: "Name"

Usage Guide

1. Using Predefined Resource Keys

The library provides predefined keys for common UI elements:

using Ecng.Localization;

// Logging-related resources
Console.WriteLine(LocalizedStrings.Inherited);    // "Inherited"
Console.WriteLine(LocalizedStrings.Verbose);      // "Verbose"
Console.WriteLine(LocalizedStrings.Debug);        // "Debug"
Console.WriteLine(LocalizedStrings.Info);         // "Info"
Console.WriteLine(LocalizedStrings.Warnings);     // "Warnings"
Console.WriteLine(LocalizedStrings.Errors);       // "Errors"
Console.WriteLine(LocalizedStrings.Off);          // "Off"

// General purpose resources
Console.WriteLine(LocalizedStrings.Id);           // "Id"
Console.WriteLine(LocalizedStrings.Name);         // "Name"
Console.WriteLine(LocalizedStrings.Logging);      // "Logging"
Console.WriteLine(LocalizedStrings.LogLevel);     // "LogLevel"

// Chart-related resources
Console.WriteLine(LocalizedStrings.Line2);        // "Line2"
Console.WriteLine(LocalizedStrings.Area);         // "Area"
Console.WriteLine(LocalizedStrings.Histogram);    // "Histogram"
Console.WriteLine(LocalizedStrings.Band);         // "Band"

2. Using String Extension Methods

Localize any string inline using extension methods:

using Ecng.Localization;

// Localize by English text
string message = "Connection established".Localize();

// Localize by key
string errorMsg = "ERROR_CONNECTION".LocalizeByKey();

// Use in string interpolation
Console.WriteLine($"Status: {"Active".Localize()}");

3. Implementing a Database-Backed Localizer

using Ecng.Localization;

public class DatabaseLocalizer : ILocalizer
{
    private readonly ITranslationRepository _repository;
    private readonly string _cultureCode;
    private readonly Dictionary<string, string> _cache;

    public DatabaseLocalizer(ITranslationRepository repository, string cultureCode)
    {
        _repository = repository;
        _cultureCode = cultureCode;
        _cache = new Dictionary<string, string>();
        LoadTranslations();
    }

    private void LoadTranslations()
    {
        // Load all translations for the current culture into cache
        var translations = _repository.GetTranslations(_cultureCode);
        foreach (var translation in translations)
        {
            _cache[translation.Key] = translation.Value;
        }
    }

    public string Localize(string enStr)
    {
        if (_cache.TryGetValue(enStr, out var translation))
            return translation;

        // Log missing translation for later addition
        _repository.LogMissingTranslation(enStr, _cultureCode);
        return enStr;
    }

    public string LocalizeByKey(string key)
    {
        return _cache.GetValueOrDefault(key, key);
    }
}

// Usage
var repository = new SqlTranslationRepository(connectionString);
LocalizedStrings.Localizer = new DatabaseLocalizer(repository, "ru-RU");

4. Implementing a Resource File Localizer

using Ecng.Localization;
using System.Resources;
using System.Globalization;

public class ResourceFileLocalizer : ILocalizer
{
    private readonly ResourceManager _resourceManager;
    private readonly CultureInfo _culture;

    public ResourceFileLocalizer(Type resourceType, CultureInfo culture)
    {
        _resourceManager = new ResourceManager(resourceType);
        _culture = culture;
    }

    public string Localize(string enStr)
    {
        try
        {
            // Try to get the resource using the English string as the key
            var result = _resourceManager.GetString(enStr, _culture);
            return result ?? enStr;
        }
        catch
        {
            return enStr;
        }
    }

    public string LocalizeByKey(string key)
    {
        try
        {
            var result = _resourceManager.GetString(key, _culture);
            return result ?? key;
        }
        catch
        {
            return key;
        }
    }
}

// Usage
LocalizedStrings.Localizer = new ResourceFileLocalizer(
    typeof(MyResources),
    new CultureInfo("fr-FR")
);

5. Culture-Aware Localizer with Fallback

using Ecng.Localization;
using System.Globalization;

public class MultiCultureLocalizer : ILocalizer
{
    private readonly Dictionary<string, Dictionary<string, string>> _translations;
    private CultureInfo _currentCulture;

    public MultiCultureLocalizer()
    {
        _translations = new Dictionary<string, Dictionary<string, string>>
        {
            ["en-US"] = new Dictionary<string, string>(),
            ["ru-RU"] = new Dictionary<string, string>
            {
                ["Name"] = "Имя",
                ["Errors"] = "Ошибки",
                ["Warnings"] = "Предупреждения"
            },
            ["de-DE"] = new Dictionary<string, string>
            {
                ["Name"] = "Name",
                ["Errors"] = "Fehler",
                ["Warnings"] = "Warnungen"
            }
        };

        _currentCulture = CultureInfo.CurrentUICulture;
    }

    public CultureInfo Culture
    {
        get => _currentCulture;
        set => _currentCulture = value ?? CultureInfo.InvariantCulture;
    }

    public string Localize(string enStr)
    {
        return LocalizeByKey(enStr);
    }

    public string LocalizeByKey(string key)
    {
        // Try exact culture match
        if (_translations.TryGetValue(_currentCulture.Name, out var cultureDict))
        {
            if (cultureDict.TryGetValue(key, out var translation))
                return translation;
        }

        // Try neutral culture (e.g., "ru" from "ru-RU")
        if (!_currentCulture.IsNeutralCulture)
        {
            var neutralCulture = _currentCulture.Parent.Name;
            if (_translations.TryGetValue(neutralCulture, out var neutralDict))
            {
                if (neutralDict.TryGetValue(key, out var translation))
                    return translation;
            }
        }

        // Fallback to English or return key
        if (_translations.TryGetValue("en-US", out var enDict))
        {
            if (enDict.TryGetValue(key, out var enTranslation))
                return enTranslation;
        }

        return key;
    }

    public void AddTranslation(string culture, string key, string value)
    {
        if (!_translations.ContainsKey(culture))
            _translations[culture] = new Dictionary<string, string>();

        _translations[culture][key] = value;
    }
}

// Usage
var localizer = new MultiCultureLocalizer();
localizer.Culture = new CultureInfo("ru-RU");
LocalizedStrings.Localizer = localizer;

Console.WriteLine(LocalizedStrings.Name);     // Output: Имя
Console.WriteLine(LocalizedStrings.Warnings); // Output: Предупреждения

Advanced Scenarios

Thread-Safe Localizer with Caching

using Ecng.Localization;
using System.Collections.Concurrent;

public class CachedLocalizer : ILocalizer
{
    private readonly ILocalizer _innerLocalizer;
    private readonly ConcurrentDictionary<string, string> _cache;
    private readonly int _maxCacheSize;

    public CachedLocalizer(ILocalizer innerLocalizer, int maxCacheSize = 1000)
    {
        _innerLocalizer = innerLocalizer;
        _cache = new ConcurrentDictionary<string, string>();
        _maxCacheSize = maxCacheSize;
    }

    public string Localize(string enStr)
    {
        return _cache.GetOrAdd(enStr, key =>
        {
            if (_cache.Count >= _maxCacheSize)
                _cache.Clear();
            return _innerLocalizer.Localize(key);
        });
    }

    public string LocalizeByKey(string key)
    {
        return _cache.GetOrAdd($"KEY_{key}", _ =>
        {
            if (_cache.Count >= _maxCacheSize)
                _cache.Clear();
            return _innerLocalizer.LocalizeByKey(key);
        });
    }

    public void ClearCache() => _cache.Clear();
}

Composite Localizer with Multiple Sources

using Ecng.Localization;

public class CompositeLocalizer : ILocalizer
{
    private readonly List<ILocalizer> _localizers;

    public CompositeLocalizer(params ILocalizer[] localizers)
    {
        _localizers = new List<ILocalizer>(localizers);
    }

    public string Localize(string enStr)
    {
        foreach (var localizer in _localizers)
        {
            var result = localizer.Localize(enStr);
            if (result != enStr)
                return result;
        }
        return enStr;
    }

    public string LocalizeByKey(string key)
    {
        foreach (var localizer in _localizers)
        {
            var result = localizer.LocalizeByKey(key);
            if (result != key)
                return result;
        }
        return key;
    }
}

// Usage: Try database first, then fall back to resource files
var dbLocalizer = new DatabaseLocalizer(repository, "ru-RU");
var fileLocalizer = new ResourceFileLocalizer(typeof(Resources), culture);
LocalizedStrings.Localizer = new CompositeLocalizer(dbLocalizer, fileLocalizer);

Logging Localizer (Debugging)

using Ecng.Localization;

public class LoggingLocalizer : ILocalizer
{
    private readonly ILocalizer _innerLocalizer;
    private readonly Action<string> _logger;

    public LoggingLocalizer(ILocalizer innerLocalizer, Action<string> logger)
    {
        _innerLocalizer = innerLocalizer;
        _logger = logger;
    }

    public string Localize(string enStr)
    {
        var result = _innerLocalizer.Localize(enStr);
        _logger($"Localize('{enStr}') -> '{result}'");
        return result;
    }

    public string LocalizeByKey(string key)
    {
        var result = _innerLocalizer.LocalizeByKey(key);
        _logger($"LocalizeByKey('{key}') -> '{result}'");
        return result;
    }
}

// Usage
var baseLocalizer = new RussianLocalizer();
LocalizedStrings.Localizer = new LoggingLocalizer(
    baseLocalizer,
    msg => Console.WriteLine($"[LOCALIZATION] {msg}")
);

Integration with .NET Attributes

The localization system integrates seamlessly with .NET's DisplayAttribute for declarative localization in data models:

Using with Enum Types

using System.ComponentModel.DataAnnotations;
using Ecng.Localization;

public enum LogLevels
{
    [Display(ResourceType = typeof(LocalizedStrings), Name = nameof(LocalizedStrings.InheritedKey))]
    Inherit,

    [Display(ResourceType = typeof(LocalizedStrings), Name = nameof(LocalizedStrings.VerboseKey))]
    Verbose,

    [Display(ResourceType = typeof(LocalizedStrings), Name = nameof(LocalizedStrings.DebugKey))]
    Debug,

    [Display(ResourceType = typeof(LocalizedStrings), Name = nameof(LocalizedStrings.InfoKey))]
    Info,

    [Display(ResourceType = typeof(LocalizedStrings), Name = nameof(LocalizedStrings.WarningsKey))]
    Warning,

    [Display(ResourceType = typeof(LocalizedStrings), Name = nameof(LocalizedStrings.ErrorsKey))]
    Error
}

// Usage with Ecng.ComponentModel extensions
using Ecng.ComponentModel;

var level = LogLevels.Warning;
string displayName = level.GetFieldDisplayName(); // Gets localized "Warnings"

Using with Class Properties

using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using Ecng.Localization;

public class LogSource
{
    [Display(
        ResourceType = typeof(LocalizedStrings),
        Name = nameof(LocalizedStrings.IdKey),
        Description = nameof(LocalizedStrings.IdKey),
        GroupName = nameof(LocalizedStrings.LoggingKey),
        Order = 1)]
    public Guid Id { get; set; }

    [Display(
        ResourceType = typeof(LocalizedStrings),
        Name = nameof(LocalizedStrings.NameKey),
        Description = nameof(LocalizedStrings.LogSourceNameKey),
        GroupName = nameof(LocalizedStrings.LoggingKey),
        Order = 2)]
    public string Name { get; set; }

    [Display(
        ResourceType = typeof(LocalizedStrings),
        Name = nameof(LocalizedStrings.LogLevelKey),
        Description = nameof(LocalizedStrings.LogLevelDescKey),
        GroupName = nameof(LocalizedStrings.LoggingKey),
        Order = 3)]
    public LogLevels LogLevel { get; set; }
}

Custom Resource Keys

Extend LocalizedStrings with your own resource keys:

using Ecng.Localization;

public static class MyLocalizedStrings
{
    // Define keys
    public const string UserNameKey = nameof(UserName);
    public const string PasswordKey = nameof(Password);
    public const string LoginButtonKey = nameof(LoginButton);

    // Define properties that use the localizer
    public static string UserName => UserNameKey.LocalizeByKey();
    public static string Password => PasswordKey.LocalizeByKey();
    public static string LoginButton => LoginButtonKey.LocalizeByKey();
}

// Usage in attributes
public class LoginModel
{
    [Display(
        ResourceType = typeof(MyLocalizedStrings),
        Name = nameof(MyLocalizedStrings.UserNameKey))]
    public string UserName { get; set; }

    [Display(
        ResourceType = typeof(MyLocalizedStrings),
        Name = nameof(MyLocalizedStrings.PasswordKey))]
    public string Password { get; set; }
}

Best Practices

1. Initialize Localizer Early

Set up your localizer during application startup:

public class Program
{
    public static void Main(string[] args)
    {
        // Initialize localization before anything else
        InitializeLocalization();

        var app = CreateApplication();
        app.Run();
    }

    private static void InitializeLocalization()
    {
        var culture = GetUserPreferredCulture();
        LocalizedStrings.Localizer = new MyLocalizer(culture);
    }
}

2. Use Resource Keys for Stability

Prefer LocalizeByKey() over Localize() for better refactoring support:

// Good: Key-based (won't break if English text changes)
public const string ErrorKey = "ERROR_INVALID_INPUT";
string message = ErrorKey.LocalizeByKey();

// Less ideal: Text-based (fragile to typos and changes)
string message = "Invalid input".Localize();

3. Provide Fallback Behavior

Always return meaningful fallback text:

public string Localize(string enStr)
{
    // Try translation
    if (_translations.TryGetValue(enStr, out var result))
        return result;

    // Fallback to original (better than throwing exception)
    return enStr;
}

4. Don't Set Localizer to Null

The library explicitly prevents null localizers:

// This will throw ArgumentNullException
LocalizedStrings.Localizer = null; // ERROR!

// Instead, use a pass-through localizer if needed
LocalizedStrings.Localizer = new PassThroughLocalizer();

5. Cache Translations When Possible

Avoid repeated lookups for frequently used strings:

// Cache in a field or property
private readonly string _errorMessage = LocalizedStrings.Errors;

public void LogError()
{
    // Reuse cached translation
    Console.WriteLine(_errorMessage);
}

6. Test Missing Translations

Implement tests to catch missing translations:

[Test]
public void AllKeysHaveTranslations()
{
    var localizer = new RussianLocalizer();
    LocalizedStrings.Localizer = localizer;

    // Test all predefined keys
    Assert.AreNotEqual("Name", LocalizedStrings.Name);
    Assert.AreNotEqual("Errors", LocalizedStrings.Errors);
    Assert.AreNotEqual("Warnings", LocalizedStrings.Warnings);
}

API Reference

ILocalizer Interface

public interface ILocalizer
{
    /// <summary>
    /// Localizes a string using the English text as the lookup key.
    /// </summary>
    /// <param name="enStr">The English string to localize.</param>
    /// <returns>The localized string, or the original if not found.</returns>
    string Localize(string enStr);

    /// <summary>
    /// Localizes a string using a resource key for lookup.
    /// </summary>
    /// <param name="key">The resource key.</param>
    /// <returns>The localized string, or the key if not found.</returns>
    string LocalizeByKey(string key);
}

LocalizedStrings Class

Properties
public static class LocalizedStrings
{
    /// <summary>
    /// Gets or sets the global localizer instance.
    /// Cannot be set to null.
    /// </summary>
    public static ILocalizer Localizer { get; set; }

    // Predefined localized string properties
    public static string Inherited { get; }
    public static string Verbose { get; }
    public static string Debug { get; }
    public static string Info { get; }
    public static string Warnings { get; }
    public static string Errors { get; }
    public static string Off { get; }
    public static string Id { get; }
    public static string Logging { get; }
    public static string Name { get; }
    public static string LogSourceName { get; }
    public static string LogLevel { get; }
    public static string LogLevelDesc { get; }
    // ... and more
}
Resource Keys
public static class LocalizedStrings
{
    // Resource key constants for use with DisplayAttribute
    public const string InheritedKey = "Inherited";
    public const string VerboseKey = "Verbose";
    public const string DebugKey = "Debug";
    public const string InfoKey = "Info";
    public const string WarningsKey = "Warnings";
    public const string ErrorsKey = "Errors";
    public const string OffKey = "Off";
    public const string IdKey = "Id";
    public const string LoggingKey = "Logging";
    public const string NameKey = "Name";
    // ... and more
}
Extension Methods
public static class LocalizedStrings
{
    /// <summary>
    /// Localizes a string using English text as the key.
    /// </summary>
    public static string Localize(this string enStr);

    /// <summary>
    /// Localizes a string using a resource key.
    /// </summary>
    public static string LocalizeByKey(this string key);
}

Usage Examples

// Get localized string via property
string name = LocalizedStrings.Name;

// Get localized string via extension method
string custom = "My Text".Localize();

// Get localized string via key
string byKey = "CustomKey".LocalizeByKey();

// Use in DisplayAttribute
[Display(ResourceType = typeof(LocalizedStrings), Name = LocalizedStrings.NameKey)]
public string UserName { get; set; }

License

This project is part of the Ecng framework. Please refer to the main repository for licensing information.

Contributing

Contributions are welcome! When adding new localization features:

  1. Ensure backward compatibility
  2. Add resource keys to LocalizedStrings class
  3. Provide both key constants and localized properties
  4. Update documentation with examples
  5. Add unit tests for new functionality

Support

For issues, questions, or contributions, please refer to the main Ecng repository.

Product Compatible and additional computed target framework versions.
.NET net5.0 was computed.  net5.0-windows was computed.  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 was computed.  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.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  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. 
.NET Core netcoreapp2.0 was computed.  netcoreapp2.1 was computed.  netcoreapp2.2 was computed.  netcoreapp3.0 was computed.  netcoreapp3.1 was computed. 
.NET Standard netstandard2.0 is compatible.  netstandard2.1 was computed. 
.NET Framework net461 was computed.  net462 was computed.  net463 was computed.  net47 was computed.  net471 was computed.  net472 was computed.  net48 was computed.  net481 was computed. 
MonoAndroid monoandroid was computed. 
MonoMac monomac was computed. 
MonoTouch monotouch was computed. 
Tizen tizen40 was computed.  tizen60 was computed. 
Xamarin.iOS xamarinios was computed. 
Xamarin.Mac xamarinmac was computed. 
Xamarin.TVOS xamarintvos was computed. 
Xamarin.WatchOS xamarinwatchos was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages (6)

Showing the top 5 NuGet packages that depend on Ecng.Localization:

Package Downloads
Ecng.ComponentModel

Ecng system framework

Ecng.Net.SocketIO

Ecng system framework

Ecng.Drawing

Ecng system framework

Ecng.Compilation.Roslyn

Ecng system framework

Ecng.Logging

Ecng system framework

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.0.244 235 2/4/2026
1.0.243 1,360 2/1/2026
1.0.242 1,919 1/22/2026
1.0.241 1,733 1/19/2026
1.0.240 1,089 1/18/2026
1.0.239 582 1/18/2026
1.0.238 1,150 1/14/2026
1.0.237 656 1/13/2026
1.0.236 584 1/13/2026
1.0.235 954 1/9/2026
1.0.234 3,528 1/4/2026
1.0.233 1,833 12/30/2025
1.0.232 626 12/29/2025
1.0.231 1,048 12/26/2025
1.0.230 620 12/26/2025
1.0.229 616 12/26/2025
1.0.228 645 12/26/2025
1.0.227 712 12/25/2025
1.0.226 714 12/25/2025
1.0.225 1,224 12/22/2025
1.0.224 715 12/21/2025
1.0.223 747 12/19/2025
1.0.222 742 12/19/2025
1.0.221 946 12/17/2025
1.0.220 1,401 12/15/2025
1.0.219 2,227 12/12/2025
1.0.218 2,578 12/12/2025
1.0.217 2,640 11/29/2025
1.0.216 684 11/28/2025
1.0.215 662 11/28/2025
1.0.214 726 11/27/2025
1.0.213 838 11/24/2025
1.0.212 753 11/24/2025
1.0.211 757 11/23/2025
1.0.210 1,220 11/22/2025
1.0.209 1,946 11/20/2025
1.0.208 1,004 11/18/2025
1.0.207 928 11/18/2025
1.0.206 941 11/13/2025
1.0.205 853 11/10/2025
1.0.204 1,744 11/1/2025
1.0.203 989 10/28/2025
1.0.202 964 10/27/2025
1.0.201 805 10/27/2025
1.0.200 689 10/25/2025
1.0.199 4,349 10/3/2025
1.0.198 2,730 9/25/2025
1.0.197 5,423 9/5/2025
1.0.196 6,430 8/30/2025
1.0.195 1,768 8/19/2025
1.0.194 7,687 7/13/2025
1.0.193 681 7/13/2025
1.0.192 684 7/12/2025
1.0.191 2,161 7/8/2025
1.0.190 6,996 6/16/2025
1.0.189 878 6/9/2025
1.0.188 790 6/8/2025
1.0.187 2,343 5/21/2025
1.0.186 893 5/17/2025
1.0.185 2,443 5/12/2025
1.0.184 800 5/12/2025
1.0.183 2,921 4/17/2025
1.0.182 6,544 3/20/2025
1.0.181 742 3/19/2025
1.0.180 5,485 2/26/2025
1.0.179 788 2/26/2025
1.0.178 9,250 2/5/2025
1.0.177 1,255 2/5/2025
1.0.176 2,763 4/14/2024
1.0.175 5,755 3/28/2024
1.0.174 433 3/17/2024
1.0.173 2,550 2/23/2024
1.0.172 370 2/23/2024
1.0.171 3,638 2/18/2024
1.0.170 375 2/16/2024
1.0.169 2,330 2/13/2024
1.0.168 2,210 2/8/2024
1.0.167 2,613 2/4/2024
1.0.166 2,650 1/23/2024
1.0.165 453 1/12/2024
1.0.164 4,915 1/2/2024
1.0.163 393 12/29/2023
1.0.162 13,198 11/12/2023
1.0.161 394 11/10/2023
1.0.160 391 11/10/2023
1.0.159 396 11/9/2023
1.0.158 420 11/3/2023
1.0.157 407 11/1/2023
1.0.156 394 11/1/2023
1.0.155 24,769 9/8/2023
1.0.154 442 9/8/2023
1.0.153 456 9/3/2023
1.0.152 514 8/21/2023
1.0.151 501 8/14/2023
1.0.150 461 8/10/2023
1.0.149 39,399 6/29/2023
1.0.148 13,855 5/27/2023
1.0.147 512 5/19/2023
1.0.146 26,217 5/8/2023
1.0.145 594 4/21/2023
1.0.144 50,529 4/3/2023
1.0.143 723 3/13/2023
1.0.142 18,360 3/6/2023
1.0.141 627 2/26/2023
1.0.140 46,772 2/9/2023
1.0.139 15,946 2/7/2023
1.0.138 648 2/4/2023
1.0.137 18,941 2/2/2023
1.0.136 17,445 1/30/2023
1.0.135 649 1/18/2023
1.0.134 42,231 12/30/2022
1.0.133 679 12/23/2022
1.0.132 20,308 12/12/2022
1.0.131 20,420 12/4/2022
1.0.130 677 12/4/2022
1.0.129 695 11/30/2022
1.0.128 688 11/28/2022
1.0.127 739 11/18/2022
1.0.126 28,431 11/11/2022
1.0.125 702 11/11/2022
1.0.124 662 11/10/2022
1.0.123 759 11/5/2022
1.0.122 737 11/4/2022
1.0.121 23,768 11/1/2022
1.0.120 24,882 10/16/2022
1.0.119 936 9/10/2022
1.0.118 49,934 9/8/2022
1.0.117 783 9/8/2022
1.0.116 701 9/8/2022
1.0.115 811 9/4/2022
1.0.114 88,303 8/24/2022
1.0.113 901 8/8/2022
1.0.112 918 7/26/2022
1.0.111 845 7/26/2022
1.0.110 51,995 7/19/2022
1.0.109 45,108 7/18/2022
1.0.108 884 7/8/2022
1.0.107 903 6/18/2022
1.0.106 886 6/6/2022
1.0.105 95,192 4/30/2022
1.0.104 877 4/20/2022
1.0.103 893 4/10/2022
1.0.102 872 4/7/2022
1.0.101 844 4/7/2022
1.0.100 872 4/2/2022
1.0.99 12,230 3/29/2022
1.0.98 862 3/27/2022
1.0.97 259,691 1/24/2022
1.0.96 157,306 12/29/2021
1.0.95 28,000 12/20/2021
1.0.94 746 12/13/2021
1.0.93 54,725 12/6/2021
1.0.92 670 12/2/2021
1.0.91 645 12/2/2021
1.0.90 33,489 11/29/2021
1.0.89 32,165 11/22/2021
1.0.88 4,149 11/17/2021
1.0.87 33,848 11/13/2021
1.0.86 7,635 11/10/2021
1.0.85 4,357 11/9/2021
1.0.84 66,880 11/5/2021
1.0.83 6,110 11/4/2021
1.0.82 4,182 11/4/2021
1.0.81 4,084 11/3/2021
1.0.80 4,422 10/30/2021
1.0.79 35,519 10/21/2021
1.0.78 4,897 10/17/2021
1.0.77 65,501 10/14/2021
1.0.76 15,550 10/13/2021
1.0.75 4,526 10/12/2021
1.0.74 36,105 10/11/2021
1.0.73 4,316 10/9/2021
1.0.72 39,189 10/7/2021
1.0.71 41,283 10/7/2021
1.0.70 4,376 10/7/2021
1.0.69 4,295 10/6/2021
1.0.68 2,649 9/28/2021
1.0.67 36,230 9/23/2021
1.0.66 4,256 9/10/2021
1.0.65 2,335 9/9/2021
1.0.64 2,336 9/8/2021
1.0.63 2,339 9/8/2021
1.0.62 33,010 9/6/2021
1.0.61 2,567 8/31/2021
1.0.60 2,515 8/30/2021
1.0.59 35,694 7/31/2021
1.0.58 61,497 7/30/2021
1.0.57 2,982 7/26/2021
1.0.56 90,867 7/5/2021
1.0.55 2,986 7/1/2021
1.0.54 64,747 6/4/2021
1.0.53 92,228 4/26/2021
1.0.52 33,498 4/19/2021
1.0.51 150,347 4/7/2021
1.0.50 32,639 4/3/2021
1.0.49 178,916 3/22/2021
1.0.48 113,390 3/4/2021
1.0.47 35,694 2/26/2021
1.0.46 167,977 2/2/2021
1.0.45 59,614 1/26/2021
1.0.44 58,559 1/24/2021
1.0.43 3,189 1/24/2021
1.0.42 3,250 1/23/2021
1.0.41 60,180 1/20/2021
1.0.40 3,261 1/20/2021
1.0.39 31,305 1/18/2021
1.0.38 3,200 1/18/2021
1.0.37 30,324 1/16/2021
1.0.36 119,918 12/16/2020
1.0.35 57,760 12/14/2020
1.0.34 35,595 12/9/2020
1.0.33 5,574 12/6/2020
1.0.32 3,727 12/2/2020
1.0.31 3,625 12/2/2020
1.0.30 31,241 12/1/2020
1.0.29 187,377 11/12/2020
1.0.29-atestpub 1,673 11/11/2020
1.0.28 32,545 10/11/2020
1.0.27 112,645 9/9/2020
1.0.26 31,015 9/3/2020
1.0.25 31,339 8/20/2020
1.0.24 85,625 8/9/2020
1.0.23 31,792 7/28/2020
1.0.22 30,761 7/19/2020
1.0.21 57,380 7/6/2020
1.0.20 85,886 6/6/2020
1.0.19 32,103 6/4/2020
1.0.18 58,845 5/29/2020
1.0.17 58,780 5/21/2020
1.0.16 4,289 5/17/2020
1.0.15 58,143 5/12/2020
1.0.14 113,077 5/4/2020
1.0.13 8,295 4/24/2020
1.0.12 10,954 4/22/2020
1.0.11 4,085 4/22/2020
1.0.10 4,075 4/21/2020
1.0.9 33,206 4/18/2020
1.0.8 30,944 4/16/2020
1.0.7 3,978 4/16/2020
1.0.6 26,472 4/15/2020
1.0.5 29,014 4/11/2020
1.0.4 28,034 4/3/2020
1.0.3 3,640 4/1/2020
1.0.2 14,838 3/27/2020
1.0.1 13,942 3/22/2020
1.0.0 6,139 3/22/2020