Add Language Selector component to footer
Introduced a Language Selector component (`LanguageSelector.razor`) to enable users to switch between German, English, and French. The component includes a dropdown UI with flag icons and language labels, along with logic for toggling the dropdown and changing the app's culture. Injected `IJSRuntime`, `NavigationManager`, and `CultureService` into the component to handle culture changes and navigation updates. Updated `MainLayout.razor` to include the Language Selector in the footer. Refactored footer layout in `MainLayout.razor.css` to use flexbox for better alignment and spacing. Added styles for the Language Selector in `app.css` to ensure a clean and responsive design. Integrated the `flag-icons` library in `index.html` to display flag icons for language representation.
This commit is contained in:
62
EnvelopeGenerator.ReceiverUI/Shared/LanguageSelector.razor
Normal file
62
EnvelopeGenerator.ReceiverUI/Shared/LanguageSelector.razor
Normal file
@@ -0,0 +1,62 @@
|
||||
@using System.Globalization
|
||||
@using EnvelopeGenerator.ReceiverUI.Services
|
||||
@inject IJSRuntime JSRuntime
|
||||
@inject NavigationManager Navigation
|
||||
@inject CultureService CultureService
|
||||
|
||||
<div class="language-selector">
|
||||
<button class="language-selector__trigger" @onclick="ToggleDropdown" aria-label="Select Language">
|
||||
<span class="fi fi-@GetFlagCode(CurrentCulture)"></span>
|
||||
<span class="language-selector__arrow">?</span>
|
||||
</button>
|
||||
|
||||
@if (isOpen)
|
||||
{
|
||||
<div class="language-selector__dropdown">
|
||||
<button class="language-selector__option" @onclick="@(() => ChangeLanguageAsync("de-DE"))">
|
||||
<span class="fi fi-de"></span>
|
||||
<span>Deutsch</span>
|
||||
</button>
|
||||
<button class="language-selector__option" @onclick="@(() => ChangeLanguageAsync("en-US"))">
|
||||
<span class="fi fi-us"></span>
|
||||
<span>English</span>
|
||||
</button>
|
||||
<button class="language-selector__option" @onclick="@(() => ChangeLanguageAsync("fr-FR"))">
|
||||
<span class="fi fi-fr"></span>
|
||||
<span>Français</span>
|
||||
</button>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
||||
@code {
|
||||
private bool isOpen = false;
|
||||
private string CurrentCulture => CultureInfo.CurrentCulture.Name;
|
||||
|
||||
private void ToggleDropdown()
|
||||
{
|
||||
isOpen = !isOpen;
|
||||
}
|
||||
|
||||
private async Task ChangeLanguageAsync(string culture)
|
||||
{
|
||||
if (CultureInfo.CurrentCulture.Name != culture)
|
||||
{
|
||||
await CultureService.SetCultureAsync(culture);
|
||||
Navigation.NavigateTo(Navigation.Uri, forceLoad: true);
|
||||
}
|
||||
|
||||
isOpen = false;
|
||||
}
|
||||
|
||||
private string GetFlagCode(string culture)
|
||||
{
|
||||
return culture switch
|
||||
{
|
||||
"de-DE" => "de",
|
||||
"en-US" => "us",
|
||||
"fr-FR" => "fr",
|
||||
_ => "de"
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -8,9 +8,12 @@
|
||||
</article>
|
||||
</main>
|
||||
<footer class="receiver-footer">
|
||||
<span>© SignFlow 2023-2024 <a href="https://digitaldata.works" target="_blank" rel="noopener">Digital Data GmbH</a></span>
|
||||
<span class="receiver-footer__sep">|</span>
|
||||
<a href="docs/privacy-policy.de-DE.html" target="_blank" rel="noopener">Datenschutz</a>
|
||||
<div class="receiver-footer__content">
|
||||
<span>© SignFlow 2023-2024 <a href="https://digitaldata.works" target="_blank" rel="noopener">Digital Data GmbH</a></span>
|
||||
<span class="receiver-footer__sep">|</span>
|
||||
<a href="docs/privacy-policy.de-DE.html" target="_blank" rel="noopener">Datenschutz</a>
|
||||
</div>
|
||||
<LanguageSelector />
|
||||
</footer>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -15,3 +15,18 @@ article {
|
||||
padding: 0 !important;
|
||||
margin: 0 !important;
|
||||
}
|
||||
|
||||
.receiver-footer {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 1rem 2rem;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.receiver-footer__content {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
|
||||
@@ -366,3 +366,74 @@ article {
|
||||
.receiver-footer__sep {
|
||||
opacity: 0.4;
|
||||
}
|
||||
|
||||
/* ── Language Selector (Footer) ──────────────────────────────────────────── */
|
||||
.language-selector {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.language-selector__trigger {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.4rem;
|
||||
padding: 0.25rem 0.5rem;
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
border: 1px solid rgba(255, 255, 255, 0.2);
|
||||
border-radius: 4px;
|
||||
color: rgba(255, 255, 255, 0.9);
|
||||
cursor: pointer;
|
||||
font-size: 1rem;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.language-selector__trigger:hover {
|
||||
background: rgba(255, 255, 255, 0.15);
|
||||
border-color: rgba(255, 255, 255, 0.3);
|
||||
}
|
||||
|
||||
.language-selector__arrow {
|
||||
font-size: 0.6rem;
|
||||
transition: transform 0.2s ease;
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
.language-selector__dropdown {
|
||||
position: absolute;
|
||||
bottom: calc(100% + 0.5rem);
|
||||
right: 0;
|
||||
background: white;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 6px;
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
||||
min-width: 160px;
|
||||
z-index: 1000;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.language-selector__option {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.75rem;
|
||||
width: 100%;
|
||||
padding: 0.65rem 1rem;
|
||||
background: white;
|
||||
border: none;
|
||||
color: #333;
|
||||
cursor: pointer;
|
||||
font-size: 0.9rem;
|
||||
transition: background-color 0.2s ease;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.language-selector__option:hover {
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
|
||||
.language-selector__option .fi {
|
||||
font-size: 1.25rem;
|
||||
}
|
||||
|
||||
.language-selector__option span:last-child {
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
<link href="css/bootstrap/bootstrap.min.css" rel="stylesheet" />
|
||||
<link href="EnvelopeGenerator.ReceiverUI.styles.css" rel="stylesheet" />
|
||||
<link href="css/app.css" rel="stylesheet" />
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/lipis/flag-icons@7.2.3/css/flag-icons.min.css" />
|
||||
<style type="text/css">
|
||||
.splash-screen {
|
||||
display: flex;
|
||||
|
||||
Reference in New Issue
Block a user