Files
cruciverba_1/webapp/lib/i18n.ts

339 lines
11 KiB
TypeScript

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<Locale, Dictionary> = {
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];
}