feat(localization): expand language support and enhance UI for user settings
All checks were successful
Deploy to production / deploy (push) Successful in 3m0s
All checks were successful
Deploy to production / deploy (push) Successful in 3m0s
- Added support for additional UI locales including Cebuano and Spanish, improving accessibility for a broader user base. - Updated language selection components in the AppHeader and SettingsWidget to reflect new language options, enhancing user experience. - Enhanced localization of various UI elements across components, ensuring consistent language representation and improved user engagement. - Implemented logic to synchronize user language preferences with backend settings, providing a seamless experience when changing languages.
This commit is contained in:
@@ -2,10 +2,10 @@
|
||||
<div class="home-logged-in">
|
||||
<section class="dashboard-hero surface-card">
|
||||
<div class="dashboard-hero__copy">
|
||||
<span class="dashboard-kicker">Dein Bereich</span>
|
||||
<h1>Willkommen zurück!</h1>
|
||||
<span class="dashboard-kicker">{{ $t('home.dashboard.kicker') }}</span>
|
||||
<h1>{{ $t('home.dashboard.title') }}</h1>
|
||||
<p class="dashboard-subtitle">
|
||||
Dein persönlicher Einstieg in Community, Termine, Falukant und laufende Aktivitäten.
|
||||
{{ $t('home.dashboard.subtitle') }}
|
||||
</p>
|
||||
</div>
|
||||
<div class="dashboard-toolbar surface-card">
|
||||
@@ -15,7 +15,7 @@
|
||||
class="btn-edit"
|
||||
@click="editMode = true"
|
||||
>
|
||||
Dashboard bearbeiten
|
||||
{{ $t('home.dashboard.edit') }}
|
||||
</button>
|
||||
<template v-else>
|
||||
<div class="widget-add-row">
|
||||
@@ -24,7 +24,7 @@
|
||||
class="widget-type-select"
|
||||
@change="onSelectWidgetType"
|
||||
>
|
||||
<option value="">+ Widget hinzufügen …</option>
|
||||
<option value="">{{ $t('home.dashboard.addWidget') }}</option>
|
||||
<option
|
||||
v-for="wt in widgetTypeOptions"
|
||||
:key="wt.id"
|
||||
@@ -39,11 +39,11 @@
|
||||
class="btn-add-again"
|
||||
@click="addSameWidgetType"
|
||||
>
|
||||
Nochmal hinzufügen
|
||||
{{ $t('home.dashboard.addAgain') }}
|
||||
</button>
|
||||
</div>
|
||||
<button type="button" class="btn-done" @click="doneEditing">
|
||||
Fertig
|
||||
{{ $t('home.dashboard.done') }}
|
||||
</button>
|
||||
</template>
|
||||
</div>
|
||||
@@ -51,19 +51,19 @@
|
||||
|
||||
<section class="dashboard-overview">
|
||||
<article class="overview-card surface-card">
|
||||
<span class="overview-card__label">Aktive Widgets</span>
|
||||
<span class="overview-card__label">{{ $t('home.dashboard.overview.activeWidgetsLabel') }}</span>
|
||||
<strong>{{ widgets.length }}</strong>
|
||||
<p>Dein Dashboard ist modular aufgebaut und kann jederzeit umsortiert werden.</p>
|
||||
<p>{{ $t('home.dashboard.overview.activeWidgetsText') }}</p>
|
||||
</article>
|
||||
<article class="overview-card surface-card">
|
||||
<span class="overview-card__label">Verfügbare Module</span>
|
||||
<span class="overview-card__label">{{ $t('home.dashboard.overview.availableModulesLabel') }}</span>
|
||||
<strong>{{ widgetTypeOptions.length }}</strong>
|
||||
<p>Du kannst Community-, Kalender-, News- und Falukant-Module kombinieren.</p>
|
||||
<p>{{ $t('home.dashboard.overview.availableModulesText') }}</p>
|
||||
</article>
|
||||
<article class="overview-card surface-card">
|
||||
<span class="overview-card__label">Bearbeitungsmodus</span>
|
||||
<strong>{{ editMode ? 'Aktiv' : 'Aus' }}</strong>
|
||||
<p>{{ editMode ? 'Widgets können gerade ergänzt und angepasst werden.' : 'Inhalte bleiben fokussiert und ruhig lesbar.' }}</p>
|
||||
<span class="overview-card__label">{{ $t('home.dashboard.overview.editModeLabel') }}</span>
|
||||
<strong>{{ editMode ? $t('home.dashboard.overview.editModeActive') : $t('home.dashboard.overview.editModeInactive') }}</strong>
|
||||
<p>{{ editMode ? $t('home.dashboard.overview.editModeActiveText') : $t('home.dashboard.overview.editModeInactiveText') }}</p>
|
||||
</article>
|
||||
</section>
|
||||
|
||||
@@ -85,8 +85,8 @@
|
||||
>
|
||||
<div class="dashboard-shell__header">
|
||||
<div>
|
||||
<h2>Deine Übersicht</h2>
|
||||
<p>Widgets lassen sich verschieben und im Bearbeitungsmodus anpassen.</p>
|
||||
<h2>{{ $t('home.dashboard.sectionTitle') }}</h2>
|
||||
<p>{{ $t('home.dashboard.sectionIntro') }}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
@@ -120,17 +120,17 @@
|
||||
<input
|
||||
v-model="w.title"
|
||||
type="text"
|
||||
placeholder="Titel"
|
||||
:placeholder="$t('home.dashboard.widgetTitlePlaceholder')"
|
||||
class="widget-edit-input"
|
||||
/>
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
class="btn-remove"
|
||||
title="Widget entfernen"
|
||||
:title="$t('home.dashboard.removeWidget')"
|
||||
@click="removeWidget(index)"
|
||||
>
|
||||
Entfernen
|
||||
{{ $t('home.dashboard.remove') }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -139,7 +139,7 @@
|
||||
</div>
|
||||
|
||||
<div v-if="widgets.length === 0 && !loading" class="dashboard-empty">
|
||||
<p>Noch keine Widgets. Klicke auf „Dashboard bearbeiten“ und dann „+ Widget hinzufügen“.</p>
|
||||
<p>{{ $t('home.dashboard.empty') }}</p>
|
||||
</div>
|
||||
|
||||
<div class="actions">
|
||||
@@ -164,7 +164,7 @@ export default {
|
||||
computed: {
|
||||
widgetTypeOptions() {
|
||||
if (this.availableWidgets.length > 0) return this.availableWidgets;
|
||||
return [{ id: 'default', label: 'Termine', endpoint: '/api/termine' }];
|
||||
return [{ id: 'default', label: this.$t('home.dashboard.defaultAppointmentsWidget'), endpoint: '/api/termine' }];
|
||||
}
|
||||
},
|
||||
data() {
|
||||
@@ -185,6 +185,41 @@ export default {
|
||||
this.loadAvailableWidgets();
|
||||
},
|
||||
methods: {
|
||||
getLocalizedWidgetLabel(endpoint, fallbackLabel = '') {
|
||||
const key = {
|
||||
'/api/termine': 'home.dashboard.widgetLabels.appointments',
|
||||
'/api/falukant/dashboard-widget': 'home.dashboard.widgetLabels.falukant',
|
||||
'/api/news': 'home.dashboard.widgetLabels.news',
|
||||
'/api/calendar/widget/birthdays': 'home.dashboard.widgetLabels.birthdays',
|
||||
'/api/calendar/widget/upcoming': 'home.dashboard.widgetLabels.upcoming',
|
||||
'/api/calendar/widget/mini': 'home.dashboard.widgetLabels.calendar'
|
||||
}[endpoint];
|
||||
return key ? this.$t(key) : fallbackLabel;
|
||||
},
|
||||
normalizeWidgetType(widgetType) {
|
||||
return {
|
||||
...widgetType,
|
||||
label: this.getLocalizedWidgetLabel(widgetType?.endpoint, widgetType?.label || '')
|
||||
};
|
||||
},
|
||||
normalizeWidgetConfig(widget) {
|
||||
const localizedLabel = this.getLocalizedWidgetLabel(widget?.endpoint, '');
|
||||
const title = String(widget?.title || '').trim();
|
||||
const knownDefaultLabels = [
|
||||
'Termine',
|
||||
'Falukant',
|
||||
'News',
|
||||
'Geburtstage',
|
||||
'Nächste Termine',
|
||||
'Kalender'
|
||||
];
|
||||
return {
|
||||
...widget,
|
||||
title: !title || knownDefaultLabels.includes(title)
|
||||
? (localizedLabel || title)
|
||||
: title
|
||||
};
|
||||
},
|
||||
/** Endpoint aus Widget-Typ (anhand gespeichertem endpoint gematcht), sonst w.endpoint. */
|
||||
effectiveEndpoint(w) {
|
||||
if (!w?.endpoint) return '';
|
||||
@@ -204,7 +239,7 @@ export default {
|
||||
async loadAvailableWidgets() {
|
||||
try {
|
||||
const { data } = await apiClient.get('/api/dashboard/widgets');
|
||||
this.availableWidgets = Array.isArray(data) ? data : [];
|
||||
this.availableWidgets = Array.isArray(data) ? data.map(widget => this.normalizeWidgetType(widget)) : [];
|
||||
} catch (e) {
|
||||
this.availableWidgets = [];
|
||||
}
|
||||
@@ -216,14 +251,14 @@ export default {
|
||||
const { data } = await apiClient.get('/api/dashboard/config');
|
||||
let list = Array.isArray(data?.widgets) ? [...data.widgets] : [];
|
||||
if (list.length === 0) {
|
||||
list = [{ id: generateId(), title: 'Termine', endpoint: '/api/termine' }];
|
||||
list = [{ id: generateId(), title: this.$t('home.dashboard.defaultAppointmentsWidget'), endpoint: '/api/termine' }];
|
||||
this.widgets = list;
|
||||
await this.saveConfig();
|
||||
} else {
|
||||
this.widgets = list;
|
||||
this.widgets = list.map(widget => this.normalizeWidgetConfig(widget));
|
||||
}
|
||||
} catch (e) {
|
||||
this.loadError = e.response?.data?.error || e.message || 'Dashboard konnte nicht geladen werden.';
|
||||
this.loadError = e.response?.data?.error || e.message || this.$t('home.dashboard.loadError');
|
||||
} finally {
|
||||
this.loading = false;
|
||||
}
|
||||
@@ -239,7 +274,7 @@ export default {
|
||||
await apiClient.put('/api/dashboard/config', { widgets: payload });
|
||||
} catch (e) {
|
||||
console.error('Dashboard speichern fehlgeschlagen:', e);
|
||||
this.saveError = e.response?.data?.error || e.message || 'Dashboard konnte nicht gespeichert werden.';
|
||||
this.saveError = e.response?.data?.error || e.message || this.$t('home.dashboard.saveError');
|
||||
}
|
||||
},
|
||||
addWidgetFromType(wt) {
|
||||
|
||||
Reference in New Issue
Block a user