export const LOCALES = ["it", "en", "es"] as const; export type Locale = (typeof LOCALES)[number]; export function isLocale(value: string): value is Locale { return LOCALES.includes(value as Locale); } type Dictionary = { home: { badge: string; title: string; subtitle: string; requestTitle: string; requestText: string; structureTitle: string; structureText: string; structureChips: string[]; demoTitle: string; openDemo: string; openConfig: string; kpis: { words: string; intersections: string; rows: string; cols: string; }; }; newPage: { badge: string; title: string; subtitle: string; }; form: { topic: string; difficulty: string; initialWords: string; themedFillCount: string; targetEmptyRatio: string; seed: string; lexiconFile: string; submit: string; submitting: string; note: string; }; play: { badge: string; subtitle: string; loading: string; errorPrefix: string; overviewTitle: string; boardTitle: string; cluesTitle: string; instructions: string; actions: { newCrossword: string; viewSolution: string; }; stats: { words: string; intersections: string; filledCells: string; topicWords: string; }; }; solution: { badge: string; title: string; subtitle: string; solvedGridTitle: string; answersTitle: string; backToGame: string; answer: string; clue: string; }; player: { topic: string; difficulty: string; lexicon: string; }; clues: { across: string; down: string; }; }; const dictionaries: Record = { it: { home: { badge: "Alpha 01 Backoffice + Web UI", title: "Cruciverba su richiesta, pronti per essere giocati.", subtitle: "Questa prima interfaccia Next.js separa nettamente il motore Python dal frontend: il sito invia una richiesta JSON, il backend genera il cruciverba e la pagina finale lo rende giocabile in modo interattivo.", requestTitle: "Nuova richiesta", requestText: "Qui l'utente configurerà il cruciverba. Per ora il form prepara già la struttura della richiesta e apre la pagina del gioco reale.", structureTitle: "Perche questa struttura", structureText: "Il frontend non deve conoscere i dettagli dell'algoritmo: riceve solo il JSON finale del cruciverba e lo trasforma in esperienza giocabile, stampabile e riapribile.", structureChips: ["Next.js UI", "Backend JSON", "Motore Python separato", "PDF e mobile-ready"], demoTitle: "Stato demo", openDemo: "Apri demo giocabile", openConfig: "Vai alla configurazione", kpis: { words: "Parole", intersections: "Intersezioni", rows: "Righe", cols: "Colonne", }, }, newPage: { badge: "Configurazione", title: "Chiedi al motore un nuovo cruciverba.", subtitle: "Questa pagina e il punto naturale da cui il sito invierà al backend la request JSON del contratto che abbiamo definito.", }, form: { topic: "Topic", difficulty: "Difficolta", initialWords: "Parole seme", themedFillCount: "Parole filler in tema", targetEmptyRatio: "Rapporto vuoti", seed: "Seed", lexiconFile: "Lessico runtime", submit: "Genera cruciverba", submitting: "Preparazione...", note: "La richiesta ora chiama davvero il motore Python e apre il cruciverba generato.", }, play: { badge: "Cruciverba interattivo", subtitle: "Questa pagina carica il JSON reale generato dal motore Python del backoffice.", loading: "Generazione in corso, sto caricando il cruciverba...", errorPrefix: "Errore di caricamento", overviewTitle: "Schema pronto per essere giocato", boardTitle: "Griglia di gioco", cluesTitle: "Definizioni", instructions: "Clicca una casella, poi scrivi da tastiera. Le definizioni restano leggibili nella colonna laterale.", actions: { newCrossword: "Nuovo cruciverba", viewSolution: "Vedi soluzioni", }, stats: { words: "Parole", intersections: "Intersezioni", filledCells: "Caselle piene", topicWords: "Parole in tema", }, }, solution: { badge: "Pagina soluzioni", title: "Controllo finale del cruciverba", subtitle: "Qui trovi la griglia compilata e l'elenco completo delle risposte collegate alle definizioni.", solvedGridTitle: "Griglia risolta", answersTitle: "Elenco soluzioni", backToGame: "Torna al gioco", answer: "Soluzione", clue: "Definizione", }, player: { topic: "Topic", difficulty: "Difficolta", lexicon: "Lessico", }, clues: { across: "Orizzontali", down: "Verticali", }, }, en: { home: { badge: "Alpha 01 Backoffice + Web UI", title: "Crosswords on demand, ready to be played.", subtitle: "This first Next.js interface clearly separates the Python engine from the frontend: the site sends a JSON request, the backend generates the crossword, and the final page turns it into an interactive experience.", requestTitle: "New request", requestText: "This is where the user configures the crossword. The form already prepares the request structure and opens the real game page.", structureTitle: "Why this structure", structureText: "The frontend should not know the details of the algorithm: it only receives the final crossword JSON and turns it into a playable, printable and reusable experience.", structureChips: ["Next.js UI", "JSON backend", "Separate Python engine", "PDF and mobile-ready"], demoTitle: "Demo status", openDemo: "Open playable demo", openConfig: "Go to configuration", kpis: { words: "Words", intersections: "Intersections", rows: "Rows", cols: "Columns", }, }, newPage: { badge: "Configuration", title: "Ask the engine for a new crossword.", subtitle: "This page is the natural place from which the site sends the JSON request defined in our contract to the backend.", }, form: { topic: "Topic", difficulty: "Difficulty", initialWords: "Seed words", themedFillCount: "Themed filler words", targetEmptyRatio: "Empty ratio", seed: "Seed", lexiconFile: "Runtime lexicon", submit: "Generate crossword", submitting: "Preparing...", note: "The request now calls the real Python engine and opens the generated crossword.", }, play: { badge: "Interactive crossword", subtitle: "This page loads the real JSON generated by the Python backoffice engine.", loading: "Generation in progress, loading crossword...", errorPrefix: "Loading error", overviewTitle: "Crossword ready to be played", boardTitle: "Game grid", cluesTitle: "Clues", instructions: "Click a cell, then type from the keyboard. The clues stay readable in the side panel.", actions: { newCrossword: "New crossword", viewSolution: "View solutions", }, stats: { words: "Words", intersections: "Intersections", filledCells: "Filled cells", topicWords: "Theme words", }, }, solution: { badge: "Solutions page", title: "Final crossword check", subtitle: "Here you can inspect the solved grid and the full list of answers tied to each clue.", solvedGridTitle: "Solved grid", answersTitle: "Answer key", backToGame: "Back to game", answer: "Answer", clue: "Clue", }, player: { topic: "Topic", difficulty: "Difficulty", lexicon: "Lexicon", }, clues: { across: "Across", down: "Down", }, }, es: { home: { badge: "Alpha 01 Backoffice + Web UI", title: "Crucigramas a pedido, listos para jugar.", subtitle: "Esta primera interfaz en Next.js separa claramente el motor Python del frontend: el sitio envia una solicitud JSON, el backend genera el crucigrama y la pagina final lo convierte en una experiencia interactiva.", requestTitle: "Nueva solicitud", requestText: "Aqui el usuario configura el crucigrama. El formulario ya prepara la estructura de la solicitud y abre la pagina del juego real.", structureTitle: "Por que esta estructura", structureText: "El frontend no debe conocer los detalles del algoritmo: solo recibe el JSON final del crucigrama y lo transforma en una experiencia jugable, imprimible y reutilizable.", structureChips: ["Next.js UI", "Backend JSON", "Motor Python separado", "PDF y movil listos"], demoTitle: "Estado de la demo", openDemo: "Abrir demo jugable", openConfig: "Ir a configuracion", kpis: { words: "Palabras", intersections: "Intersecciones", rows: "Filas", cols: "Columnas", }, }, newPage: { badge: "Configuracion", title: "Pide al motor un nuevo crucigrama.", subtitle: "Esta pagina es el punto natural desde el que el sitio enviara al backend la solicitud JSON del contrato que hemos definido.", }, form: { topic: "Tema", difficulty: "Dificultad", initialWords: "Palabras semilla", themedFillCount: "Palabras de relleno tematico", targetEmptyRatio: "Proporcion de vacios", seed: "Semilla", lexiconFile: "Lexico activo", submit: "Generar crucigrama", submitting: "Preparando...", note: "La solicitud ahora llama de verdad al motor Python y abre el crucigrama generado.", }, play: { badge: "Crucigrama interactivo", subtitle: "Esta pagina carga el JSON real generado por el motor Python del backoffice.", loading: "Generacion en curso, cargando crucigrama...", errorPrefix: "Error de carga", overviewTitle: "Crucigrama listo para jugar", boardTitle: "Cuadricula de juego", cluesTitle: "Definiciones", instructions: "Haz clic en una casilla y escribe desde el teclado. Las definiciones siguen legibles en la columna lateral.", actions: { newCrossword: "Nuevo crucigrama", viewSolution: "Ver soluciones", }, stats: { words: "Palabras", intersections: "Intersecciones", filledCells: "Casillas llenas", topicWords: "Palabras tematicas", }, }, solution: { badge: "Pagina de soluciones", title: "Revision final del crucigrama", subtitle: "Aqui puedes ver la cuadricula completa y la lista de respuestas asociadas a cada definicion.", solvedGridTitle: "Cuadricula resuelta", answersTitle: "Listado de soluciones", backToGame: "Volver al juego", answer: "Solucion", clue: "Definicion", }, player: { topic: "Tema", difficulty: "Dificultad", lexicon: "Lexico", }, clues: { across: "Horizontales", down: "Verticales", }, }, }; export function getDictionary(locale: Locale): Dictionary { return dictionaries[locale]; }