Enhance notification display and localization in MessagesDialog component

- Updated the MessagesDialog component to display notifications with titles and descriptions, improving clarity and user experience.
- Enhanced the formatBody method to support new notification structures, including extraction and formatting of parameters for better message presentation.
- Added a new formatParams method to handle various parameter types, ensuring accurate representation of values in notifications.
- Updated localization files in both German and English to include structured titles and descriptions for random events, enriching the user experience with detailed information.
This commit is contained in:
Torsten Schulz (local)
2025-12-08 14:42:17 +01:00
parent bcb0b01324
commit 791314bef2
3 changed files with 238 additions and 25 deletions

View File

@@ -11,7 +11,10 @@
> >
<ul class="messages"> <ul class="messages">
<li v-for="n in messages" :key="n.id" :class="{ unread: !n.shown }"> <li v-for="n in messages" :key="n.id" :class="{ unread: !n.shown }">
<div class="body">{{ formatBody(n) }}</div> <div class="body">
<div v-if="formatBody(n).title" class="notification-title">{{ formatBody(n).title }}</div>
<div class="notification-description">{{ formatBody(n).description || formatBody(n) }}</div>
</div>
<div class="footer"> <div class="footer">
<span>{{ formatDate(n.createdAt) }}</span> <span>{{ formatDate(n.createdAt) }}</span>
</div> </div>
@@ -118,11 +121,21 @@ export default {
} catch { return dt; } } catch { return dt; }
}, },
formatBody(n) { formatBody(n) {
// Wenn die Notification bereits title und description hat (z.B. von WebSocket Events)
if (n.title && n.description) {
// Parameter aus effects oder anderen Feldern extrahieren
const params = this.extractParams(n);
return {
title: this.interpolateString(n.title, params),
description: this.interpolateString(n.description, params)
};
}
let raw = n.tr || ''; let raw = n.tr || '';
let key = raw; let key = raw;
let params = {}; let params = {};
// 1) JSON-Format unterstützen: {"tr":"production.overproduction","value":21} // 1) JSON-Format unterstützen: {"tr":"random_event.windfall","amount":1000,"characterName":"Max"}
if (typeof raw === 'string') { if (typeof raw === 'string') {
const trimmed = raw.trim(); const trimmed = raw.trim();
if (trimmed.startsWith('{') && trimmed.endsWith('}')) { if (trimmed.startsWith('{') && trimmed.endsWith('}')) {
@@ -131,7 +144,8 @@ export default {
if (parsed && parsed.tr) { if (parsed && parsed.tr) {
raw = parsed.tr; raw = parsed.tr;
key = parsed.tr; key = parsed.tr;
params = { ...parsed }; // Alle anderen Felder als Parameter verwenden und formatieren
params = this.formatParams({ ...parsed });
delete params.tr; delete params.tr;
} }
} catch (e) { } catch (e) {
@@ -153,7 +167,137 @@ export default {
} }
} }
// 3) Prüfe, ob es sich um ein random_event handelt mit title/description Struktur
if (key.startsWith('falukant.notifications.random_event.')) {
const eventId = key.replace('falukant.notifications.random_event.', '');
const eventKey = `falukant.notifications.random_event.${eventId}`;
try {
const titleKey = `${eventKey}.title`;
const descKey = `${eventKey}.description`;
if (this.$te(titleKey) && this.$te(descKey)) {
const title = this.$t(titleKey, params);
const description = this.$t(descKey, params);
return { title, description };
}
} catch (e) {
// Fallback zur alten Methode
}
}
// Fallback: Alte Methode für andere Notification-Typen
return this.$t(key, params); return this.$t(key, params);
},
formatParams(params) {
const formatted = {};
// Geldbeträge formatieren
if (params.amount !== undefined) {
formatted.amount = this.formatMoney(params.amount);
}
if (params.absolute !== undefined) {
formatted.amount = this.formatMoney(params.absolute);
}
if (params.percent !== undefined) {
formatted.amount = `${params.percent > 0 ? '+' : ''}${params.percent.toFixed(1)}%`;
}
// Gesundheit formatieren
if (params.change !== undefined) {
formatted.healthChange = params.change > 0 ? `+${params.change}` : `${params.change}`;
}
if (params.healthChange !== undefined) {
formatted.healthChange = params.healthChange > 0 ? `+${params.healthChange}` : `${params.healthChange}`;
}
// Schaden formatieren
if (params.inventory_damage_percent !== undefined) {
formatted.damagePercent = ` Lagerbestand beschädigt: ${params.inventory_damage_percent.toFixed(1)}%.`;
}
if (params.storage_destruction_percent !== undefined) {
formatted.destructionPercent = ` Lager zerstört: ${params.storage_destruction_percent.toFixed(1)}%.`;
}
// Alle anderen Parameter übernehmen
for (const [key, value] of Object.entries(params)) {
if (!formatted.hasOwnProperty(key) && key !== 'tr') {
formatted[key] = value;
}
}
return formatted;
},
extractParams(n) {
const params = {};
// Parameter aus effects extrahieren
if (n.effects && Array.isArray(n.effects)) {
for (const effect of n.effects) {
if (effect.type === 'money_change') {
if (effect.absolute !== undefined) {
params.amount = effect.absolute;
} else if (effect.percent !== undefined) {
params.percent = effect.percent;
}
} else if (effect.type === 'character_health_change') {
if (effect.character_id && n.character_id === effect.character_id) {
params.characterName = n.characterName || '';
}
if (effect.change !== undefined) {
params.change = effect.change;
}
} else if (effect.type === 'character_death') {
if (effect.character_id && n.character_id === effect.character_id) {
params.characterName = n.characterName || '';
}
} else if (effect.type === 'storage_damage') {
if (effect.inventory_damage_percent !== undefined) {
params.inventory_damage_percent = effect.inventory_damage_percent;
}
if (effect.storage_destruction_percent !== undefined) {
params.storage_destruction_percent = effect.storage_destruction_percent;
}
}
}
}
// Weitere Parameter aus der Notification selbst
if (n.region_id && n.regionName) {
params.regionName = n.regionName;
}
if (n.character_id && n.characterName) {
params.characterName = n.characterName;
}
if (n.amount !== undefined) {
params.amount = n.amount;
}
return this.formatParams(params);
},
interpolateString(str, params) {
if (!str || typeof str !== 'string') return str;
let result = str;
for (const [key, value] of Object.entries(params)) {
if (value !== undefined && value !== null && value !== '') {
result = result.replace(new RegExp(`\\{${key}\\}`, 'g'), String(value));
} else {
// Entferne Platzhalter, wenn kein Wert vorhanden ist
result = result.replace(new RegExp(`\\s*\\{${key}\\}\\s*`, 'g'), '');
}
}
// Entferne doppelte Leerzeichen und trimme
result = result.replace(/\s+/g, ' ').trim();
return result;
},
formatMoney(amount) {
return new Intl.NumberFormat(navigator.language, {
minimumFractionDigits: 2,
maximumFractionDigits: 2
}).format(amount);
} }
}, },
computed: { computed: {
@@ -174,6 +318,9 @@ export default {
} }
.messages > li { border: 1px solid #7BBE55; margin-bottom: .25em; padding: .5em; } .messages > li { border: 1px solid #7BBE55; margin-bottom: .25em; padding: .5em; }
.messages > li.unread { font-weight: bold; } .messages > li.unread { font-weight: bold; }
.messages > li .body { display: flex; flex-direction: column; gap: 0.25em; }
.messages > li .notification-title { font-weight: bold; font-size: 1.05em; }
.messages > li .notification-description { color: #555; }
.messages > li .footer { color: #f9a22c; font-size: .8em; margin-top: .3em; display: flex; } .messages > li .footer { color: #f9a22c; font-size: .8em; margin-top: .3em; display: flex; }
.messages > li .footer span:first-child { flex: 1; } .messages > li .footer span:first-child { flex: 1; }
.empty { text-align: center; color: #777; padding: 1em; } .empty { text-align: center; color: #777; padding: 1em; }

View File

@@ -33,17 +33,50 @@
"waiting": "Transport wartet" "waiting": "Transport wartet"
}, },
"random_event": { "random_event": {
"windfall": "Unerwarteter Geldsegen: Du hast {amount} erhalten!", "windfall": {
"theft": "Diebstahl: {amount} wurde aus deinem Lager gestohlen.", "title": "Unerwarteter Geldsegen",
"warehouse_fire": "Lagerfeuer: Ein Feuer hat dein Lager beschädigt. Verluste: {amount}.", "description": "Du findest eine vergessene Geldbörse auf der Straße. Du erhältst {amount}."
"character_illness": "Krankheit: {characterName} ist erkrankt.", },
"character_recovery": "Genesung: {characterName} hat sich von der Krankheit erholt.", "theft": {
"character_accident": "Unfall: {characterName} hatte einen Unfall.", "title": "Diebstahl",
"sudden_infant_death": "Tragödie: {characterName} ist plötzlich verstorben.", "description": "Ein Dieb hat einen Teil deines Geldes gestohlen. Verlust: {amount}."
"regional_storm": "Regionaler Sturm: Ein schwerer Sturm hat die Region {regionName} getroffen.", },
"regional_festival": "Regionales Fest: Ein Fest findet in {regionName} statt.", "warehouse_fire": {
"regional_epidemic": "Regionale Epidemie: Eine Epidemie hat {regionName} getroffen.", "title": "Lagerbrand",
"earthquake": "Erdbeben: Ein Erdbeben hat die Region {regionName} erschüttert." "description": "Ein Feuer hat Teile deines Lagers beschädigt.{damagePercent}{destructionPercent}"
},
"character_illness": {
"title": "Krankheit",
"description": "{characterName} ist erkrankt und hat {healthChange} Gesundheit verloren."
},
"character_recovery": {
"title": "Genesung",
"description": "{characterName} hat sich von einer Krankheit erholt und {healthChange} Gesundheit zurückgewonnen."
},
"character_accident": {
"title": "Unfall",
"description": "Ein schwerer Unfall hat {characterName} schwer verletzt. Gesundheit: {healthChange}."
},
"sudden_infant_death": {
"title": "Plötzlicher Kindstod",
"description": "{characterName} ist plötzlich verstorben."
},
"regional_storm": {
"title": "Sturm in der Region",
"description": "Ein schwerer Sturm hat die Region {regionName} getroffen."
},
"regional_festival": {
"title": "Regionales Fest",
"description": "Ein großes Fest findet in der Region {regionName} statt."
},
"regional_epidemic": {
"title": "Epidemie",
"description": "Eine Seuche hat die Region {regionName} erfasst."
},
"earthquake": {
"title": "Erdbeben",
"description": "Ein Erdbeben hat die Region {regionName} erschüttert."
}
} }
}, },
"health": { "health": {

View File

@@ -24,17 +24,50 @@
"waiting": "Transport waiting" "waiting": "Transport waiting"
}, },
"random_event": { "random_event": {
"windfall": "Unexpected windfall: You received {amount}!", "windfall": {
"theft": "Theft: {amount} was stolen from your warehouse.", "title": "Unexpected Windfall",
"warehouse_fire": "Warehouse Fire: A fire has damaged your warehouse. Losses: {amount}.", "description": "You find a forgotten wallet on the street. You receive {amount}."
"character_illness": "Illness: {characterName} has fallen ill.", },
"character_recovery": "Recovery: {characterName} has recovered from the illness.", "theft": {
"character_accident": "Accident: {characterName} has had an accident.", "title": "Theft",
"sudden_infant_death": "Tragedy: {characterName} has suddenly passed away.", "description": "A thief has stolen part of your money. Loss: {amount}."
"regional_storm": "Regional Storm: A severe storm has hit the region {regionName}.", },
"regional_festival": "Regional Festival: A festival is taking place in {regionName}.", "warehouse_fire": {
"regional_epidemic": "Regional Epidemic: An epidemic has struck {regionName}.", "title": "Warehouse Fire",
"earthquake": "Earthquake: An earthquake has shaken the region {regionName}." "description": "A fire has damaged parts of your warehouse.{damagePercent}{destructionPercent}"
},
"character_illness": {
"title": "Illness",
"description": "{characterName} has fallen ill and lost {healthChange} health."
},
"character_recovery": {
"title": "Recovery",
"description": "{characterName} has recovered from an illness and regained {healthChange} health."
},
"character_accident": {
"title": "Accident",
"description": "A serious accident has severely injured {characterName}. Health: {healthChange}."
},
"sudden_infant_death": {
"title": "Sudden Infant Death",
"description": "{characterName} has suddenly passed away."
},
"regional_storm": {
"title": "Storm in the Region",
"description": "A severe storm has hit the region {regionName}."
},
"regional_festival": {
"title": "Regional Festival",
"description": "A large festival is taking place in the region {regionName}."
},
"regional_epidemic": {
"title": "Epidemic",
"description": "A plague has struck the region {regionName}."
},
"earthquake": {
"title": "Earthquake",
"description": "An earthquake has shaken the region {regionName}."
}
} }
}, },
"statusbar": { "statusbar": {