Tutorial: Setting the App's Language — Blazor
BPT ships with eight supported cultures out of the box and gives you five different ways to pick which one is active — from a visual picker your users see, to a one-time cookie your server sets, to programmatic overrides driven by tenant config.
Level: Intermediate · Time: ~10 minutes
The whole picture
Three pieces collaborate.
IsabyteCultureis the catalog of supported cultures.IsabyteCultureStateis the cascading service that tracks the currently active one and translates resource keys.IsabyteLanguageis the optional visual picker your users interact with. Each of the five "ways to set the language" below ultimately funnels into the sameIsabyteCultureState.
Supported cultures
BPT ships with eight cultures, each with its own resource translations, emoji + SVG flag, currency symbol, and number format defaults:
| Code | Language | Currency |
|---|---|---|
nb-NO |
Norwegian Bokmål | NOK (kr) |
en-GB |
English (UK) | GBP (£) |
en-US |
English (US) | USD ($) |
sv-SE |
Swedish | SEK (kr) |
de-DE |
German | EUR (€) |
fr-FR |
French | EUR (€) |
es-ES |
Spanish | EUR (€) |
zh-CN |
Chinese (Simplified) | CNY (¥) |
The catalog lives in IsabyteCulture.GetSupportedCultures(). Any culture code not in this list falls back to the closest supported neighbor via IsabyteCulture.MapToSupported(...) — for example "en-AU" maps to en-US and "nb" maps to nb-NO.
The five ways to set the active culture
Way 1: Drop in the IsabyteLanguage picker
The simplest path. Add the picker to your MainLayout and users pick their own language. Their selection automatically persists across visits via localStorage.
@using Isabyte.PowerTools.Components.Controls
<!-- Compact: just a flag icon that opens a dropdown -->
<IsabyteLanguage DisplayMode="IsabyteLanguageDisplayMode.Compact" />
<!-- Full: flag + language name + culture code -->
<IsabyteLanguage DisplayMode="IsabyteLanguageDisplayMode.Full"
OnCultureChanged="OnCultureChanged" />
@code {
private void OnCultureChanged(string code)
{
// code = "nb-NO", "en-GB", etc. Optional — the picker already updates IsabyteCultureState.
}
}
Number-format overrides
Some apps need locale-mixed formatting (e.g. Norwegian text but US number separators). Use
DecimalSeparatorandThousandsSeparatoronIsabyteLanguage, orDecimalSeparatorOverrideandThousandsSeparatorOverridedirectly onIsabyteCultureState, to override the culture's defaults without changing the language.
Way 2: Pin the culture programmatically
When the active language is decided by application logic (per-tenant config, user profile in DB, A/B bucket), set it once on IsabyteRootComponent at the top of your app:
<!-- App.razor (or MainLayout.razor) -->
@using Isabyte.PowerTools.Components.Common
<IsabyteRootComponent CultureCode="@_tenantCulture">
<Router AppAssembly="typeof(Program).Assembly">
...
</Router>
</IsabyteRootComponent>
@code {
[Inject] ITenantService TenantService { get; set; } = default!;
private string _tenantCulture = "en-US";
protected override async Task OnInitializedAsync()
{
var tenant = await TenantService.GetCurrentAsync();
_tenantCulture = tenant.DefaultCultureCode; // e.g. "nb-NO"
}
}
For runtime changes (a user toggles their preferred language inside the app), inject IsabyteCultureState and call SetCurrentCulture. This switches the culture for the current session only — it does not write localStorage, so a returning visitor gets their stored choice back unless you also save it (see Way 4):
@using Isabyte.PowerTools.Components.Common
@inject IsabyteCultureState CultureState
<button @onclick='() => CultureState.SetCurrentCulture("sv-SE")'>
Switch to Swedish
</button>
Way 3: A one-time culture cookie from the server
There is no ?culture= query parameter. When the server knows the language — a preference stored with the user's account after sign-in, a campaign link your own endpoint handles — set a cookie named isabyte_culture_sync to a supported culture code. On first render IsabyteRootComponent reads it, applies it, saves it to localStorage, and deletes the cookie:
Set-Cookie: isabyte_culture_sync=nb-NO; Path=/
It takes precedence over the saved localStorage preference, once. To honour a ?culture= link, read the query string in your own endpoint or middleware and set this cookie.
Way 4: localStorage persistence
By default the IsabyteLanguage picker persists the user's choice to localStorage under the key isabyte_current_culture. On subsequent visits, IsabyteRootComponent (and any interactive IsabyteLanguage) reads that value once it is interactive and restores the language.
You can interact with this key from any other JS code on the page — for instance, syncing the language with a server-side cookie:
// Read what IsabyteLanguage has stored.
const culture = localStorage.getItem("isabyte_current_culture");
// Mirror to a server-readable cookie so SSR-rendered pages know the culture too.
document.cookie = `culture=${culture}; path=/; max-age=31536000`;
Way 5: Auto-detect from the browser
If none of the above apply (no culture cookie, no localStorage, no programmatic override), BPT falls back to the browser's navigator.language via IsabyteCulture.MapToSupported(...), and saves the result to localStorage:
| Browser sends | BPT resolves to |
|---|---|
en-AU, en-CA, en-IN |
en-US |
nb, nn, no |
nb-NO |
de-AT, de-CH |
de-DE |
zh-TW, zh-HK |
zh-CN |
| anything else | en-US |
Reading the active culture in your own components
IsabyteCultureState is registered as a root cascading value, so any component — Server or WebAssembly — can pick it up:
@using Isabyte.PowerTools.Components.Common
@code {
[CascadingParameter] public IsabyteCultureState? CultureState { get; set; }
private string Greeting => CultureState?.CurrentCulture switch
{
"nb-NO" => "Hei!",
"sv-SE" => "Hej!",
"de-DE" => "Hallo!",
"zh-CN" => "你好!",
_ => "Hello!"
};
// Look up translations from BPT's resx files (Isabyte.PowerTools.Components.Common.Resources.IsabyteTranslations).
// T returns the key itself when it has no translation, so compare against the key.
private string L(string key, string fallback)
{
var text = CultureState?.T(key);
return text is null || text == key ? fallback : text;
}
}
Quick comparison
| Mechanism | Persists? | Best for |
|---|---|---|
<IsabyteLanguage /> picker |
localStorage | End-user choice. The default for most apps. |
IsabyteRootComponent CultureCode |
Per-render | App-wide lock driven by tenant or user profile. |
IsabyteCultureState.SetCurrentCulture(...) |
In memory (this session) | Runtime switching from your own UI. |
isabyte_culture_sync cookie |
localStorage (applied once) | A preference the server knows: after sign-in, campaign links. |
| Browser auto-detect | localStorage (first visit) | Anonymous first-time visitors who haven't chosen yet. |
Cross-circuit synchronization
isabyteCulture.jsbroadcasts aCustomEventwhenever the culture changes. This means a Server-rendered island and a WebAssembly-rendered island on the same page see culture switches in real time without page reload — no extra wiring needed.
Next
- Next: Custom theme development — Build a brand-matching theme in Theme Studio and ship it via IsaThemeLoader.
- Live Demo — Try the IsabyteLanguage picker with all eight cultures.