Skip to content

Dynamic Localization ​

The RIVORA Framework provides a database-backed localization system with hot reload, supporting multi-culture content management without redeployment.

Core Abstractions ​

ILocalizationStore ​

The store reads and writes localization entries from a persistent backend:

csharp
public interface ILocalizationStore
{
    Task<string?> GetStringAsync(string key, string culture, CancellationToken ct = default);
    Task SetStringAsync(string key, string culture, string value, CancellationToken ct = default);
    Task<IReadOnlyDictionary<string, string>> GetAllStringsAsync(string culture, CancellationToken ct = default);
    Task DeleteStringAsync(string key, string culture, CancellationToken ct = default);
}

DatabaseStringLocalizer ​

Implements IStringLocalizer backed by the database, with in-memory caching and automatic invalidation:

csharp
public class DatabaseStringLocalizer : IStringLocalizer
{
    // Automatically resolves culture from CultureInfo.CurrentUICulture
    // Falls back to parent culture, then to default culture
    // Caches results in-memory with configurable TTL
}

Registration ​

csharp
builder.Services.AddRvrLocalization(options =>
{
    options.DefaultCulture = "fr-FR";
    options.SupportedCultures = new[] { "fr-FR", "en-US", "ar-MA", "de-DE" };
    options.CacheDuration = TimeSpan.FromMinutes(30);
    options.EnableHotReload = true;
    options.FallbackToDefaultCulture = true;
});

Usage in Controllers ​

csharp
public class ProductsController : ControllerBase
{
    private readonly IStringLocalizer _localizer;

    public ProductsController(IStringLocalizer localizer)
    {
        _localizer = localizer;
    }

    [HttpGet("{id}")]
    public async Task<IActionResult> GetById(Guid id)
    {
        var product = await _productService.GetAsync(id);
        if (product == null)
            return NotFound(_localizer["Product.NotFound"]);

        return Ok(product);
    }

    [HttpPost]
    public async Task<IActionResult> Create(CreateProductDto dto)
    {
        if (dto.Price < 0)
            return BadRequest(_localizer["Product.InvalidPrice"]);

        var product = await _productService.CreateAsync(dto);
        return CreatedAtAction(nameof(GetById), new { id = product.Id },
            new { product, message = _localizer["Product.Created"] });
    }
}

Usage in Razor/Blazor Views ​

razor
@inject IStringLocalizer Localizer

<h1>@Localizer["Dashboard.Title"]</h1>
<p>@Localizer["Dashboard.Welcome", userName]</p>

Managing Translations at Runtime ​

Add or update translations without redeploying:

csharp
public class LocalizationAdminController : ControllerBase
{
    private readonly ILocalizationStore _store;

    [HttpPost("translations")]
    [Authorize(Roles = "Admin")]
    public async Task<IActionResult> SetTranslation(
        [FromBody] SetTranslationDto dto, CancellationToken ct)
    {
        await _store.SetStringAsync(dto.Key, dto.Culture, dto.Value, ct);
        return Ok();
    }

    [HttpGet("translations/{culture}")]
    public async Task<IActionResult> GetAll(string culture, CancellationToken ct)
    {
        var strings = await _store.GetAllStringsAsync(culture, ct);
        return Ok(strings);
    }
}

Hot Reload ​

When EnableHotReload is true, the localizer watches for changes in the database and invalidates its cache automatically. Changes made via the admin API or directly in the database are reflected within the CacheDuration window.

Culture Resolution ​

The framework resolves the current culture in this order:

  1. Accept-Language HTTP header
  2. culture query string parameter
  3. User profile preference (if authenticated)
  4. Default culture from configuration
csharp
// In Program.cs
app.UseRequestLocalization(new RequestLocalizationOptions
{
    DefaultRequestCulture = new RequestCulture("fr-FR"),
    SupportedCultures = new[] { new CultureInfo("fr-FR"), new CultureInfo("en-US") },
    SupportedUICultures = new[] { new CultureInfo("fr-FR"), new CultureInfo("en-US") }
});

Released under the MIT License.