feat(MiniCalendarWidget): enhance localization and refactor calendar logic
All checks were successful
Deploy to production / deploy (push) Successful in 1m55s

- Updated the MiniCalendarWidget to utilize localized strings for month names, weekdays, and loading messages, improving user experience across multiple languages.
- Refactored the calendar logic to use constants for month keys and weekday order, ensuring consistency with backend configurations.
- Added new translation entries for the mini widget in Cebuano, German, English, Spanish, and French, enhancing accessibility for users.
This commit is contained in:
Torsten Schulz (local)
2026-04-17 16:44:27 +02:00
parent 14881803df
commit 7732498875
12 changed files with 88 additions and 25 deletions

View File

@@ -4,7 +4,7 @@
{{ monthName }} {{ calendarData.year }}
</div>
<div class="mini-calendar-weekdays">
<span v-for="day in weekdays" :key="day" class="mini-calendar-weekday">{{ day }}</span>
<span v-for="day in weekdayLabels" :key="day" class="mini-calendar-weekday">{{ day }}</span>
</div>
<div class="mini-calendar-days">
<span
@@ -22,37 +22,36 @@
</span>
</div>
<router-link to="/personal/calendar" class="mini-calendar-link">
Zum Kalender
{{ $t('personal.calendar.miniWidget.linkToCalendar') }}
</router-link>
</div>
<div v-else class="mini-calendar-empty">
Kalender wird geladen...
{{ $t('personal.calendar.miniWidget.loading') }}
</div>
</template>
<script>
const MONTH_NAMES = [
'Januar', 'Februar', 'März', 'April', 'Mai', 'Juni',
'Juli', 'August', 'September', 'Oktober', 'November', 'Dezember'
];
/** Order matches personal.calendar.months keys (jan … dec). */
const CAL_MONTH_KEYS = ['jan', 'feb', 'mar', 'apr', 'may', 'jun', 'jul', 'aug', 'sep', 'oct', 'nov', 'dec'];
/** MonSun; matches backend firstDayOfWeek (Monday = 1). */
const WEEKDAY_ORDER = ['mon', 'tue', 'wed', 'thu', 'fri', 'sat', 'sun'];
export default {
name: 'MiniCalendarWidget',
props: {
data: { type: Object, default: null }
},
data() {
return {
weekdays: ['Mo', 'Di', 'Mi', 'Do', 'Fr', 'Sa', 'So']
};
},
computed: {
calendarData() {
return this.data;
},
monthName() {
if (!this.calendarData) return '';
return MONTH_NAMES[this.calendarData.month - 1];
const key = CAL_MONTH_KEYS[this.calendarData.month - 1];
return key ? this.$t(`personal.calendar.months.${key}`) : '';
},
weekdayLabels() {
return WEEKDAY_ORDER.map((k) => this.$t(`personal.calendar.weekdays.${k}`));
},
calendarDays() {
if (!this.calendarData) return [];

View File

@@ -49,6 +49,10 @@
"nov": "Nobyembre",
"dec": "Disyembre"
},
"miniWidget": {
"loading": "Nag-load sa kalendaryo…",
"linkToCalendar": "Adto sa kalendaryo →"
},
"categories": {
"personal": "Personal",
"work": "Trabaho",

View File

@@ -198,10 +198,17 @@
"linkSignup": "Paghimo og account sa OpenAI (bag-ong bintana)",
"linkApiKeys": "Pagdumala sa API keys sa OpenAI (bag-ong bintana)",
"enabled": "Tugoti ang paggamit para sa mga bahin sa pinulongan",
"baseUrl": "API base URL (opsyonal)",
"presetsTitle": "Paspas nga sugod (1 ka klik)",
"presetOllama": "Libre sa lokal (Ollama)",
"presetOpenAi": "Sukdanan (sundon ang OpenAI)",
"presetHintBefore": "Ang preset sa Ollama nagbutang og ",
"presetHintBetween": " ug ",
"presetHintAfter": ". Walay gikinahanglan nga API key.",
"baseUrl": "URL sa API (opsyonal)",
"baseUrlPlaceholder": "Walay sulod = default (OpenAI). Para sa Ollama pananglitan http://127.0.0.1:11434/v1",
"model": "Ngalan sa modelo",
"apiKey": "API key",
"modelPlaceholder": "gpt-4o-mini",
"apiKey": "Yabi sa API",
"apiKeyHint": "Biyai nga walay sulod aron mapadayon ang natipig nga yabi.",
"apiKeyPlaceholderNew": "I-paste ang bag-ong yabi",
"apiKeyPlaceholderHasKey": "Ang natipig nagtapos sa …{last4} — walay sulod = padayon",

View File

@@ -49,6 +49,10 @@
"nov": "November",
"dec": "Dezember"
},
"miniWidget": {
"loading": "Kalender wird geladen…",
"linkToCalendar": "Zum Kalender →"
},
"categories": {
"personal": "Persönlich",
"work": "Arbeit",

View File

@@ -198,9 +198,16 @@
"linkSignup": "Konto bei OpenAI anlegen (neues Fenster)",
"linkApiKeys": "API-Keys bei OpenAI verwalten (neues Fenster)",
"enabled": "Nutzung für Sprachfunktionen erlauben",
"presetsTitle": "Schnellstart (1-Klick)",
"presetOllama": "Kostenlos lokal (Ollama)",
"presetOpenAi": "Standard (OpenAI-kompatibel)",
"presetHintBefore": "Der Ollama-Preset setzt ",
"presetHintBetween": " und ",
"presetHintAfter": ". Kein API-Key erforderlich.",
"baseUrl": "API-Basis-URL (optional)",
"baseUrlPlaceholder": "Leer = Standard (OpenAI). Für Ollama z. B. http://127.0.0.1:11434/v1",
"model": "Modellname",
"modelPlaceholder": "gpt-4o-mini",
"apiKey": "API-Schlüssel",
"apiKeyHint": "Leer lassen, um den gespeicherten Schlüssel beizubehalten.",
"apiKeyPlaceholderNew": "Neuen Schlüssel einfügen",

View File

@@ -49,6 +49,10 @@
"nov": "November",
"dec": "December"
},
"miniWidget": {
"loading": "Loading calendar…",
"linkToCalendar": "Go to calendar →"
},
"categories": {
"personal": "Personal",
"work": "Work",

View File

@@ -198,9 +198,16 @@
"linkSignup": "Create an OpenAI account (new tab)",
"linkApiKeys": "Manage OpenAI API keys (new tab)",
"enabled": "Allow use for language features",
"presetsTitle": "Quick start (1-click)",
"presetOllama": "Free local (Ollama)",
"presetOpenAi": "Default (OpenAI-compatible)",
"presetHintBefore": "The Ollama preset sets ",
"presetHintBetween": " and ",
"presetHintAfter": ". No API key required.",
"baseUrl": "API base URL (optional)",
"baseUrlPlaceholder": "Empty = default (OpenAI). For Ollama e.g. http://127.0.0.1:11434/v1",
"model": "Model name",
"modelPlaceholder": "gpt-4o-mini",
"apiKey": "API key",
"apiKeyHint": "Leave empty to keep the stored key.",
"apiKeyPlaceholderNew": "Paste new key",

View File

@@ -49,6 +49,10 @@
"nov": "Noviembre",
"dec": "Diciembre"
},
"miniWidget": {
"loading": "Cargando calendario…",
"linkToCalendar": "Ir al calendario →"
},
"categories": {
"personal": "Personal",
"work": "Trabajo",

View File

@@ -198,9 +198,16 @@
"linkSignup": "Crear cuenta en OpenAI (nueva pestaña)",
"linkApiKeys": "Gestionar claves API de OpenAI (nueva pestaña)",
"enabled": "Permitir uso para funciones de idioma",
"presetsTitle": "Inicio rápido (1 clic)",
"presetOllama": "Gratis en local (Ollama)",
"presetOpenAi": "Predeterminado (compatible con OpenAI)",
"presetHintBefore": "El preset de Ollama establece ",
"presetHintBetween": " y ",
"presetHintAfter": ". No hace falta clave API.",
"baseUrl": "URL base de la API (opcional)",
"baseUrlPlaceholder": "Vacío = predeterminado (OpenAI). Para Ollama p. ej. http://127.0.0.1:11434/v1",
"model": "Nombre del modelo",
"modelPlaceholder": "gpt-4o-mini",
"apiKey": "Clave API",
"apiKeyHint": "Déjalo vacío para conservar la clave guardada.",
"apiKeyPlaceholderNew": "Pegar nueva clave",

View File

@@ -49,6 +49,10 @@
"nov": "Novembre",
"dec": "Décembre"
},
"miniWidget": {
"loading": "Chargement du calendrier…",
"linkToCalendar": "Ouvrir le calendrier →"
},
"categories": {
"personal": "Personnel",
"work": "Travail",

View File

@@ -198,9 +198,16 @@
"linkSignup": "Créer un compte avec OpenAI (nouvelle fenêtre)",
"linkApiKeys": "Gérer les clés API chez OpenAI (nouvelle fenêtre)",
"enabled": "Autoriser l'utilisation des fonctions vocales",
"presetsTitle": "Démarrage rapide (1 clic)",
"presetOllama": "Gratuit en local (Ollama)",
"presetOpenAi": "Par défaut (compatible OpenAI)",
"presetHintBefore": "Le préréglage Ollama définit ",
"presetHintBetween": " et ",
"presetHintAfter": ". Aucune clé API requise.",
"baseUrl": "URL de base de l'API (facultatif)",
"baseUrlPlaceholder": "Vide = Par défaut (OpenAI). Pour Ollama par ex. Par ex. http://127.0.0.1:11434/v1",
"model": "Nom du modèle",
"modelPlaceholder": "gpt-4o-mini",
"apiKey": "Clé API",
"apiKeyHint": "Laissez vide pour conserver la clé enregistrée.",
"apiKeyPlaceholderNew": "Insérer une nouvelle clé",

View File

@@ -29,18 +29,17 @@
</label>
<div class="language-assistant-settings__presets">
<p class="language-assistant-settings__presets-title">Schnellstart (1-Klick)</p>
<p class="language-assistant-settings__presets-title">{{ $t('settings.languageAssistant.presetsTitle') }}</p>
<div class="language-assistant-settings__preset-actions">
<button type="button" class="preset-btn" @click="applyOllamaPreset">
Kostenlos lokal (Ollama)
{{ $t('settings.languageAssistant.presetOllama') }}
</button>
<button type="button" class="preset-btn preset-btn--secondary" @click="applyOpenAiPreset">
Standard (OpenAI-kompatibel)
{{ $t('settings.languageAssistant.presetOpenAi') }}
</button>
</div>
<p class="language-assistant-settings__preset-hint">
Der Ollama-Preset setzt <code>http://127.0.0.1:11434/v1</code> und
<code>qwen2.5:3b-instruct</code>. Kein API-Key erforderlich.
{{ $t('settings.languageAssistant.presetHintBefore') }}<code>{{ ollamaPresetBaseUrl }}</code>{{ $t('settings.languageAssistant.presetHintBetween') }}<code>{{ ollamaPresetModel }}</code>{{ $t('settings.languageAssistant.presetHintAfter') }}
</p>
</div>
@@ -56,7 +55,7 @@
</label>
<label class="language-assistant-settings__field">
<span>{{ $t('settings.languageAssistant.model') }}</span>
<input v-model="form.model" type="text" autocomplete="off" placeholder="gpt-4o-mini" />
<input v-model="form.model" type="text" autocomplete="off" :placeholder="$t('settings.languageAssistant.modelPlaceholder')" />
</label>
<label class="language-assistant-settings__field language-assistant-settings__field--full">
<span>{{ $t('settings.languageAssistant.apiKey') }}</span>
@@ -90,6 +89,10 @@ import apiClient from '@/utils/axios.js';
import { mapGetters } from 'vuex';
import { showApiError, showError, showSuccess } from '@/utils/feedback.js';
const OLLAMA_DEFAULT_BASE_URL = 'http://127.0.0.1:11434/v1';
const OLLAMA_DEFAULT_MODEL = 'qwen2.5:3b-instruct';
const DEFAULT_OPENAI_MODEL = 'gpt-4o-mini';
export default {
name: 'LanguageAssistantSettingsView',
data() {
@@ -110,6 +113,12 @@ export default {
},
computed: {
...mapGetters(['user']),
ollamaPresetBaseUrl() {
return OLLAMA_DEFAULT_BASE_URL;
},
ollamaPresetModel() {
return OLLAMA_DEFAULT_MODEL;
},
apiKeyPlaceholder() {
if (this.form.clearKey) {
return this.$t('settings.languageAssistant.apiKeyPlaceholderClear');
@@ -136,15 +145,15 @@ export default {
methods: {
applyOllamaPreset() {
this.form.enabled = true;
this.form.baseUrl = 'http://127.0.0.1:11434/v1';
this.form.model = 'qwen2.5:3b-instruct';
this.form.baseUrl = OLLAMA_DEFAULT_BASE_URL;
this.form.model = OLLAMA_DEFAULT_MODEL;
this.form.apiKey = '';
this.form.clearKey = false;
},
applyOpenAiPreset() {
this.form.enabled = true;
this.form.baseUrl = '';
this.form.model = 'gpt-4o-mini';
this.form.model = DEFAULT_OPENAI_MODEL;
this.form.clearKey = false;
},
async load() {
@@ -153,7 +162,7 @@ export default {
const { data } = await apiClient.get('/api/settings/llm');
this.form.enabled = data.enabled !== false;
this.form.baseUrl = data.baseUrl || '';
this.form.model = data.model || 'gpt-4o-mini';
this.form.model = data.model || DEFAULT_OPENAI_MODEL;
this.hasKey = data.hasKey;
this.keyLast4 = data.keyLast4;
this.keyStatus = data.keyStatus || (data.hasKey ? 'stored' : 'missing');