feat(falukant): enhance notification handling and localization updates
All checks were successful
Deploy to production / deploy (push) Successful in 2m52s
All checks were successful
Deploy to production / deploy (push) Successful in 2m52s
- Updated the `enrichNotificationsWithCharacterNames` method in FalukantService to include region name enrichment and handle additional character IDs. - Introduced a new `serializeNotificationForClient` function to format notifications for the client, ensuring all relevant data is included. - Enhanced the MessagesDialog component to merge notification payloads and extract parameters more effectively, improving the clarity of displayed messages. - Added new localization entries for director resignation risk and regional festival effects in multiple languages, ensuring comprehensive user notifications.
This commit is contained in:
@@ -7328,7 +7328,7 @@ ORDER BY r.id`,
|
||||
// Enrich notifications: parse JSON payloads and resolve character names
|
||||
await enrichNotificationsWithCharacterNames(notifications);
|
||||
|
||||
return notifications;
|
||||
return notifications.map(serializeNotificationForClient);
|
||||
}
|
||||
|
||||
async getAllNotifications(hashedUserId, page = 1, size = 10) {
|
||||
@@ -7344,7 +7344,12 @@ ORDER BY r.id`,
|
||||
|
||||
await enrichNotificationsWithCharacterNames(rows);
|
||||
|
||||
return { items: rows, total: count, page: Number(page) || 1, size: limit };
|
||||
return {
|
||||
items: rows.map(serializeNotificationForClient),
|
||||
total: count,
|
||||
page: Number(page) || 1,
|
||||
size: limit,
|
||||
};
|
||||
}
|
||||
|
||||
async markNotificationsShown(hashedUserId) {
|
||||
@@ -8536,6 +8541,14 @@ ORDER BY r.id`,
|
||||
|
||||
export default new FalukantService();
|
||||
|
||||
/** Stellt sicher, dass Anreicherungen (z. B. region_name ohne DB-Spalte) im API-JSON landen. */
|
||||
function serializeNotificationForClient(row) {
|
||||
const j = typeof row.toJSON === 'function' ? row.toJSON() : { ...row };
|
||||
const dv = row.dataValues || {};
|
||||
if (dv.region_name != null && j.region_name == null) j.region_name = dv.region_name;
|
||||
return j;
|
||||
}
|
||||
|
||||
// Helper: parse notifications for character references and attach characterName
|
||||
async function enrichNotificationsWithCharacterNames(notifications) {
|
||||
if (!Array.isArray(notifications) || notifications.length === 0) return;
|
||||
@@ -8552,7 +8565,7 @@ async function enrichNotificationsWithCharacterNames(notifications) {
|
||||
if (typeof obj !== 'object') return;
|
||||
for (const [k, v] of Object.entries(obj)) {
|
||||
if (!v) continue;
|
||||
if (k === 'character_id' || k === 'characterId') {
|
||||
if (k === 'character_id' || k === 'characterId' || k === 'director_character_id') {
|
||||
charIds.add(Number(v));
|
||||
continue;
|
||||
}
|
||||
@@ -8567,15 +8580,25 @@ async function enrichNotificationsWithCharacterNames(notifications) {
|
||||
}
|
||||
}
|
||||
|
||||
// First pass: collect all referenced character ids from notifications
|
||||
const regionIds = new Set();
|
||||
|
||||
// First pass: collect all referenced character ids and region ids from notifications
|
||||
for (const n of notifications) {
|
||||
// parse n.tr if it's JSON
|
||||
let parsed = null;
|
||||
try {
|
||||
if (typeof n.tr === 'string' && n.tr.trim().startsWith('{')) {
|
||||
const parsed = JSON.parse(n.tr);
|
||||
parsed = JSON.parse(n.tr);
|
||||
collectIds(parsed);
|
||||
} else if (n.tr && typeof n.tr === 'object') {
|
||||
parsed = n.tr;
|
||||
collectIds(parsed);
|
||||
}
|
||||
} catch (err) { /* ignore */ }
|
||||
} catch (err) {
|
||||
parsed = null;
|
||||
}
|
||||
if (parsed?.region_id != null) {
|
||||
regionIds.add(Number(parsed.region_id));
|
||||
}
|
||||
|
||||
// parse n.effects if present
|
||||
try {
|
||||
@@ -8591,24 +8614,34 @@ async function enrichNotificationsWithCharacterNames(notifications) {
|
||||
}
|
||||
|
||||
const ids = Array.from(charIds).filter(Boolean);
|
||||
if (!ids.length) return;
|
||||
|
||||
// Batch load characters and their display names
|
||||
const characters = await FalukantCharacter.findAll({
|
||||
where: { id: { [Op.in]: ids } },
|
||||
include: [
|
||||
{ model: FalukantPredefineFirstname, as: 'definedFirstName', attributes: ['name'] },
|
||||
{ model: FalukantPredefineLastname, as: 'definedLastName', attributes: ['name'] }
|
||||
],
|
||||
attributes: ['id']
|
||||
});
|
||||
const regionIdList = Array.from(regionIds).filter((id) => Number.isFinite(id));
|
||||
if (!ids.length && !regionIdList.length) return;
|
||||
|
||||
const nameMap = new Map();
|
||||
for (const c of characters) {
|
||||
const first = c.definedFirstName?.name || '';
|
||||
const last = c.definedLastName?.name || '';
|
||||
const display = `${first} ${last}`.trim() || null;
|
||||
nameMap.set(Number(c.id), display || `#${c.id}`);
|
||||
if (ids.length) {
|
||||
const characters = await FalukantCharacter.findAll({
|
||||
where: { id: { [Op.in]: ids } },
|
||||
include: [
|
||||
{ model: FalukantPredefineFirstname, as: 'definedFirstName', attributes: ['name'] },
|
||||
{ model: FalukantPredefineLastname, as: 'definedLastName', attributes: ['name'] }
|
||||
],
|
||||
attributes: ['id']
|
||||
});
|
||||
|
||||
for (const c of characters) {
|
||||
const first = c.definedFirstName?.name || '';
|
||||
const last = c.definedLastName?.name || '';
|
||||
const display = `${first} ${last}`.trim() || null;
|
||||
nameMap.set(Number(c.id), display || `#${c.id}`);
|
||||
}
|
||||
}
|
||||
let regionNameById = new Map();
|
||||
if (regionIdList.length) {
|
||||
const regions = await RegionData.findAll({
|
||||
where: { id: { [Op.in]: regionIdList } },
|
||||
attributes: ['id', 'name'],
|
||||
});
|
||||
regionNameById = new Map(regions.map((r) => [Number(r.id), r.name]));
|
||||
}
|
||||
|
||||
// helper to find first character id in an object
|
||||
@@ -8625,6 +8658,7 @@ async function enrichNotificationsWithCharacterNames(notifications) {
|
||||
for (const [k, v] of Object.entries(obj)) {
|
||||
if (!v) continue;
|
||||
if (k === 'character_id' || k === 'characterId') return Number(v);
|
||||
if (k === 'director_character_id') return Number(v);
|
||||
if (k === 'character' && typeof v === 'object') {
|
||||
if (v.id) return Number(v.id);
|
||||
const r = findFirstId(v);
|
||||
@@ -8638,10 +8672,23 @@ async function enrichNotificationsWithCharacterNames(notifications) {
|
||||
|
||||
// Attach resolved name to notifications (set character_name; characterName is a getter that reads from it)
|
||||
for (const n of notifications) {
|
||||
let foundId = null;
|
||||
let parsed = null;
|
||||
try {
|
||||
if (typeof n.tr === 'string' && n.tr.trim().startsWith('{')) {
|
||||
const parsed = JSON.parse(n.tr);
|
||||
parsed = JSON.parse(n.tr);
|
||||
} else if (n.tr && typeof n.tr === 'object') {
|
||||
parsed = n.tr;
|
||||
}
|
||||
} catch (err) {
|
||||
parsed = null;
|
||||
}
|
||||
|
||||
let foundId = null;
|
||||
if (parsed?.director_character_id != null) {
|
||||
foundId = Number(parsed.director_character_id);
|
||||
}
|
||||
try {
|
||||
if (!foundId && parsed) {
|
||||
foundId = findFirstId(parsed) || foundId;
|
||||
}
|
||||
} catch (err) { /* ignore */ }
|
||||
@@ -8661,5 +8708,11 @@ async function enrichNotificationsWithCharacterNames(notifications) {
|
||||
// Set character_name directly (characterName is a getter that reads from character_name)
|
||||
n.character_name = resolved;
|
||||
}
|
||||
|
||||
if (parsed?.region_id != null && regionNameById.has(Number(parsed.region_id))) {
|
||||
const rn = regionNameById.get(Number(parsed.region_id));
|
||||
// Kein DB-Feld: explizit in dataValues setzen, damit toJSON() es mitschickt
|
||||
if (n.dataValues) n.dataValues.region_name = rn;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -138,6 +138,9 @@ export default {
|
||||
if (!n || typeof n !== 'object') return n || {};
|
||||
let merged = { ...n };
|
||||
const raw = n.tr;
|
||||
if (raw && typeof raw === 'object') {
|
||||
merged = { ...merged, ...raw };
|
||||
}
|
||||
if (typeof raw === 'string') {
|
||||
const trimmed = raw.trim();
|
||||
if (trimmed.startsWith('{') && trimmed.endsWith('}')) {
|
||||
@@ -151,6 +154,9 @@ export default {
|
||||
}
|
||||
}
|
||||
}
|
||||
if (merged.character_name && merged.characterName == null) {
|
||||
merged.characterName = merged.character_name;
|
||||
}
|
||||
return merged;
|
||||
},
|
||||
|
||||
@@ -228,10 +234,8 @@ export default {
|
||||
try {
|
||||
const titleKey = `${eventKey}.title`;
|
||||
const descKey = `${eventKey}.description`;
|
||||
// If no params were parsed from JSON, try to extract them from the notification (effects, character_id, etc.)
|
||||
if ((!params || Object.keys(params).length === 0) && payload) {
|
||||
params = this.extractParams(payload) || {};
|
||||
}
|
||||
const extracted = this.extractParams(payload) || {};
|
||||
params = { ...extracted, ...params };
|
||||
|
||||
if (this.$te(titleKey) && this.$te(descKey)) {
|
||||
const title = this.$t(titleKey, params);
|
||||
@@ -243,6 +247,10 @@ export default {
|
||||
}
|
||||
}
|
||||
|
||||
// Immer Parameter aus dem vollständigen Payload (nach merge), z. B. director.resignation_risk_high
|
||||
const extractedAll = this.extractParams(payload) || {};
|
||||
params = { ...extractedAll, ...params };
|
||||
|
||||
// Fallback: Alte Methode für andere Notification-Typen
|
||||
return this.$t(key, params);
|
||||
},
|
||||
@@ -312,6 +320,14 @@ export default {
|
||||
extractParams(n) {
|
||||
const base = this.mergeNotificationPayload(n);
|
||||
const params = {};
|
||||
const locale = this.$i18n?.locale || 'de';
|
||||
const isGerman = String(locale).startsWith('de');
|
||||
|
||||
if (base.characterName) {
|
||||
params.characterName = base.characterName;
|
||||
} else if (base.character_name) {
|
||||
params.characterName = base.character_name;
|
||||
}
|
||||
|
||||
// Parameter aus effects extrahieren (Daemon: effects oft nur im JSON in tr)
|
||||
if (base.effects && Array.isArray(base.effects)) {
|
||||
@@ -326,11 +342,27 @@ export default {
|
||||
} else if (effect.percent !== undefined) {
|
||||
params.percent = effect.percent;
|
||||
}
|
||||
} else if (effect.type === 'price_change') {
|
||||
if (effect.percent !== undefined && effect.percent !== null) {
|
||||
const p = Number(effect.percent);
|
||||
if (Number.isFinite(p)) {
|
||||
params.priceChangePercent = `${p > 0 ? '+' : ''}${p.toFixed(1)}`;
|
||||
}
|
||||
}
|
||||
} else if (effect.type === 'production_quality_change') {
|
||||
if (effect.change !== undefined && effect.change !== null) {
|
||||
const v = Number(effect.change);
|
||||
if (Number.isFinite(v)) {
|
||||
params.productionQualityChange = (v > 0 ? '+' : '') + (Number.isInteger(v) ? String(v) : v.toFixed(1));
|
||||
}
|
||||
}
|
||||
} else if (effect.type === 'weather_change') {
|
||||
params.hasWeatherChange = true;
|
||||
} else if (effect.type === 'character_health_change') {
|
||||
if (effect.character_id) {
|
||||
// Prefer explicit characterName from notification, otherwise fall back to provided name or use id placeholder
|
||||
params.character_id = effect.character_id;
|
||||
params.characterName = params.characterName || base.characterName || `#${effect.character_id}`;
|
||||
params.characterName = params.characterName || base.characterName || base.character_name || `#${effect.character_id}`;
|
||||
}
|
||||
if (effect.change !== undefined) {
|
||||
params.change = effect.change;
|
||||
@@ -352,9 +384,33 @@ export default {
|
||||
}
|
||||
|
||||
// Weitere Parameter aus der Notification selbst
|
||||
if (base.region_id && base.regionName) {
|
||||
params.regionName = base.regionName;
|
||||
if (base.region_id != null) {
|
||||
if (base.regionName) {
|
||||
params.regionName = base.regionName;
|
||||
} else if (base.region_name) {
|
||||
params.regionName = base.region_name;
|
||||
}
|
||||
}
|
||||
|
||||
// Daemon: Meta-Objekte (auch wenn effects[] schon Werte liefert — fehlende Werte ergänzen)
|
||||
if (params.priceChangePercent == null && base.price_change && typeof base.price_change === 'object') {
|
||||
const applied = base.price_change.applied !== false;
|
||||
const pct = base.price_change.percent;
|
||||
if (applied && pct != null && Number.isFinite(Number(pct))) {
|
||||
const p = Number(pct);
|
||||
params.priceChangePercent = `${p > 0 ? '+' : ''}${p.toFixed(1)}`;
|
||||
}
|
||||
}
|
||||
if (params.productionQualityChange == null && base.production_quality && typeof base.production_quality === 'object') {
|
||||
const applied = base.production_quality.applied !== false;
|
||||
const ch = base.production_quality.change;
|
||||
if (applied && ch != null && Number.isFinite(Number(ch))) {
|
||||
const v = Number(ch);
|
||||
params.productionQualityChange = (v > 0 ? '+' : '') + (Number.isInteger(v) ? String(v) : v.toFixed(1));
|
||||
}
|
||||
}
|
||||
|
||||
params.priceEffectLine = this.buildRegionalFestivalEffectsLine(params, isGerman);
|
||||
if (base.character_id && base.characterName) {
|
||||
params.characterName = base.characterName;
|
||||
}
|
||||
@@ -370,6 +426,13 @@ export default {
|
||||
if (base.threshold_percent !== undefined) {
|
||||
params.threshold_percent = base.threshold_percent;
|
||||
}
|
||||
const isDirectorResign = base.event === 'director_resignation_risk_high'
|
||||
|| (typeof base.tr === 'string' && base.tr.includes('director.resignation'));
|
||||
if (isDirectorResign) {
|
||||
params.directorName = params.characterName
|
||||
|| base.character_name
|
||||
|| (base.director_character_id != null ? `#${base.director_character_id}` : '');
|
||||
}
|
||||
if (base.director_id !== undefined) {
|
||||
params.director_id = base.director_id;
|
||||
}
|
||||
@@ -440,7 +503,48 @@ export default {
|
||||
minimumFractionDigits: 2,
|
||||
maximumFractionDigits: 2
|
||||
}).format(amount);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Zeilen für regionale Events (Fest o. ä.): Wetter, Preis, Produktionsqualität — aus effects[] und/oder price_change / production_quality.
|
||||
*/
|
||||
buildRegionalFestivalEffectsLine(params, isGerman) {
|
||||
const parts = [];
|
||||
const kw = 'falukant.notifications.random_event.effects.festival_weather_line';
|
||||
const kp = 'falukant.notifications.random_event.effects.festival_price_line';
|
||||
const kq = 'falukant.notifications.random_event.effects.festival_quality_line';
|
||||
if (params.hasWeatherChange) {
|
||||
if (this.$te(kw)) parts.push(String(this.$t(kw)));
|
||||
else parts.push(isGerman ? 'Wetter in der Region schlägt um.' : 'The regional weather shifts.');
|
||||
}
|
||||
if (params.priceChangePercent != null) {
|
||||
if (this.$te(kp)) {
|
||||
parts.push(String(this.$t(kp, { percent: params.priceChangePercent })));
|
||||
} else {
|
||||
const legacy = 'falukant.notifications.random_event.effects.price_effect_suffix';
|
||||
parts.push(
|
||||
this.$te(legacy)
|
||||
? String(this.$t(legacy, { percent: params.priceChangePercent }))
|
||||
: (isGerman
|
||||
? `Warenpreise etwa ${params.priceChangePercent} %.`
|
||||
: `Goods prices about ${params.priceChangePercent}%.`)
|
||||
);
|
||||
}
|
||||
}
|
||||
if (params.productionQualityChange != null) {
|
||||
if (this.$te(kq)) {
|
||||
parts.push(String(this.$t(kq, { change: params.productionQualityChange })));
|
||||
} else {
|
||||
parts.push(
|
||||
isGerman
|
||||
? `Produktionsqualität etwa ${params.productionQualityChange}.`
|
||||
: `Production quality about ${params.productionQualityChange}.`
|
||||
);
|
||||
}
|
||||
}
|
||||
if (!parts.length) return '';
|
||||
return ` ${parts.join(' ')}`;
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
totalPages() {
|
||||
|
||||
@@ -35,7 +35,7 @@
|
||||
"notify_election_created": "Giskedyul ang usa ka bag-ong eleksiyon.",
|
||||
"notify_office_filled": "Na puno ang usa ka politikal nga opisina.",
|
||||
"director": {
|
||||
"resignation_risk_high": "Taas ang risgo nga mubiyaa ang direktor: risgo {risk_percent}% (threshold {threshold_percent}%). Karon nga satisfaction {satisfaction}."
|
||||
"resignation_risk_high": "Taas ang risgo nga mobiya ang direktor nga si {directorName}: risgo {risk_percent}% (threshold {threshold_percent}%). Karon nga satisfaction {satisfaction}."
|
||||
},
|
||||
"director_death": "Namatay si {characterName} sa edad nga {ageYears}. Isip amo, kinahanglan kang magtudlo og bag-ong direktor.{regionLabel}{spouses}{children}{lovers}",
|
||||
"relationship_death": "Namatay si {characterName} sa edad nga {ageYears}.{regionLabel}{spouses}{children}{lovers}",
|
||||
@@ -93,7 +93,13 @@
|
||||
},
|
||||
"regional_festival": {
|
||||
"title": "Pista sa rehiyon",
|
||||
"description": "Adunay dakong pista sa rehiyon nga {regionName}."
|
||||
"description": "Adunay dakong pista sa rehiyon nga {regionName}.{priceEffectLine}"
|
||||
},
|
||||
"effects": {
|
||||
"price_effect_suffix": " Epekto: gipaabot nga pagbag-o sa presyo sa mga paliton nga ~{percent}%.",
|
||||
"festival_weather_line": "Ang panahon sa rehiyon nagbag-o.",
|
||||
"festival_price_line": "Presyo sa mga paliton ~{percent}%.",
|
||||
"festival_quality_line": "Kalidad sa produksyon ~{change}."
|
||||
},
|
||||
"regional_epidemic": {
|
||||
"title": "Epidemya",
|
||||
|
||||
@@ -49,7 +49,7 @@
|
||||
"notify_election_created": "Es wurde eine neue Wahl ausgeschrieben.",
|
||||
"notify_office_filled": "Ein politisches Amt wurde neu besetzt.",
|
||||
"director": {
|
||||
"resignation_risk_high": "Hohe Kündigungsgefahr bei einem Direktor: Risiko {risk_percent}% (Schwelle {threshold_percent}%). Zufriedenheit aktuell {satisfaction}."
|
||||
"resignation_risk_high": "Hohe Kündigungsgefahr für den Direktor {directorName}: Risiko {risk_percent} % (Schwelle {threshold_percent} %). Zufriedenheit aktuell {satisfaction}."
|
||||
},
|
||||
"director_death": "{characterName} ist im Alter von {ageYears} Jahren verstorben. Als Arbeitgeber musst du die Direktion neu besetzen.{regionLabel}{spouses}{children}{lovers}",
|
||||
"relationship_death": "{characterName} ist im Alter von {ageYears} Jahren verstorben.{regionLabel}{spouses}{children}{lovers}",
|
||||
@@ -95,7 +95,13 @@
|
||||
},
|
||||
"regional_festival": {
|
||||
"title": "Regionales Fest",
|
||||
"description": "Ein großes Fest findet in der Region {regionName} statt."
|
||||
"description": "Ein großes Fest findet in der Region {regionName} statt.{priceEffectLine}"
|
||||
},
|
||||
"effects": {
|
||||
"price_effect_suffix": " Auswirkung: erwartete Warenpreisänderung ca. {percent} %.",
|
||||
"festival_weather_line": "Wetter in der Region schlägt um.",
|
||||
"festival_price_line": "Warenpreise etwa {percent} %.",
|
||||
"festival_quality_line": "Produktionsqualität etwa {change}."
|
||||
},
|
||||
"regional_epidemic": {
|
||||
"title": "Epidemie",
|
||||
|
||||
@@ -35,7 +35,7 @@
|
||||
"notify_election_created": "A new election has been scheduled.",
|
||||
"notify_office_filled": "A political office has been filled.",
|
||||
"director": {
|
||||
"resignation_risk_high": "High resignation risk for a director: risk {risk_percent}% (threshold {threshold_percent}%). Current satisfaction {satisfaction}."
|
||||
"resignation_risk_high": "High resignation risk for director {directorName}: risk {risk_percent}% (threshold {threshold_percent}%). Current satisfaction {satisfaction}."
|
||||
},
|
||||
"director_death": "{characterName} died at the age of {ageYears}. As employer you need to appoint a new director.{regionLabel}{spouses}{children}{lovers}",
|
||||
"relationship_death": "{characterName} died at the age of {ageYears}.{regionLabel}{spouses}{children}{lovers}",
|
||||
@@ -93,7 +93,13 @@
|
||||
},
|
||||
"regional_festival": {
|
||||
"title": "Regional Festival",
|
||||
"description": "A large festival is taking place in the region {regionName}."
|
||||
"description": "A large festival is taking place in the region {regionName}.{priceEffectLine}"
|
||||
},
|
||||
"effects": {
|
||||
"price_effect_suffix": " Effect: expected goods price change ~{percent}%.",
|
||||
"festival_weather_line": "The regional weather shifts.",
|
||||
"festival_price_line": "Goods prices about {percent}%.",
|
||||
"festival_quality_line": "Production quality about {change}."
|
||||
},
|
||||
"regional_epidemic": {
|
||||
"title": "Epidemic",
|
||||
|
||||
@@ -49,7 +49,7 @@
|
||||
"notify_election_created": "Se ha convocado una nueva elección.",
|
||||
"notify_office_filled": "Se ha cubierto un cargo político.",
|
||||
"director": {
|
||||
"resignation_risk_high": "Alto riesgo de renuncia de un director: riesgo {risk_percent}% (umbral {threshold_percent}%). Satisfacción actual {satisfaction}."
|
||||
"resignation_risk_high": "Alto riesgo de renuncia del director {directorName}: riesgo {risk_percent}% (umbral {threshold_percent}%). Satisfacción actual {satisfaction}."
|
||||
},
|
||||
"director_death": "{characterName} ha fallecido a la edad de {ageYears} años. Como empleador debes nombrar un nuevo director.{regionLabel}{spouses}{children}{lovers}",
|
||||
"relationship_death": "{characterName} ha fallecido a la edad de {ageYears} años.{regionLabel}{spouses}{children}{lovers}",
|
||||
@@ -95,7 +95,13 @@
|
||||
},
|
||||
"regional_festival": {
|
||||
"title": "Fiesta regional",
|
||||
"description": "Se celebra una gran fiesta en la región {regionName}."
|
||||
"description": "Se celebra una gran fiesta en la región {regionName}.{priceEffectLine}"
|
||||
},
|
||||
"effects": {
|
||||
"price_effect_suffix": " Efecto: variación esperada del precio de las mercancías ~{percent}%.",
|
||||
"festival_weather_line": "El tiempo en la región cambia.",
|
||||
"festival_price_line": "Precios de mercancías ~{percent} %.",
|
||||
"festival_quality_line": "Calidad de producción ~{change}."
|
||||
},
|
||||
"regional_epidemic": {
|
||||
"title": "Epidemia",
|
||||
|
||||
@@ -49,7 +49,7 @@
|
||||
"notify_election_created": "Une nouvelle élection a été déclenchée.",
|
||||
"notify_office_filled": "Une fonction politique a été pourvue.",
|
||||
"director": {
|
||||
"resignation_risk_high": "Risque élevé de démission d’un directeur : risque {risk_percent}% (seuil {threshold_percent}%). Satisfaction actuelle {satisfaction}."
|
||||
"resignation_risk_high": "Risque élevé de démission du directeur {directorName} : risque {risk_percent}% (seuil {threshold_percent}%). Satisfaction actuelle {satisfaction}."
|
||||
},
|
||||
"director_death": "{characterName} est décédé à l'âge de {ageYears}. En tant qu'employeur, vous devez remplir le conseil d'administration.{regionLabel}{spouses}{children}{lovers}",
|
||||
"relationship_death": "{characterName} est décédé à l'âge de {ageYears}.{regionLabel}{spouses}{children}{lovers}",
|
||||
@@ -95,7 +95,13 @@
|
||||
},
|
||||
"regional_festival": {
|
||||
"title": "Fête régionale",
|
||||
"description": "Un grand festival a lieu dans la région {regionName}."
|
||||
"description": "Un grand festival a lieu dans la région {regionName}.{priceEffectLine}"
|
||||
},
|
||||
"effects": {
|
||||
"price_effect_suffix": " Effet : variation attendue des prix des marchandises ~{percent} %.",
|
||||
"festival_weather_line": "Le temps dans la région change.",
|
||||
"festival_price_line": "Prix des marchandises ~{percent} %.",
|
||||
"festival_quality_line": "Qualité de production ~{change}."
|
||||
},
|
||||
"regional_epidemic": {
|
||||
"title": "Épidémie",
|
||||
|
||||
Reference in New Issue
Block a user