feat(core): implement translation

This commit is contained in:
2025-08-27 17:05:51 +02:00
parent 8f1cf01f67
commit 890257c77e
12 changed files with 234 additions and 19 deletions

View File

@@ -1,4 +1,4 @@
import { ApplicationConfig, LOCALE_ID, provideBrowserGlobalErrorListeners, provideZoneChangeDetection } from '@angular/core';
import { ApplicationConfig, LOCALE_ID, provideAppInitializer, provideBrowserGlobalErrorListeners, provideZoneChangeDetection } from '@angular/core';
import { provideRouter, withComponentInputBinding } from '@angular/router';
import { providePrimeNG } from 'primeng/config';
@@ -6,9 +6,14 @@ import { Theme } from '../../public/theme/theme';
import { routes } from './app.routes';
import { registerLocaleData } from '@angular/common';
import localeDeAt from '@angular/common/locales/de-AT';
import localeDeAt from '@angular/common/locales/de';
import { germanTranslation } from './core/config/translations';
import {provideTranslateService} from "@ngx-translate/core";
import {provideTranslateHttpLoader} from "@ngx-translate/http-loader";
import { provideHttpClient } from '@angular/common/http';
import { initializeTranslations } from './core/config/translation-init';
registerLocaleData(localeDeAt);
export const appConfig: ApplicationConfig = {
@@ -16,6 +21,7 @@ export const appConfig: ApplicationConfig = {
provideBrowserGlobalErrorListeners(),
provideZoneChangeDetection({ eventCoalescing: true }),
provideRouter(routes, withComponentInputBinding()),
provideHttpClient(),
{ provide: LOCALE_ID, useValue: 'de-AT' },
providePrimeNG({
theme: {
@@ -31,5 +37,12 @@ export const appConfig: ApplicationConfig = {
},
translation: germanTranslation, // TODO: dynamic - selected language
}),
provideTranslateService({
loader: provideTranslateHttpLoader({
prefix: 'i18n/',
suffix: '.json'
}),
}),
provideAppInitializer(initializeTranslations),
],
};

View File

@@ -1,4 +1,4 @@
import { Component } from '@angular/core';
import { Component, inject } from '@angular/core';
import { RouterOutlet } from '@angular/router';
import { ThemeSwitchService } from './core/service/theme-switch.service';
import { Background } from "./core/components/background/background";
@@ -11,7 +11,9 @@ import { NavigationBar } from "./core/components/navigation-bar/navigation-bar";
})
export class App {
constructor(private themeSwitchService: ThemeSwitchService) {
private themeSwitchService = inject(ThemeSwitchService);
constructor() {
this.themeSwitchService.initialize();
}

View File

@@ -1,4 +1,4 @@
import { Component, ElementRef, HostListener, ViewChild } from '@angular/core';
import { Component, ElementRef, HostListener, inject, ViewChild } from '@angular/core';
import { ThemeSwitchService } from '../../service/theme-switch.service';
@Component({
@@ -9,9 +9,9 @@ import { ThemeSwitchService } from '../../service/theme-switch.service';
})
export class Background {
constructor(
private themeSwitchService: ThemeSwitchService,
) {}
private themeSwitchService = inject(ThemeSwitchService);
constructor() {}
@ViewChild('canvas', { static: true }) canvasRef!: ElementRef<HTMLCanvasElement>;

View File

@@ -7,10 +7,10 @@
<ul>
<li class="relative list-none flex flex-row gap-3">
<a routerLink="/" routerLinkActive="active" [routerLinkActiveOptions]="{ exact: true }">
Home
{{ 'common.home' | translate }}
</a>
<a routerLink="/about" routerLinkActive="active">
About
{{ 'common.about' | translate }}
</a>
</li>
</ul>
@@ -26,7 +26,9 @@
<fa-icon [icon]="faSun"/>
</div>
}
<div class="language size-[50px]">EN</div>
<div class="language size-[50px]" (click)="toggleLanguage()">
{{ translateService.getCurrentLang().toUpperCase() }}
</div>
</div>
</nav>

View File

@@ -1,28 +1,34 @@
import { Component } from '@angular/core';
import { Component, inject } from '@angular/core';
import { Router, RouterLink, RouterLinkActive } from '@angular/router';
import { FontAwesomeModule } from '@fortawesome/angular-fontawesome';
import { faMoon } from '@fortawesome/free-solid-svg-icons';
import { faSun } from '@fortawesome/free-solid-svg-icons';
import { ThemeSwitchService } from '../../service/theme-switch.service';
import { Card } from 'primeng/card';
import { TranslatePipe, TranslateService } from '@ngx-translate/core';
@Component({
selector: 'app-navigation-bar',
imports: [FontAwesomeModule, RouterLink, RouterLinkActive, Card ],
imports: [FontAwesomeModule, RouterLink, RouterLinkActive, Card, TranslatePipe],
templateUrl: './navigation-bar.html',
styleUrl: './navigation-bar.scss'
})
export class NavigationBar {
constructor(
private router: Router,
protected themeSwitchService: ThemeSwitchService,
) {}
private router = inject(Router);
protected themeSwitchService = inject(ThemeSwitchService);
protected translateService = inject(TranslateService);
constructor() {}
isActive(route: string): boolean {
return this.router.url === route;
}
toggleLanguage(): void {
this.translateService.use(this.translateService.getCurrentLang() === 'de' ? 'en' : 'de');
}
faMoon = faMoon;
faSun = faSun;

View File

@@ -0,0 +1,22 @@
import { inject } from '@angular/core';
import { TranslateService } from '@ngx-translate/core';
export function initializeTranslations() {
const translate = inject(TranslateService);
const availableLangs = ['de', 'at', 'en'];
translate.addLangs(availableLangs);
const defaultLang = 'de';
let langToUse = translate.getBrowserLang() ?? defaultLang;
langToUse = availableLangs.includes(langToUse) ? langToUse : defaultLang;
translate.setFallbackLang(defaultLang);
translate.use(langToUse);
document.documentElement.lang = langToUse;
translate.onLangChange.subscribe((event) => {
document.documentElement.lang = event.lang;
});
}

View File

@@ -69,7 +69,6 @@ export const germanTranslation: Translation = {
emptySelectionMessage: 'Kein Element ausgewählt',
emptySearchMessage: 'Keine Ergebnisse gefunden',
emptyMessage: 'Keine Optionen verfügbar',
aria: {
trueLabel: 'Wahr',
falseLabel: 'Falsch',
@@ -121,3 +120,132 @@ export const germanTranslation: Translation = {
rotateLeft: 'Nach links drehen'
}
};
export const austrianTranslation: Translation = {
...germanTranslation,
monthNames: [
'Jänner','Februar','März','April','Mai','Juni',
'Juli','August','September','Oktober','November','Dezember'
],
};
export const englishTranslation: Translation = {
startsWith: 'Starts with',
contains: 'Contains',
notContains: 'Does not contain',
endsWith: 'Ends with',
equals: 'Equals',
notEquals: 'Not equals',
noFilter: 'No Filter',
lt: 'Less than',
lte: 'Less than or equal to',
gt: 'Greater than',
gte: 'Greater than or equal to',
dateIs: 'Date is',
dateIsNot: 'Date is not',
dateBefore: 'Date is before',
dateAfter: 'Date is after',
clear: 'Clear',
apply: 'Apply',
matchAll: 'Match All',
matchAny: 'Match Any',
addRule: 'Add Rule',
removeRule: 'Remove Rule',
accept: 'Yes',
reject: 'No',
choose: 'Choose',
upload: 'Upload',
cancel: 'Cancel',
completed: 'Completed',
pending: 'Pending',
fileSizeTypes: ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'],
dayNames: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'],
dayNamesShort: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
dayNamesMin: ['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa'],
monthNames: [
'January','February','March','April','May','June',
'July','August','September','October','November','December'
],
monthNamesShort: ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'],
chooseYear: 'Choose Year',
chooseMonth: 'Choose Month',
chooseDate: 'Choose Date',
prevDecade: 'Previous Decade',
nextDecade: 'Next Decade',
prevYear: 'Previous Year',
nextYear: 'Next Year',
prevMonth: 'Previous Month',
nextMonth: 'Next Month',
prevHour: 'Previous Hour',
nextHour: 'Next Hour',
prevMinute: 'Previous Minute',
nextMinute: 'Next Minute',
prevSecond: 'Previous Second',
nextSecond: 'Next Second',
am: 'AM',
pm: 'PM',
today: 'Today',
weekHeader: 'Wk',
firstDayOfWeek: 0,
dateFormat: 'mm/dd/yy',
weak: 'Weak',
medium: 'Medium',
strong: 'Strong',
passwordPrompt: 'Enter a password',
emptyFilterMessage: 'No results found',
searchMessage: '{0} results available',
selectionMessage: '{0} items selected',
emptySelectionMessage: 'No item selected',
emptySearchMessage: 'No results found',
emptyMessage: 'No options available',
aria: {
trueLabel: 'True',
falseLabel: 'False',
nullLabel: 'Not selected',
star: '1 Star',
stars: '{star} Stars',
selectAll: 'Select all items',
unselectAll: 'Unselect all items',
close: 'Close',
previous: 'Previous',
next: 'Next',
navigation: 'Navigation',
scrollTop: 'Scroll to top',
moveTop: 'Move to top',
moveUp: 'Move up',
moveDown: 'Move down',
moveBottom: 'Move to bottom',
moveToTarget: 'Move to target',
moveToSource: 'Move to source',
moveAllToTarget: 'Move all to target',
moveAllToSource: 'Move all to source',
pageLabel: '{page}',
firstPageLabel: 'First Page',
lastPageLabel: 'Last Page',
nextPageLabel: 'Next Page',
prevPageLabel: 'Previous Page',
rowsPerPageLabel: 'Rows per page',
jumpToPageDropdownLabel: 'Select page',
jumpToPageInputLabel: 'Enter page',
selectRow: 'Row selected',
unselectRow: 'Row unselected',
expandRow: 'Row expanded',
collapseRow: 'Row collapsed',
showFilterMenu: 'Show filter menu',
hideFilterMenu: 'Hide filter menu',
filterOperator: 'Filter operator',
filterConstraint: 'Filter constraint',
editRow: 'Edit row',
saveEdit: 'Save edit',
cancelEdit: 'Cancel edit',
listView: 'List view',
gridView: 'Grid view',
slide: 'Slide',
slideNumber: '{slideNumber}',
zoomImage: 'Zoom image',
zoomIn: 'Zoom in',
zoomOut: 'Zoom out',
rotateRight: 'Rotate right',
rotateLeft: 'Rotate left'
}
};