mirror of
https://github.com/radzenhq/radzen-blazor.git
synced 2026-07-08 18:16:08 +00:00
Ship satellite resource files (RadzenStrings.{de,fr,es,it,ja}.resx) with all
535 strings translated, so components localize out of the box via the standard
.NET ResourceManager.
Make culture handling reactive everywhere:
- RadzenDatePicker rebuilds its month/year lists when Culture or the
DefaultCulture cascade changes (previously cached at init).
- Scheduler views, HtmlEditor tools and the spreadsheet ChartOverlay localize
through their parent component's UICulture instead of CurrentUICulture;
spreadsheet dialogs honor a DefaultUICulture cascade. UICulture is added to
IScheduler/ISpreadsheet as default interface members, so this is non-breaking.
Update the Localization demo with an interactive language switcher (cascading
DefaultUICulture + DefaultCulture) backed by an ILocalizer so it works in-place
on Blazor WebAssembly.
175 lines
6.1 KiB
C#
175 lines
6.1 KiB
C#
using System.Collections.Generic;
|
|
using System.Globalization;
|
|
using Bunit;
|
|
using Microsoft.AspNetCore.Components;
|
|
using Microsoft.Extensions.DependencyInjection;
|
|
using Radzen;
|
|
using Radzen.Blazor;
|
|
using Xunit;
|
|
|
|
#nullable enable
|
|
namespace Radzen.Blazor.Tests;
|
|
|
|
public class LocalizerTests
|
|
{
|
|
[Fact]
|
|
public void Localizer_Returns_Resx_Default()
|
|
{
|
|
var result = Localizer.Default.Get(nameof(RadzenStrings.Spreadsheet_OK), CultureInfo.InvariantCulture);
|
|
|
|
Assert.Equal("OK", result);
|
|
}
|
|
|
|
[Fact]
|
|
public void Localizer_Returns_Key_When_Not_Found()
|
|
{
|
|
var result = Localizer.Default.Get("NonExistent_Key", CultureInfo.InvariantCulture);
|
|
|
|
Assert.Equal("NonExistent_Key", result);
|
|
}
|
|
|
|
[Fact]
|
|
public void Localizer_Custom_Override_Takes_Precedence()
|
|
{
|
|
var custom = new TestLocalizer("Spreadsheet_OK", "Aceptar");
|
|
var localizer = new Localizer(custom);
|
|
|
|
var result = localizer.Get(nameof(RadzenStrings.Spreadsheet_OK), CultureInfo.InvariantCulture);
|
|
|
|
Assert.Equal("Aceptar", result);
|
|
}
|
|
|
|
[Fact]
|
|
public void Localizer_Custom_Returns_Null_Falls_Back_To_Resx()
|
|
{
|
|
var custom = new TestLocalizer("other_key", "other_value");
|
|
var localizer = new Localizer(custom);
|
|
|
|
var result = localizer.Get(nameof(RadzenStrings.Spreadsheet_OK), CultureInfo.InvariantCulture);
|
|
|
|
Assert.Equal("OK", result);
|
|
}
|
|
|
|
[Fact]
|
|
public void Localizer_Returns_Resx_Default_For_Various_Keys()
|
|
{
|
|
var localizer = Localizer.Default;
|
|
|
|
Assert.Equal("Sort ascending", localizer.Get(nameof(RadzenStrings.Spreadsheet_SortAscending), CultureInfo.InvariantCulture));
|
|
Assert.Equal("Cancel", localizer.Get(nameof(RadzenStrings.Spreadsheet_Cancel), CultureInfo.InvariantCulture));
|
|
Assert.Equal("contains", localizer.Get(nameof(RadzenStrings.Spreadsheet_FilterContains), CultureInfo.InvariantCulture));
|
|
Assert.Equal("Format Cells...", localizer.Get(nameof(RadzenStrings.Spreadsheet_FormatCells), CultureInfo.InvariantCulture));
|
|
}
|
|
|
|
[Fact]
|
|
public void AddRadzenComponents_Registers_Localizer()
|
|
{
|
|
var services = new ServiceCollection();
|
|
services.AddRadzenComponents();
|
|
var provider = services.BuildServiceProvider();
|
|
|
|
var localizer = provider.GetService<Localizer>();
|
|
|
|
Assert.NotNull(localizer);
|
|
}
|
|
|
|
[Fact]
|
|
public void Localizer_Works_Without_ILocalizer_Registered()
|
|
{
|
|
var services = new ServiceCollection();
|
|
services.AddRadzenComponents();
|
|
var provider = services.BuildServiceProvider();
|
|
|
|
var localizer = provider.GetRequiredService<Localizer>();
|
|
var result = localizer.Get(nameof(RadzenStrings.Spreadsheet_OK), CultureInfo.InvariantCulture);
|
|
|
|
Assert.Equal("OK", result);
|
|
}
|
|
|
|
[Fact]
|
|
public void Localizer_Uses_Custom_ILocalizer_When_Registered()
|
|
{
|
|
var services = new ServiceCollection();
|
|
services.AddRadzenComponents();
|
|
services.AddSingleton<ILocalizer>(new TestLocalizer("Spreadsheet_OK", "Aceptar"));
|
|
var provider = services.BuildServiceProvider();
|
|
|
|
var localizer = provider.GetRequiredService<Localizer>();
|
|
var result = localizer.Get(nameof(RadzenStrings.Spreadsheet_OK), CultureInfo.InvariantCulture);
|
|
|
|
Assert.Equal("Aceptar", result);
|
|
}
|
|
|
|
[Fact]
|
|
public void Localizer_Format_String_Key_Contains_Placeholder()
|
|
{
|
|
var localizer = Localizer.Default;
|
|
var template = localizer.Get(nameof(RadzenStrings.Spreadsheet_SheetNameAlreadyExists), CultureInfo.InvariantCulture);
|
|
var result = string.Format(template, "Sheet2");
|
|
|
|
Assert.Equal("A sheet named 'Sheet2' already exists.", result);
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData("de")]
|
|
[InlineData("fr")]
|
|
[InlineData("es")]
|
|
[InlineData("it")]
|
|
[InlineData("ja")]
|
|
public void Localizer_Resolves_Builtin_Translations_For_Culture(string lang)
|
|
{
|
|
var culture = new CultureInfo(lang);
|
|
|
|
var cancel = Localizer.Default.Get(nameof(RadzenStrings.Spreadsheet_Cancel), culture);
|
|
|
|
// The built-in satellite resource must resolve and translate Cancel away from English.
|
|
Assert.NotEqual("Cancel", cancel);
|
|
Assert.False(string.IsNullOrWhiteSpace(cancel));
|
|
}
|
|
|
|
[Fact]
|
|
public void Localizer_Translates_Known_Strings()
|
|
{
|
|
Assert.Equal("Abbrechen", Localizer.Default.Get(nameof(RadzenStrings.Spreadsheet_Cancel), new CultureInfo("de")));
|
|
Assert.Equal("Aujourd'hui", Localizer.Default.Get(nameof(RadzenStrings.Scheduler_TodayText), new CultureInfo("fr")));
|
|
Assert.Equal("キャンセル", Localizer.Default.Get(nameof(RadzenStrings.Spreadsheet_Cancel), new CultureInfo("ja")));
|
|
}
|
|
|
|
[Fact]
|
|
public void DataGrid_Uses_DefaultUICulture_Cascade_For_Localized_Strings()
|
|
{
|
|
using var ctx = new TestContext();
|
|
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
|
ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js");
|
|
ctx.Services.AddRadzenComponents();
|
|
|
|
var component = ctx.RenderComponent<CascadingValue<CultureInfo>>(parameters => parameters
|
|
.Add(p => p.Name, nameof(RadzenComponent.DefaultUICulture))
|
|
.Add(p => p.Value, new CultureInfo("de"))
|
|
.AddChildContent<RadzenDataGrid<dynamic>>(grid => grid
|
|
.Add(g => g.Data, (IEnumerable<dynamic>)new[] { new { Id = 1 } })
|
|
.Add(g => g.AllowGrouping, true)));
|
|
|
|
// German translation of DataGrid_GroupPanelText must appear via the cascade + satellite resource.
|
|
Assert.Contains("Ziehen Sie eine Spaltenüberschrift", component.Markup);
|
|
Assert.DoesNotContain("Drag a column header here", component.Markup);
|
|
}
|
|
|
|
private class TestLocalizer : ILocalizer
|
|
{
|
|
private readonly string key;
|
|
private readonly string value;
|
|
|
|
public TestLocalizer(string key, string value)
|
|
{
|
|
this.key = key;
|
|
this.value = value;
|
|
}
|
|
|
|
public string? Get(string key, CultureInfo culture)
|
|
{
|
|
return key == this.key ? value : null;
|
|
}
|
|
}
|
|
}
|