strudel/website/src/i18n.mjs
hacklab 7b9f1155b4 Mejoras hacklab: i18n, pestaña Projects, transporte con seek, guía y temas
- i18n ligero de la interfaz del REPL (castellano, inglés, francés, alemán,
  italiano, portugués...) con selector en el header
- Pestaña Projects: gestor local de proyectos con carpetas, buscador y
  librería de tracks de fábrica (bases techno/acid originales)
- Barra de transporte bajo el editor: play/pausa/anterior/siguiente, tiempo
  y barra de progreso arrastrable; seek real vía Cyclist.setCycle()
- Pestaña Guía: chuleta de síntesis y sonidos para el directo
- Packs de temas del editor: neon-madrid + hacklab-pack (5 neón + 5 estándar)
- pnpm-workspace: allowBuilds para instalación limpia en Linux
2026-07-17 01:47:12 +02:00

725 lines
19 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// i18n ligero para la interfaz del REPL.
// El idioma vive en settings ('language') y se cambia desde el selector del header.
// t(key) devuelve la cadena en el idioma activo (con fallback inglés → clave).
import { settingsMap, useSettings } from './settings.mjs';
export const LANGS = {
es: 'Español',
en: 'English',
fr: 'Français',
de: 'Deutsch',
it: 'Italiano',
pt: 'Português',
nl: 'Nederlands',
pl: 'Polski',
cs: 'Čeština',
sk: 'Slovenčina',
ro: 'Română',
hu: 'Magyar',
el: 'Ελληνικά',
sv: 'Svenska',
da: 'Dansk',
no: 'Norsk',
fi: 'Suomi',
uk: 'Українська',
};
// Diccionario: clave → { idioma: texto }. Orden de idiomas igual que LANGS.
const S = {
play: {
es: 'play',
en: 'play',
fr: 'lecture',
de: 'Play',
it: 'play',
pt: 'tocar',
nl: 'afspelen',
pl: 'graj',
cs: 'přehrát',
sk: 'prehrať',
ro: 'redare',
hu: 'lejátszás',
el: 'αναπαραγωγή',
sv: 'spela',
da: 'afspil',
no: 'spill',
fi: 'toista',
uk: 'грати',
},
stop: {
es: 'stop',
en: 'stop',
fr: 'stop',
de: 'Stopp',
it: 'stop',
pt: 'parar',
nl: 'stop',
pl: 'stop',
cs: 'stop',
sk: 'stop',
ro: 'stop',
hu: 'állj',
el: 'στοπ',
sv: 'stopp',
da: 'stop',
no: 'stopp',
fi: 'pysäytä',
uk: 'стоп',
},
pause: {
es: 'pausa',
en: 'pause',
fr: 'pause',
de: 'Pause',
it: 'pausa',
pt: 'pausa',
nl: 'pauze',
pl: 'pauza',
cs: 'pauza',
sk: 'pauza',
ro: 'pauză',
hu: 'szünet',
el: 'παύση',
sv: 'paus',
da: 'pause',
no: 'pause',
fi: 'tauko',
uk: 'пауза',
},
previous: {
es: 'anterior',
en: 'previous',
fr: 'précédent',
de: 'Zurück',
it: 'precedente',
pt: 'anterior',
nl: 'vorige',
pl: 'poprzedni',
cs: 'předchozí',
sk: 'predchádzajúci',
ro: 'anterior',
hu: 'előző',
el: 'προηγούμενο',
sv: 'föregående',
da: 'forrige',
no: 'forrige',
fi: 'edellinen',
uk: 'попередній',
},
next: {
es: 'siguiente',
en: 'next',
fr: 'suivant',
de: 'Weiter',
it: 'successivo',
pt: 'seguinte',
nl: 'volgende',
pl: 'następny',
cs: 'další',
sk: 'ďalší',
ro: 'următor',
hu: 'következő',
el: 'επόμενο',
sv: 'nästa',
da: 'næste',
no: 'neste',
fi: 'seuraava',
uk: 'наступний',
},
update: {
es: 'actualizar',
en: 'update',
fr: 'mettre à jour',
de: 'Aktualisieren',
it: 'aggiorna',
pt: 'atualizar',
nl: 'bijwerken',
pl: 'aktualizuj',
cs: 'aktualizovat',
sk: 'aktualizovať',
ro: 'actualizează',
hu: 'frissítés',
el: 'ενημέρωση',
sv: 'uppdatera',
da: 'opdater',
no: 'oppdater',
fi: 'päivitä',
uk: 'оновити',
},
share: {
es: 'compartir',
en: 'share',
fr: 'partager',
de: 'Teilen',
it: 'condividi',
pt: 'partilhar',
nl: 'delen',
pl: 'udostępnij',
cs: 'sdílet',
sk: 'zdieľať',
ro: 'partajează',
hu: 'megosztás',
el: 'κοινοποίηση',
sv: 'dela',
da: 'del',
no: 'del',
fi: 'jaa',
uk: 'поділитися',
},
learn: {
es: 'aprender',
en: 'learn',
fr: 'apprendre',
de: 'Lernen',
it: 'impara',
pt: 'aprender',
nl: 'leren',
pl: 'nauka',
cs: 'učit se',
sk: 'učiť sa',
ro: 'învață',
hu: 'tanulás',
el: 'μάθηση',
sv: 'lär dig',
da: 'lær',
no: 'lær',
fi: 'opi',
uk: 'вчитися',
},
// pestañas del panel
tabProjects: {
es: 'projects',
en: 'projects',
fr: 'projets',
de: 'Projekte',
it: 'progetti',
pt: 'projetos',
nl: 'projecten',
pl: 'projekty',
cs: 'projekty',
sk: 'projekty',
ro: 'proiecte',
hu: 'projektek',
el: 'έργα',
sv: 'projekt',
da: 'projekter',
no: 'prosjekter',
fi: 'projektit',
uk: 'проєкти',
},
tabGuide: {
es: 'guía',
en: 'guide',
fr: 'guide',
de: 'Anleitung',
it: 'guida',
pt: 'guia',
nl: 'gids',
pl: 'przewodnik',
cs: 'průvodce',
sk: 'sprievodca',
ro: 'ghid',
hu: 'útmutató',
el: 'οδηγός',
sv: 'guide',
da: 'guide',
no: 'guide',
fi: 'opas',
uk: 'посібник',
},
tabIntro: {
es: 'intro',
en: 'intro',
fr: 'intro',
de: 'Intro',
it: 'intro',
pt: 'intro',
nl: 'intro',
pl: 'intro',
cs: 'úvod',
sk: 'úvod',
ro: 'intro',
hu: 'intro',
el: 'εισαγωγή',
sv: 'intro',
da: 'intro',
no: 'intro',
fi: 'intro',
uk: 'вступ',
},
tabPatterns: {
es: 'patterns',
en: 'patterns',
fr: 'motifs',
de: 'Patterns',
it: 'pattern',
pt: 'padrões',
nl: 'patronen',
pl: 'wzorce',
cs: 'vzory',
sk: 'vzory',
ro: 'tipare',
hu: 'minták',
el: 'μοτίβα',
sv: 'mönster',
da: 'mønstre',
no: 'mønstre',
fi: 'kuviot',
uk: 'патерни',
},
tabSounds: {
es: 'sonidos',
en: 'sounds',
fr: 'sons',
de: 'Klänge',
it: 'suoni',
pt: 'sons',
nl: 'geluiden',
pl: 'dźwięki',
cs: 'zvuky',
sk: 'zvuky',
ro: 'sunete',
hu: 'hangok',
el: 'ήχοι',
sv: 'ljud',
da: 'lyde',
no: 'lyder',
fi: 'äänet',
uk: 'звуки',
},
tabReference: {
es: 'referencia',
en: 'reference',
fr: 'référence',
de: 'Referenz',
it: 'riferimento',
pt: 'referência',
nl: 'referentie',
pl: 'odniesienie',
cs: 'reference',
sk: 'referencia',
ro: 'referință',
hu: 'referencia',
el: 'αναφορά',
sv: 'referens',
da: 'reference',
no: 'referanse',
fi: 'viite',
uk: 'довідник',
},
tabExport: {
es: 'exportar',
en: 'export',
fr: 'exporter',
de: 'Export',
it: 'esporta',
pt: 'exportar',
nl: 'exporteren',
pl: 'eksport',
cs: 'export',
sk: 'export',
ro: 'export',
hu: 'exportálás',
el: 'εξαγωγή',
sv: 'exportera',
da: 'eksport',
no: 'eksport',
fi: 'vie',
uk: 'експорт',
},
tabConsole: {
es: 'consola',
en: 'console',
fr: 'console',
de: 'Konsole',
it: 'console',
pt: 'consola',
nl: 'console',
pl: 'konsola',
cs: 'konzole',
sk: 'konzola',
ro: 'consolă',
hu: 'konzol',
el: 'κονσόλα',
sv: 'konsol',
da: 'konsol',
no: 'konsoll',
fi: 'konsoli',
uk: 'консоль',
},
tabSettings: {
es: 'ajustes',
en: 'settings',
fr: 'réglages',
de: 'Einstellungen',
it: 'impostazioni',
pt: 'definições',
nl: 'instellingen',
pl: 'ustawienia',
cs: 'nastavení',
sk: 'nastavenia',
ro: 'setări',
hu: 'beállítások',
el: 'ρυθμίσεις',
sv: 'inställningar',
da: 'indstillinger',
no: 'innstillinger',
fi: 'asetukset',
uk: 'налаштування',
},
tabFiles: {
es: 'archivos',
en: 'files',
fr: 'fichiers',
de: 'Dateien',
it: 'file',
pt: 'ficheiros',
nl: 'bestanden',
pl: 'pliki',
cs: 'soubory',
sk: 'súbory',
ro: 'fișiere',
hu: 'fájlok',
el: 'αρχεία',
sv: 'filer',
da: 'filer',
no: 'filer',
fi: 'tiedostot',
uk: 'файли',
},
// transporte
seekHint: {
es: 'click o arrastra para saltar a ese punto del loop',
en: 'click or drag to jump to that point of the loop',
fr: 'cliquez ou glissez pour sauter à ce point de la boucle',
de: 'Klicken oder ziehen, um zu diesem Punkt des Loops zu springen',
it: 'clicca o trascina per saltare a quel punto del loop',
pt: 'clica ou arrasta para saltar para esse ponto do loop',
nl: 'klik of sleep om naar dat punt van de loop te springen',
pl: 'kliknij lub przeciągnij, aby skoczyć do tego punktu pętli',
cs: 'kliknutím nebo tažením skočíte na dané místo smyčky',
sk: 'kliknutím alebo ťahaním skočíte na dané miesto slučky',
ro: 'clic sau trage pentru a sări la acel punct al buclei',
hu: 'kattints vagy húzd a hurok adott pontjára ugráshoz',
el: 'κλικ ή σύρσιμο για μετάβαση σε αυτό το σημείο του loop',
sv: 'klicka eller dra för att hoppa till den punkten i loopen',
da: 'klik eller træk for at hoppe til det punkt i loopet',
no: 'klikk eller dra for å hoppe til det punktet i loopen',
fi: 'klikkaa tai vedä hypätäksesi siihen kohtaan luuppia',
uk: 'клацніть або перетягніть, щоб перейти до цієї точки циклу',
},
resizeHint: {
es: 'arrastra para redimensionar',
en: 'drag to resize',
fr: 'glissez pour redimensionner',
de: 'Zum Anpassen ziehen',
it: 'trascina per ridimensionare',
pt: 'arrasta para redimensionar',
nl: 'sleep om te vergroten/verkleinen',
pl: 'przeciągnij, aby zmienić rozmiar',
cs: 'tažením změníte velikost',
sk: 'ťahaním zmeníte veľkosť',
ro: 'trage pentru redimensionare',
hu: 'húzd az átméretezéshez',
el: 'σύρετε για αλλαγή μεγέθους',
sv: 'dra för att ändra storlek',
da: 'træk for at ændre størrelse',
no: 'dra for å endre størrelse',
fi: 'vedä muuttaaksesi kokoa',
uk: 'перетягніть, щоб змінити розмір',
},
cycles: {
es: 'ciclos',
en: 'cycles',
fr: 'cycles',
de: 'Zyklen',
it: 'cicli',
pt: 'ciclos',
nl: 'cycli',
pl: 'cykle',
cs: 'cykly',
sk: 'cykly',
ro: 'cicluri',
hu: 'ciklusok',
el: 'κύκλοι',
sv: 'cykler',
da: 'cyklusser',
no: 'sykluser',
fi: 'syklit',
uk: 'цикли',
},
// projects
save: {
es: 'guardar',
en: 'save',
fr: 'enregistrer',
de: 'Speichern',
it: 'salva',
pt: 'guardar',
nl: 'opslaan',
pl: 'zapisz',
cs: 'uložit',
sk: 'uložiť',
ro: 'salvează',
hu: 'mentés',
el: 'αποθήκευση',
sv: 'spara',
da: 'gem',
no: 'lagre',
fi: 'tallenna',
uk: 'зберегти',
},
projectPlaceholder: {
es: 'nombre del proyecto (o carpeta/nombre)...',
en: 'project name (or folder/name)...',
fr: 'nom du projet (ou dossier/nom)...',
de: 'Projektname (oder Ordner/Name)...',
it: 'nome del progetto (o cartella/nome)...',
pt: 'nome do projeto (ou pasta/nome)...',
nl: 'projectnaam (of map/naam)...',
pl: 'nazwa projektu (lub folder/nazwa)...',
cs: 'název projektu (nebo složka/název)...',
sk: 'názov projektu (alebo priečinok/názov)...',
ro: 'numele proiectului (sau dosar/nume)...',
hu: 'projekt neve (vagy mappa/név)...',
el: 'όνομα έργου (ή φάκελος/όνομα)...',
sv: 'projektnamn (eller mapp/namn)...',
da: 'projektnavn (eller mappe/navn)...',
no: 'prosjektnavn (eller mappe/navn)...',
fi: 'projektin nimi (tai kansio/nimi)...',
uk: 'назва проєкту (або папка/назва)...',
},
copy: {
es: 'copiar',
en: 'copy',
fr: 'copier',
de: 'Kopieren',
it: 'copia',
pt: 'copiar',
nl: 'kopiëren',
pl: 'kopiuj',
cs: 'kopírovat',
sk: 'kopírovať',
ro: 'copiază',
hu: 'másolás',
el: 'αντιγραφή',
sv: 'kopiera',
da: 'kopiér',
no: 'kopier',
fi: 'kopioi',
uk: 'копіювати',
},
copied: {
es: 'copiado',
en: 'copied',
fr: 'copié',
de: 'Kopiert',
it: 'copiato',
pt: 'copiado',
nl: 'gekopieerd',
pl: 'skopiowano',
cs: 'zkopírováno',
sk: 'skopírované',
ro: 'copiat',
hu: 'másolva',
el: 'αντιγράφηκε',
sv: 'kopierad',
da: 'kopieret',
no: 'kopiert',
fi: 'kopioitu',
uk: 'скопійовано',
},
load: {
es: 'cargar',
en: 'load',
fr: 'charger',
de: 'Laden',
it: 'carica',
pt: 'carregar',
nl: 'laden',
pl: 'wczytaj',
cs: 'načíst',
sk: 'načítať',
ro: 'încarcă',
hu: 'betöltés',
el: 'φόρτωση',
sv: 'ladda',
da: 'indlæs',
no: 'last inn',
fi: 'lataa',
uk: 'завантажити',
},
read: {
es: 'leer',
en: 'read',
fr: 'lire',
de: 'Lesen',
it: 'leggi',
pt: 'ler',
nl: 'lezen',
pl: 'czytaj',
cs: 'číst',
sk: 'čítať',
ro: 'citește',
hu: 'olvasás',
el: 'ανάγνωση',
sv: 'läs',
da: 'læs',
no: 'les',
fi: 'lue',
uk: 'читати',
},
myProjects: {
es: 'mis proyectos',
en: 'my projects',
fr: 'mes projets',
de: 'meine Projekte',
it: 'i miei progetti',
pt: 'os meus projetos',
nl: 'mijn projecten',
pl: 'moje projekty',
cs: 'moje projekty',
sk: 'moje projekty',
ro: 'proiectele mele',
hu: 'projektjeim',
el: 'τα έργα μου',
sv: 'mina projekt',
da: 'mine projekter',
no: 'mine prosjekter',
fi: 'omat projektit',
uk: 'мої проєкти',
},
noProjects: {
es: 'Aún no hay proyectos. Escribe un nombre y pulsa guardar.',
en: 'No projects yet. Type a name and press save.',
fr: 'Pas encore de projets. Saisissez un nom et appuyez sur enregistrer.',
de: 'Noch keine Projekte. Namen eingeben und Speichern drücken.',
it: 'Ancora nessun progetto. Scrivi un nome e premi salva.',
pt: 'Ainda não há projetos. Escreve um nome e carrega em guardar.',
nl: 'Nog geen projecten. Typ een naam en druk op opslaan.',
pl: 'Brak projektów. Wpisz nazwę i naciśnij zapisz.',
cs: 'Zatím žádné projekty. Zadejte název a stiskněte uložit.',
sk: 'Zatiaľ žiadne projekty. Zadajte názov a stlačte uložiť.',
ro: 'Încă nu există proiecte. Scrie un nume și apasă salvează.',
hu: 'Még nincsenek projektek. Írj be egy nevet és nyomd meg a mentést.',
el: 'Δεν υπάρχουν έργα ακόμα. Γράψτε ένα όνομα και πατήστε αποθήκευση.',
sv: 'Inga projekt än. Skriv ett namn och tryck på spara.',
da: 'Ingen projekter endnu. Skriv et navn og tryk på gem.',
no: 'Ingen prosjekter ennå. Skriv et navn og trykk lagre.',
fi: 'Ei vielä projekteja. Kirjoita nimi ja paina tallenna.',
uk: 'Проєктів ще немає. Введіть назву й натисніть зберегти.',
},
previewTitle: {
es: 'vista previa',
en: 'preview',
fr: 'aperçu',
de: 'Vorschau',
it: 'anteprima',
pt: 'pré-visualização',
nl: 'voorbeeld',
pl: 'podgląd',
cs: 'náhled',
sk: 'náhľad',
ro: 'previzualizare',
hu: 'előnézet',
el: 'προεπισκόπηση',
sv: 'förhandsvisning',
da: 'forhåndsvisning',
no: 'forhåndsvisning',
fi: 'esikatselu',
uk: 'попередній перегляд',
},
loadInEditor: {
es: 'cargar en editor',
en: 'load in editor',
fr: "charger dans l'éditeur",
de: 'In Editor laden',
it: "carica nell'editor",
pt: 'carregar no editor',
nl: 'laden in editor',
pl: 'wczytaj do edytora',
cs: 'načíst do editoru',
sk: 'načítať do editora',
ro: 'încarcă în editor',
hu: 'betöltés a szerkesztőbe',
el: 'φόρτωση στον επεξεργαστή',
sv: 'ladda i editorn',
da: 'indlæs i editor',
no: 'last inn i editor',
fi: 'lataa editoriin',
uk: 'завантажити в редактор',
},
previewHelp: {
es: 'Haz click en el nombre de un tema para leer su código aquí. No se ejecuta ni toca lo que está sonando — solo «cargar» reemplaza el editor.',
en: 'Click a track name to read its code here. It does not run nor touch what is playing — only "load" replaces the editor.',
fr: "Cliquez sur le nom d'un morceau pour lire son code ici. Rien ne s'exécute — seul « charger » remplace l'éditeur.",
de: 'Klicke auf einen Tracknamen, um seinen Code hier zu lesen. Nichts wird ausgeführt — nur „Laden" ersetzt den Editor.',
it: "Clicca sul nome di un brano per leggerne il codice qui. Non viene eseguito — solo «carica» sostituisce l'editor.",
pt: 'Clica no nome de uma faixa para ler o seu código aqui. Nada é executado — só «carregar» substitui o editor.',
nl: 'Klik op een tracknaam om de code hier te lezen. Er wordt niets uitgevoerd — alleen "laden" vervangt de editor.',
pl: 'Kliknij nazwę utworu, aby przeczytać jego kod. Nic się nie uruchamia — tylko „wczytaj" zastępuje edytor.',
cs: 'Klikněte na název skladby a přečtěte si zde její kód. Nic se nespustí — jen „načíst" nahradí editor.',
sk: 'Kliknite na názov skladby a prečítajte si tu jej kód. Nič sa nespustí — len „načítať" nahradí editor.',
ro: 'Dă clic pe numele unei piese pentru a-i citi codul aici. Nimic nu rulează — doar „încarcă" înlocuiește editorul.',
hu: 'Kattints egy szám nevére, hogy itt elolvasd a kódját. Semmi sem fut le — csak a „betöltés" cseréli le a szerkesztőt.',
el: 'Κάντε κλικ στο όνομα ενός κομματιού για να δείτε τον κώδικά του εδώ. Δεν εκτελείται τίποτα — μόνο η «φόρτωση» αντικαθιστά τον επεξεργαστή.',
sv: 'Klicka på ett spårnamn för att läsa dess kod här. Inget körs — bara "ladda" ersätter editorn.',
da: 'Klik på et nummernavn for at læse dets kode her. Intet kører — kun "indlæs" erstatter editoren.',
no: 'Klikk på et spornavn for å lese koden her. Ingenting kjøres — bare "last inn" erstatter editoren.',
fi: 'Klikkaa kappaleen nimeä lukeaksesi sen koodin tässä. Mitään ei suoriteta — vain "lataa" korvaa editorin.',
uk: 'Клацніть назву треку, щоб прочитати його код тут. Нічого не виконується — лише «завантажити» замінює редактор.',
},
theme: {
es: 'tema',
en: 'theme',
fr: 'thème',
de: 'Thema',
it: 'tema',
pt: 'tema',
nl: 'thema',
pl: 'motyw',
cs: 'motiv',
sk: 'motív',
ro: 'temă',
hu: 'téma',
el: 'θέμα',
sv: 'tema',
da: 'tema',
no: 'tema',
fi: 'teema',
uk: 'тема',
},
language: {
es: 'idioma',
en: 'language',
fr: 'langue',
de: 'Sprache',
it: 'lingua',
pt: 'idioma',
nl: 'taal',
pl: 'język',
cs: 'jazyk',
sk: 'jazyk',
ro: 'limbă',
hu: 'nyelv',
el: 'γλώσσα',
sv: 'språk',
da: 'sprog',
no: 'språk',
fi: 'kieli',
uk: 'мова',
},
};
export const getLang = () => settingsMap.get().language || 'es';
export const translate = (key, lang) => S[key]?.[lang] ?? S[key]?.en ?? key;
// hook de React: re-renderiza al cambiar el idioma en settings
export function useT() {
const { language } = useSettings();
const lang = language || 'es';
return (key) => translate(key, lang);
}
export const setLanguage = (lang) => settingsMap.setKey('language', lang);