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:
@@ -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; }
|
||||||
|
|||||||
@@ -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": {
|
||||||
|
|||||||
@@ -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": {
|
||||||
|
|||||||
Reference in New Issue
Block a user