feat(i18n): update Cebuano localization for minigames and enhance UI text
All checks were successful
Deploy to production / deploy (push) Successful in 1m56s

- Added new localized strings for minigames in Cebuano, including loading hints, objectives, and play focus descriptions to improve user experience.
- Updated existing translations for clarity and consistency across various game elements.
- Enhanced the Match3Game component to utilize localized strings, ensuring dynamic text rendering based on user language preferences.
- Included a new entry in .gitignore for the locale audit report to maintain a clean repository.
This commit is contained in:
Torsten Schulz (local)
2026-04-17 16:20:20 +02:00
parent 71d5922409
commit 9c121d2dc2
9 changed files with 316 additions and 47 deletions

1
.gitignore vendored
View File

@@ -19,6 +19,7 @@ frontend/dist
frontend/dist/* frontend/dist/*
frontend/scripts/.i18n-de-fr-cache.json frontend/scripts/.i18n-de-fr-cache.json
frontend/scripts/.falukant-fr-smooth-cache.json frontend/scripts/.falukant-fr-smooth-cache.json
frontend/ceb-locale-audit-report.json
frontedtree.txt frontedtree.txt
backend/dist/ backend/dist/
backend/data/model-cache backend/data/model-cache

View File

@@ -0,0 +1,140 @@
#!/usr/bin/env node
/**
* Vergleicht en- mit ceb-Locale-Chunks (wie in src/i18n/index.js).
* - identical: Blattstrings, die nach Merge noch gleich en sind (typisch: fehlende Bisaya-Übersetzung).
* - germanHints: Blattstrings in ceb mit deutschen Anzeichen (Umlaute / typische Wörter).
*
* Usage: node frontend/scripts/audit-ceb-locale.mjs
*/
import fs from 'fs';
import path from 'path';
import { fileURLToPath } from 'url';
const __dirname = path.dirname(fileURLToPath(import.meta.url));
const localesRoot = path.join(__dirname, '../src/i18n/locales');
const EN_CHUNKS = [
'general.json',
'header.json',
'navigation.json',
'home.json',
'chat.json',
'register.json',
'passwordReset.json',
'error.json',
'activate.json',
'settings.json',
'admin.json',
'socialnetwork.json',
'friends.json',
'falukant.json',
'blog.json',
'minigames.json',
'message.json',
'personal.json',
'seo.json',
];
function isPlainObject(value) {
return value !== null && typeof value === 'object' && !Array.isArray(value);
}
function deepMerge(target, source) {
if (!isPlainObject(source)) return target;
const base = isPlainObject(target) ? { ...target } : {};
for (const key of Object.keys(source)) {
const sv = source[key];
const tv = base[key];
if (isPlainObject(sv) && isPlainObject(tv)) {
base[key] = deepMerge(tv, sv);
} else if (isPlainObject(sv)) {
base[key] = deepMerge({}, sv);
} else {
base[key] = sv;
}
}
return base;
}
function loadJson(rel) {
const p = path.join(localesRoot, rel);
const raw = fs.readFileSync(p, 'utf8');
return JSON.parse(raw);
}
function flatten(obj, prefix = '', out = {}) {
if (!isPlainObject(obj)) return out;
for (const k of Object.keys(obj)) {
const v = obj[k];
const key = prefix ? `${prefix}.${k}` : k;
if (isPlainObject(v)) {
flatten(v, key, out);
} else if (typeof v === 'string') {
out[key] = v;
}
}
return out;
}
const germanWordHints =
/\b(der|die|das|und|nicht|wird|werden|schon|keine|kein|Ziel|Ziele|Zug|Züge|Spiel|Level|Bitte|Hier|wurde|können|müssen)\b/i;
const germanChars = /[äöüÄÖÜß]/;
function audit() {
let enMerged = {};
let cebMerged = {};
for (const f of EN_CHUNKS) {
const enPath = path.join('en', f);
const cebPath = path.join('ceb', f);
if (!fs.existsSync(path.join(localesRoot, cebPath))) {
console.warn(`Warnung: fehlt ${cebPath} (übersprungen)`);
continue;
}
enMerged = deepMerge(enMerged, loadJson(enPath));
cebMerged = deepMerge(cebMerged, loadJson(enPath));
cebMerged = deepMerge(cebMerged, loadJson(cebPath));
}
const enFlat = flatten(enMerged);
const cebFlat = flatten(cebMerged);
const identical = [];
const germanHints = [];
for (const key of Object.keys(cebFlat)) {
const ev = enFlat[key];
const cv = cebFlat[key];
if (typeof cv !== 'string') continue;
if (ev === cv && typeof ev === 'string') {
identical.push(key);
}
if (germanChars.test(cv) || germanWordHints.test(cv)) {
germanHints.push({ key, sample: cv.slice(0, 120) });
}
}
identical.sort();
germanHints.sort((a, b) => a.key.localeCompare(b.key));
const report = {
generated: new Date().toISOString(),
summary: {
totalLeafStringsCeb: Object.keys(cebFlat).length,
identicalToEnCount: identical.length,
germanHintCount: germanHints.length,
},
identicalToEn: identical,
germanHintsInCeb: germanHints,
};
const outFile = path.join(__dirname, '../ceb-locale-audit-report.json');
fs.writeFileSync(outFile, JSON.stringify(report, null, 2), 'utf8');
console.log(`Bericht geschrieben: ${outFile}`);
console.log(
`Zusammenfassung: ${report.summary.identicalToEnCount} Blätter noch identisch mit EN, ` +
`${report.summary.germanHintCount} Strings mit DE-Hinweis (heuristisch).`
);
}
audit();

View File

@@ -73,15 +73,15 @@
}, },
"character_illness": { "character_illness": {
"title": "Sakit", "title": "Sakit",
"description": "Nagsakit si {characterName} ug nawad-an og {healthChange} health." "description": "Nagsakit si {characterName} ug nawad-an og {healthChange} panglawas."
}, },
"character_recovery": { "character_recovery": {
"title": "Pagkamaayo", "title": "Pagkamaayo",
"description": "Naayo si {characterName} gikan sa sakit ug nabawi ang {healthChange} health." "description": "Naayo si {characterName} gikan sa sakit ug nabawi ang {healthChange} panglawas."
}, },
"character_accident": { "character_accident": {
"title": "Aksidente", "title": "Aksidente",
"description": "Ang grabe nga aksidente nakapasakit pag-ayo kang {characterName}. Health: {healthChange}." "description": "Ang grabe nga aksidente nakapasakit pag-ayo kang {characterName}. Panglawas: {healthChange}."
}, },
"sudden_infant_death": { "sudden_infant_death": {
"title": "Kalit nga pagkamatay sa masuso", "title": "Kalit nga pagkamatay sa masuso",
@@ -124,6 +124,16 @@
"children": "Mga anak", "children": "Mga anak",
"children_unbaptised": "Mga anak nga wala pa mabunyagi" "children_unbaptised": "Mga anak nga wala pa mabunyagi"
}, },
"create": {
"title": "Apil sa duwa",
"gender": "Sekso",
"male": "Lalaki",
"female": "Babaye",
"firstname": "Ngalan",
"lastname": "Apelyido",
"random": "Random",
"submit": "Apil"
},
"overview": { "overview": {
"title": "Falukant - Overview", "title": "Falukant - Overview",
"heroIntro": "Ang imong kahimtang sa ekonomiya, pamilya ug kabtangan sa usa ka mubo ug klarong overview.", "heroIntro": "Ang imong kahimtang sa ekonomiya, pamilya ug kabtangan sa usa ka mubo ug klarong overview.",
@@ -211,7 +221,10 @@
"branches": { "branches": {
"title": "Mga branch", "title": "Mga branch",
"level": { "level": {
"city": "Siyudad" "city": "Siyudad",
"production": "Produksyon",
"store": "Pagbaligya",
"fullstack": "Produksyon uban sa pagbaligya"
} }
}, },
"routine": { "routine": {
@@ -443,7 +456,46 @@
} }
}, },
"sale": { "sale": {
"runningGuards": "Mga guwardiya" "title": "Imbentaryo",
"info": "Dinhi nimo makita ang katingbanan sa mga produkto sa branch.",
"region": "Rehiyon",
"product": "Produkto",
"quality": "Kalidad",
"quantity": "Kadaghanon",
"noInventory": "Walay imbentaryo nga available.",
"loadError": "Sayop sa pag-load sa imbentaryo.",
"sell": "Pagbaligya",
"sellButton": "Ibaligya",
"sellAllButton": "Ibaligya tanan",
"transportTitle": "Paghimo og transport",
"transportSource": "Artikulo",
"transportSourcePlaceholder": "Pilia ang artikulo",
"transportVehicle": "Sakyanan sa transport",
"transportVehiclePlaceholder": "Pilia ang sakyanan",
"transportTarget": "Target nga siyudad",
"transportTargetPlaceholder": "Pilia ang target",
"transportQuantity": "Kadaghanon",
"transportMax": "Pinakadaghan: {max}",
"transportCreate": "Sugdi ang transport",
"transportError": "Dili mahimo ang pag-set sa transport.",
"transportDuration": "Gidugayon sa transport: {duration}",
"transportArrival": "Oras sa pag-abot: {datetime}",
"transportRoute": "Ruta",
"transportCost": "Gasto sa transport: {cost}",
"transportStarted": "Nasugdan na ang transport.",
"runningTransportsTitle": "Nagdagan nga mga transport",
"runningDirection": "Direksyon",
"runningProduct": "Artikulo",
"runningQuantity": "Kadaghanon",
"runningNoProduct": "Transport sa sakyanan",
"runningSource": "Gigikanan",
"runningTarget": "Padulngan",
"runningEta": "Pag-abot",
"runningRemaining": "Nabiling oras",
"runningVehicleCount": "Mga sakyanan",
"runningGuards": "Mga guwardiya",
"runningDirectionOut": "Paingon gawas",
"runningDirectionIn": "Padulong sulod"
}, },
"storage": { "storage": {
"buyPartialError": "Sayop sa pagpalit sa usa ka bahin sa kapasidad sa storage.", "buyPartialError": "Sayop sa pagpalit sa usa ka bahin sa kapasidad sa storage.",
@@ -884,37 +936,37 @@
"name": "Ngalan", "name": "Ngalan",
"age": "Edad", "age": "Edad",
"gender": "Sekso", "gender": "Sekso",
"heir": "Heir", "heir": "Manununod",
"isHeir": "Heir", "isHeir": "Manununod",
"notHeir": "Not Heir", "notHeir": "Dili manununod",
"setAsHeir": "Set as Heir", "setAsHeir": "Himua nga manununod",
"heirSetSuccess": "Ang bata naay successfully set as heir.", "heirSetSuccess": "Nailhan na ang bata isip manununod.",
"heirSetError": "Sayop setting heir.", "heirSetError": "Napakyas ang pagtakda sa manununod.",
"actions": "Aksyons", "actions": "Aksyons",
"none": "No bataren available.", "none": "Walay anak nga ipakita.",
"detailButton": "Show Details", "detailButton": "Ipakita ang detalye",
"addChild": "Add Bata", "addChild": "Dugangi og anak",
"baptism": "Baptize", "baptism": "Bunyagi",
"notBaptized": "Not yet baptized", "notBaptized": "Wala pa mabunyagi",
"baptismNotice": "This bata naay dili baptized yet ug therefore naay no name.", "baptismNotice": "Wala pa mabunyagi kining bata, busa wala pa siyay ngalan.",
"legitimacy": { "legitimacy": {
"legitimate": "Legitimate", "legitimate": "Lehitimo",
"acknowledged_bastard": "Ackahibalod illegitimate", "acknowledged_bastard": "Giilang dili-lehitimo",
"hidden_bastard": "Illegitimate" "hidden_bastard": "Dili-lehitimo"
}, },
"otherParent": "Other parent", "otherParent": "Pikas nga ginikanan",
"otherParentUnknown": "Unknown", "otherParentUnknown": "Wala mailhi",
"birthContextLabel": "Origin", "birthContextLabel": "Gigikanan",
"birthContextShort": { "birthContextShort": {
"marriage": "Kasal", "marriage": "Kasal",
"lover": "Uyab" "lover": "Uyab"
}, },
"birthContextLong": { "birthContextLong": {
"marriage": "From kasal", "marriage": "Gikan sa kasal",
"lover": "From usa ka uyab" "lover": "Gikan sa usa ka uyab"
}, },
"details": { "details": {
"title": "Bata Details" "title": "Detalye sa bata"
} }
}, },
"taxes": { "taxes": {

View File

@@ -38,7 +38,20 @@
"objectivesShow": "Ipakita ang mga tumong", "objectivesShow": "Ipakita ang mga tumong",
"objectives": "Mga tumong", "objectives": "Mga tumong",
"loadingBoard": "Ginaandam ang game board...", "loadingBoard": "Ginaandam ang game board...",
"loadingHint": "Ang datos sa level, mga tumong ug layout sa board gi-sync pa." "loadingHint": "Ang datos sa level, mga tumong ug layout sa board gi-sync pa.",
"badgeMinigames": "Mga minidula",
"objectivesProgress": "{completed}/{total} ka tumong",
"movesLeftBadge": "{count} ka lihok ang nahabilin",
"playFocus": {
"pausedTitle": "Gi-pause ang dula",
"pausedDescription": "Ipadayon ang level o sugdan pag-usab nga kontrolado nga dili mawala ang konteksto.",
"preparingTitle": "Nag-load ang level",
"preparingDescription": "Kung na-load na ang level, makita dinhi ang sunod nga tumong ug angunay nga aksyon.",
"defaultObjectiveTitle": "Humana ang kasamtangang tumong",
"finishLevelTitle": "Humana ang level nga tarong",
"focusDescription": "Unahon kini nga tumong. Nahuman na: {completed} sa {total}.",
"allObjectivesDoneDescription": "Nahuman na ang tanang makita nga tumong. Karon, humana lang ang level nga tarong."
}
}, },
"taxi": { "taxi": {
"title": "Taxi Simulator", "title": "Taxi Simulator",

View File

@@ -38,7 +38,20 @@
"objectivesShow": "Ziele anzeigen", "objectivesShow": "Ziele anzeigen",
"objectives": "Ziele", "objectives": "Ziele",
"loadingBoard": "Spielbrett wird vorbereitet...", "loadingBoard": "Spielbrett wird vorbereitet...",
"loadingHint": "Leveldaten, Ziele und Feldlayout werden gerade synchronisiert." "loadingHint": "Leveldaten, Ziele und Feldlayout werden gerade synchronisiert.",
"badgeMinigames": "Minispiele",
"objectivesProgress": "{completed}/{total} Ziele",
"movesLeftBadge": "{count} Züge übrig",
"playFocus": {
"pausedTitle": "Spiel ist pausiert",
"pausedDescription": "Setze das Level fort oder starte es kontrolliert neu, ohne den aktuellen Kontext zu verlieren.",
"preparingTitle": "Level wird vorbereitet",
"preparingDescription": "Sobald das Level geladen ist, erscheinen hier das nächste Ziel und die passende Hauptaktion.",
"defaultObjectiveTitle": "Aktuelles Ziel abschließen",
"finishLevelTitle": "Level sauber zu Ende spielen",
"focusDescription": "Konzentriere dich zuerst auf dieses Ziel. Bereits erledigt: {completed} von {total}.",
"allObjectivesDoneDescription": "Alle sichtbaren Ziele sind erledigt. Jetzt zählt nur noch der saubere Abschluss des Levels."
}
}, },
"taxi": { "taxi": {
"title": "Taxi Simulator", "title": "Taxi Simulator",

View File

@@ -38,7 +38,20 @@
"objectivesShow": "Show objectives", "objectivesShow": "Show objectives",
"objectives": "Objectives", "objectives": "Objectives",
"loadingBoard": "Preparing game board...", "loadingBoard": "Preparing game board...",
"loadingHint": "Level data, objectives and board layout are being synchronized." "loadingHint": "Level data, objectives and board layout are being synchronized.",
"badgeMinigames": "Mini games",
"objectivesProgress": "{completed}/{total} objectives",
"movesLeftBadge": "{count} moves left",
"playFocus": {
"pausedTitle": "Game is paused",
"pausedDescription": "Resume the level or restart in a controlled way without losing context.",
"preparingTitle": "Level is loading",
"preparingDescription": "Once the level loads, the next goal and main action will appear here.",
"defaultObjectiveTitle": "Complete the current objective",
"finishLevelTitle": "Finish the level cleanly",
"focusDescription": "Focus on this goal first. Already completed: {completed} of {total}.",
"allObjectivesDoneDescription": "All visible objectives are done. Now finish the level cleanly."
}
}, },
"taxi": { "taxi": {
"title": "Taxi Simulator", "title": "Taxi Simulator",

View File

@@ -38,7 +38,20 @@
"objectivesShow": "Mostrar objetivos", "objectivesShow": "Mostrar objetivos",
"objectives": "Objetivos", "objectives": "Objetivos",
"loadingBoard": "Preparando el tablero…", "loadingBoard": "Preparando el tablero…",
"loadingHint": "Sincronizando datos del nivel, objetivos y disposición del campo." "loadingHint": "Sincronizando datos del nivel, objetivos y disposición del campo.",
"badgeMinigames": "Minijuegos",
"objectivesProgress": "{completed}/{total} objetivos",
"movesLeftBadge": "{count} movimientos restantes",
"playFocus": {
"pausedTitle": "Juego en pausa",
"pausedDescription": "Continúa el nivel o reinícialo de forma controlada sin perder el contexto.",
"preparingTitle": "Cargando nivel",
"preparingDescription": "Cuando el nivel esté cargado, aquí aparecerá el siguiente objetivo y la acción principal.",
"defaultObjectiveTitle": "Completa el objetivo actual",
"finishLevelTitle": "Termina el nivel limpiamente",
"focusDescription": "Concéntrate primero en este objetivo. Ya completados: {completed} de {total}.",
"allObjectivesDoneDescription": "Todos los objetivos visibles están hechos. Ahora solo importa terminar el nivel limpiamente."
}
}, },
"taxi": { "taxi": {
"title": "Taxi Simulator", "title": "Taxi Simulator",

View File

@@ -38,7 +38,20 @@
"objectivesShow": "Afficher les objectifs", "objectivesShow": "Afficher les objectifs",
"objectives": "Objectifs", "objectives": "Objectifs",
"loadingBoard": "Le plateau de jeu est en préparation...", "loadingBoard": "Le plateau de jeu est en préparation...",
"loadingHint": "Les données de niveau, les objectifs et la disposition du terrain sont actuellement en cours de synchronisation." "loadingHint": "Les données de niveau, les objectifs et la disposition du terrain sont actuellement en cours de synchronisation.",
"badgeMinigames": "Mini-jeux",
"objectivesProgress": "{completed}/{total} objectifs",
"movesLeftBadge": "{count} coups restants",
"playFocus": {
"pausedTitle": "Jeu en pause",
"pausedDescription": "Reprends le niveau ou relance-le proprement sans perdre le contexte.",
"preparingTitle": "Chargement du niveau",
"preparingDescription": "Une fois le niveau chargé, le prochain objectif et laction principale apparaîtront ici.",
"defaultObjectiveTitle": "Terminer lobjectif en cours",
"finishLevelTitle": "Terminer le niveau proprement",
"focusDescription": "Concentre-toi dabord sur cet objectif. Déjà réalisés : {completed} sur {total}.",
"allObjectivesDoneDescription": "Tous les objectifs visibles sont faits. Il ne reste plus quà finir le niveau proprement."
}
}, },
"taxi": { "taxi": {
"title": "Simulateur de taxi", "title": "Simulateur de taxi",

View File

@@ -2,28 +2,33 @@
<div class="match3-view"> <div class="match3-view">
<!-- Spiel-Titel --> <!-- Spiel-Titel -->
<section class="game-title surface-card"> <section class="game-title surface-card">
<span class="game-title__eyebrow">Minispiele</span> <span class="game-title__eyebrow">{{ $t('minigames.match3.badgeMinigames') }}</span>
<h1>{{ $t('minigames.match3.title') }}</h1> <h1>{{ $t('minigames.match3.title') }}</h1>
<p>{{ $t('minigames.match3.campaignDescription') }}</p> <p>{{ $t('minigames.match3.campaignDescription') }}</p>
</section> </section>
<section class="play-focus surface-card"> <section class="play-focus surface-card">
<div class="play-focus__main"> <div class="play-focus__main">
<span class="play-focus__eyebrow">Nächster Schritt</span> <span class="play-focus__eyebrow">{{ $t('minigames.match3.nextStep') }}</span>
<h2>{{ playFocusTitle }}</h2> <h2>{{ playFocusTitle }}</h2>
<p>{{ playFocusDescription }}</p> <p>{{ playFocusDescription }}</p>
</div> </div>
<div class="play-focus__stats"> <div class="play-focus__stats">
<span class="play-focus__pill">Level {{ currentLevel }}</span> <span class="play-focus__pill">{{ $t('minigames.match3.level') }} {{ currentLevel }}</span>
<span class="play-focus__pill">{{ completedObjectivesCount }}/{{ totalObjectivesCount || 0 }} Ziele</span> <span class="play-focus__pill">{{
<span class="play-focus__pill">{{ safeMovesLeft }} Züge übrig</span> $t('minigames.match3.objectivesProgress', {
completed: completedObjectivesCount,
total: totalObjectivesCount || 0
})
}}</span>
<span class="play-focus__pill">{{ $t('minigames.match3.movesLeftBadge', { count: safeMovesLeft }) }}</span>
</div> </div>
<div class="play-focus__actions"> <div class="play-focus__actions">
<button class="btn btn-primary" @click="isPaused ? resumeGame() : pauseGame()"> <button class="btn btn-primary" @click="isPaused ? resumeGame() : pauseGame()">
{{ isPaused ? $t('minigames.match3.resume') : $t('minigames.match3.pause') }} {{ isPaused ? $t('minigames.match3.resume') : $t('minigames.match3.pause') }}
</button> </button>
<button class="btn btn-secondary" @click="toggleLevelDescription"> <button class="btn btn-secondary" @click="toggleLevelDescription">
{{ levelDescriptionExpanded ? 'Ziele einklappen' : 'Ziele anzeigen' }} {{ levelDescriptionExpanded ? $t('minigames.match3.objectivesCollapse') : $t('minigames.match3.objectivesShow') }}
</button> </button>
<button class="btn btn-secondary" @click="restartLevel"> <button class="btn btn-secondary" @click="restartLevel">
{{ $t('minigames.match3.restartLevel') }} {{ $t('minigames.match3.restartLevel') }}
@@ -140,8 +145,8 @@
<!-- Loading State --> <!-- Loading State -->
<div v-else class="game-board-loading"> <div v-else class="game-board-loading">
<p>Spielbrett wird vorbereitet...</p> <p>{{ $t('minigames.match3.loadingBoard') }}</p>
<p class="game-board-loading__hint">Leveldaten, Ziele und Feldlayout werden gerade synchronisiert.</p> <p class="game-board-loading__hint">{{ $t('minigames.match3.loadingHint') }}</p>
</div> </div>
<!-- Power-Up Animationen (relativ zum Game-Board) --> <!-- Power-Up Animationen (relativ zum Game-Board) -->
@@ -6062,27 +6067,33 @@ export default {
}, },
playFocusTitle() { playFocusTitle() {
if (this.isPaused) { if (this.isPaused) {
return 'Spiel ist pausiert'; return this.$t('minigames.match3.playFocus.pausedTitle');
} }
if (!this.currentLevelData) { if (!this.currentLevelData) {
return 'Level wird vorbereitet'; return this.$t('minigames.match3.playFocus.preparingTitle');
} }
if (this.nextPendingObjective) { if (this.nextPendingObjective) {
return this.nextPendingObjective.description || 'Aktuelles Ziel abschließen'; return (
this.nextPendingObjective.description ||
this.$t('minigames.match3.playFocus.defaultObjectiveTitle')
);
} }
return 'Level sauber zu Ende spielen'; return this.$t('minigames.match3.playFocus.finishLevelTitle');
}, },
playFocusDescription() { playFocusDescription() {
if (this.isPaused) { if (this.isPaused) {
return 'Setze das Level fort oder starte es kontrolliert neu, ohne den aktuellen Kontext zu verlieren.'; return this.$t('minigames.match3.playFocus.pausedDescription');
} }
if (!this.currentLevelData) { if (!this.currentLevelData) {
return 'Sobald das Level geladen ist, erscheinen hier das nächste Ziel und die passende Hauptaktion.'; return this.$t('minigames.match3.playFocus.preparingDescription');
} }
if (this.nextPendingObjective) { if (this.nextPendingObjective) {
return `Konzentriere dich zuerst auf dieses Ziel. Bereits erledigt: ${this.completedObjectivesCount} von ${this.totalObjectivesCount}.`; return this.$t('minigames.match3.playFocus.focusDescription', {
completed: this.completedObjectivesCount,
total: this.totalObjectivesCount
});
} }
return 'Alle sichtbaren Ziele sind erledigt. Jetzt zählt nur noch der saubere Abschluss des Levels.'; return this.$t('minigames.match3.playFocus.allObjectivesDoneDescription');
} }
} }
} }