Add localization support for multi-language UI
This commit introduces comprehensive localization support for the application, enabling dynamic language switching and localized UI elements. Key changes include: - Added `.resx` files for German (de-DE), English (en-US), and French (fr-FR). - Updated Razor components to use `IStringLocalizer` for dynamic string localization. - Introduced the `L(string key, string fallback)` helper method for consistent localized string retrieval with fallback support. - Implemented dynamic culture initialization in `Program.cs` via an API call to `/api/Localization/lang`. - Localized error pages, grid columns, tabs, popups, validation messages, and tooltips. - Dynamically generated privacy policy links based on the current culture. - Improved user experience with fully localized loading states, error handling, and UI labels. These changes enhance accessibility and usability, making the application more user-friendly for a global audience.
This commit is contained in:
@@ -1,8 +1,11 @@
|
||||
@page "/sender/login"
|
||||
@rendermode InteractiveWebAssembly
|
||||
@using EnvelopeGenerator.Server.Client.Services
|
||||
@using EnvelopeGenerator.Application.Resources
|
||||
@using Microsoft.Extensions.Localization
|
||||
@inject AuthService AuthService
|
||||
@inject NavigationManager Navigation
|
||||
@inject IStringLocalizer<Resource> UiLocalizer
|
||||
|
||||
<div class="login-page-wrapper d-flex align-items-center justify-content-center min-vh-100">
|
||||
<div class="login-card card shadow border-0" style="max-width: 440px; width: 100%;">
|
||||
@@ -14,14 +17,14 @@
|
||||
<path fill-rule="evenodd" d="M0 8a8 8 0 1 1 16 0A8 8 0 0 1 0 8zm8-7a7 7 0 0 0-5.468 11.37C3.242 11.226 4.805 10 8 10s4.757 1.225 5.468 2.37A7 7 0 0 0 8 1z"/>
|
||||
</svg>
|
||||
</div>
|
||||
<h5 class="mb-0 fw-semibold">Sender Anmeldung</h5>
|
||||
<p class="mb-0 mt-1 opacity-75" style="font-size: 0.85rem;">Sicherer Zugang zum Sender-Dashboard</p>
|
||||
<h5 class="mb-0 fw-semibold">@L("SenderLogin_Title", "Sender Anmeldung")</h5>
|
||||
<p class="mb-0 mt-1 opacity-75" style="font-size: 0.85rem;">@L("SenderLogin_Subtitle", "Sicherer Zugang zum Sender-Dashboard")</p>
|
||||
</div>
|
||||
|
||||
<div class="card-body p-4">
|
||||
|
||||
<p class="text-muted mb-4" style="font-size: 0.875rem; line-height: 1.5;">
|
||||
Bitte melden Sie sich mit Ihren Zugangsdaten an, um auf das Sender-Dashboard zuzugreifen.
|
||||
@L("SenderLogin_Description", "Bitte melden Sie sich mit Ihren Zugangsdaten an, um auf das Sender-Dashboard zuzugreifen.")
|
||||
</p>
|
||||
|
||||
@if (LoginResult == SenderLoginResult.InvalidCredentials) {
|
||||
@@ -30,8 +33,8 @@
|
||||
<path d="M16 8A8 8 0 1 1 0 8a8 8 0 0 1 16 0zM5.354 4.646a.5.5 0 1 0-.708.708L7.293 8l-2.647 2.646a.5.5 0 0 0 .708.708L8 8.707l2.646 2.647a.5.5 0 0 0 .708-.708L8.707 8l2.647-2.646a.5.5 0 0 0-.708-.708L8 7.293 5.354 4.646z"/>
|
||||
</svg>
|
||||
<div>
|
||||
<strong>Ungültige Anmeldedaten.</strong><br />
|
||||
<span style="font-size:0.85rem;">Benutzername oder Passwort ist falsch. Bitte versuchen Sie es erneut.</span>
|
||||
<strong>@L("SenderLogin_InvalidCredentials_Title", "Ungültige Anmeldedaten.")</strong><br />
|
||||
<span style="font-size:0.85rem;">@L("SenderLogin_InvalidCredentials_Body", "Benutzername oder Passwort ist falsch. Bitte versuchen Sie es erneut.")</span>
|
||||
</div>
|
||||
</div>
|
||||
} else if (LoginResult == SenderLoginResult.Error) {
|
||||
@@ -41,15 +44,15 @@
|
||||
<path d="M7.002 11a1 1 0 1 1 2 0 1 1 0 0 1-2 0zM7.1 4.995a.905.905 0 1 1 1.8 0l-.35 3.507a.552.552 0 0 1-1.1 0L7.1 4.995z"/>
|
||||
</svg>
|
||||
<div>
|
||||
<strong>Serverfehler.</strong><br />
|
||||
<span style="font-size:0.85rem;">Ein unerwarteter Fehler ist aufgetreten. Bitte versuchen Sie es später erneut.</span>
|
||||
<strong>@L("ServerError", "Serverfehler.")</strong><br />
|
||||
<span style="font-size:0.85rem;">@L("UnexpectedErrorTryAgainLater", "Ein unerwarteter Fehler ist aufgetreten. Bitte versuchen Sie es später erneut.")</span>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label fw-medium" for="login-username">
|
||||
Benutzername
|
||||
@L("UserName", "Benutzername")
|
||||
<span class="text-danger ms-1">*</span>
|
||||
</label>
|
||||
<div class="input-group">
|
||||
@@ -61,7 +64,7 @@
|
||||
<input id="login-username"
|
||||
type="text"
|
||||
class="form-control @(LoginResult == SenderLoginResult.InvalidCredentials ? "is-invalid" : null)"
|
||||
placeholder="Benutzername eingeben"
|
||||
placeholder="@L("SenderLogin_Username_Placeholder", "Benutzername eingeben")"
|
||||
@bind="Username"
|
||||
@bind:event="oninput"
|
||||
@onkeydown="OnKeyDownAsync"
|
||||
@@ -72,7 +75,7 @@
|
||||
|
||||
<div class="mb-4">
|
||||
<label class="form-label fw-medium" for="login-password">
|
||||
Passwort
|
||||
@L("Password", "Passwort")
|
||||
<span class="text-danger ms-1">*</span>
|
||||
</label>
|
||||
<div class="input-group">
|
||||
@@ -84,7 +87,7 @@
|
||||
<input id="login-password"
|
||||
type="@(ShowPassword ? "text" : "password")"
|
||||
class="form-control border-start-0 border-end-0 @(LoginResult == SenderLoginResult.InvalidCredentials ? "is-invalid" : null)"
|
||||
placeholder="Passwort eingeben"
|
||||
placeholder="@L("SenderLogin_Password_Placeholder", "Passwort eingeben")"
|
||||
@bind="Password"
|
||||
@bind:event="oninput"
|
||||
@onkeydown="OnKeyDownAsync"
|
||||
@@ -118,20 +121,20 @@
|
||||
disabled="@(IsLoading || string.IsNullOrWhiteSpace(Username) || string.IsNullOrWhiteSpace(Password))">
|
||||
@if (IsLoading) {
|
||||
<span class="spinner-border spinner-border-sm me-2" role="status" aria-hidden="true"></span>
|
||||
<span>Anmelden …</span>
|
||||
<span>@L("LoginInProgress", "Anmelden …")</span>
|
||||
} else {
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="me-2" viewBox="0 0 16 16">
|
||||
<path fill-rule="evenodd" d="M10 3.5a.5.5 0 0 0-.5-.5h-8a.5.5 0 0 0-.5.5v9a.5.5 0 0 0 .5.5h8a.5.5 0 0 0 .5-.5v-2a.5.5 0 0 1 1 0v2A1.5 1.5 0 0 1 9.5 14h-8A1.5 1.5 0 0 1 0 12.5v-9A1.5 1.5 0 0 1 1.5 2h8A1.5 1.5 0 0 1 11 3.5v2a.5.5 0 0 1-1 0v-2z"/>
|
||||
<path fill-rule="evenodd" d="M4.146 8.354a.5.5 0 0 1 0-.708l3-3a.5.5 0 1 1 .708.708L5.707 7.5H14.5a.5.5 0 0 1 0 1H5.707l2.147 2.146a.5.5 0 0 1-.708.708l-3-3z"/>
|
||||
</svg>
|
||||
<span>Anmelden</span>
|
||||
<span>@L("Login", "Anmelden")</span>
|
||||
}
|
||||
</button>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="card-footer text-center text-muted py-3 border-0 bg-transparent" style="font-size: 0.78rem;">
|
||||
Bei Problemen wenden Sie sich bitte an den Administrator.
|
||||
@L("SenderLogin_FooterHelp", "Bei Problemen wenden Sie sich bitte an den Administrator.")
|
||||
</div>
|
||||
|
||||
</div>
|
||||
@@ -167,4 +170,10 @@
|
||||
IsLoading = false;
|
||||
await InvokeAsync(StateHasChanged);
|
||||
}
|
||||
|
||||
string L(string key, string fallback)
|
||||
{
|
||||
var value = UiLocalizer[key];
|
||||
return value.ResourceNotFound ? fallback : value.Value;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user