Added flags for language change

This commit is contained in:
2025-11-07 09:31:41 +01:00
parent 46e5cd1e01
commit 6c7bad3e7f
8 changed files with 102 additions and 30 deletions

36
src/app/app.html Normal file
View File

@@ -0,0 +1,36 @@
<mat-toolbar color="primary">
<span>{{ 'APP.TITLE' | translate }}</span>
<span class="spacer"></span>
<!-- Language -->
<mat-form-field appearance="outline" style="width: 170px; margin-right: 8px;">
<mat-select [value]="lang.lang()" (selectionChange)="lang.use($event.value)">
<mat-select-trigger>
<img class="flag-icon" [src]="lang.lang() === 'de' ? '/assets/flags/de.svg' : '/assets/flags/gb.svg'"
alt="" aria-hidden="true">
<span style="margin-left: 8px;">
{{ lang.lang() === 'de' ? ('LANG.DE' | translate) : ('LANG.EN' | translate) }}
</span>
</mat-select-trigger>
<mat-option value="de">
<img class="flag-icon" src="/assets/flags/de.svg" alt="" aria-hidden="true">
<span> {{ 'LANG.DE' | translate }} </span>
</mat-option>
<mat-option value="en">
<img class="flag-icon" src="/assets/flags/gb.svg" alt="" aria-hidden="true">
<span> {{ 'LANG.EN' | translate }} </span>
</mat-option>
</mat-select>
</mat-form-field>
<!-- Theme -->
<button mat-icon-button (click)="theme.toggle()">
<mat-icon>{{ themeIcon() }}</mat-icon>
</button>
</mat-toolbar>
<main class="container app-surface">
<router-outlet />
</main>

13
src/app/app.scss Normal file
View File

@@ -0,0 +1,13 @@
.spacer { flex: 1 1 auto; }
.container { max-width: 960px; margin: 24px auto; padding: 0 16px; }
mat-form-field { --mdc-outlined-text-field-container-shape: 20px; }
.flag-icon {
width: 20px;
height: 14px;
object-fit: cover;
border-radius: 2px;
box-shadow: 0 0 0 1px rgba(0,0,0,.08) inset;
vertical-align: -2px;
}
mat-option .flag-icon { margin-right: 8px; }

View File

@@ -1,4 +1,4 @@
import { Component, computed, inject } from '@angular/core';
import {Component, computed, effect, inject} from '@angular/core';
import { RouterOutlet } from '@angular/router';
import { MatToolbarModule } from '@angular/material/toolbar';
import { MatIconModule } from '@angular/material/icon';
@@ -22,35 +22,8 @@ import {LanguageService} from './service/language.service';
FormsModule,
TranslateModule
],
template: `
<mat-toolbar color="primary">
<span>{{ 'APP.TITLE' | translate }}</span>
<span class="spacer"></span>
<!-- Sprache -->
<mat-form-field appearance="outline" style="width: 150px; margin-right: 8px;">
<mat-select [value]="lang.lang()" (selectionChange)="lang.use($event.value)">
<mat-option value="de">{{ 'LANG.DE' | translate }}</mat-option>
<mat-option value="en">{{ 'LANG.EN' | translate }}</mat-option>
</mat-select>
</mat-form-field>
<!-- Theme -->
<button mat-icon-button aria-label="Theme umschalten" (click)="theme.toggle()">
<mat-icon>{{ themeIcon() }}</mat-icon>
</button>
</mat-toolbar>
<main class="container app-surface">
<router-outlet />
</main>
<style>
.spacer { flex: 1 1 auto; }
.container { max-width: 960px; margin: 24px auto; padding: 0 16px; }
mat-form-field { --mdc-outlined-text-field-container-shape: 20px; }
</style>
`,
templateUrl: './app.html',
styleUrl: './app.scss',
})
export class App {
readonly theme = inject(ThemeService);

View File

@@ -2,4 +2,6 @@
static readonly THEME_KEY = 'theme';
static readonly LANGUAGE_KEY = 'lang';
static readonly RELOAD_ALL_LANG_LISTENER_KEY = 'language_dirty_flag';
}

View File

@@ -12,6 +12,7 @@ export class LanguageService {
constructor() {
this.translate.addLangs(['de', 'en']);
this.translate.setFallbackLang('en');
this.lang.set(this.getInitial());
this.use(this.lang());
}

View File

@@ -0,0 +1,32 @@
import { Injectable, NgZone, signal } from '@angular/core';
import {Constants} from '../constants/Constants';
@Injectable({ providedIn: 'root' })
export class ReloadService {
private readonly _reloadTick = signal(0);
readonly reloadTick = this._reloadTick.asReadonly();
private readonly _languageChangedTick = signal(0);
readonly languageChangedTick = this._languageChangedTick.asReadonly();
constructor(zone: NgZone) {
zone.runOutsideAngular(() => {
globalThis.addEventListener('storage', (e: StorageEvent) => {
this.informListeners(e, zone);
});
});
}
private informListeners(e: StorageEvent, zone: NgZone) {
if (e.key === Constants.LANGUAGE_KEY) {
zone.run(() => this._languageChangedTick.update(v => v + 1));
}
}
bumpLanguageChanged(): void {
this._reloadTick.update(v => v + 1);
localStorage.setItem(Constants.RELOAD_ALL_LANG_LISTENER_KEY, String(Date.now()));
}
}

5
src/assets/flags/de.svg Normal file
View File

@@ -0,0 +1,5 @@
<svg xmlns="http://www.w3.org/2000/svg" width="640" height="480">
<path fill="#000" d="M0 0h640v160H0z"/>
<path fill="#dd0000" d="M0 160h640v160H0z"/>
<path fill="#ffce00" d="M0 320h640v160H0z"/>
</svg>

10
src/assets/flags/gb.svg Normal file
View File

@@ -0,0 +1,10 @@
<svg xmlns="http://www.w3.org/2000/svg" width="640" height="480">
<defs><clipPath id="a"><path d="M0 0h640v480H0z"/></clipPath></defs>
<g clip-path="url(#a)">
<path fill="#012169" d="M0 0h640v480H0z"/>
<path stroke="#fff" stroke-width="60" d="M0 0l640 480M640 0L0 480"/>
<path stroke="#c8102e" stroke-width="40" d="M0 0l640 480M640 0L0 480"/>
<path fill="#fff" d="M0 192h640v96H0zM272 0h96v480h-96z"/>
<path fill="#c8102e" d="M0 208h640v64H0zM288 0h64v480h-64z"/>
</g>
</svg>