+
{{ formatBody(n).title }}
+
{{ formatBody(n).description || formatBody(n) }}
+
@@ -118,11 +121,21 @@ export default {
} catch { return dt; }
},
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 key = raw;
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') {
const trimmed = raw.trim();
if (trimmed.startsWith('{') && trimmed.endsWith('}')) {
@@ -131,7 +144,8 @@ export default {
if (parsed && parsed.tr) {
raw = parsed.tr;
key = parsed.tr;
- params = { ...parsed };
+ // Alle anderen Felder als Parameter verwenden und formatieren
+ params = this.formatParams({ ...parsed });
delete params.tr;
}
} 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);
+ },
+
+ 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: {
@@ -174,6 +318,9 @@ export default {
}
.messages > li { border: 1px solid #7BBE55; margin-bottom: .25em; padding: .5em; }
.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 span:first-child { flex: 1; }
.empty { text-align: center; color: #777; padding: 1em; }
diff --git a/frontend/src/i18n/locales/de/falukant.json b/frontend/src/i18n/locales/de/falukant.json
index 2586e5d..2e4cd8d 100644
--- a/frontend/src/i18n/locales/de/falukant.json
+++ b/frontend/src/i18n/locales/de/falukant.json
@@ -33,17 +33,50 @@
"waiting": "Transport wartet"
},
"random_event": {
- "windfall": "Unerwarteter Geldsegen: Du hast {amount} erhalten!",
- "theft": "Diebstahl: {amount} wurde aus deinem Lager gestohlen.",
- "warehouse_fire": "Lagerfeuer: Ein Feuer hat dein Lager beschädigt. Verluste: {amount}.",
- "character_illness": "Krankheit: {characterName} ist erkrankt.",
- "character_recovery": "Genesung: {characterName} hat sich von der Krankheit erholt.",
- "character_accident": "Unfall: {characterName} hatte einen Unfall.",
- "sudden_infant_death": "Tragödie: {characterName} ist plötzlich verstorben.",
- "regional_storm": "Regionaler Sturm: Ein schwerer Sturm hat die Region {regionName} getroffen.",
- "regional_festival": "Regionales Fest: Ein Fest findet in {regionName} statt.",
- "regional_epidemic": "Regionale Epidemie: Eine Epidemie hat {regionName} getroffen.",
- "earthquake": "Erdbeben: Ein Erdbeben hat die Region {regionName} erschüttert."
+ "windfall": {
+ "title": "Unerwarteter Geldsegen",
+ "description": "Du findest eine vergessene Geldbörse auf der Straße. Du erhältst {amount}."
+ },
+ "theft": {
+ "title": "Diebstahl",
+ "description": "Ein Dieb hat einen Teil deines Geldes gestohlen. Verlust: {amount}."
+ },
+ "warehouse_fire": {
+ "title": "Lagerbrand",
+ "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": {
diff --git a/frontend/src/i18n/locales/en/falukant.json b/frontend/src/i18n/locales/en/falukant.json
index bbfafa3..72e2222 100644
--- a/frontend/src/i18n/locales/en/falukant.json
+++ b/frontend/src/i18n/locales/en/falukant.json
@@ -24,17 +24,50 @@
"waiting": "Transport waiting"
},
"random_event": {
- "windfall": "Unexpected windfall: You received {amount}!",
- "theft": "Theft: {amount} was stolen from your warehouse.",
- "warehouse_fire": "Warehouse Fire: A fire has damaged your warehouse. Losses: {amount}.",
- "character_illness": "Illness: {characterName} has fallen ill.",
- "character_recovery": "Recovery: {characterName} has recovered from the illness.",
- "character_accident": "Accident: {characterName} has had an accident.",
- "sudden_infant_death": "Tragedy: {characterName} has suddenly passed away.",
- "regional_storm": "Regional Storm: A severe storm has hit the region {regionName}.",
- "regional_festival": "Regional Festival: A festival is taking place in {regionName}.",
- "regional_epidemic": "Regional Epidemic: An epidemic has struck {regionName}.",
- "earthquake": "Earthquake: An earthquake has shaken the region {regionName}."
+ "windfall": {
+ "title": "Unexpected Windfall",
+ "description": "You find a forgotten wallet on the street. You receive {amount}."
+ },
+ "theft": {
+ "title": "Theft",
+ "description": "A thief has stolen part of your money. Loss: {amount}."
+ },
+ "warehouse_fire": {
+ "title": "Warehouse Fire",
+ "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": {