` * ve tarayıcı diline düşer. * * ── ÇEVRİLMEYEN TERİMLER ──────────────────────────────────────────── * mono, stereo, stereo image, stereo field, width, mid, side, M/S, * correlation, phase, pan, dB, XP, level. * * Bunlar stüdyo jargonu: Türkçe/Almanca/İspanyolca/Portekizce konuşan bir * miks mühendisi de "width" der. Çevirmek metni okunaklı yapmıyor, tersine * kullanıcının aletindeki etiketle metni birbirinden koparıyor. Kod tabanının * mevcut Türkçe metni de zaten böyle yazılmış ("dar stereo image", "stereo * field") — bu dosya o kuralı beş dile birden uyguluyor. */ const STRINGS = { en: { 'round.reviewBadge': "you missed this before", 'explain.compare.subtle': "The two were genuinely close in width, so this is a threshold miss rather than a mistake. Instead of judging the overall size, listen to how solid the centre is: the wider one has a thinner, more hollow middle.", 'explain.compare.clear': "The gap between the two was wide enough to hear. A common trap is judging loudness instead of width — both were level-matched, so the only real difference is how far the sound reaches to the sides and how solid the middle stays.", 'explain.tooWide': "Your range was wider than this level allows, so it scored zero wherever the real width was. Bring it inside the allowed width first — accuracy only counts inside a legal range.", 'explain.near': "The real width fell just {p} percentage points outside your range — you were very close. On rounds you are unsure about, a slightly wider range would have caught it.", 'explain.guessedNarrow': "You guessed narrower than it was, by {p} points. Judging from the centre alone makes everything sound narrow; check how far out to the sides the edges of the sound actually reach.", 'explain.guessedWide': "You guessed wider than it was, by {p} points. A strong reverb tail or a bright top end can make a sound feel wide when the stereo image itself is not; listen to where the centre sits, not to how big the sound feels.", /* ── çerçeve ── */ 'app.title': 'Stereo Width', 'app.tagline': 'Ear training', 'badge.level': 'Level {n}', 'badge.compare': 'Comparison {n}/{max}', 'badge.estimate': 'Width estimate', 'theme.label': 'Theme: {mode}', 'theme.system': 'System', 'theme.light': 'Light', 'theme.dark': 'Dark', /* ── tanıtım ── */ 'intro.title': 'What is stereo width?', 'intro.body': 'Stereo width is how far a sound spreads across the stereo field. Panning tells you WHERE a sound sits; width tells you HOW MUCH SPACE it fills.', 'intro.demo': 'The two examples below are the same sound. The only difference is stereo width. Switch between them as often as you like.', 'intro.aLabel': 'A — narrow stereo image', 'intro.bLabel': 'B — wide stereo image', 'intro.cta': 'I hear the difference, start', 'intro.headphones': 'Listen on headphones. Stereo width does not read correctly on speakers.', 'intro.preparing': 'Preparing…', /* ── ilerleme ── */ 'stage.evidence': 'evidence {n} / {of}', 'stage.evidenceMove': 'evidence {from} → {to}', 'stage.atLevel': 'target level {lvl} · now {now}', 'stage.belowLevel': 'now {now} — warming up to the target', 'level.xp': '{cur} / {req} XP', 'difficulty.inLevel': 'Difficulty within level', /* ── soru ── */ /* ── yerleşim + set ──────────────────── */ 'ui.replay': 'space · listen again', 'ui.confirm': 'confirm →', 'ui.next': 'next →', 'ikiz.hint': 'a / b · listen', 'set.progressAria': 'Set progress', 'set.exit': 'end set', 'set.doneTitle': 'You can stop here', 'set.level': 'level', 'set.rounds': '{n} rounds in this set', 'set.new': 'New set', 'set.finish': 'Finish', 'ab.listen': 'listen', 'play.hint': 'listen to the sound', 'choice.aWider': 'A is wider', 'choice.bWider': 'B is wider', 'compare.question': 'Which sound is wider?', 'compare.rescue': 'This looks like hard going, so I opened the difference up a little. Take your time and switch between A and B a few times.', 'estimate.question': 'Which range is this sound’s stereo width in?', 'estimate.reference': 'Hear the reference (100%)', 'estimate.submit': 'Confirm range', /* ── sonuç ── */ 'verdict.correct': 'Correct', 'verdict.wrong': 'Wrong', 'verdict.inRange': 'Range held', 'verdict.tooWide': 'Range too wide', 'verdict.missed': 'Missed', 'result.bonus': ' (+{n} streak)', 'result.compare.correct': 'Correct — {which} was wider. {wide} ↔ {narrow}.', 'result.compare.wrong': 'Listen again. This time focus on how much of the stereo field the sound spreads across. The right answer was {which} ({wide}), the other was {narrow}.', 'result.estimate.correct': 'The real value was {target}; you said {low}–{high}.', 'result.estimate.wrong': 'The real value fell {dir} the range you chose. Real value {target}, you said {low}–{high}.', 'result.estimate.tooWide': 'The range you chose is wider than this level allows.', 'dir.below': 'below', 'dir.above': 'above', 'scope.sound': 'Sound', /* ── istatistik / alt bilgi ── */ 'stat.rounds': 'Round', 'stat.accuracy': 'Accuracy', 'stat.streak': 'Streak', 'stat.totalXp': 'Total XP', 'btn.next': 'Next round', 'btn.reset': 'Reset progress', 'confirm.reset': 'All progress will be deleted. Are you sure?', 'footer.weakest': 'Weakest area: {zone} ({accuracy})', /* ── genişlik bölgeleri ── */ 'zone.mono': 'Almost mono', 'zone.narrow': 'Narrow', 'zone.natural': 'Natural', 'zone.wide': 'Wide', 'zone.ultra': 'Very wide', /* ── müfredat ── */ 'level.compare-1.title': 'Very obvious difference', 'level.compare-2.title': 'Clear difference', 'level.compare-3.title': 'Medium difference', 'level.compare-4.title': 'Fine difference', 'level.compare-5.title': 'Very fine difference', 'level.compare-1.micro': 'A narrow stereo image feels compact and close to the centre. A wide stereo image makes the sound feel spread further out to the left and right.', 'level.compare-2.micro': 'The difference just got smaller. Focus on how much space the sound covers, not on where it comes from.', 'level.compare-3.micro': 'On headphones a wide sound seems to spill outside your head; a narrow sound gathers in the middle.', 'level.compare-4.micro': 'The difference is fine now. Listen to the edges of the sound, not its middle.', 'level.compare-5.micro': 'The last level of comparison training. Once you pass it you will start estimating width as a number.', 'level.estimate.title': 'Width estimate · {rank}', 'level.estimate.micro': 'From here on you hear a single sound. The scale: 0% mono, 100% natural stereo, 200% very wide. When you are unsure, keep the range wide — an honest range scores the most.', 'levelUp.toEstimate': 'You finished the comparison phase. From now on you will estimate width as a number.', 'levelUp.level': 'Level {level} unlocked.', /* ── rütbeler ── */ 'rank.apprentice': 'Apprentice ear', 'rank.assistant': 'Assistant', 'rank.mixEngineer': 'Mix engineer', 'rank.senior': 'Senior engineer', 'rank.mastering': 'Mastering ear', 'rank.reference': 'Reference ear', /* ── malzeme ── */ 'material.pad': 'Synth pad', 'material.warmpad': 'Dark pad', 'material.ambience': 'Ambience', 'material.hats': 'Hi-hat / shaker', 'material.perc': 'Percussion', 'material.loop': 'Drum loop', 'material.fx': 'FX / riser', 'material.vox': 'Vocal layer', /* ── Dinleme Odası (brief) — sınavsız, puansız keşif ─────────── */ 'room.screen.title': 'Listening room', 'room.screen.enter': 'Start the level', 'room.screen.back': 'Back to the room', 'room.screen.picker': 'Rooms', 'room.sequential': 'Play in order', 'room.auto': 'Hands-free A/B', 'room.material': 'Material', 'room.reference': 'Reference', 'room.compare-1.title': 'What is width, anyway?', 'room.compare-1.hint': 'Drag the slider all the way down to mono, then back up past natural. Width is the only thing changing.', 'room.compare-2.title': 'Width, or pan?', 'room.compare-2.hint': 'Two of these get wider or narrower. One does something else entirely: the whole image drifts to a side. Width leaves the centre alone — this does not.', 'room.compare-2.panned': 'Drifted instead', 'room.compare-3.title': 'The material decides', 'room.compare-3.hint': 'Same width, different source — switch the material below and listen to the same percentage change character.', 'room.compare-4.title': 'Width, or reverb?', 'room.compare-4.hint': 'A reverberant narrow sound can feel almost as spacious as a wide dry one — but it is not the same thing.', 'room.compare-4.dryNarrow': 'Dry, narrow', 'room.compare-4.dryWide': 'Dry, wide', 'room.compare-4.reverbNarrow': 'Reverberant, narrow', 'room.compare-5.title': 'Where the centre hollows out', 'room.compare-5.hint': 'The two buttons are a fine difference near natural width. The slider goes further — past 150%, past 190% — to where extreme width starts to feel hollow and less mono-compatible.', 'room.estimate.title': 'Estimate the width yourself', 'room.estimate.hint': 'Drag across the whole range and read the number — this is the scale every estimate level uses.', 'room.screen.kind': '· LISTENING ROOM', 'room.screen.close': 'Close', 'room.screen.note': 'no score · not recorded · does not affect progress', 'room.screen.rooms': 'Rooms', 'room.screen.progressNote': 'listening does not affect progress', 'room.list.done': 'heard', 'room.list.here': 'here', 'room.list.locked': 'locked', 'room.ray.position': 'Width', 'room.ray.intensity': 'Difference', 'room.ray.exaggerated': '{v} · exaggerated', 'room.ray.real': '{v} · real', /* ── eksen ── */ 'axis.aria': 'Stereo width range', 'axis.lowBound': 'Lower bound', 'axis.highBound': 'Upper bound', 'axis.target': 'real {v}%', 'axis.readout': '{used}% of the allowed range · a narrow, accurate range scores more', /* ── geri bildirim sesi ── */ 'sound.title': 'Feedback sound', 'sound.on': 'Feedback sound: on', 'sound.off': 'Feedback sound: off', }, tr: { 'round.reviewBadge': "daha önce bunu kaçırmıştın", 'explain.compare.subtle': "İkisi gerçekten birbirine yakın genişlikteydi, yani bu bir hata değil eşik meselesi. Genel büyüklüğe bakmak yerine merkezin ne kadar dolu olduğunu dinle: geniş olanın ortası daha ince ve daha boş olur.", 'explain.compare.clear': "İkisi arasındaki fark duyulacak kadar açıktı. Sık düşülen tuzak genişlik yerine yüksekliğe bakmaktır — ikisinin seviyesi eşitlendi, yani tek gerçek fark sesin yanlara ne kadar uzandığı ve ortasının ne kadar dolu kaldığı.", 'explain.tooWide': "Aralığın bu seviyenin izin verdiğinden genişti, bu yüzden gerçek genişlik nerede olursa olsun sıfır aldı. Önce izin verilen genişliğin içine çek — isabet ancak geçerli bir aralıkta sayılır.", 'explain.near': "Gerçek genişlik, aralığının yalnızca {p} puan dışına düştü — çok yakındın. Emin olmadığın turlarda biraz geniş bir aralık bunu yakalardı.", 'explain.guessedNarrow': "Olduğundan dar tahmin ettin, {p} puan kadar. Yalnızca merkeze bakarak karar vermek her şeyi dar duyurur; sesin kenarlarının yanlara gerçekte ne kadar uzandığını kontrol et.", 'explain.guessedWide': "Olduğundan geniş tahmin ettin, {p} puan kadar. Güçlü bir reverb kuyruğu ya da parlak bir tiz uç, stereo görüntü aslında geniş değilken sesi geniş hissettirebilir; sesin ne kadar büyük hissettirdiğine değil, merkezin nerede durduğuna kulak ver.", 'app.title': 'Stereo Width', 'app.tagline': 'Kulak eğitimi', 'badge.level': 'Level {n}', 'badge.compare': 'Karşılaştırma {n}/{max}', 'badge.estimate': 'Width tahmini', 'theme.label': 'Tema: {mode}', 'theme.system': 'Sistem', 'theme.light': 'Açık', 'theme.dark': 'Koyu', 'intro.title': 'Stereo width nedir?', 'intro.body': 'Stereo width, bir sesin stereo field içinde ne kadar geniş yayıldığıdır. Pan sesin NEREDE olduğunu söyler; width ise NE KADAR ALANA yayıldığını.', 'intro.demo': 'Aşağıdaki iki örnek aynı ses. Aralarındaki tek fark stereo width. İstediğin kadar geçiş yap.', 'intro.aLabel': 'A — dar stereo image', 'intro.bLabel': 'B — geniş stereo image', 'intro.cta': 'Farkı duydum, başla', 'intro.headphones': 'Kulaklıkla dinle. Hoparlörde stereo width doğru duyulmaz.', 'intro.preparing': 'Hazırlanıyor…', 'stage.evidence': 'kanıt {n} / {of}', 'stage.evidenceMove': 'kanıt {from} → {to}', 'stage.atLevel': 'hedef seviye {lvl} · şu an {now}', 'stage.belowLevel': 'şu an {now} — hedefe ısınıyorsun', 'level.xp': '{cur} / {req} XP', 'difficulty.inLevel': 'Level içi zorluk', /* ── yerleşim + set ──────────────────── */ 'ui.replay': 'boşluk · tekrar dinle', 'ui.confirm': 'onayla →', 'ui.next': 'sonraki →', 'ikiz.hint': 'a / b · dinle', 'set.progressAria': 'Set ilerlemesi', 'set.exit': 'seti bitir', 'set.doneTitle': 'Durabilirsin', 'set.level': 'level', 'set.rounds': 'bu sette {n} round', 'set.new': 'Yeni set', 'set.finish': 'Bitir', 'ab.listen': 'dinle', 'play.hint': 'sesi dinle', 'choice.aWider': 'A daha geniş', 'choice.bWider': 'B daha geniş', 'compare.question': 'Hangi ses daha geniş?', 'compare.rescue': 'Zorlanıyorsun gibi görünüyor; farkı biraz açtım. Acele etme, A ve B arasında birkaç kez geçiş yap.', 'estimate.question': 'Bu sesin stereo width’i hangi aralıkta?', 'estimate.reference': 'Referans dinle (100%)', 'estimate.submit': 'Aralığı onayla', 'verdict.correct': 'Doğru', 'verdict.wrong': 'Yanlış', 'verdict.inRange': 'Aralık tuttu', 'verdict.tooWide': 'Aralık çok geniş', 'verdict.missed': 'Iskaladın', 'result.bonus': ' (+{n} seri)', 'result.compare.correct': 'Doğru — {which} daha genişti. {wide} ↔ {narrow}.', 'result.compare.wrong': 'Tekrar dinle. Bu sefer sesin stereo field içinde ne kadar alana yayıldığına odaklan. Doğru cevap {which} ({wide}), diğeri {narrow}.', 'result.estimate.correct': 'Gerçek değer {target}; sen {low}–{high} dedin.', 'result.estimate.wrong': 'Gerçek değer seçtiğin aralığın {dir} kaldı. Gerçek değer {target}, sen {low}–{high} dedin.', 'result.estimate.tooWide': 'Seçtiğin aralık bu level için izin verilenden geniş.', 'dir.below': 'altında', 'dir.above': 'üstünde', 'scope.sound': 'Ses', 'stat.rounds': 'Round', 'stat.accuracy': 'Doğruluk', 'stat.streak': 'Seri', 'stat.totalXp': 'Toplam XP', 'btn.next': 'Sıradaki round', 'btn.reset': 'İlerlemeyi sıfırla', 'confirm.reset': 'Tüm ilerleme silinecek. Emin misin?', 'footer.weakest': 'En zayıf bölge: {zone} ({accuracy})', 'zone.mono': 'Neredeyse mono', 'zone.narrow': 'Dar', 'zone.natural': 'Doğal', 'zone.wide': 'Geniş', 'zone.ultra': 'Çok geniş', 'level.compare-1.title': 'Çok belirgin fark', 'level.compare-2.title': 'Belirgin fark', 'level.compare-3.title': 'Orta seviye fark', 'level.compare-4.title': 'İnce fark', 'level.compare-5.title': 'Çok ince fark', 'level.compare-1.micro': 'Dar stereo image sesi merkeze yakın ve kompakt hissettirir. Geniş stereo image ise sesin sola ve sağa daha çok yayıldığını hissettirir.', 'level.compare-2.micro': 'Fark biraz küçüldü. Sesin nereden geldiğine değil, ne kadar alana yayıldığına odaklan.', 'level.compare-3.micro': 'Geniş bir ses kulaklıkta başın dışına taşar gibi duyulur; dar ses başın ortasında toplanır.', 'level.compare-4.micro': 'Fark artık ince. Sesin ortasını değil, kenarlarını dinle.', 'level.compare-5.micro': 'Karşılaştırma eğitiminin son seviyesi. Bunu geçince width’i sayıyla tahmin etmeye başlayacaksın.', 'level.estimate.title': 'Width tahmini · {rank}', 'level.estimate.micro': 'Artık tek ses duyacaksın. Ölçek: 0% mono, 100% doğal stereo, 200% çok geniş. Emin değilsen aralığı geniş tut — dürüst aralık en çok puanı getirir.', 'levelUp.toEstimate': 'Karşılaştırma fazını tamamladın. Artık width’i sayıyla tahmin edeceksin.', 'levelUp.level': 'Level {level} açıldı.', 'rank.apprentice': 'Çırak kulak', 'rank.assistant': 'Asistan', 'rank.mixEngineer': 'Miks mühendisi', 'rank.senior': 'Kıdemli mühendis', 'rank.mastering': 'Mastering kulağı', 'rank.reference': 'Referans kulak', 'material.pad': 'Synth pad', 'material.warmpad': 'Koyu pad', 'material.ambience': 'Ambiyans', 'material.hats': 'Hi-hat / shaker', 'material.perc': 'Perküsyon', 'material.loop': 'Davul loop', 'material.fx': 'FX / riser', 'material.vox': 'Vokal katmanı', /* ── Dinleme Odası (brief) — sınavsız, puansız keşif ─────────── */ 'room.screen.title': 'Dinleme odası', 'room.screen.enter': "Level'a başla", 'room.screen.back': 'Odaya dön', 'room.screen.picker': 'Odalar', 'room.sequential': 'Sırayla çal', 'room.auto': 'Eller serbest A/B', 'room.material': 'Malzeme', 'room.reference': 'Referans', 'room.compare-1.title': 'Genişlik nedir ki?', 'room.compare-1.hint': "Kaydırıcıyı en dibe, mono'ya kadar indir, sonra doğalın da ötesine çıkar. Değişen tek şey genişlik.", 'room.compare-2.title': 'Genişlik mi, pan mı?', 'room.compare-2.hint': 'Bunlardan ikisi genişliyor ya da daralıyor. Biri tamamen farklı bir şey yapıyor: bütün görüntü bir yana kayıyor. Genişlik merkeze dokunmaz — bu dokunuyor.', 'room.compare-2.panned': 'Bunun yerine kaydı', 'room.compare-3.title': 'Malzeme belirler', 'room.compare-3.hint': 'Aynı genişlik, farklı kaynak — aşağıdan malzemeyi değiştir, aynı yüzdenin karakterinin nasıl değiştiğini dinle.', 'room.compare-4.title': 'Genişlik mi, reverb mi?', 'room.compare-4.hint': "Reverb'li dar bir ses, geniş kuru bir sesle neredeyse aynı ferahlıkta hissedilebilir — ama aynı şey değildir.", 'room.compare-4.dryNarrow': 'Kuru, dar', 'room.compare-4.dryWide': 'Kuru, geniş', 'room.compare-4.reverbNarrow': "Reverb'li, dar", 'room.compare-5.title': 'İçi nerede boşalıyor', 'room.compare-5.hint': "İki düğme doğal genişliğe yakın ince bir farkı gösteriyor. Kaydırıcı daha ileri gidiyor — %150'yi, %190'ı geçip — aşırı genişliğin içinin boşalmaya ve mono uyumluluğunu kaybetmeye başladığı yere.", 'room.estimate.title': 'Genişliği kendin tahmin et', 'room.estimate.hint': 'Bütün aralıkta sürükle ve sayıyı oku — her tahmin level\'ının kullandığı ölçek bu.', 'room.screen.kind': '· DİNLEME ODASI', 'room.screen.close': 'Kapat', 'room.screen.note': 'puan yok · kayıt yok · ilerlemeyi etkilemez', 'room.screen.rooms': 'Odalar', 'room.screen.progressNote': 'dinlemek ilerlemeyi etkilemez', 'room.list.done': 'dinlendi', 'room.list.here': 'burada', 'room.list.locked': 'kilitli', 'room.ray.position': 'Genişlik', 'room.ray.intensity': 'Fark', 'room.ray.exaggerated': '{v} · abartılı', 'room.ray.real': '{v} · gerçek', 'axis.aria': 'Stereo width aralığı', 'axis.lowBound': 'Alt sınır', 'axis.highBound': 'Üst sınır', 'axis.target': 'gerçek {v}%', 'axis.readout': 'izin verilen aralığın %{used}’i · dar ve doğru aralık daha çok puan', 'sound.title': 'Geri bildirim sesi', 'sound.on': 'Geri bildirim sesi: açık', 'sound.off': 'Geri bildirim sesi: kapalı', }, de: { 'round.reviewBadge': "das hattest du schon einmal verpasst", 'explain.compare.subtle': "Die beiden waren wirklich ähnlich breit — das ist eher eine Schwellenfrage als ein Fehler. Statt die Gesamtgröße zu beurteilen, höre auf die Mitte: die breitere hat eine dünnere, hohlere Mitte.", 'explain.compare.clear': "Der Abstand war groß genug, um ihn zu hören. Eine häufige Falle ist, Lautheit statt Breite zu beurteilen — beide waren pegelangeglichen, der einzige echte Unterschied ist die seitliche Ausdehnung und wie solide die Mitte bleibt.", 'explain.tooWide': "Dein Bereich war breiter als erlaubt und bekam deshalb null. Bring ihn zuerst in die erlaubte Breite — Treffsicherheit zählt nur im gültigen Bereich.", 'explain.near': "Die echte Breite lag nur {p} Prozentpunkte außerhalb deines Bereichs — sehr knapp. Bei unsicheren Runden hätte ein etwas breiterer Bereich gereicht.", 'explain.guessedNarrow': "Du hast schmaler geschätzt, um {p} Punkte. Nur nach der Mitte zu urteilen lässt alles schmal klingen; prüfe, wie weit die Ränder des Klangs tatsächlich nach außen reichen.", 'explain.guessedWide': "Du hast breiter geschätzt, um {p} Punkte. Eine kräftige Reverb-Fahne oder ein heller Hochtonbereich kann breit wirken, obwohl das Stereobild es nicht ist; achte darauf, wo die Mitte sitzt, nicht wie groß es sich anfühlt.", 'app.title': 'Stereo Width', 'app.tagline': 'Gehörbildung', 'badge.level': 'Level {n}', 'badge.compare': 'Vergleich {n}/{max}', 'badge.estimate': 'Width schätzen', 'theme.label': 'Thema: {mode}', 'theme.system': 'System', 'theme.light': 'Hell', 'theme.dark': 'Dunkel', 'intro.title': 'Was ist Stereo Width?', 'intro.body': 'Stereo Width beschreibt, wie weit sich ein Klang im Stereo Field ausbreitet. Pan sagt dir, WO ein Klang sitzt; Width sagt dir, WIE VIEL RAUM er einnimmt.', 'intro.demo': 'Die beiden Beispiele unten sind derselbe Klang. Der einzige Unterschied ist die Stereo Width. Wechsle so oft du magst.', 'intro.aLabel': 'A — schmales Stereo Image', 'intro.bLabel': 'B — breites Stereo Image', 'intro.cta': 'Ich höre den Unterschied, los', 'intro.headphones': 'Hör über Kopfhörer. Über Lautsprecher lässt sich Stereo Width nicht sauber beurteilen.', 'intro.preparing': 'Wird vorbereitet…', 'stage.evidence': 'Nachweis {n} / {of}', 'stage.evidenceMove': 'Nachweis {from} → {to}', 'stage.atLevel': 'Ziel-Level {lvl} · jetzt {now}', 'stage.belowLevel': 'jetzt {now} — du wärmst dich zum Ziel auf', 'level.xp': '{cur} / {req} XP', 'difficulty.inLevel': 'Schwierigkeit im Level', /* ── yerleşim + set ──────────────────── */ 'ui.replay': 'leertaste · nochmal hören', 'ui.confirm': 'bestätigen →', 'ui.next': 'weiter →', 'ikiz.hint': 'a / b · hören', 'set.progressAria': 'Set-Fortschritt', 'set.exit': 'Set beenden', 'set.doneTitle': 'Du kannst hier aufhören', 'set.level': 'Level', 'set.rounds': '{n} Runden in diesem Set', 'set.new': 'Neues Set', 'set.finish': 'Fertig', 'ab.listen': 'hören', 'play.hint': 'Klang hören', 'choice.aWider': 'A ist breiter', 'choice.bWider': 'B ist breiter', 'compare.question': 'Welcher Klang ist breiter?', 'compare.rescue': 'Das sieht mühsam aus, also habe ich den Unterschied etwas vergrößert. Lass dir Zeit und wechsle ein paarmal zwischen A und B.', 'estimate.question': 'In welchem Bereich liegt die Stereo Width dieses Klangs?', 'estimate.reference': 'Referenz hören (100%)', 'estimate.submit': 'Bereich bestätigen', 'verdict.correct': 'Richtig', 'verdict.wrong': 'Falsch', 'verdict.inRange': 'Bereich getroffen', 'verdict.tooWide': 'Bereich zu breit', 'verdict.missed': 'Daneben', 'result.bonus': ' (+{n} Serie)', 'result.compare.correct': 'Richtig — {which} war breiter. {wide} ↔ {narrow}.', 'result.compare.wrong': 'Hör noch einmal hin. Achte diesmal darauf, wie viel Raum der Klang im Stereo Field einnimmt. Richtig war {which} ({wide}), der andere {narrow}.', 'result.estimate.correct': 'Der echte Wert war {target}; du hast {low}–{high} gesagt.', 'result.estimate.wrong': 'Der echte Wert lag {dir} deinem Bereich. Echter Wert {target}, du hast {low}–{high} gesagt.', 'result.estimate.tooWide': 'Dein Bereich ist breiter, als dieses Level erlaubt.', 'dir.below': 'unter', 'dir.above': 'über', 'scope.sound': 'Klang', 'stat.rounds': 'Runde', 'stat.accuracy': 'Trefferquote', 'stat.streak': 'Serie', 'stat.totalXp': 'Gesamt-XP', 'btn.next': 'Nächste Runde', 'btn.reset': 'Fortschritt zurücksetzen', 'confirm.reset': 'Der gesamte Fortschritt wird gelöscht. Sicher?', 'footer.weakest': 'Schwächster Bereich: {zone} ({accuracy})', 'zone.mono': 'Fast mono', 'zone.narrow': 'Schmal', 'zone.natural': 'Natürlich', 'zone.wide': 'Breit', 'zone.ultra': 'Sehr breit', 'level.compare-1.title': 'Sehr deutlicher Unterschied', 'level.compare-2.title': 'Deutlicher Unterschied', 'level.compare-3.title': 'Mittlerer Unterschied', 'level.compare-4.title': 'Feiner Unterschied', 'level.compare-5.title': 'Sehr feiner Unterschied', 'level.compare-1.micro': 'Ein schmales Stereo Image wirkt kompakt und nah an der Mitte. Ein breites Stereo Image lässt den Klang weiter nach links und rechts ausgreifen.', 'level.compare-2.micro': 'Der Unterschied ist kleiner geworden. Achte darauf, wie viel Raum der Klang einnimmt, nicht darauf, woher er kommt.', 'level.compare-3.micro': 'Über Kopfhörer scheint ein breiter Klang aus dem Kopf herauszulaufen; ein schmaler sammelt sich in der Mitte.', 'level.compare-4.micro': 'Der Unterschied ist jetzt fein. Hör auf die Ränder des Klangs, nicht auf seine Mitte.', 'level.compare-5.micro': 'Die letzte Stufe des Vergleichstrainings. Danach schätzt du Width als Zahl.', 'level.estimate.title': 'Width schätzen · {rank}', 'level.estimate.micro': 'Ab jetzt hörst du einen einzelnen Klang. Die Skala: 0% mono, 100% natürliches Stereo, 200% sehr breit. Wenn du unsicher bist, halte den Bereich breit — ein ehrlicher Bereich bringt die meisten Punkte.', 'levelUp.toEstimate': 'Die Vergleichsphase ist abgeschlossen. Ab jetzt schätzt du Width als Zahl.', 'levelUp.level': 'Level {level} freigeschaltet.', 'rank.apprentice': 'Lehrlingsohr', 'rank.assistant': 'Assistent', 'rank.mixEngineer': 'Mixing Engineer', 'rank.senior': 'Senior Engineer', 'rank.mastering': 'Mastering-Ohr', 'rank.reference': 'Referenzohr', 'material.pad': 'Synth Pad', 'material.warmpad': 'Dunkles Pad', 'material.ambience': 'Ambience', 'material.hats': 'Hi-hat / Shaker', 'material.perc': 'Perkussion', 'material.loop': 'Drum-Loop', 'material.fx': 'FX / Riser', 'material.vox': 'Vocal-Layer', 'axis.aria': 'Stereo-Width-Bereich', 'axis.lowBound': 'Untere Grenze', 'axis.highBound': 'Obere Grenze', 'axis.target': 'echt {v}%', 'axis.readout': '{used}% des erlaubten Bereichs · ein schmaler, treffender Bereich bringt mehr Punkte', 'sound.title': 'Feedback-Sound', 'sound.on': 'Feedback-Sound: an', 'sound.off': 'Feedback-Sound: aus', }, es: { 'round.reviewBadge': "esto se te escapó antes", 'explain.compare.subtle': "Los dos eran de ancho realmente parecido, así que es un fallo de umbral, no un error. En vez de juzgar el tamaño general, escucha qué tan sólido es el centro: el más ancho tiene el medio más fino y hueco.", 'explain.compare.clear': "La diferencia entre ambos era lo bastante grande para oírse. Una trampa común es juzgar volumen en vez de anchura: los dos estaban igualados en nivel, así que lo único que cambia es cuánto se extiende el sonido a los lados y cuán sólido queda el medio.", 'explain.tooWide': "Tu rango superaba lo permitido en este nivel, así que sacó cero. Redúcelo primero a lo permitido: acertar solo cuenta dentro de un rango válido.", 'explain.near': "El ancho real cayó apenas {p} puntos fuera de tu rango: estuviste muy cerca. En rondas dudosas, un rango algo más ancho lo habría atrapado.", 'explain.guessedNarrow': "Estimaste más estrecho de lo que era, por {p} puntos. Juzgar solo por el centro hace que todo suene estrecho; comprueba hasta dónde llegan de verdad los bordes hacia los lados.", 'explain.guessedWide': "Estimaste más ancho de lo que era, por {p} puntos. Una cola de reverb fuerte o unos agudos brillantes pueden dar sensación de anchura aunque la imagen estéreo no lo sea; escucha dónde se sitúa el centro, no cuán grande se siente.", 'app.title': 'Stereo Width', 'app.tagline': 'Entrenamiento auditivo', 'badge.level': 'Level {n}', 'badge.compare': 'Comparación {n}/{max}', 'badge.estimate': 'Estimar width', 'theme.label': 'Tema: {mode}', 'theme.system': 'Sistema', 'theme.light': 'Claro', 'theme.dark': 'Oscuro', 'intro.title': '¿Qué es el stereo width?', 'intro.body': 'El stereo width es cuánto se extiende un sonido por el stereo field. El pan dice DÓNDE está el sonido; el width dice CUÁNTO ESPACIO ocupa.', 'intro.demo': 'Los dos ejemplos de abajo son el mismo sonido. La única diferencia es el stereo width. Cambia entre ellos tantas veces como quieras.', 'intro.aLabel': 'A — stereo image estrecha', 'intro.bLabel': 'B — stereo image ancha', 'intro.cta': 'Oigo la diferencia, empezar', 'intro.headphones': 'Escucha con auriculares. En altavoces el stereo width no se juzga bien.', 'intro.preparing': 'Preparando…', 'stage.evidence': 'evidencia {n} / {of}', 'stage.evidenceMove': 'evidencia {from} → {to}', 'stage.atLevel': 'nivel objetivo {lvl} · ahora {now}', 'stage.belowLevel': 'ahora {now} — calentando hacia el objetivo', 'level.xp': '{cur} / {req} XP', 'difficulty.inLevel': 'Dificultad dentro del level', /* ── yerleşim + set ──────────────────── */ 'ui.replay': 'espacio · escuchar otra vez', 'ui.confirm': 'confirmar →', 'ui.next': 'siguiente →', 'ikiz.hint': 'a / b · escuchar', 'set.progressAria': 'Progreso de la serie', 'set.exit': 'terminar la serie', 'set.doneTitle': 'Puedes parar aquí', 'set.level': 'nivel', 'set.rounds': '{n} rondas en esta serie', 'set.new': 'Nueva serie', 'set.finish': 'Terminar', 'ab.listen': 'escuchar', 'play.hint': 'escuchar el sonido', 'choice.aWider': 'A es más ancho', 'choice.bWider': 'B es más ancho', 'compare.question': '¿Qué sonido es más ancho?', 'compare.rescue': 'Parece que cuesta, así que he abierto un poco la diferencia. Tómate tu tiempo y cambia varias veces entre A y B.', 'estimate.question': '¿En qué rango está el stereo width de este sonido?', 'estimate.reference': 'Oír la referencia (100%)', 'estimate.submit': 'Confirmar rango', 'verdict.correct': 'Correcto', 'verdict.wrong': 'Incorrecto', 'verdict.inRange': 'Rango acertado', 'verdict.tooWide': 'Rango demasiado ancho', 'verdict.missed': 'Fallaste', 'result.bonus': ' (+{n} racha)', 'result.compare.correct': 'Correcto — {which} era más ancho. {wide} ↔ {narrow}.', 'result.compare.wrong': 'Escucha otra vez. Esta vez fíjate en cuánto espacio ocupa el sonido dentro del stereo field. La respuesta era {which} ({wide}); el otro, {narrow}.', 'result.estimate.correct': 'El valor real era {target}; dijiste {low}–{high}.', 'result.estimate.wrong': 'El valor real quedó {dir} del rango que elegiste. Valor real {target}, dijiste {low}–{high}.', 'result.estimate.tooWide': 'El rango que elegiste es más ancho de lo que permite este level.', 'dir.below': 'por debajo', 'dir.above': 'por encima', 'scope.sound': 'Sonido', 'stat.rounds': 'Ronda', 'stat.accuracy': 'Acierto', 'stat.streak': 'Racha', 'stat.totalXp': 'XP total', 'btn.next': 'Siguiente ronda', 'btn.reset': 'Reiniciar progreso', 'confirm.reset': 'Se borrará todo el progreso. ¿Seguro?', 'footer.weakest': 'Zona más floja: {zone} ({accuracy})', 'zone.mono': 'Casi mono', 'zone.narrow': 'Estrecho', 'zone.natural': 'Natural', 'zone.wide': 'Ancho', 'zone.ultra': 'Muy ancho', 'level.compare-1.title': 'Diferencia muy obvia', 'level.compare-2.title': 'Diferencia clara', 'level.compare-3.title': 'Diferencia media', 'level.compare-4.title': 'Diferencia fina', 'level.compare-5.title': 'Diferencia muy fina', 'level.compare-1.micro': 'Una stereo image estrecha se siente compacta y cerca del centro. Una stereo image ancha hace que el sonido se extienda más hacia la izquierda y la derecha.', 'level.compare-2.micro': 'La diferencia se ha reducido. Fíjate en cuánto espacio ocupa el sonido, no en de dónde viene.', 'level.compare-3.micro': 'En auriculares un sonido ancho parece salirse de la cabeza; uno estrecho se concentra en el centro.', 'level.compare-4.micro': 'La diferencia ya es fina. Escucha los bordes del sonido, no su centro.', 'level.compare-5.micro': 'El último nivel del entrenamiento de comparación. Al superarlo empezarás a estimar el width con un número.', 'level.estimate.title': 'Estimar width · {rank}', 'level.estimate.micro': 'A partir de ahora oirás un solo sonido. La escala: 0% mono, 100% stereo natural, 200% muy ancho. Si dudas, mantén el rango ancho — un rango honesto da más puntos.', 'levelUp.toEstimate': 'Has terminado la fase de comparación. A partir de ahora estimarás el width con un número.', 'levelUp.level': 'Level {level} desbloqueado.', 'rank.apprentice': 'Oído aprendiz', 'rank.assistant': 'Asistente', 'rank.mixEngineer': 'Ingeniero de mezcla', 'rank.senior': 'Ingeniero sénior', 'rank.mastering': 'Oído de mastering', 'rank.reference': 'Oído de referencia', 'material.pad': 'Synth pad', 'material.warmpad': 'Pad oscuro', 'material.ambience': 'Ambiente', 'material.hats': 'Hi-hat / shaker', 'material.perc': 'Percusión', 'material.loop': 'Loop de batería', 'material.fx': 'FX / riser', 'material.vox': 'Capa vocal', 'axis.aria': 'Rango de stereo width', 'axis.lowBound': 'Límite inferior', 'axis.highBound': 'Límite superior', 'axis.target': 'real {v}%', 'axis.readout': '{used}% del rango permitido · un rango estrecho y certero da más puntos', 'sound.title': 'Sonido de feedback', 'sound.on': 'Sonido de feedback: activado', 'sound.off': 'Sonido de feedback: desactivado', }, pt: { 'round.reviewBadge': "você errou isto antes", 'explain.compare.subtle': "Os dois tinham largura realmente parecida, então é uma questão de limiar, não um erro. Em vez de julgar o tamanho geral, ouça o quanto o centro é sólido: o mais largo tem o meio mais fino e oco.", 'explain.compare.clear': "A diferença entre os dois era grande o bastante para ser ouvida. Uma armadilha comum é julgar volume em vez de largura — os dois estavam igualados em nível, então a única diferença real é o quanto o som se estende para os lados e o quanto o meio permanece sólido.", 'explain.tooWide': "Seu intervalo passava do permitido neste nível, então ficou zero. Traga-o para dentro da largura permitida — acertar só conta dentro de um intervalo válido.", 'explain.near': "A largura real caiu só {p} pontos fora do seu intervalo — você chegou muito perto.", 'explain.guessedNarrow': "Você estimou mais estreito do que era, por {p} pontos. Julgar só pelo centro faz tudo soar estreito; verifique até onde as bordas do som realmente chegam para os lados.", 'explain.guessedWide': "Você estimou mais largo do que era, por {p} pontos. Uma cauda de reverb forte ou agudos brilhantes podem dar sensação de largura mesmo quando a imagem estéreo não é; ouça onde o centro se assenta, não o quanto o som parece grande.", 'app.title': 'Stereo Width', 'app.tagline': 'Treino auditivo', 'badge.level': 'Level {n}', 'badge.compare': 'Comparação {n}/{max}', 'badge.estimate': 'Estimar width', 'theme.label': 'Tema: {mode}', 'theme.system': 'Sistema', 'theme.light': 'Claro', 'theme.dark': 'Escuro', 'intro.title': 'O que é stereo width?', 'intro.body': 'Stereo width é o quanto um som se espalha pelo stereo field. O pan diz ONDE o som está; o width diz QUANTO ESPAÇO ele ocupa.', 'intro.demo': 'Os dois exemplos abaixo são o mesmo som. A única diferença é o stereo width. Alterne entre eles quantas vezes quiser.', 'intro.aLabel': 'A — stereo image estreita', 'intro.bLabel': 'B — stereo image larga', 'intro.cta': 'Ouvi a diferença, começar', 'intro.headphones': 'Ouça com fones. Em alto-falantes o stereo width não é julgado corretamente.', 'intro.preparing': 'Preparando…', 'stage.evidence': 'evidência {n} / {of}', 'stage.evidenceMove': 'evidência {from} → {to}', 'stage.atLevel': 'nível alvo {lvl} · agora {now}', 'stage.belowLevel': 'agora {now} — aquecendo até o alvo', 'level.xp': '{cur} / {req} XP', 'difficulty.inLevel': 'Dificuldade dentro do level', /* ── yerleşim + set ──────────────────── */ 'ui.replay': 'espaço · ouvir de novo', 'ui.confirm': 'confirmar →', 'ui.next': 'próxima →', 'ikiz.hint': 'a / b · ouvir', 'set.progressAria': 'Progresso do set', 'set.exit': 'encerrar o set', 'set.doneTitle': 'Você pode parar aqui', 'set.level': 'nível', 'set.rounds': '{n} rodadas neste set', 'set.new': 'Novo set', 'set.finish': 'Concluir', 'ab.listen': 'ouvir', 'play.hint': 'ouvir o som', 'choice.aWider': 'A é mais largo', 'choice.bWider': 'B é mais largo', 'compare.question': 'Qual som é mais largo?', 'compare.rescue': 'Parece estar difícil, então abri um pouco a diferença. Sem pressa: alterne algumas vezes entre A e B.', 'estimate.question': 'Em que intervalo está o stereo width deste som?', 'estimate.reference': 'Ouvir a referência (100%)', 'estimate.submit': 'Confirmar intervalo', 'verdict.correct': 'Correto', 'verdict.wrong': 'Errado', 'verdict.inRange': 'Intervalo acertou', 'verdict.tooWide': 'Intervalo largo demais', 'verdict.missed': 'Errou', 'result.bonus': ' (+{n} sequência)', 'result.compare.correct': 'Correto — {which} era mais largo. {wide} ↔ {narrow}.', 'result.compare.wrong': 'Ouça de novo. Desta vez repare em quanto espaço o som ocupa dentro do stereo field. A resposta certa era {which} ({wide}); o outro, {narrow}.', 'result.estimate.correct': 'O valor real era {target}; você disse {low}–{high}.', 'result.estimate.wrong': 'O valor real ficou {dir} do intervalo que você escolheu. Valor real {target}, você disse {low}–{high}.', 'result.estimate.tooWide': 'O intervalo que você escolheu é mais largo do que este level permite.', 'dir.below': 'abaixo', 'dir.above': 'acima', 'scope.sound': 'Som', 'stat.rounds': 'Rodada', 'stat.accuracy': 'Acerto', 'stat.streak': 'Sequência', 'stat.totalXp': 'XP total', 'btn.next': 'Próxima rodada', 'btn.reset': 'Reiniciar progresso', 'confirm.reset': 'Todo o progresso será apagado. Tem certeza?', 'footer.weakest': 'Área mais fraca: {zone} ({accuracy})', 'zone.mono': 'Quase mono', 'zone.narrow': 'Estreito', 'zone.natural': 'Natural', 'zone.wide': 'Largo', 'zone.ultra': 'Muito largo', 'level.compare-1.title': 'Diferença muito óbvia', 'level.compare-2.title': 'Diferença clara', 'level.compare-3.title': 'Diferença média', 'level.compare-4.title': 'Diferença fina', 'level.compare-5.title': 'Diferença muito fina', 'level.compare-1.micro': 'Uma stereo image estreita soa compacta e perto do centro. Uma stereo image larga faz o som se espalhar mais para a esquerda e para a direita.', 'level.compare-2.micro': 'A diferença diminuiu. Repare em quanto espaço o som ocupa, não de onde ele vem.', 'level.compare-3.micro': 'Em fones, um som largo parece sair para fora da cabeça; um som estreito se junta no meio.', 'level.compare-4.micro': 'A diferença agora é fina. Ouça as bordas do som, não o meio dele.', 'level.compare-5.micro': 'O último nível do treino de comparação. Ao passar, você começa a estimar o width em número.', 'level.estimate.title': 'Estimar width · {rank}', 'level.estimate.micro': 'Daqui em diante você ouve um único som. A escala: 0% mono, 100% stereo natural, 200% muito largo. Na dúvida, mantenha o intervalo largo — um intervalo honesto rende mais pontos.', 'levelUp.toEstimate': 'Você concluiu a fase de comparação. Agora vai estimar o width em número.', 'levelUp.level': 'Level {level} liberado.', 'rank.apprentice': 'Ouvido aprendiz', 'rank.assistant': 'Assistente', 'rank.mixEngineer': 'Engenheiro de mixagem', 'rank.senior': 'Engenheiro sênior', 'rank.mastering': 'Ouvido de mastering', 'rank.reference': 'Ouvido de referência', 'material.pad': 'Synth pad', 'material.warmpad': 'Pad escuro', 'material.ambience': 'Ambiência', 'material.hats': 'Hi-hat / shaker', 'material.perc': 'Percussão', 'material.loop': 'Loop de bateria', 'material.fx': 'FX / riser', 'material.vox': 'Camada vocal', 'axis.aria': 'Intervalo de stereo width', 'axis.lowBound': 'Limite inferior', 'axis.highBound': 'Limite superior', 'axis.target': 'real {v}%', 'axis.readout': '{used}% do intervalo permitido · um intervalo estreito e certeiro rende mais pontos', 'sound.title': 'Som de feedback', 'sound.on': 'Som de feedback: ligado', 'sound.off': 'Som de feedback: desligado', }, }; /* ── src\core\i18n.js ── */ /** * i18n.js — oyunun kendi metinleri. * * KURAL: oyun, platform olmadan da tek başına çalışmak zorunda. Bu yüzden * çeviriler platformda değil, OYUNUN İÇİNDE durur; platform yalnızca * "hangi dil" bilgisini geçirir. * * Dil çözümleme sırası: * 1. `window.__ET_LOCALE__` — platform köprüsü yazar (site diliyle aynı) * 2. `` — oyun tek başına açıldıysa * 3. `navigator.language` * 4. 'en' — kaynak dil * * Eksik anahtar sessizce İngilizceye düşer; oyun hiçbir dilde kırılmaz. */ const LOCALES = ['en', 'tr', 'de', 'es', 'pt']; const DEFAULT_LOCALE = 'en'; function resolveLocale(catalogs) { const known = (code) => { if (!code) return null; const base = String(code).toLowerCase().split(/[-_]/)[0]; return catalogs[base] ? base : null; }; return known(globalThis.__ET_LOCALE__) ?? known(globalThis.document?.documentElement?.lang) ?? known(globalThis.navigator?.language) ?? DEFAULT_LOCALE; } /** * @param {Record>} catalogs { en: {...}, tr: {...} } * @param {string} [locale] */ function createT(catalogs, locale = resolveLocale(catalogs)) { const cat = catalogs[locale] ?? catalogs[DEFAULT_LOCALE]; const fallback = catalogs[DEFAULT_LOCALE]; const t = (key, params) => { let msg = cat[key]; if (msg === undefined) msg = fallback[key]; if (msg === undefined) return key; // görünür kalsın if (!params) return msg; return msg.replace(/\{(\w+)\}/g, (m, k) => (params[k] ?? m)); }; t.locale = locale; /** * HTML'deki sabit metinleri çevirir. * Round *